TT Lab
Get started
Learn Learning paths Courses

Production Backend API Capstone

Turn Decisions and Incidents Into a Portfolio With No Secrets

Continue in TT Lab

One-line summary

An ADR preserves the context, alternatives, choice, and consequences at the time a decision was made, and an incident report records the actual impact, timeline, root cause, detection, and corrective actions that have owners. A portfolio does not copy these originals; it publishes only the permitted summaries.

Why code alone cannot explain a judgment

The ON CONFLICT syntax shows what was chosen, but it does not say why the database boundary was chosen over client-only deduplication. Someone looking at that code six months later will do one of two things: leave it alone because they do not know the reason, or rip it out because they do not know the reason. Both are bad.

An ADR (Architecture Decision Record) records the constraints at the time, the alternatives compared, and the expected cost. The shorter the format, the better.

# 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 를 넘으면 재검토한다.

An ADR with a "condition for reverting" and one without are different documents. With a condition, you can measure it later and decide; without one, the decision lives forever.

What makes an incident report a learning record

When an incident happens, you look back through the incident report at whether the design's assumptions actually held. It must be a learning record for building the system's next safeguard, not a document for finding someone to blame. Several mechanisms protect this character.

The timeline includes the time zone and distinguishes three times.

Time Meaning If this value is large
Start When user impact began —
Detection When a person noticed Observability is insufficient
Recovery When the impact ended The response procedure is slow

If detection took 40 minutes, then regardless of the root cause, "why did we not know for 40 minutes" is an independent corrective item. Half of all incidents come from here.

The root cause points to a condition, not a person. Not "so-and-so deployed a bad configuration" but "there was no validation on the path by which a bad configuration reaches deployment." The former does not prevent the next incident, and the latter does.

A corrective action has an owner, a due date, and a verifiable completion condition, not "be careful."

- [ ] values 스키마 검증을 CI 에 추가 (담당: 배포팀, 기한: 3/25)
      완료 조건: 잘못된 replicas 값을 넣은 PR 이 CI 에서 떨어지는 것을 확인
- [ ] 에러율 경보를 백엔드별로 분리 (담당: 관측팀, 기한: 3/20)
      완료 조건: 한 대만 죽였을 때 5분 안에 경보가 울리는 것을 확인

How to build public evidence in practice

A hiring portfolio must not contain raw Authorization headers, tokens, passwords, private IPs, internal .svc.cluster.local addresses, or full terminal output. The original incident report contains customer names and revenue impact, and an ADR reveals internal system structure.

Instead, publish only permitted evidence.

What not to include What to include instead
The full curl -H "Authorization: Bearer ey..." "6 HTTP behavior tests passed"
10.0.3.12, db.internal.svc "3-tier layout, DB in a private subnet"
"Company A payments down 12 minutes, revenue loss 32 million KRW" "Payment path down 12 minutes, impact size undisclosed"
Pasting the entire log A list of metric names and one graph

Pin the schema version and reject unknown fields, so that downstream tools cannot accidentally add the original secrets.

{"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분 중단"}
 ]}

If you lock the schema with additionalProperties: false, validation fails the moment someone adds a raw_log field for convenience. A tool blocks it instead of relying on human attention.

Practical judgment criteria

A good portfolio shows reproducible contracts and judgment rather than flashy screenshots. This is also what interviews actually ask about.

The final quiz of this course checks whether the implementation, operations, and documentation evidence connect into a single story.