Tips & Tricks

7 Insiden Produksi Nyata dengan Claude Code: Pemulihan Lengkap dengan RCA & Pencegahan

7 insiden produksi nyata dengan Claude Code: kebocoran API key, penghapusan DB, ledakan tagihan, dan gangguan layanan — dengan analisis akar masalah dan strategi pencegahan.

“Claude Code memang praktis, tapi saya takut menggunakannya di produksi” — banyak developer merasakan hal ini. Dan intuisi itu benar.

Claude Code beroperasi pada filesystem dan shell Anda dengan hak istimewa lebih tinggi dari IDE biasa. Bersamaan dengan itu, terus-menerus mengklik tombol approve mengurangi kewaspadaan — kelemahan psikologis manusia yang sudah dikenal. Ketika dua faktor ini bergabung, insiden produksi pun terjadi.

Artikel ini mendokumentasikan 7 insiden produksi nyata yang melibatkan Claude Code, lengkap dengan penyebab, cakupan dampak, prosedur pemulihan, RCA (analisis akar masalah), dan strategi pencegahan. Bacalah sebelum kecelakaan terjadi di organisasi Anda.


Insiden 1: Kebocoran API Key → Tagihan Tidak Sah Rp 42 Juta

Kronologi

09:12  Claude Code diperintahkan: "commit .env untuk melewatkan environment variable ke CI"
09:13  git add .env && git push dieksekusi tanpa persetujuan (daftar allow terlalu permisif)
09:14  GitHub Secret Scanning mendeteksinya, notifikasi email terkirim
09:31  Crawler AWS mendeteksi kunci OpenAI, penggunaan tidak sah dimulai
11:00  Tagihan Rp 42 juta dikonfirmasi di dashboard OpenAI

Prosedur Pemulihan

# Langkah 1: Segera cabut API key (prioritas utama — dalam 5 menit)
# → Cabut kunci di dashboard OpenAI / setiap layanan

# Langkah 2: Hapus .env sepenuhnya dari riwayat git
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch .env" \
  --prune-empty --tag-name-filter cat -- --all

# Langkah 3: Force push semua branch
git push origin --force --all
git push origin --force --tags

# Langkah 4: Tambahkan ke .gitignore untuk mencegah pengulangan
echo ".env" >> .gitignore
git add .gitignore && git commit -m "security: add .env to gitignore"

# Langkah 5: Buat API key baru dan atur di .env

RCA

  • Penyebab langsung: settings.json memiliki Bash(git add*) di daftar allow, memungkinkan eksekusi tanpa konfirmasi
  • Akar masalah: Konfigurasi keamanan diprioritaskan belakangan demi kode produk

Pencegahan

// .claude/settings.json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash(git add*)",
      "hooks": [{
        "type": "command",
        "command": "git diff --cached --name-only | grep -E '\\.env' && echo '🚨 .env terdeteksi! Membatalkan.' && exit 1 || exit 0"
      }]
    }]
  }
}

Insiden 2: rm -rf Menghapus Seluruh Proyek

Kronologi

14:33  Perintah: "bersihkan node_modules dan instal ulang"
14:33  Claude Code mengeksekusi rm -rf node_modules (normal sampai di sini)
14:34  Perintah lanjutan "hapus juga file build lama" → rm -rf dist/ dieksekusi
14:34  Kesalahan interpretasi path: rm -rf dist /src dijalankan (spasi sebagai pemisah)
14:35  Direktori src/ terhapus sepenuhnya. File config di luar git juga hilang
14:40  git checkout . memulihkan file yang terlacak, tapi .env dan config lokal hilang selamanya

Prosedur Pemulihan

# File yang dilacak git dapat dipulihkan
git checkout .
git clean -fd   # Hapus file berlebih

# Cari file yang dihapus di git stash atau reflog
git stash list
git reflog

# File di luar git (.env dll.) harus dipulihkan dari backup
# → Jika tidak ada backup, konfigurasi ulang dari awal

RCA

  • Penyebab langsung: Path yang mengandung spasi tidak dikutip dengan benar, menghasilkan rm -rf dist /src
  • Akar masalah: rm -rf ada di allow bukan di ask

Pencegahan

