Post-Mortem Yazma
Production incident sonrası blameless post-mortem yazmayı öğreten skill — timeline, kök neden, katkı faktörleri, aksiyonlar, öğrenilen dersler. Aynı incident iki kez olmasın diye.
İçerik
Post-Mortem Yazma
Production incident'ı olduğunda amaç kimin suçlu olduğunu bulmak değil, sistemin neden bu şekilde başarısız olduğunu ve aynı hatanın tekrarlanmaması için ne yapılacağını anlamak. Bu skill blameless post-mortem disiplinini öğretir.
Ne zaman aktif olur
- Prod incident (outage, data loss, security breach)
- SLA ihlali olan bir yavaşlık
- Near-miss (patlayabilirdi ama patlamadı — bundan da öğrenilir)
- Major customer-facing bug
- Deploy-induced regression
Küçük bug / minor fix için post-mortem gerekmez. Eşik: kullanıcı etkisi, süre, veri etkisi.
Çekirdek prensipler
1. Blameless
Kimin ne hata yaptığı aramaz. Sistem neden bu hatayı mümkün kıldığı aranır.
Yerine:
- "Ahmet yanlış komutu çalıştırdı" ❌
- "Komutun geri döndürülemez etkisi olduğu konusunda uyarı yoktu" ✅
Yerine:
- "Ayşe test etmeden deploy etti" ❌
- "Deploy pipeline'ı prod öncesi staging gate'i bypass edilebiliyordu" ✅
Hataya düşen insan sistemin ürünü. Sistem değişmezse aynı pozisyondaki başka biri aynı hataya düşer.
2. Timeline-first, analysis-second
Önce ne oldu olgusal şekilde yazılır. Sonra analiz edilir. İkisi karışırsa (örn. "saat 14:03'te yanlış deploy edildi") olgu ile yargı karıştırılmış olur.
3. 5 Whys — kök nedene inme
Prod 503 verdi
→ Neden? DB connection pool doldu
→ Neden? Her request yeni connection açıyordu
→ Neden? Connection pool config yüklenmiyordu
→ Neden? Env var yanlış scope'ta tanımlanmıştı
→ Neden? Deploy config template'inde pattern belirsizdi
5 why zorunlu değil — ya "sistemik" ya "kök" cevaba ulaşana kadar git.
4. Kök neden + katkı faktörleri
Tek bir root cause nadiren var. Genelde contributing factors zinciri:
- "Config bug + monitoring eksik + rollout manuel" Hiçbiri tek başına incident'a yol açmazdı; birleştiğinde yaptı.
5. Aksiyonlar somut, sahipli, deadline'lı
"Monitoring iyileştirilecek" ≠ aksiyon. "DB connection pool saturation alert'i ekle, sahip @sre-team, 30 Nisan'a kadar" = aksiyon.
Şablon
# Post-Mortem: <Incident Başlığı>
**Tarih:** <ISO>
**Süre:** <başlangıç> - <bitiş> (<toplam süre>)
**Severity:** SEV-1 | SEV-2 | SEV-3
**Yazar:** <isim>
**Reviewers:** <liste>
**Status:** Draft | In Review | Final
## Özet
<2-3 cümle: ne oldu, kimi etkiledi, nasıl çözüldü>
## Etki
- **Etkilenen kullanıcı:** <sayı, %>
- **Etkilenen servisler:** <liste>
- **Süre:** <>
- **Data loss:** <Evet/Hayır + detay>
- **Revenue impact (varsa):** <>
## Timeline (olgusal)
| Zaman (UTC) | Ne oldu | Kim fark etti / yaptı |
|-------------|---------|---------------------|
| 14:00 | Deploy start: v2.4.1 | @release-bot |
| 14:03 | Error rate spike — 0% → 12% | PagerDuty alert |
| 14:05 | On-call @ayse confirm — prod impact | - |
| 14:07 | `#incidents` oluşturuldu, SEV-2 | @ayse |
| 14:12 | Rollback başlatıldı (v2.4.0) | @ayse |
| 14:18 | Rollback tamamlandı, error rate normal | - |
| 14:22 | Incident kapandı, status page update | @ayse |
## Kök neden & katkı faktörleri
### Kök neden
<En derin "why" cevabı>
### Katkı faktörleri
- <Faktör 1>
- <Faktör 2>
## Ne işe yaradı (what went well)
- <Tespit hızlı oldu — 3 dakikada alert>
- <Rollback procedure çalıştı>
- <Status page zamanında güncellendi>
## Ne yanlış gitti (what didn't go well)
- <Alert SEV yanlış etiketlendi>
- <Monitoring dashboard incident sırasında yavaşladı>
## Şanslıydık (what we got lucky with)
- <On-call saat gündüzdü, gece olsaydı daha yavaş>
## Aksiyonlar
| # | Aksiyon | Sahibi | Deadline | Ticket |
|---|---------|-------|----------|--------|
| 1 | DB connection pool saturation alert'i ekle | @sre-team | 2026-04-30 | SRE-501 |
| 2 | Deploy config template'ine pool size validation ekle | @platform | 2026-05-05 | PLAT-202 |
| 3 | Runbook: connection pool saturation playbook | @sre-team | 2026-05-10 | SRE-502 |
## Öğrenilen dersler
<2-3 paragraf: bu incident bize ne öğretti, gelecekteki kararlarda bunu nasıl kullanacağız>
## Referanslar
- Incident channel: <link>
- Grafana dashboard: <link>
- Deploy log: <link>
- İlgili RFC / PR: <link>
Severity kategorileri (örnek)
| SEV | Tanım | Örnek |
|---|---|---|
| SEV-1 | Major outage, tüm kullanıcılar veya kritik özellik | Site down, login broken, data loss |
| SEV-2 | Partial outage, bir segment etkileniyor | Bölgesel yavaşlık, payment intermittent |
| SEV-3 | Minor degradation, workaround var | Tek endpoint slow, cosmetic bug |
SEV-1 post-mortem zorunlu + tüm takımla paylaşılır. SEV-3 belki kısa bir not yeter.
Timeline'ı yazmak
Olgusal tutun
Zaman + olay. Yargı katma.
İyi:
14:05 UTC — Error rate 0% → 12%
Kötü:
14:05 UTC — Sistem mahvoldu, panik başladı
Kaynağı belirt
Kim fark etti, kim aksiyon aldı. Blameless tonu kullan:
- "On-call fark etti" değil "Mehmet geç fark etti"
- "Ayşe rollback başlattı" (övgü için OK, eleştiri için kullanma)
Ayrıntı seviyesi
Her dakikayı yazma — önemli karar / gözlem noktaları yaz.
Kök neden arama — 5 Whys örneği
Incident: Prod API 8 dakika 503 döndü.
Why 1: API 503 dönüyordu
→ Backend pod'ları unhealthy
Why 2: Pod'lar unhealthy neden?
→ Liveness probe fail ediyordu
Why 3: Neden fail?
→ /health endpoint'i 10s üstünde cevap veriyordu
Why 4: Neden yavaş?
→ DB connection timeout — pool tükenmiş
Why 5: Neden tükendi?
→ Yeni deploy her request'te yeni connection açıyor, pool yok
→ Configuration yükleniyormuş ama parse edilmemişti
→ Config change PR'da test yoktu, deploy öncesi fark edilmedi
Kök neden: Config parse hatası, deploy öncesi test eksikliği
Katkı faktörleri:
- Staging env'de bu config değişikliği yoktu → shadow değil
- Liveness probe timeout 10s çok düşüktü (5s'e çekilecek)
- Pool saturation alert'i yoktu
Aksiyon yazımı
Aksiyon iyi mi?
✅ "DB connection pool saturation alert'i eklenecek —
@sre-team — 2026-04-30 — SRE-501"
❌ "Monitoring iyileştirilmeli"
❌ "Dikkatli olacağız"
❌ "Incident prevented in future"
Kriter:
- Somut (ne yapılacak)
- Sahibi (kim)
- Deadline (ne zaman)
- Takip edilebilir (ticket / issue)
Aksiyonların tamamlanması
Post-mortem yayınlandıktan sonra 30 gün içinde çoğu aksiyon kapanmış olmalı. 30 gün sonra hala açıksa review: hala relevant mi, yeniden önceliklendir.
Toplantı akışı
Post-mortem meeting (1 hafta içinde)
- 45-60 dk, tüm incident-related kişiler
- Gündem: timeline okunur → kök neden tartışılır → aksiyonlar finalize edilir
- Not: blameless disiplini moderator tarafından korunur
Paylaşım
- Takıma: Slack / wiki
- Organizasyon geneli: eğer SEV-1 veya öğretici ise
- Müşteriye: external-facing versiyon (hassasiyet gözeterek)
Retro
Quartal bazda tüm post-mortem'ler gözden geçirilir: hangi tema tekrar ediyor, sistemik iyileştirme neresinde yapılmalı.
Somut örnek (kısaltılmış)
# Post-Mortem: Payment API 8 dakika 503 (2026-04-22)
**Severity:** SEV-1
**Süre:** 14:00-14:18 UTC (18 dk, 8 dk kullanıcı etkili)
**Etki:** ~24K payment request'i failed, $42K pending retry
**Status:** Final
## Özet
v2.4.1 deploy sonrası /api/payments endpoint'i liveness probe fail etti,
kubernetes pod'ları kill-recreate döngüsüne girdi. Rollback sonrası normal.
## Etki
- 24K failed request (retry'da çoğu başarılı)
- 8 dakika customer-visible downtime
- 2 kurumsal müşteri incident channel'da rapor etti
## Timeline
14:00 — deploy start
14:03 — PagerDuty alert (error rate)
14:05 — on-call confirm
14:07 — #incidents kanalı açıldı, SEV-1
14:12 — rollback başlatıldı (v2.4.0)
14:18 — stable
## Kök neden
v2.4.1'deki config change `DB_POOL_SIZE` env var'ının yeni parser'a
uyumlu değildi; default value (5) kullanıldı ama load 50 request/s,
pool saturate oldu → health check timeout.
## Katkı faktörleri
- Staging env'inde yeni config kullanılmadığı için test edilmedi
- Liveness probe timeout 10s; gerçek saturation'da yetersiz
- Pool saturation metrik'i yok, sadece latency alert var
## Ne iyiydi
- Alert 3 dakika içinde
- Rollback 5 dakika içinde tamamlandı
- Runbook netti
## Ne kötüydü
- Staging shadow testi yok
- Saturation alert'i yok
## Şanslıydık
- Gündüz saatiydi, on-call hemen cevap verdi
## Aksiyonlar
1. Config parser için unit test — @backend — 2026-04-30 — BE-812
2. Saturation alert (pool > %80) — @sre — 2026-04-28 — SRE-501
3. Staging config parity check script — @platform — 2026-05-10 — PLAT-202
4. Liveness timeout 10s → 5s (fail fast) — @platform — 2026-04-25 — PLAT-203
## Öğrenilen dersler
Config değişiklikleri deploy pipeline'ımızda "trivial" kategorisinde
geçiyordu. Aslında kod değişikliği kadar risk taşır. Pipeline'ı her
config diff'i için staging'de shadow traffic ile test edecek şekilde
genişletmeliyiz (PLAT-202).
## Referanslar
- #incidents-2026-04-22: https://...
- Grafana pool metric: https://...
- PR: https://.../pr/1432
Anti-pattern'ler
- ❌ Blame yönelimli ("Ahmet unuttu", "Ayşe dikkatsizdi")
- ❌ Timeline'da yargı ("panik oldu", "kötü karar verildi")
- ❌ "Monitoring geliştirilmeli" gibi muğlak aksiyon
- ❌ Sahipsiz / deadline'sız aksiyon
- ❌ Kök neden "human error" ile kapatmak (sistemik neden aranmadı)
- ❌ Aksiyonları takip etmemek (30 gün sonra kimse bakmamış)
- ❌ Post-mortem'i kişisel e-posta ile paylaşmak (bilgi izole)
- ❌ Sadece SEV-1 için yazmak (SEV-2'lerde de ders var)
- ❌ Meeting'i savunma mahkemesine çevirmek
- ❌ Öğrenilen dersleri gelecek RFC'lere / runbook'lara bağlamamak
- ❌ Near-miss'leri yok saymak (patlayabilirdi, yazılsa öğreniriz)
Checklist — post-mortem yayınlanmadan önce
- Timeline olgusal, yargı yok
- Etki somut (sayı, süre, user)
- Kök neden sistemik, insan hatasına indirgenmemiş
- Aksiyonlar somut + sahipli + deadline'lı
- "What went well" bölümü dolu (moral destek)
- Blameless dil tutarlı
- İlgili kişiler review etti
- Organizasyona paylaşılacak kanal netleşti
- Aksiyonlar ticket olarak açıldı