error-handling について
開発者向けのエラー処理スキルです。JavaScriptの同期・非同期処理やレイヤー間の伝播、復旧、プロセス終了の指針を扱います。
主な機能
- 01プログラミングエラーと運用エラーの区別
- 02同期・非同期例外の処理
- 03DomainErrorによる業務エラー表現
- 04再試行とプロセスレベルのハンドラー
- 05グレースフルシャットダウン
活用シーン
- 01APIの業務エラーを構造化コードに変換
- 02一時的なネットワーク障害の再試行
- 03未捕捉例外や未処理の拒否の調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ metarhia/ metaskills --skill error-handlingインストール後、Claude Code で/error-handlingを入力して読み込まれたか確認します。
詳細
エラー処理
エラーの分類
- プログラミングエラー:バグ(TypeError、ReferenceError、アサーション失敗など)。コードを修正してください。捕捉して処理を続行しないでください
- 運用エラー:想定される失敗(ネットワークタイムアウト、ファイルが見つからない、入力が無効など)。復旧、エスカレーション、ユーザーへの通知、ログ記録などにより適切に処理してください
同期エラー処理
try {
const result = parse(input);
return result;
} catch (error) {
console.error({ error });
return defaultValue;
}
非同期エラー処理
try {
const data = await fetchData(url);
return data;
} catch (error) {
if (error.code === 'ECONNREFUSED') return fallback();
throw err;
}
DomainError
API レイヤーで使用する、コードを含む構造化されたビジネスエラーです。
({
method: async ({ email }) => {
const user = await domain.user.findByEmail(email);
if (!user) return new DomainError('ENOTFOUND');
return user;
},
errors: { ENOTFOUND: 'User not found' },
});
ドメインレイヤーでは通常のエラーをスローし、必要に応じて API レイヤーで DomainError に変換します。
レイヤー間でのエラー伝播
domain throws Error → API catches → returns DomainError(code) → client gets structured error
内部エラーの詳細をクライアントに公開しないでください。既知のエラーコードまたは一般的なエラーに変換してください。
規約
- プログラミングエラーと運用エラーを区別し、復旧するのは運用エラーのみとします
- API メソッドでのビジネス検証には DomainError を使用し、プログラミングバグにはエラーをスローします
- エラーを黙って握りつぶさないでください。必ずログに記録するか、伝播させてください
- グレースフルシャットダウンを実装してください。新しい接続の受付を停止し、既存の接続を処理し、リソースを解放します
- 一時的な障害(ネットワーク、DB 接続)には、指数バックオフを使用して再試行してください
- プロセスレベルで
uncaughtExceptionとunhandledRejectionの両方を必ず処理してください
再試行パターン
const retry = async (fn, { attempts = 3, delay = 1000 } = {}) => {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
await new Promise((r) => setTimeout(r, delay * (i + 1)));
}
}
};
プロセスレベルのハンドラー
process.on('uncaughtException', (error) => {
console.error('Uncaught:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
console.error('Unhandled rejection:', reason);
});
uncaughtException が発生したらログに記録して終了してください(状態が破損している可能性があります)。unhandledRejection をログに記録し、修正すべきバグとして扱ってください。
グレースフルシャットダウン
接続を追跡し、終了前に処理を完了してください。
const connections = new Map();
server.on('connection', (conn) => {
const res = null;
connections.set(conn, res);
conn.on('close', () => connections.delete(conn));
});
const shutdown = () => {
server.close(() => {
freeResources();
process.exit(0);
});
for (const [conn, res] of connections) {
if (res) res.end('Server shutting down');
conn.destroy();
}
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
コメント
Markdown 対応