{
  "permissions": {
    "deny": ["Bash(rm -rf ~*)", "Bash(rm -rf /*)"],
    "ask": ["Bash(rm*)"]
  },
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash(rm*)",
      "hooks": [{
        "type": "command",
        "command": "echo '⚠️ Perintah hapus terdeteksi. Target: $CLAUDE_TOOL_INPUT_COMMAND\nEksekusi dalam 5 detik. Ctrl+C untuk membatalkan.' && sleep 5"
      }]
    }]
  }
}

Insiden 3: git push --force Menghapus Commit 3 Rekan Kerja

Kronologi

16:00  Perintah: "ada konflik dengan remote. Prioritaskan lokal dan timpa"
16:01  git push --force origin main dieksekusi
16:01  ~200 baris kode yang di-commit 3 rekan kerja hari itu terhapus
16:10  Rekan A di Slack: "Hei, commit saya hilang"
16:15  Penyebab teridentifikasi. A dan B masih punya salinan lokal, tapi C sudah menghapus miliknya — hilang selamanya

Prosedur Pemulihan

# Cari commit yang hilang di reflog (jalankan di mesin yang melakukan push)
git reflog | head -30
# → Contoh: abc1234 HEAD@{3}: commit sebelum --force push

# Pulihkan commit yang hilang
git checkout -b recovery abc1234
git push origin recovery

# Merge ke main dan bersihkan
git checkout main
git merge recovery --no-ff
git push origin main

RCA

  • Penyebab langsung: Niat untuk menyelesaikan konflik diinterpretasikan sebagai --force bukan --force-with-lease
  • Akar masalah: Force push ke branch main tidak ada di daftar deny

Pencegahan

{
  "permissions": {
    "deny": [
      "Bash(git push --force *main*)",
      "Bash(git push --force *master*)",
      "Bash(git push -f *main*)"
    ]
  }
}
<!-- CLAUDE.md -->
## Aturan Git
- `git push --force` dilarang
- Gunakan `git push --force-with-lease` untuk penyelesaian konflik
- Selalu minta konfirmasi pengguna sebelum push ke main/master

Insiden 4: Migrasi DB Gagal Menghapus 40.000 Record Produksi

Kronologi

10:00  Perintah: "jalankan migrasi untuk menambahkan kolom phone_number ke tabel users"
10:01  Claude Code menghasilkan dan mengeksekusi skrip migrasi
10:01  Bug dalam skrip menyebabkan migrasi terbalik (dengan DROP COLUMN) berjalan
10:02  Kolom users.email (NOT NULL) dihapus dari produksi
10:02  Semua API mengembalikan error 500, layanan sepenuhnya mati
10:05  Insiden dikenali, penyelidikan akar masalah dimulai
10:30  Pemulihan dari snapshot hari sebelumnya (data satu hari hilang)

Prosedur Pemulihan

# 1. Segera masukkan layanan ke mode pemeliharaan
# nginx: return 503;  atau  Vercel: halaman pemeliharaan

# 2. Periksa status DB saat ini
psql $DATABASE_URL -c "\d users"

# 3. Pulihkan dari backup (untuk RDS)
aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier mydb \
  --target-db-instance-identifier mydb-restored \
  --restore-time 2026-04-17T23:00:00Z

# 4. Jika kolom masih ada, tambahkan secara manual
ALTER TABLE users ADD COLUMN email VARCHAR(255);
UPDATE users SET email = '(pemulihan diperlukan)' WHERE email IS NULL;

# 5. Pulihkan layanan

RCA

  • Penyebab langsung: Generator skrip migrasi mengacaukan migrasi up/down
  • Akar masalah: Migrasi dijalankan di produksi tanpa mengujinya di lingkungan staging

Pencegahan

<!-- CLAUDE.md -->
## Aturan Wajib Migrasi DB
1. Selalu uji di lingkungan staging sebelum menerapkan ke produksi
2. Selalu buat backup manual sebelum migrasi:
   pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql
3. Skrip yang mengandung DROP COLUMN / TRUNCATE / DELETE (tanpa WHERE) harus
   selalu dikonfirmasi pengguna sebelum dieksekusi
4. Jika menggunakan DATABASE_URL produksi, tampilkan 'Menulis ke DB PRODUKSI. Lanjutkan?' sebelum eksekusi

