Pola Error Handling dengan Claude Code: klasifikasikan kegagalan di boundary
Panduan Claude Code untuk error handling: validasi API, API eksternal, job batch, TypeScript, dan strategi recovery.
Error handling bukan berarti menambah try-catch di semua tempat. Kalau kita hanya meminta Claude Code “tolong perbaiki error ini”, hasilnya sering terlalu umum: semua kegagalan menjadi 500, log sulit dicari, dan tidak jelas apakah user harus memperbaiki input, menunggu layanan eksternal, atau menjalankan ulang job.
Pola yang lebih kuat adalah mengklasifikasikan kegagalan di boundary dan menulis jalur recovery secara eksplisit. Boundary adalah titik ketika aplikasi bertemu sesuatu yang tidak sepenuhnya kita kontrol: request body API, payment provider, CRM, layanan email, scheduled job, import CSV, atau layar frontend. Saat boundary jelas, Claude Code bisa membuat patch yang lebih aman: validasi mengembalikan 400, kegagalan eksternal sementara bisa retry, dan batch failure disimpan agar dapat dijalankan ulang.
Artikel ini dibuat untuk pemula, tetapi tetap dekat dengan kebutuhan produksi. Ada contoh TypeScript yang bisa langsung dijalankan, tiga use case, jebakan umum, prompt review Claude Code, referensi resmi, dan CTA untuk mengubah pola ini menjadi materi training atau produk.
Mulai dari recovery, bukan nama class
Sebelum memilih nama AppError atau DomainError, tanyakan dulu: setelah gagal, sistem harus melakukan apa?
| Boundary | Contoh | Respons | Recovery |
|---|---|---|---|
| Validasi API | Email salah, angka di luar rentang, JSON rusak | 400 | User memperbaiki input |
| API eksternal | Payment, CRM, email, analytics down | 502 atau 503 | Retry, pause, tampilkan error sementara |
| Job/Batch | Laporan malam, import CSV, kirim notifikasi | Catatan gagal internal | Rerun aman, failure queue, alert |
message yang mudah dibaca saja tidak cukup. Program perlu field seperti kind, code, retryable, dan status untuk mengambil keputusan. Claude Code juga lebih mudah mereview perilaku dibanding menebak maksud dari teks bebas.
Contoh TypeScript yang bisa dijalankan
Contoh berikut berjalan di Node.js 18 atau lebih baru. Kita memakai tsx untuk menjalankan TypeScript langsung. Tidak ada panggilan API nyata, karena fetcher disuntikkan agar failure path mudah diuji.
npm install -D tsx typescript
npx tsx error-patterns-demo.ts
type Kind = "validation" | "external" | "job";
type AppError = { kind: Kind; code: string; message: string; retryable: boolean; status: number; detail?: unknown };
type Result<T> = { ok: true; value: T } | { ok: false; error: AppError };
const ok = <T>(value: T): Result<T> => ({ ok: true, value });
const fail = <T>(error: AppError): Result<T> => ({ ok: false, error });
function parseUser(body: unknown): Result<{ email: string; age: number }> {
if (typeof body !== "object" || body === null) {
return fail({ kind: "validation", code: "BODY_REQUIRED", message: "body must be an object", retryable: false, status: 400 });
}
const data = body as Record<string, unknown>;
if (typeof data.email !== "string" || !data.email.includes("@")) {
return fail({ kind: "validation", code: "EMAIL_INVALID", message: "email is invalid", retryable: false, status: 400 });
}
if (typeof data.age !== "number" || data.age < 13) {
return fail({ kind: "validation", code: "AGE_INVALID", message: "age must be 13 or greater", retryable: false, status: 400 });
}
return ok({ email: data.email, age: data.age });
}
async function callPartner(fetcher: () => Promise<Response>): Promise<Result<{ id: string }>> {
try {
const response = await fetcher();
if (!response.ok) {
return fail({ kind: "external", code: "PARTNER_HTTP", message: `partner returned ${response.status}`, retryable: response.status >= 500, status: 503 });
}
const json = (await response.json()) as { id?: unknown };
if (typeof json.id !== "string") {
return fail({ kind: "external", code: "PARTNER_PAYLOAD", message: "partner payload is invalid", retryable: false, status: 502, detail: json });
}
return ok({ id: json.id });
} catch (error) {
return fail({ kind: "external", code: "PARTNER_UNREACHABLE", message: "partner is unreachable", retryable: true, status: 503, detail: error });
}
}
async function runJob<T>(name: string, work: () => Promise<T>, retries = 2): Promise<Result<T>> {
for (let attempt = 1; attempt <= retries + 1; attempt += 1) {
try {
return ok(await work());
} catch (error) {
if (attempt <= retries) continue;
return fail({ kind: "job", code: "JOB_FAILED", message: `${name} failed`, retryable: true, status: 500, detail: { attempt, error } });
}
}
return fail({ kind: "job", code: "JOB_FAILED", message: `${name} failed`, retryable: true, status: 500 });
}
console.log(parseUser({ email: "bad", age: 10 }));
console.log(await callPartner(async () => new Response("down", { status: 503 })));
console.log(await runJob("daily-report", async () => ({ exportedRows: 42 })));
Use case 1: validasi input API
Kegagalan validasi biasanya bisa diperbaiki oleh user. Email tidak valid, usia terlalu kecil, JSON bukan object, atau plan tidak dikenal bukanlah crash server. Di contoh, parseUser mengklasifikasikan kegagalan sebagai validation, mengembalikan 400, dan memberi retryable: false.
Hasilnya lebih mudah dipakai. Frontend bisa menyorot field yang salah, backend bisa mencari log dengan EMAIL_INVALID, dan Claude Code bisa menambah test untuk jalur gagal. Untuk memperluasnya, baca juga API testing dengan Claude Code dan strategi testing Claude Code.
Use case 2: kegagalan API eksternal
Layanan eksternal bisa gagal walau kode kita benar. Payment, email, CRM, spreadsheet, dan analytics bisa timeout, rate limit, atau memberi payload aneh. Hal paling penting adalah memisahkan failure yang layak retry dari failure yang tidak boleh retry.
Di contoh, 5xx dianggap retryable: true, tetapi payload dengan bentuk salah menjadi retryable: false. Me-retry payload rusak hanya membuat queue dan log penuh. Untuk semantik respons HTTP, lihat MDN Response.ok. Jika memakai Express, cek juga Express error handling.
Use case 3: kegagalan job dan batch
Job sering gagal ketika user tidak lagi melihat layar. Laporan malam, import CSV, invoice, dan pengiriman notifikasi perlu meninggalkan jejak yang bisa dipakai untuk rerun. runJob menyimpan nama job, attempt terakhir, dan apakah error bisa diulang. Di produksi, data ini sebaiknya masuk ke structured log, failure queue, admin screen, atau alert.
Pertanyaan terpenting adalah idempotency: apakah aman dijalankan ulang? Jika import CSV membuat baris duplikat, retry menjadi berbahaya. Jika job billing menagih dua kali, error handling berubah menjadi incident. Minta Claude Code mereview replay safety, bukan hanya syntax.
Jebakan umum
Jebakan pertama adalah catch { return null; }, karena bukti kegagalan hilang. Jebakan kedua adalah membocorkan stack trace, nama SQL, environment variable, path internal, atau potongan token ke user. Untuk sisi keamanan, baca audit keamanan dengan Claude Code. Jebakan ketiga adalah retry semua error. Validation error dan payload salah tidak akan membaik hanya karena menunggu.
Jebakan lain adalah membuat tipe rapi tanpa aturan tim. TypeScript narrowing dan discriminated union membantu, tetapi besok seseorang bisa kembali menulis throw new Error("failed"). Pakai referensi resmi TypeScript Narrowing, lalu tulis aturan boundary di AGENTS.md atau CLAUDE.md.
Prompt review untuk Claude Code
Review error handling di PR ini.
Cek:
1. Failure diklasifikasikan di boundary API input, API eksternal, dan job/batch.
2. Error yang bisa diperbaiki user tidak menjadi 500.
3. retryable dan non-retryable dipisahkan.
4. Response tidak membocorkan stack trace, SQL, secret, atau path internal.
5. Log menyimpan code, kind, attempt, dan cause yang aman.
6. Ada minimal 3 test untuk failure path.
Berikan patch paling kecil yang aman dan test pendukungnya.
Untuk mengunci failure path, Node.js test runner sering cukup. Pengaturan dan project memory Claude Code bisa dicek dari Claude Code overview. Jika ingin mulai dari test, lanjutkan ke TDD dengan Claude Code dan teknik debugging Claude Code.
CTA dan hasil uji Masa
Error handling cocok untuk training tim karena menghubungkan API, log, security, test, dan operasi. Untuk mulai sendiri, ambil cheat sheet gratis. Jika butuh prompt, checklist review, dan template siap tim, lihat produk. Untuk memperbaiki repository yang sudah berjalan, halaman training dan konsultasi Claude Code bisa membantu memetakan boundary API, integrasi eksternal, dan batch.
Di workflow Masa, menyatukan validation, external API, dan batch failure ke bentuk mirip AppError membuat prompt jauh lebih pendek. Dulu instruksinya “perbaiki error ini”; sekarang bisa “validation return 400, external pakai retryable, job simpan attempt”. Review pun fokus ke response, log, dan test. Ini bukan arsitektur ajaib, tetapi jauh lebih mudah dioperasikan daripada banyak 500 anonim.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Permission receipt Claude Code: mencatat scope, bukti, dan rollback
Pola permission receipt untuk Claude Code: aksi yang diizinkan, batas approval, command verifikasi, rollback, dan cek CTA revenue.
Agent Harness Aman untuk Claude Code dan Codex: Permission, Verifikasi, dan Rollback
Rancang Agent Harness praktis untuk Claude Code dan Codex dengan policy, plan, verification, dan recovery layer.
Subagent Claude Code: panduan praktis untuk delegasi artikel dan kode
Panduan subagent Claude Code untuk membagi pekerjaan artikel dan kode: aturan delegasi, prompt, risiko, dan checklist.