決定と事故を機密のないポートフォリオに変える
一言でいうと
ADRは、決定を下した当時のコンテキスト・代替案・選択・結果を保存し、インシデントレポートは、実際の 影響・タイムライン・根本原因・検知・担当者のいる是正措置を残します。ポートフォリオは、この 原本をコピーせずに、許可された要約だけを発行します。
なぜコードだけでは判断を説明できないのか
ON CONFLICTの構文は、何を選択したかは示しますが、なぜクライアントだけでの重複
排除よりも、データベースの境界を選んだのかは語りません。6か月後にそのコードを見る
人は、2つのうち1つをします。理由がわからずにそのままにするか、理由がわからずに取り除くかです。
どちらも良くありません。
ADR(Architecture Decision Record)は、当時の制約と、比較した代替案、予想したコストを 記録します。形式は、短いほど良いものです。
# ADR-014: 중복 제거를 DB 제약으로 옮긴다
- 상태: 채택 (2026-03-11)
- 맥락: 결제 웹훅이 재시도로 같은 이벤트를 최대 3번 보낸다. 클라이언트 쪽
메모리 캐시로 걸렀는데 파드가 늘면서 캐시가 나뉘어 중복이 새기 시작했다.
- 대안:
1. Redis 분산 락 — 새 의존성이 생기고, 락 해제 실패 시 결제가 멈춘다.
2. 애플리케이션 조회 후 삽입 — 조회와 삽입 사이에 경쟁이 남는다.
3. unique 제약 + ON CONFLICT DO NOTHING — 경쟁이 DB 안에서 끝난다.
- 결정: 3안. 멱등 키를 (provider, event_id) 로 두고 유니크 인덱스를 건다.
- 결과: 중복 결제 0건. 대신 삽입 지연이 p99 기준 4ms 늘었다.
- 되돌리는 조건: 삽입 지연이 p99 20ms 를 넘으면 재검토한다.
このコードブロックの韓国語の例は、重複排除をDBの制約に移すというADRで、状態は採択、背景は、決済のWebhookが再試行で同じイベントを最大3回送るのに、クライアント側のメモリキャッシュでは重複が漏れ始めたこと、代替案はRedisの分散ロック・アプリケーションでの参照後の挿入・unique制約とON CONFLICT DO NOTHINGの3つ、決定は3番目の案、結果は重複決済0件で挿入の遅延がp99で4ms増えたこと、見直す条件は挿入の遅延がp99で20msを超えたとき、という内容です。
「元に戻す条件」があるADRとないADRは、別の文書です。条件があれば、後で その条件を測って判断でき、なければ永遠に残ります。
インシデントレポートが学習の記録になるには
インシデントが起きたら、設計の仮定が実際に正しかったのかを、インシデントレポートで振り返ります。責めるべき 人を探す文書ではなく、システムの次の安全装置を作るための学習の記録でなければ なりません。この性格を守る仕組みがいくつかあります。
タイムラインはタイムゾーンを含み、3つの時刻を区別します。
| 時刻 | 意味 | この値が大きいと |
|---|---|---|
| 開始 | ユーザーへの影響が始まったとき | — |
| 検知 | 人が気づいたとき | 観測が足りない |
| 復旧 | 影響が終わったとき | 対応の手順が遅い |
検知まで40分かかったなら、根本原因とは別に、「なぜ40分間気づかなかったのか」が独立した 是正項目です。インシデントの半分は、ここから出てきます。
根本原因は、人ではなく条件を指します。「誰それが誤った設定をデプロイ した」ではなく、「誤った設定がデプロイまで到達する経路に、検証がなかった」です。 前の文は次のインシデントを防げず、後の文は防げます。
是正措置は、「注意する」ではなく、責任者と期限、検証可能な完了条件を持ちます。
- [ ] values 스키마 검증을 CI 에 추가 (담당: 배포팀, 기한: 3/25)
완료 조건: 잘못된 replicas 값을 넣은 PR 이 CI 에서 떨어지는 것을 확인
- [ ] 에러율 경보를 백엔드별로 분리 (담당: 관측팀, 기한: 3/20)
완료 조건: 한 대만 죽였을 때 5분 안에 경보가 울리는 것을 확인
このコードブロックの韓国語コメントは、是正措置の例で、valuesのスキーマ検証をCIに追加(担当はデプロイチーム、期限は3/25、完了条件は誤ったreplicas値を入れたPRがCIで落ちるのを確認すること)と、エラー率のアラートをバックエンドごとに分離(担当は観測チーム、期限は3/20、完了条件は1台だけ落としたとき5分以内にアラートが鳴るのを確認すること)を述べています。
現場で公開用の証拠を作る方法
採用向けのポートフォリオには、元のAuthorizationヘッダー、トークン、パスワード、プライベートIP、内部の
.svc.cluster.localアドレス、ターミナルの全出力を入れません。インシデントレポートの原本には、
顧客の社名と売上への影響が入っていて、ADRには、内部システムの構造が現れます。
その代わり、許可された証拠だけを選んで発行します。
| 入れないもの | 代わりに入れるもの |
|---|---|
curl -H "Authorization: Bearer ey..."の全文 |
「HTTPの動作テスト6件に合格」 |
10.0.3.12、db.internal.svc |
「3層構成、DBはプライベートサブネット」 |
| 「A社の決済が12分停止、売上への影響3,200万ウォン」 | 「決済経路が12分停止、影響の規模は非公開」 |
| ログ全体の貼り付け | メトリクス名の一覧とグラフ1枚 |
スキーマのバージョンを固定し、不明なフィールドを拒否して、後続のツールが元の秘密を誤って 追加できないようにします。
{"schema": "labhub.portfolio/v1",
"evidence": [
{"kind": "test", "name": "http-contract", "passed": 6, "failed": 0},
{"kind": "image", "claim": "non-root, digest-pinned"},
{"kind": "adr", "id": "ADR-014", "one_line": "중복 제거를 DB 제약으로 옮김"},
{"kind": "incident", "id": "INC-2026-03", "one_line": "설정 검증 부재로 12분 중단"}
]}
このコードブロックの韓国語の値は、one_lineの2つの文で、順に「重複排除をDBの制約に移した」「設定の検証がなくて12分停止した」という意味です。
additionalProperties: falseでスキーマをロックしておけば、誰かが便宜上raw_logフィールドを
追加した瞬間に、検証が失敗します。人の注意力の代わりに、ツールが防ぎます。
実務での判断基準
良いポートフォリオは、派手なスクリーンショットよりも、再現可能な契約と判断力を示して くれます。面接で実際に聞かれるのも、こちらです。
- どんな失敗の反例をテストしたか。成功の経路だけをテストしたものは、テストではありません。
- データの境界と権限の境界を、どう分けたか。なぜその線を引いたのかが、設計です。
- 障害で何を変えたか。原因の分析で止まったレポートは、半分です。
- 元に戻す条件を決めておいたか。これがあれば、決定を管理できる人です。
このコースの最後のクイズでは、実装・運用・ドキュメントの証拠が、1つの物語としてつながっているかを 確認します。