Insiden 5: Panggilan API Tak Terbatas Menghasilkan Rp 11,5 Juta Semalam

Kronologi

23:00  Perintah: "coba ulang otomatis saat ada error" dan pemrosesan batch dimulai
23:01  API eksternal mulai mengembalikan 503
23:01  Logika retry berjalan tanpa batas, memukul API setiap detik
07:00  Pagi berikutnya, notifikasi Anthropic: "Penggunaan mendekati batas"
07:05  Ditemukan 28.000 panggilan API dan tagihan Rp 11,5 juta

Prosedur Pemulihan

# 1. Segera hentikan prosesnya
pkill -f "node batch-process.js"

# 2. Tinjau tagihan dan hubungi dukungan Anthropic
# → Komunikasi yang tulus dapat menghasilkan pengembalian dana sebagian

# 3. Atur peringatan penggunaan
# Konsol Anthropic → Usage Limits → Atur peringatan anggaran bulanan

Pencegahan

// utils/retry.ts — selalu gunakan utilitas ini
export async function withRetry<T>(
  fn: () => Promise<T>,
  { maxAttempts = 3, baseDelayMs = 1000, maxDelayMs = 30000 } = {}
): Promise<T> {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (attempt === maxAttempts) throw err;
      const delay = Math.min(
        baseDelayMs * 2 ** (attempt - 1) + Math.random() * 500,
        maxDelayMs
      );
      console.warn(`Retry ${attempt}/${maxAttempts} in ${Math.round(delay)}ms`);
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error("unreachable");
}
<!-- CLAUDE.md -->
## Aturan Panggilan API
- Maksimal 3 percobaan ulang dengan exponential backoff wajib
- while(true) + panggilan API dilarang keras
- Pekerjaan batch harus memiliki batas jumlah yang eksplisit
- Jalankan smoke test --dry-run sebelum eksekusi produksi

Insiden 6: Dependensi Rusak Setelah Deploy Mematikan Layanan

Kronologi

15:00  Perintah: "perbarui paket ke versi terbaru"
15:01  npm update dieksekusi, package-lock.json berubah signifikan
15:05  Build lokal berhasil
15:10  Deploy ke produksi
15:12  Ketidakcocokan versi mayor dalam dependensi produksi, startup gagal
15:12  Error 503, layanan sepenuhnya mati
15:30  Rollback ke versi sebelumnya selesai

Prosedur Pemulihan

# Untuk Vercel / Cloudflare Pages: aktifkan kembali deployment sebelumnya segera
# → Rollback satu klik dari dashboard

# Kembalikan kode dengan git revert
git revert HEAD~1
git push

# Pulihkan package-lock.json ke versi sebelumnya
git checkout HEAD~1 -- package-lock.json
npm ci  # Menggunakan package-lock.json secara ketat

RCA

  • Penyebab langsung: npm update mengupgrade versi mayor, menyebabkan breaking change
  • Akar masalah: Verifikasi deployment di lingkungan staging dilewati

Pencegahan

<!-- CLAUDE.md -->
## Aturan Manajemen Paket
- npm update dilarang (npm update --save-dev boleh dengan syarat)
- Upgrade versi mayor paket memerlukan konfirmasi pengguna
- Selalu verifikasi perilaku di staging sebelum deploy
- Pantau log error selama 5 menit setelah deployment produksi

Insiden 7: Izin yang Salah Konfigurasi Mengekspos Data Semua Pengguna

Kronologi

11:00  Perintah: "tambahkan daftar pengguna ke endpoint API panel admin"
11:05  /api/admin/users diimplementasikan tanpa pemeriksaan autentikasi
11:10  Deploy ke produksi
11:10  Informasi pribadi semua pengguna dapat diakses siapa saja
13:30  Alat audit keamanan mendeteksi endpoint yang tidak terautentikasi
13:35  Endpoint segera dinonaktifkan

Prosedur Pemulihan

# 1. Segera nonaktifkan endpoint yang terpengaruh
# nginx: location /api/admin { return 403; }

# 2. Tinjau log akses untuk menentukan cakupan paparan
grep "/api/admin/users" /var/log/nginx/access.log | \
  awk '{print $1}' | sort | uniq -c | sort -rn

