Guide de Post-Mortem d'Incident
Workflow en 6 étapes
1. Déclencher le post-mortem (J+0, pendant ou juste après l'incident)
Critère : rédiger un post-mortem pour tout incident SEV1 ou SEV2, et pour tout SEV3 récurrent (3e occurrence en 30 jours).
Désigner immédiatement :
- Incident Commander — coordonne la résolution en direct
- Scribe — capture la timeline en temps réel dans un doc partagé (Confluence, Notion, doc Google)
- Auteur du post-mortem — rédige l'analyse a posteriori (peut être différent du scribe)
2. Collecter les données brutes (J+0 → J+1)
Extraire les logs, métriques et événements avant d'interpréter. Sources typiques :
# Logs applicatifs sur une fenêtre horaire (exemple Loki/logcli)
logcli query '{app="api-gateway"} |= "ERROR"' \
--from="2026-06-24T14:00:00Z" --to="2026-06-24T15:00:00Z" \
--output=jsonl > incident_logs.jsonl
# Métriques Prometheus : taux d'erreur 5xx sur 1h
promtool query range \
'sum(rate(http_requests_total{status=~"5.."}[1m]))' \
--start=2026-06-24T14:00:00Z --end=2026-06-24T15:00:00Z \
--step=60s
# Historique des déploiements (exemple kubectl)
kubectl rollout history deployment/api-gateway --namespace=prod
# Historique des commits récents sur la branche main
git log --oneline --since="24 hours ago" origin/main
Documenter chaque événement avec timestamp UTC précis. Ne pas reconstituer de mémoire.
3. Construire la timeline (J+1)
Règle : une ligne = un fait daté, source citée. Pas d'interprétation dans la timeline.
| Heure (UTC) | Événement | Source |
|----------------|--------------------------------------------------------|-------------------|
| 14:00:12 | Déploiement v2.3.1 mergé en prod (commit `a3f9c12`) | CI/CD Argo |
| 14:14:47 | Alerte Prometheus : error_rate > 5% (seuil configuré) | Alertmanager |
| 14:16:03 | PagerDuty notifie @on-call | PagerDuty log |
| 14:23:00 | Diagnostic : pool DB à 100% (max_connections=50) | pg_stat_activity |
| 14:29:15 | Décision rollback vers v2.3.0 | Slack #incidents |
| 14:31:08 | Rollback déclenché (`kubectl rollout undo`) | kubectl events |
| 14:34:22 | error_rate < 1%, service restauré | Grafana dashboard |
| 14:45:00 | Confirmation client : service nominal | Support ticket |
4. Analyser les causes racines
Choisir la méthode selon la complexité :
| Complexité |
Méthode recommandée |
| Incident simple, cause linéaire |
5 Whys |
| Plusieurs causes interdépendantes |
Fishbone (Ishikawa) |
| Incidents systémiques récurrents |
STAMP / fault tree |
Exemple 5 Whys complet :
Symptôme : API de paiement en erreur 503 pendant 34 minutes
1. Pourquoi le service retourne 503 ? → Pool de connexions DB épuisé (50/50 actives)
2. Pourquoi le pool est épuisé ? → Une requête `SELECT * FROM orders` sans WHERE ni LIMIT
3. Pourquoi cette requête existe-t-elle ? → Ajoutée dans v2.3.1 pour l'export CSV, pas testée sur data prod
4. Pourquoi pas testée sur data prod ? → L'env de staging n'a que 1 000 lignes ; prod en a 4,2 millions
5. Pourquoi cette différence n'est pas détectée ? → Pas de test de performance dans la PR checklist
Cause racine systémique : absence de tests de charge représentatifs avant mise en prod
Facteurs contributifs (ne pas confondre avec la cause racine) :
max_connections=50 jamais revisité depuis 2 ans
- Pas de circuit breaker sur les appels DB de longue durée
- Alerte sur la taille du pool absente du monitoring
5. Rédiger le document final (J+1 → J+2)
Template complet copiable :
# Post-Mortem : [Titre court et factuel]
**Date** : YYYY-MM-DD
**Durée** : HH:MM (détection → résolution complète)
**Sévérité** : SEV1 / SEV2 / SEV3
**Statut** : Brouillon / En revue / Finalisé
**Auteur** : [Nom]
**Réviseurs** : [Noms]
---
## Résumé exécutif (3 phrases max)
Le déploiement v2.3.1 a introduit une requête SQL non paginée qui a saturé le pool de
connexions PostgreSQL en 14 minutes. L'API de paiement a été indisponible 34 minutes
pour 100% des utilisateurs. Un rollback vers v2.3.0 a restauré le service.
## Impact
| Métrique | Valeur |
|-----------------------|---------------------|
| Utilisateurs impactés | ~12 000 |
| Durée d'indisponibilité | 00:34 |
| Transactions échouées | 847 |
| Revenu différé | ~42 000 € |
| SLA breached | Oui (99.9% → 99.1%) |
## Timeline détaillée
[Insérer tableau section 3]
## Analyse des causes racines
### Cause immédiate
Requête `SELECT * FROM orders` sans pagination exécutée en prod au déploiement de v2.3.1.
### 5 Whys
[Insérer analyse section 4]
### Facteurs contributifs
[Liste des facteurs]
## Ce qui a bien fonctionné
- Alerte Prometheus déclenchée en 14 min 47 s (SLO : < 15 min) ✓
- Rollback complet en moins de 3 minutes ✓
- Communication dans #incidents continue et structurée ✓
## Ce qui peut être amélioré
- Délai de 2 min entre l'alerte et la notification PagerDuty (bug de config)
- Aucun runbook pour les incidents "pool DB saturé"
- Staging non représentatif du volume de données prod
## Actions correctives
| # | Action | Priorité | Responsable | Deadline | Ticket |
|---|--------|----------|-------------|----------|--------|
| 1 | Alerte sur `pg_stat_activity` > 80% du pool | P1 | @ops-alice | J+3 | #1234 |
| 2 | Ajouter "test de pagination sur toute requête list" dans PR checklist | P1 | @lead-bob | J+5 | #1235 |
| 3 | Script de seed staging avec 1M lignes anonymisées | P2 | @dev-carol | J+14 | #1236 |
| 4 | Runbook "pool DB saturé" dans le wiki ops | P2 | @ops-alice | J+14 | #1237 |
| 5 | Circuit breaker sur appels DB > 5 s | P2 | @dev-dave | J+21 | #1238 |
| 6 | Load test automatisé dans la CI (k6, seuil 10 rps) | P3 | @devops-eve | J+30 | #1239 |
## Leçons apprises
- Les requêtes SQL sans pagination sont un vecteur d'incident systémique sur des données en croissance.
- L'écart staging/prod en volume de données doit être une exigence explicite, pas un état de fait.
6. Partager et suivre les actions (J+2 → clôture)
- Envoyer le post-mortem dans le canal #post-mortems et par email à toute l'équipe technique.
- Créer les tickets d'actions correctives immédiatement après validation du document.
- Faire un point de suivi 30 jours plus tard : cocher chaque action dans le doc, rouvrir si bloquée.
# Vérifier l'avancement des tickets (exemple GitHub CLI)
gh issue list --label "postmortem-2026-06-24" --state open
Niveaux de sévérité
| Niveau |
Critère |
Post-mortem obligatoire |
| SEV1 |
Service principal indisponible, impact revenue direct |
Toujours, sous 48 h |
| SEV2 |
Dégradation significative, workaround difficile |
Toujours, sous 72 h |
| SEV3 |
Impact mineur, peu d'utilisateurs touchés |
Si récurrent (3x/30j) |
Pièges et anti-patterns
Ne jamais faire :
- Rédiger la timeline de mémoire 3 jours après — reconstitution biaisée garantie.
- Terminer les 5 Whys à "erreur humaine" ou "manque d'attention" — c'est un symptôme, pas une cause.
- Laisser une action corrective sans ticket, responsable et deadline — elle ne sera jamais faite.
- Identifier une cause racine unique quand l'incident est systémique — les incidents complexes ont plusieurs causes.
- Attendre la réunion de post-mortem pour ouvrir les tickets P1 — ouvrir dès la résolution.
- Publier un post-mortem qui blame implicitement une personne ("Dave a oublié de tester") — rephrase en systémique ("l'absence de test de charge dans la CI n'a pas détecté la régression").
Signaux d'un mauvais post-mortem :
- Les actions correctives sont vagues ("améliorer le monitoring", "mieux tester").
- La section "causes racines" ne contient qu'une cause immédiate.
- Aucune mention de ce qui a bien fonctionné.
- Le document est rédigé à la première personne du singulier.
Principes blameless
- Chaque erreur humaine révèle une faiblesse systémique : process, outillage, formation, contexte.
- Un humain n'a jamais "fait une erreur" dans le vide — quelles conditions l'ont rendu possible ?
- L'objectif du post-mortem est d'empêcher le prochain incident, pas de documenter le passé.
- Partagez systématiquement les post-mortems avec les équipes voisines : un incident sur un service peut en prévenir un autre.
Checklist de validation avant publication
Communication Rules — MANDATORY
- Ultra-concise. No filler, no preamble, no pleasantries.
- Never say "happy to help", "sure!", "great question", "let me", or similar.
- Tool first, talk second. Act before explaining.
- Result first. Lead with outcome, not process.
- Stop when done. No summary, no recap, no trailing commentary.
- No politeness wrappers. Direct and blunt.
- Minimum words. If one word works, do not use ten.
- No unsolicited explanations.
- No emoji unless asked.
1---2name: productivity-incident-postmortem-guide3description: Rédaction de post-mortems techniques après un incident — timeline, analyse des causes racines, actions correctives et leçons apprises. À utiliser quand l'utilisateur doit documenter un incident, analyser une panne ou structurer un retour d'expérience. Se déclenche aussi avec "post-mortem", "postmortem", "incident report", "retour d'expérience", "RCA", "root cause analysis", "blameless postmortem", "analyse de panne". Also triggers on "write a postmortem", "blameless incident review".4---56# Guide de Post-Mortem d'Incident78## Workflow en 6 étapes910### 1. Déclencher le post-mortem (J+0, pendant ou juste après l'incident)1112Critère : rédiger un post-mortem pour tout incident SEV1 ou SEV2, et pour tout SEV3 récurrent (3e occurrence en 30 jours).1314Désigner immédiatement :15- **Incident Commander** — coordonne la résolution en direct16- **Scribe** — capture la timeline en temps réel dans un doc partagé (Confluence, Notion, doc Google)17- **Auteur du post-mortem** — rédige l'analyse a posteriori (peut être différent du scribe)1819### 2. Collecter les données brutes (J+0 → J+1)2021Extraire les logs, métriques et événements **avant** d'interpréter. Sources typiques :2223```bash24# Logs applicatifs sur une fenêtre horaire (exemple Loki/logcli)25logcli query '{app="api-gateway"} |= "ERROR"' \26 --from="2026-06-24T14:00:00Z" --to="2026-06-24T15:00:00Z" \27 --output=jsonl > incident_logs.jsonl2829# Métriques Prometheus : taux d'erreur 5xx sur 1h30promtool query range \31 'sum(rate(http_requests_total{status=~"5.."}[1m]))' \32 --start=2026-06-24T14:00:00Z --end=2026-06-24T15:00:00Z \33 --step=60s3435# Historique des déploiements (exemple kubectl)36kubectl rollout history deployment/api-gateway --namespace=prod3738# Historique des commits récents sur la branche main39git log --oneline --since="24 hours ago" origin/main40```4142Documenter chaque événement avec timestamp UTC précis. Ne pas reconstituer de mémoire.4344### 3. Construire la timeline (J+1)4546Règle : une ligne = un fait daté, source citée. Pas d'interprétation dans la timeline.4748```markdown49| Heure (UTC) | Événement | Source |50|----------------|--------------------------------------------------------|-------------------|51| 14:00:12 | Déploiement v2.3.1 mergé en prod (commit `a3f9c12`) | CI/CD Argo |52| 14:14:47 | Alerte Prometheus : error_rate > 5% (seuil configuré) | Alertmanager |53| 14:16:03 | PagerDuty notifie @on-call | PagerDuty log |54| 14:23:00 | Diagnostic : pool DB à 100% (max_connections=50) | pg_stat_activity |55| 14:29:15 | Décision rollback vers v2.3.0 | Slack #incidents |56| 14:31:08 | Rollback déclenché (`kubectl rollout undo`) | kubectl events |57| 14:34:22 | error_rate < 1%, service restauré | Grafana dashboard |58| 14:45:00 | Confirmation client : service nominal | Support ticket |59```6061### 4. Analyser les causes racines6263**Choisir la méthode selon la complexité :**6465| Complexité | Méthode recommandée |66|------------|---------------------|67| Incident simple, cause linéaire | 5 Whys |68| Plusieurs causes interdépendantes | Fishbone (Ishikawa) |69| Incidents systémiques récurrents | STAMP / fault tree |7071**Exemple 5 Whys complet :**7273```74Symptôme : API de paiement en erreur 503 pendant 34 minutes75761. Pourquoi le service retourne 503 ? → Pool de connexions DB épuisé (50/50 actives)772. Pourquoi le pool est épuisé ? → Une requête `SELECT * FROM orders` sans WHERE ni LIMIT783. Pourquoi cette requête existe-t-elle ? → Ajoutée dans v2.3.1 pour l'export CSV, pas testée sur data prod794. Pourquoi pas testée sur data prod ? → L'env de staging n'a que 1 000 lignes ; prod en a 4,2 millions805. Pourquoi cette différence n'est pas détectée ? → Pas de test de performance dans la PR checklist8182Cause racine systémique : absence de tests de charge représentatifs avant mise en prod83```8485**Facteurs contributifs** (ne pas confondre avec la cause racine) :86- `max_connections=50` jamais revisité depuis 2 ans87- Pas de circuit breaker sur les appels DB de longue durée88- Alerte sur la taille du pool absente du monitoring8990### 5. Rédiger le document final (J+1 → J+2)9192Template complet copiable :9394```markdown95# Post-Mortem : [Titre court et factuel]9697**Date** : YYYY-MM-DD98**Durée** : HH:MM (détection → résolution complète)99**Sévérité** : SEV1 / SEV2 / SEV3100**Statut** : Brouillon / En revue / Finalisé101**Auteur** : [Nom]102**Réviseurs** : [Noms]103104---105106## Résumé exécutif (3 phrases max)107108Le déploiement v2.3.1 a introduit une requête SQL non paginée qui a saturé le pool de109connexions PostgreSQL en 14 minutes. L'API de paiement a été indisponible 34 minutes110pour 100% des utilisateurs. Un rollback vers v2.3.0 a restauré le service.111112## Impact113114| Métrique | Valeur |115|-----------------------|---------------------|116| Utilisateurs impactés | ~12 000 |117| Durée d'indisponibilité | 00:34 |118| Transactions échouées | 847 |119| Revenu différé | ~42 000 € |120| SLA breached | Oui (99.9% → 99.1%) |121122## Timeline détaillée123124[Insérer tableau section 3]125126## Analyse des causes racines127128### Cause immédiate129Requête `SELECT * FROM orders` sans pagination exécutée en prod au déploiement de v2.3.1.130131### 5 Whys132[Insérer analyse section 4]133134### Facteurs contributifs135[Liste des facteurs]136137## Ce qui a bien fonctionné138- Alerte Prometheus déclenchée en 14 min 47 s (SLO : < 15 min) ✓139- Rollback complet en moins de 3 minutes ✓140- Communication dans #incidents continue et structurée ✓141142## Ce qui peut être amélioré143- Délai de 2 min entre l'alerte et la notification PagerDuty (bug de config)144- Aucun runbook pour les incidents "pool DB saturé"145- Staging non représentatif du volume de données prod146147## Actions correctives148149| # | Action | Priorité | Responsable | Deadline | Ticket |150|---|--------|----------|-------------|----------|--------|151| 1 | Alerte sur `pg_stat_activity` > 80% du pool | P1 | @ops-alice | J+3 | #1234 |152| 2 | Ajouter "test de pagination sur toute requête list" dans PR checklist | P1 | @lead-bob | J+5 | #1235 |153| 3 | Script de seed staging avec 1M lignes anonymisées | P2 | @dev-carol | J+14 | #1236 |154| 4 | Runbook "pool DB saturé" dans le wiki ops | P2 | @ops-alice | J+14 | #1237 |155| 5 | Circuit breaker sur appels DB > 5 s | P2 | @dev-dave | J+21 | #1238 |156| 6 | Load test automatisé dans la CI (k6, seuil 10 rps) | P3 | @devops-eve | J+30 | #1239 |157158## Leçons apprises159- Les requêtes SQL sans pagination sont un vecteur d'incident systémique sur des données en croissance.160- L'écart staging/prod en volume de données doit être une exigence explicite, pas un état de fait.161```162163### 6. Partager et suivre les actions (J+2 → clôture)164165- Envoyer le post-mortem dans le canal #post-mortems et par email à toute l'équipe technique.166- Créer les tickets d'actions correctives **immédiatement** après validation du document.167- Faire un point de suivi 30 jours plus tard : cocher chaque action dans le doc, rouvrir si bloquée.168169```bash170# Vérifier l'avancement des tickets (exemple GitHub CLI)171gh issue list --label "postmortem-2026-06-24" --state open172```173174---175176## Niveaux de sévérité177178| Niveau | Critère | Post-mortem obligatoire |179|--------|---------|------------------------|180| **SEV1** | Service principal indisponible, impact revenue direct | Toujours, sous 48 h |181| **SEV2** | Dégradation significative, workaround difficile | Toujours, sous 72 h |182| **SEV3** | Impact mineur, peu d'utilisateurs touchés | Si récurrent (3x/30j) |183184---185186## Pièges et anti-patterns187188**Ne jamais faire :**189- Rédiger la timeline de mémoire 3 jours après — reconstitution biaisée garantie.190- Terminer les 5 Whys à "erreur humaine" ou "manque d'attention" — c'est un symptôme, pas une cause.191- Laisser une action corrective sans ticket, responsable et deadline — elle ne sera jamais faite.192- Identifier une cause racine unique quand l'incident est systémique — les incidents complexes ont plusieurs causes.193- Attendre la réunion de post-mortem pour ouvrir les tickets P1 — ouvrir dès la résolution.194- Publier un post-mortem qui blame implicitement une personne ("Dave a oublié de tester") — rephrase en systémique ("l'absence de test de charge dans la CI n'a pas détecté la régression").195196**Signaux d'un mauvais post-mortem :**197- Les actions correctives sont vagues ("améliorer le monitoring", "mieux tester").198- La section "causes racines" ne contient qu'une cause immédiate.199- Aucune mention de ce qui a bien fonctionné.200- Le document est rédigé à la première personne du singulier.201202---203204## Principes blameless205206- Chaque erreur humaine révèle une faiblesse systémique : process, outillage, formation, contexte.207- Un humain n'a jamais "fait une erreur" dans le vide — quelles conditions l'ont rendu possible ?208- L'objectif du post-mortem est d'empêcher le prochain incident, pas de documenter le passé.209- Partagez systématiquement les post-mortems avec les équipes voisines : un incident sur un service peut en prévenir un autre.210211---212213## Checklist de validation avant publication214215- [ ] Timeline basée sur des logs/métriques, pas sur des souvenirs216- [ ] Au moins 3 niveaux de "Pourquoi ?" dans les 5 Whys217- [ ] Causes racines systémiques (process/outil), pas "erreur humaine"218- [ ] Chaque action corrective : ticket créé, responsable nommé, deadline fixée219- [ ] Section "Ce qui a bien fonctionné" renseignée220- [ ] Aucune mise en cause individuelle dans le document221- [ ] Document relu par au moins une personne extérieure à l'incident222223224## Communication Rules — MANDATORY225226- Ultra-concise. No filler, no preamble, no pleasantries.227- Never say "happy to help", "sure!", "great question", "let me", or similar.228- Tool first, talk second. Act before explaining.229- Result first. Lead with outcome, not process.230- Stop when done. No summary, no recap, no trailing commentary.231- No politeness wrappers. Direct and blunt.232- Minimum words. If one word works, do not use ten.233- No unsolicited explanations.234- No emoji unless asked.