# 3. Beritahu pengguna yang terdampak (periksa persyaratan UU Perlindungan Data Pribadi)

# 4. Tambahkan middleware autentikasi dan deploy ulang

RCA

  • Penyebab langsung: Instruksi “tambahkan ke panel admin” tidak menyampaikan persyaratan autentikasi
  • Akar masalah: Persyaratan keamanan tidak didokumentasikan dalam CLAUDE.md

Pencegahan

<!-- CLAUDE.md -->
## Persyaratan Keamanan API
- Endpoint /api/admin/* harus selalu mengimplementasikan autentikasi admin
- Endpoint /api/user/* harus selalu mengimplementasikan autentikasi login
- Hanya endpoint /api/public/* yang dapat diakses tanpa autentikasi
- Saat menambahkan API baru, komentari secara eksplisit tingkat autentikasi yang diperlukan

Alur Respons Insiden Umum (Template Postmortem)

# Postmortem: [Judul Insiden]

## Ringkasan
- Terjadi pada: YYYY-MM-DD HH:MM
- Terdeteksi pada: YYYY-MM-DD HH:MM
- Diselesaikan pada: YYYY-MM-DD HH:MM
- Dampak: (jumlah pengguna / fitur / durasi)
- Tingkat keparahan: P0/P1/P2/P3

## Kronologi
| Waktu | Kejadian |
|-------|----------|
| HH:MM | Insiden terjadi |
| HH:MM | Terdeteksi |
| HH:MM | Respons dimulai |
| HH:MM | Akar masalah teridentifikasi |
| HH:MM | Pemulihan selesai |

## Akar Masalah
- Penyebab langsung:
- Akar masalah:
- Penyebab yang memperparah:

## Tindakan Pencegahan
| Tindakan | Penanggung Jawab | Tenggat |
|----------|-----------------|---------|
|          |                 |         |

## Pelajaran yang Dipetik

Ringkasan: Konfigurasi Minimum untuk Mencegah Insiden

// Salin ini sekarang dan tempel ke .claude/settings.json
{
  "permissions": {
    "deny": [
      "Bash(rm -rf ~*)",
      "Bash(rm -rf /*)",
      "Bash(git push --force *main*)",
      "Bash(git push --force *master*)",
      "Bash(git push -f *main*)",
      "Bash(DROP TABLE*)",
      "Bash(TRUNCATE *)",
      "Bash(curl * | bash)",
      "Bash(wget * | sh)"
    ],
    "ask": [
      "Write(**)", "Edit(**)",
      "Bash(rm*)", "Bash(git commit*)",
      "Bash(git push*)", "Bash(*deploy*)",
      "Bash(npm install*)", "Bash(*migrate*)"
    ]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(git add*)",
        "hooks": [{ "type": "command",
          "command": "git diff --cached --name-only | grep '\\.env' && echo '🚨 .env terdeteksi! Membatalkan.' && exit 1 || exit 0" }]
      },
      {
        "matcher": "Bash(rm*)",
        "hooks": [{ "type": "command",
          "command": "echo '⚠️ Perintah hapus terdeteksi. Eksekusi dalam 5 detik. Ctrl+C untuk membatalkan.' && sleep 5" }]
      }
    ]
  }
}

Insiden produksi terjadi ketika Anda melewatkan 30 menit untuk mengkonfigurasi pengaturan. Semua 7 insiden dalam artikel ini bisa dicegah dengan settings.json dan CLAUDE.md yang tepat.

Artikel Terkait

Referensi

#claude-code #incident #production #sre #security #postmortem

Tingkatkan alur kerja Claude Code kamu

50 template prompt yang sudah teruji, siap copy-paste ke Claude Code sekarang juga.

Gratis

PDF Gratis: Cheatsheet Claude Code dalam 5 Menit

Cukup masukkan emailmu dan kami akan langsung mengirim cheatsheet PDF A4 satu halaman.

Kami menjaga data pribadimu dengan aman dan tidak pernah mengirim spam.

Masa

Tentang Penulis

Masa

Engineer yang aktif menggunakan Claude Code. Mengelola claudecode-lab.com, media teknologi 10 bahasa dengan lebih dari 2.000 halaman.