Split View: 기술 부채 완전 가이드: 식별하고 측정하고 갚기
기술 부채 완전 가이드: 식별하고 측정하고 갚기
- 들어가며
- 1. 은유의 원래 뜻과 오용
- 2. 부채가 아닌 것들
- 3. 부채의 유형학
- 4. 어디에 있는지 찾기 — 변경 빈도와 복잡도
- 5. 이자를 계산하는 법
- 6. 부채 목록을 만들고 유지하기
- 7. 상환 계획을 로드맵에 넣는 방식들
- 8. 갚지 않기로 결정하는 것도 결정이다
- 9. 다시 쌓이지 않게 하는 장치
- 퀴즈: 실력을 확인해 보세요
- 마치며
- 참고 자료
- 이어서 읽기
들어가며
이 블로그에는 이미 기술 부채를 비즈니스 언어로가 있습니다. 그 글은 부채를 이해관계자에게 어떻게 설명하고 우선순위를 어떻게 협상하는지를 다룹니다. 즉 설득에 관한 글입니다.
이 글은 그 앞 단계입니다. 설득할 대상이 되려면 먼저 부채가 어디에 있는지 찾아내고, 거기에 숫자를 붙여야 합니다. 근거 없는 "코드가 더럽습니다"는 협상 테이블에서 이길 수 없습니다. 그래서 이 글의 주제는 식별과 측정, 그리고 목록을 유지하는 절차입니다.
한 문장으로 줄이면 이렇습니다. 부채의 존재를 주장하는 것과 부채의 이자를 보여 주는 것은 다른 일이고, 두 번째만 예산을 움직입니다.
1. 은유의 원래 뜻과 오용
1-1. Fowler의 사분면
Martin Fowler는 기술 부채를 두 축으로 나눕니다. 의도적이었는가 우발적이었는가, 그리고 무모했는가 신중했는가입니다.
무모함(reckless) 신중함(prudent)
의도적 "설계할 시간이 없다" "지금 출하하고 나중에 갚는다"
(deliberate) 대가를 모른 채 건너뜀 대가를 알고 선택함
우발적 "계층 분리가 뭔가요?" "이제야 어떻게 했어야 하는지 알겠다"
(inadvertent) 더 나은 방법을 몰랐음 만들어 보고 나서야 알게 됨
Fowler는 우발적이면서 신중한 사분면을 특히 강조합니다. 프로젝트에서 1년쯤 프로그래밍을 해 봐야 최선의 설계 접근이 무엇이었는지 이해하게 되는 경우가 있다는 것입니다. 그리고 이 은유가 의도적인 두 사분면에만 유효한 것이 아니라 네 개 모두에 유효하다는 것이 그의 요지입니다.
1-2. 이 구분이 실무에서 하는 일
사분면은 분류 놀이가 아니라 처방이 달라지기 때문에 의미가 있습니다.
- 의도적·신중: 상환 일정과 조건을 그 자리에서 함께 정합니다. 정하지 않으면 다음 분기에 우발적 부채처럼 보이게 됩니다.
- 의도적·무모: 프로세스 문제입니다. 개인을 탓하는 대신 왜 그 선택이 가능했는지, 즉 마감과 리뷰 기준을 봅니다.
- 우발적·무모: 학습과 리뷰의 문제입니다. 같은 실수가 반복되면 개인이 아니라 온보딩과 가이드가 부족한 것입니다.
- 우발적·신중: 정상입니다. 이것을 부끄러워하면 팀은 배운 것을 코드에 반영하지 않게 됩니다.
1-3. 여기서 의견이 갈립니다
"기술 부채"라는 은유 자체가 유용한지에 대해 진짜 논쟁이 있습니다. 반대하는 쪽의 주장은 이 말이 형편없는 엔지니어링을 금융 용어로 세탁한다는 것입니다. 부채라는 말은 원래 의도적 선택과 상환 계획을 전제하는데, 실무에서는 아무 계획 없이 만들어진 나쁜 코드에도 같은 단어가 붙는다는 것입니다. 찬성하는 쪽은 이 은유가 비기술 이해관계자와 대화할 수 있는 거의 유일한 공통 언어라고 봅니다.
축은 세 가지입니다. 의도성이 실제로 있었는지, 상환 계획이 문서로 존재하는지, 그리고 이 단어가 상대방에게 어떤 행동을 유도하는지입니다. 계획 없이 쓰이면 반대 쪽 주장이 맞고, 계획과 함께 쓰이면 찬성 쪽 주장이 맞습니다.
2. 부채가 아닌 것들
목록을 만들기 전에 걸러 내야 합니다. 판별 기준은 하나입니다. 미래의 변경에 추가 비용을 물리는가. 물리지 않으면 부채가 아닙니다.
- 취향 차이: 다른 스타일로 짜인 코드는 부채가 아닙니다. 스타일 논쟁을 부채 목록에 넣으면 목록 전체의 신뢰가 떨어집니다.
- 오래되었지만 바뀌지 않는 코드: 3년째 아무도 손대지 않았고 정상 동작하는 모듈은 이자가 0입니다. 우선순위도 0입니다.
- 아직 필요하지 않은 추상화의 부재: 일반화하지 않은 것은 부채가 아니라 판단입니다. 세 번째 사례가 나타나기 전의 중복은 대체로 정상입니다.
- 버그: 결함이지 부채가 아닙니다. 결함은 고치는 것이고, 부채는 갚거나 갚지 않기로 결정하는 것입니다.
- 미완성 기능: 백로그입니다.
- 의도적으로 단순하게 유지한 설계: 확장성이 없는 것과 확장성을 포기하기로 결정한 것은 다릅니다.
반대로 흔히 부채로 인식되지 않지만 부채인 것들이 있습니다. 느린 CI, 손으로 하는 배포 전 점검, 로컬에서 재현되지 않는 환경, 신입이 첫 커밋까지 2주 걸리는 상태입니다. 이것들은 코드에 나타나지 않지만 모든 변경에 이자를 물립니다.
3. 부채의 유형학
유형을 나누는 이유는 발견 방법과 상환 방법이 유형마다 다르기 때문입니다.
| 유형 | 이자가 나타나는 곳 | 발견 방법 |
|---|---|---|
| 코드 | 변경에 걸리는 시간, 리뷰 지적 반복 | 핫스팟 분석, 리뷰 코멘트 집계 |
| 설계·아키텍처 | 한 기능이 여러 모듈을 동시에 건드림 | 변경 결합도, 함께 바뀌는 파일 쌍 |
| 테스트 | CI 시간, 불안정 실패, 배포 두려움 | 실패율, 소요 시간, 커버리지 공백 |
| 의존성·보안 | 업그레이드 불가, 스펙 위반 잔존 | 취약점 스캔, 지원 종료일 |
| 데이터·스키마 | 마이그레이션 위험, 되돌릴 수 없는 배포 | 축소되지 않은 컬럼, 이중 쓰기 잔존 |
| 운영 | 장애 빈도, 복구 시간, 수동 절차 | 장애 회고, 런북의 수동 단계 수 |
| 지식·문서 | 온보딩 기간, 같은 질문의 반복 | 온보딩 소요일, 질문 로그 |
3-1. 외부 시계가 붙은 부채
대부분의 부채는 우리가 상환 시점을 정하지만, 일부는 외부가 시한을 정합니다. 이런 항목은 목록에서 따로 표시해야 합니다.
- 보안 스펙의 변화: OAuth 2.0 보안 모범 사례인 RFC 9700은 리소스 소유자 비밀번호 자격 증명 그랜트에 대해 "사용되어서는 안 된다(MUST NOT be used)"고 못박고, 암묵적 그랜트에 대해서는 인가 응답에서의 액세스 토큰 주입이 방지되지 않는 한 "사용하지 않아야 한다(SHOULD NOT)"고 규정합니다. 이런 항목은 우리 일정이 아니라 감사와 심사 일정에 묶입니다.
- 저장 위치 문제: OWASP 세션 관리 치트시트는 인증 토큰, 세션 ID, JWT, 리프레시 토큰을
localStorage나sessionStorage에 저장하지 말라고 합니다. 이 API들은 같은 출처에서 실행되는 모든 자바스크립트가 접근할 수 있으므로 XSS 하나로 모든 토큰이 노출되기 때문입니다. 선택지 비교는 JWT와 세션, 무엇을 언제에 정리되어 있습니다.
3-2. 운영 부채는 장애로만 청구됩니다
Google SRE Book은 연쇄 장애를 "양의 되먹임의 결과로 시간이 지나며 커지는 장애"로 정의하고, 재시도는 항상 무작위화된 지수 백오프로 예약하라고 권합니다. 재시도 예산의 예시로는 프로세스당 분당 60회를 듭니다. 그리고 계층이 겹치면 재시도가 곱해져 세 계층이 각각 4회씩 재시도할 경우 사용자 동작 하나가 64번의 시도가 된다고 지적합니다.
백오프도 예산도 부하 차단도 없는 상태는 평소에는 아무 비용이 없습니다. 이자가 장애 순간에만 한꺼번에 청구되기 때문에 우선순위에서 계속 밀립니다. 이런 유형은 이자를 "발생 확률 × 장애 시 비용"으로 적어야 목록에서 살아남습니다.
3-3. 지식 부채
Design Docs at Google 글은 설계 문서가 다른 모든 문서와 마찬가지로 시간이 지나며 현실과 어긋나는 경향이 있다고 적습니다. 대응은 원본을 갱신하거나, 수정 사항을 덧붙이거나, 후속 문서를 연결하는 것입니다. 문서가 낡았다는 사실 자체보다 위험한 것은 낡았는지 여부를 아무도 모르는 상태입니다.
4. 어디에 있는지 찾기 — 변경 빈도와 복잡도
4-1. 나쁜 코드가 아니라 비싼 코드를 찾습니다
부채 탐색에서 가장 흔한 실수는 가장 지저분한 파일부터 찾는 것입니다. 지저분하지만 아무도 손대지 않는 파일의 이자는 0입니다. 찾아야 하는 것은 자주 바뀌면서 동시에 바꾸기 어려운 파일입니다.
# 예시 — 버전 관리 이력에서 변경 빈도 상위 파일 뽑기
# 최근 12개월, 파일별 커밋 수 상위 30개
git log --since='12 months ago' --name-only --pretty=format: \
| grep -v '^$' | sort | uniq -c | sort -rn | head -30
# 이 목록을 복잡도 또는 줄 수와 곱해 핫스팟 점수를 만든다
# 점수 = 변경 횟수 x 복잡도 지표
# 상위 10개가 이번 분기에 볼 후보다
4-2. 계산할 때의 함정
- 자동 생성 파일과 잠금 파일: 커밋 수가 압도적으로 많지만 사람이 읽지 않습니다. 먼저 제외합니다.
- 대량 포매팅 커밋: 한 번의 스타일 적용이 모든 파일의 변경 횟수를 똑같이 올립니다. 해당 커밋을 제외합니다.
- 파일 이름 변경: 이력이 끊겨 오래된 핫스팟이 새 파일처럼 보입니다.
- 파일 크기 편향: 큰 파일은 그냥 자주 바뀝니다. 변경 횟수를 줄 수로 나눈 밀도도 함께 봅니다.
4-3. 코드 밖의 신호
핫스팟 분석은 코드 안만 봅니다. 다음 신호들은 목록에 넣을 후보를 코드 밖에서 알려 줍니다.
- 같은 파일에서 반복해서 나는 장애: 회고 문서에서 파일 이름을 세어 봅니다.
- 리뷰에서 반복되는 지적: 같은 지적이 세 번 나오면 그것은 개인 문제가 아니라 구조 문제입니다.
- 온보딩에서 반복되는 질문: 지식 부채의 가장 정직한 지표입니다.
- 배포 전 수동 절차의 수: 런북에서 사람이 손으로 하는 단계를 셉니다.
- "그건 건드리면 안 돼요"라는 말: 이 문장이 나오는 모듈은 예외 없이 후보입니다.
4-4. 정적 분석 점수는 의미가 있는가 — 여기서 의견이 갈립니다
도구가 계산해 주는 부채 점수나 "상환에 필요한 시간" 추정치를 믿을 수 있는지에 대해 의견이 갈립니다. 유용하다는 쪽은 추세를 보는 데는 충분하며 논의의 출발점이 된다고 봅니다. 무의미하다는 쪽은 그 숫자가 규칙 위반 개수에 임의의 가중치를 곱한 것일 뿐이며, 정작 가장 비싼 부채인 설계 결합과 운영 부채는 전혀 보지 못한다고 봅니다.
축은 두 가지입니다. 점수를 절대값으로 쓰는지 추세로 쓰는지, 그리고 그 점수가 실제 변경 비용과 상관관계를 보이는지입니다. 우리 저장소에서 점수와 변경 리드 타임이 함께 움직이지 않는다면 그 점수는 우리에게 의미가 없습니다.
5. 이자를 계산하는 법
5-1. 원금이 아니라 이자가 핵심입니다
부채 항목에 "고치는 데 3주"라고만 적으면 그것은 원금입니다. 원금만으로는 우선순위를 정할 수 없습니다. 필요한 것은 갚지 않았을 때 매달 나가는 비용, 즉 이자입니다.
[이자 추정 — 관측 가능한 양으로만 적는다]
이자(월) = (이 부채 때문에 한 번의 변경에 추가로 드는 시간)
x (그 영역의 월 변경 횟수)
+ (이 부채로 인한 월평균 장애 시간 x 장애 1시간의 비용)
예시
- 결제 모듈 이중 스키마: 변경당 +4시간, 월 6회 변경 → 월 24시간
- 불안정한 E2E 테스트: 재실행 대기 1.5시간 x 주 5회 → 월 30시간
- 수동 배포 점검: 배포당 40분 x 월 20회 → 월 13시간
원금(상환 견적)은 범위로 적는다. 예: 10~15 사람-일
회수 기간 = 원금 / 이자. 위 결제 모듈이면 약 4~6개월
5-2. DORA 지표를 이자의 대리 지표로 쓰기
팀 단위로 이자를 관측하는 가장 손쉬운 방법은 배포 지표입니다. DORA는 변경 리드 타임을 "변경이 버전 관리에 커밋된 시점부터 프로덕션에 배포되기까지 걸리는 시간"으로, 변경 실패율을 "배포 이후 즉각적인 개입이 필요했던 배포의 비율"로, 실패한 배포의 복구 시간을 "즉각적인 개입이 필요한 배포 실패로부터 복구하는 데 걸리는 시간"으로 정의합니다. 재작업률은 프로덕션 장애로 인해 발생한 계획되지 않은 배포의 비율입니다.
부채가 쌓이면 이 지표들이 먼저 움직입니다. 특히 변경 실패율과 복구 시간은 테스트 부채와 운영 부채에 직접 반응합니다.
5-3. "부채를 갚느라 느려진다"는 프레임이 틀린 이유
DORA는 연구 결과 속도와 안정성이 트레이드오프가 아니며 대부분의 팀에서 두 지표가 오히려 상관관계를 보인다고 보고합니다. DORA의 표현대로 장기적으로 실제 트레이드오프는 "더 나은 소프트웨어를 더 빠르게"와 "더 나쁜 소프트웨어를 더 느리게" 사이에 있습니다.
이 결과는 부채 논의에서 자주 쓰이는 "품질과 속도 중 하나를 고르라"는 프레임을 직접 반박합니다. 협상 자리에서 쓸 수 있는 가장 강한 근거이기도 합니다. 세부적인 설득 방법은 기술 부채를 비즈니스 언어로에 있습니다.
5-4. 측정할 때의 주의
- 절대값보다 추세를 봅니다. 다른 팀, 다른 저장소와의 비교는 거의 언제나 잘못된 결론으로 갑니다.
- 지표를 개인 평가에 쓰지 않습니다. 쓰는 순간 기록이 왜곡됩니다.
- 이자 추정은 관측치로만 채웁니다. 근거 없는 추정치가 하나라도 섞이면 목록 전체의 신뢰가 무너집니다.
- 회수 기간이 팀의 계획 지평보다 길면 그 항목은 지금 갚을 항목이 아닙니다.
6. 부채 목록을 만들고 유지하기
6-1. 목록이 없으면 부채는 감정 문제가 됩니다
목록의 목적은 기록이 아니라 비교입니다. 항목끼리 비교할 수 있어야 우선순위가 논쟁이 아니라 계산이 됩니다.
# 예시 — 부채 항목 한 개의 최소 필드
id: DEBT-114
title: 주문 테이블 통화 컬럼 이중 쓰기 잔존
type: data # code | design | test | dependency | data | ops | knowledge
location: services/order/**
quadrant: deliberate-prudent # Fowler 사분면
symptom: 주문 관련 변경마다 두 컬럼을 모두 고쳐야 함, 롤백 시 불일치 위험
interest: 변경당 +4시간, 월 6회 → 월 24시간 # 관측치
principal: 10-15 사람-일 # 견적, 범위로
payoff: 축소 단계 실행 (읽기 이전 완료 확인 후 옛 컬럼 제거)
risk_if_unpaid: 스키마 변경이 비가역이 되어 롤백 불가
external_clock: none # 있으면 날짜를 적는다
owner: order-team
review_by: 2026-11-01 # 재평가 시점
6-2. 유지 규칙
- 항목 수에 상한을 둡니다. 200개짜리 목록은 목록이 아니라 창고입니다. 상한을 넘으면 이자가 낮은 항목부터 닫습니다.
- 정기 재평가 시점을 필드로 넣습니다. 그 날짜에 이자를 다시 관측하지 못하면 항목을 닫습니다.
- 관측치가 없는 항목은 후보로만 둡니다. "느낌상 문제"와 "월 24시간"은 같은 목록에 있으면 안 됩니다.
- 닫힌 항목의 이력을 남깁니다. 갚지 않기로 한 이유가 다음 논의의 출발점이 됩니다.
6-3. 코드 주석은 목록이 아닙니다
코드 안의 TODO와 FIXME는 발견되기 쉽다는 장점이 있지만 우선순위도 소유자도 만료일도 없습니다. 실무 절충은 두 가지를 연결하는 것입니다. 주석에는 목록 항목의 식별자만 남기고, 이자와 계획은 목록에 둡니다. 이렇게 하면 코드를 읽다가 항목을 찾아갈 수 있고, 항목이 닫히면 주석도 함께 사라집니다.
7. 상환 계획을 로드맵에 넣는 방식들
[네 가지 편입 방식 — 전제와 실패 모드]
1. 고정 비율 매 스프린트 용량의 N%를 부채에 배정
전제: 부채가 여러 영역에 분산되어 있다
실패: 비율이 "남는 시간"으로 해석되면 언제나 0이 된다
2. 전용 기간 분기마다 1~2주를 부채에만 사용
전제: 부채가 한 영역에 집중되어 있고 큰 덩어리다
실패: 기간이 끝나면 원래 속도로 다시 쌓인다. 근본 원인은 그대로
3. 기능에 묶기 그 기능이 지나가는 경로의 부채만 함께 상환
전제: 부채와 로드맵의 영역이 겹친다
실패: 아무 기능도 지나가지 않는 영역의 부채는 영원히 남는다
4. 게이팅 특정 부채 상환을 새 기능의 선행 조건으로 지정
전제: 그 부채가 다음 작업을 실제로 막고 있다
실패: 남용하면 모든 기능이 인질이 되고 신뢰를 잃는다
7-1. 여기서 의견이 갈립니다
전용 부채 스프린트와 고정 비율 중 무엇이 나은지는 결론이 나 있지 않습니다. 전용 기간을 지지하는 쪽은 큰 구조 변경이 조각난 시간으로는 불가능하다고 봅니다. 고정 비율을 지지하는 쪽은 전용 기간이 부채를 "특별한 이벤트"로 만들어 평소에는 안 해도 되는 일로 인식시킨다고 봅니다.
축은 세 가지입니다. 부채가 집중되어 있는지 분산되어 있는지, 조직의 계획 주기가 얼마나 경직되어 있는지, 그리고 팀이 자율적으로 시간을 쓸 수 있는 신뢰가 있는지입니다. 실무에서는 분산된 부채에 고정 비율을, 집중된 큰 덩어리에 전용 기간을 쓰는 혼합이 자주 관찰됩니다. 이 조합 권고는 위 자료가 아니라 이 글에서 정리한 것입니다.
7-2. 상환도 배포 가능한 조각이어야 합니다
부채 상환은 대개 리팩터링의 형태를 띱니다. 그래서 리팩터링 완전 가이드의 규칙이 그대로 적용됩니다. 어느 시점에 멈춰도 배포 가능해야 하고, 인터페이스 변경은 확장·이행·축소로 나누고, 큰 상환은 되돌릴 수 있는 조각으로 자릅니다. 3주짜리 부채 상환 브랜치는 부채를 갚는 대신 새로운 부채를 만듭니다.
시스템 규모의 상환이 필요하다면 Strangler Fig 패턴 완벽 가이드가 그 구조를 다룹니다. 투자 판단 자체는 리팩터링의 경제학에 있습니다.
8. 갚지 않기로 결정하는 것도 결정이다
8-1. 갚지 않아도 되는 경우
- 이자가 관측되지 않는 항목: 6개월 동안 그 영역의 변경이 0이었다면 이자는 0입니다.
- 곧 폐기될 코드: 폐기 일정이 확정되어 있다면 상환은 낭비입니다. 다만 "곧"이 2년째 반복되고 있는지 확인해야 합니다.
- 회수 기간이 계획 지평보다 긴 항목: 회수에 3년이 걸리는데 서비스 수명이 1년이면 갚지 않는 것이 옳습니다.
- 상환 자체의 위험이 이자보다 큰 항목: 안전망이 없는 영역의 대규모 리팩터링이 여기 해당합니다.
8-2. 무시와 수용은 다릅니다
무시는 아무 기록이 없는 것이고, 수용은 누가, 언제, 어떤 근거로, 언제까지 갚지 않기로 했는지가 적혀 있는 것입니다. 수용된 항목은 목록에서 삭제하지 말고 상태만 바꿉니다. 재평가 날짜가 오면 이자를 다시 관측하고, 이자가 커졌으면 다시 열립니다.
8-3. 수용할 수 없는 것
외부 시계가 붙은 항목은 수용 대상이 아닙니다. 지원이 종료된 런타임, 규정에서 금지된 인증 방식, 이미 공개된 취약점은 우리 판단으로 미룰 수 있는 항목이 아닙니다. 3-1절에서 본 스펙 항목들이 여기 해당합니다. 이 구분을 목록의 필드로 만들어 두면 논의가 짧아집니다.
9. 다시 쌓이지 않게 하는 장치
9-1. 완료의 정의에 축소 단계를 넣습니다
기술 부채가 다시 쌓이는 가장 흔한 경로는 확장과 이행만 하고 축소를 하지 않는 것입니다. 인터페이스를 병렬로 늘려 놓고 옛 경로를 제거하지 않으면 그 순간부터 이자가 붙기 시작합니다. 완료의 정의에 "옛 경로 제거 티켓이 만들어졌고 만료일이 있다"를 넣는 것이 가장 값싼 방어입니다.
9-2. 리뷰를 기준선으로 씁니다
Google의 코드 리뷰 기준은 리뷰어가 변경이 완벽하지 않더라도 작업 대상 시스템의 전반적인 코드 건강을 확실히 개선하는 상태라면 승인하는 쪽을 택해야 한다고 정리합니다. 이 기준을 그대로 쓰면 리뷰가 부채 유입을 막는 관문이 됩니다. 같은 문서는 필수가 아닌 다듬기 제안에 "Nit: "을 붙이라고 권하고, 리뷰 요청에 응답하기까지 걸려도 되는 최대 시간을 영업일 하루로 정합니다.
리뷰 속도가 중요한 이유는 별도로 있습니다. 같은 자료는 느린 리뷰가 코드 정리와 리팩터링, 기존 변경에 대한 추가 개선을 위축시킨다고 적습니다. 즉 리뷰가 느린 조직에서는 부채 상환이 구조적으로 일어나지 않습니다.
9-3. 자동화로 잠그는 것들
- 린트 예외 목록: 항목 수가 늘면 빌드 실패. 줄어드는 것만 허용
- 폐기 경고: 날짜를 정해 경고를 오류로 승격하는 일정을 미리 커밋
- 의존성 갱신: 자동 PR을 켜고, 밀린 개수를 대시보드에 노출
- 플래그 만료: 만료일이 지난 기능 플래그는 CI에서 실패로 처리
- 커버리지 하한: 전체 수치가 아니라 '변경된 파일'의 커버리지에만 건다
- 수동 배포 단계: 런북의 수동 단계 수를 지표로 추적한다
9-4. 설계 문서로 앞단에서 막기
Design Docs at Google 글은 설계 문서를 코딩 전에 쓰는 비공식 문서로 정의하며, 높은 수준의 구현 전략과 핵심 설계 결정을 트레이드오프에 방점을 두고 담는다고 설명합니다. 그리고 해법이 자명하고 의미 있는 트레이드오프가 없다면 문서를 건너뛰라고 말합니다. 트레이드오프 분석 없이 사실상 구현 매뉴얼이 되어 버린 문서는 그냥 코딩을 시작하라는 신호라는 것입니다.
이 기준은 부채 예방에 그대로 쓸 수 있습니다. 되돌리기 어려운 결정에만 문서를 요구하면, 문서를 쓰는 비용을 들이는 지점과 나중에 가장 비싼 부채가 생기는 지점이 일치하게 됩니다.
퀴즈: 실력을 확인해 보세요
퀴즈 1: 팀이 "가장 복잡도가 높은 파일 10개"를 뽑아 상환 계획을 세웠습니다. 무엇이 빠졌을까요?
정답: 변경 빈도입니다. 복잡하지만 아무도 손대지 않는 파일의 이자는 0입니다.
설명: 부채의 우선순위는 원금이 아니라 이자로 정해집니다. 이자는 대략 "이 부채 때문에 한 번의 변경에 추가로 드는 시간"에 "그 영역의 변경 횟수"를 곱한 값입니다. 버전 관리 이력에서 최근 12개월 변경 횟수를 뽑아 복잡도와 곱한 핫스팟 점수를 쓰면 실제로 비싼 곳이 드러납니다. 자동 생성 파일과 대량 포매팅 커밋은 먼저 제외해야 합니다.
퀴즈 2: 부채 목록의 한 항목에 "코드가 지저분함, 고치는 데 3주"라고만 적혀 있습니다. 무엇을 요구해야 할까요?
정답: 관측된 이자와 갚지 않았을 때의 위험, 그리고 재평가 시점을 요구합니다.
설명: "3주"는 원금이고 원금만으로는 다른 항목과 비교할 수 없습니다. 증상은 관측 가능한 형태로 적어야 하며, 예를 들어 "변경당 추가 4시간, 월 6회 변경"이면 월 24시간이라는 비교 가능한 숫자가 나옵니다. 여기에 갚지 않았을 때의 위험과 재평가 날짜를 더하면 항목이 목록에서 살아 있을 자격을 갖춥니다.
퀴즈 3: 재시도에 백오프도 예산도 없는 서비스가 있습니다. 지난 6개월간 장애는 없었습니다. 이자를 어떻게 적어야 할까요?
정답: 발생 확률과 장애 1회의 비용을 곱한 기댓값으로 적습니다. 평소 이자가 0이라는 점을 항목에 명시합니다.
설명: 운영 부채는 평소에 비용이 나타나지 않고 장애 순간에 한꺼번에 청구되므로, 관측된 시간 비용만 쓰면 우선순위에서 계속 밀립니다. SRE Book은 재시도를 항상 무작위화된 지수 백오프로 예약하라고 권하고, 프로세스당 분당 60회 같은 재시도 예산을 예시로 듭니다. 또 세 계층이 각각 4회 재시도하면 사용자 동작 하나가 64번의 시도가 된다고 지적합니다. 이 증폭 계수를 이자 계산의 근거로 쓸 수 있습니다.
퀴즈 4: 경영진이 "이번 분기는 속도를 내야 하니 부채 상환을 미루자"고 합니다. 어떤 근거로 답할 수 있을까요?
정답: DORA의 연구 결과, 즉 속도와 안정성이 트레이드오프가 아니며 대부분의 팀에서 두 지표가 상관관계를 보인다는 결과를 제시합니다.
설명: DORA는 장기적으로 실제 트레이드오프가 "더 나은 소프트웨어를 더 빠르게"와 "더 나쁜 소프트웨어를 더 느리게" 사이에 있다고 정리합니다. 다만 이 근거는 일반론이므로, 우리 팀의 변경 리드 타임과 변경 실패율 추세를 함께 보여 주어야 설득력이 생깁니다. 특정 항목을 미루는 것 자체는 정당한 결정일 수 있으며, 그 경우 수용으로 기록하고 재평가 날짜를 정하면 됩니다.
퀴즈 5: 지원이 종료된 런타임 위에서 돌고 있는 서비스가 있습니다. 이 항목을 "수용"으로 처리해도 될까요?
정답: 안 됩니다. 외부 시계가 붙은 항목은 우리 판단으로 미룰 수 있는 대상이 아닙니다.
설명: 상환 시점을 우리가 정할 수 있는 항목과 외부가 시한을 정하는 항목은 성격이 다릅니다. 지원 종료된 런타임, 규정에서 금지된 인증 방식, 이미 공개된 취약점이 후자에 해당합니다. 예를 들어 OAuth 2.0 보안 모범 사례인 RFC 9700은 리소스 소유자 비밀번호 자격 증명 그랜트를 사용해서는 안 된다고 규정합니다. 목록에 외부 시계 필드를 두고 날짜를 적어 두면 이런 항목이 일반 항목과 섞이지 않습니다.
마치며
기술 부채에서 가장 어려운 일은 고치는 것이 아닙니다. 어디에 있는지 찾아내고, 거기에 비교 가능한 숫자를 붙이고, 그 숫자를 계속 갱신하는 일입니다. 이 작업이 되어 있으면 상환 결정은 대체로 쉽고, 되어 있지 않으면 어떤 설득 기법도 오래가지 않습니다.
절차를 한 줄로 압축하면 이렇습니다. 부채가 아닌 것을 걸러 내고, 자주 바뀌면서 바꾸기 어려운 곳을 찾고, 이자를 관측치로 적고, 목록으로 비교하고, 상환을 배포 가능한 조각으로 자르고, 갚지 않기로 한 것은 근거와 재평가 날짜와 함께 기록합니다. 그리고 축소 단계를 완료의 정의에 넣어 다시 쌓이지 않게 합니다.
참고 자료
- Technical Debt Quadrant — Martin Fowler — 의도적/우발적과 무모함/신중함의 두 축, 네 사분면의 성격, 프로젝트에서 1년쯤 지나야 최선의 설계를 이해하게 된다는 서술, 그리고 은유가 네 사분면 모두에 유효하다는 요지를 인용했습니다. 2026-08-15 확인.
- DORA metrics: the four keys — DORA — 변경 리드 타임, 변경 실패율, 실패한 배포의 복구 시간, 재작업률의 정의와 "속도와 안정성은 트레이드오프가 아니다"라는 결론을 인용했습니다. 2026-08-15 확인.
- Addressing Cascading Failures — Google SRE Book — 연쇄 장애의 정의, 무작위화된 지수 백오프 권고, 분당 60회 재시도 예산 예시, 세 계층 4회 재시도가 64회가 되는 계산을 인용했습니다. 2026-08-15 확인.
- Design Docs at Google — Industrial Empathy — 설계 문서의 정의와 트레이드오프 중심이라는 성격, 자명한 해법에는 문서를 건너뛰라는 조언, 그리고 문서가 시간이 지나며 현실과 어긋난다는 서술을 인용했습니다. 2026-08-15 확인.
- Code Review Developer Guide — Google — 전반적인 코드 건강을 확실히 개선한다면 완벽하지 않아도 승인하라는 기준과 "Nit: " 관행을, 그리고 리뷰 속도 문서에서 느린 리뷰가 정리와 리팩터링을 위축시킨다는 서술과 영업일 하루 기준을 인용했습니다. 2026-08-15 확인.
- RFC 9700 — OAuth 2.0 Security Best Current Practice — 리소스 소유자 비밀번호 자격 증명 그랜트를 사용해서는 안 된다는 규정과, 암묵적 그랜트에 대한 권고를 인용했습니다. 2026-08-15 확인.
- OWASP Session Management Cheat Sheet — 인증 토큰과 세션 식별자를 브라우저 저장소에 두지 말라는 권고와 그 이유를 인용했습니다. 2026-08-15 확인.
- 2절의 부채 판별 기준, 3절의 유형 표, 4절의 핫스팟 계산과 함정 목록, 5절의 이자 추정 식, 6절의 항목 템플릿과 유지 규칙, 7절의 네 가지 편입 방식, 8절의 수용 기준, 9절의 자동화 목록은 위 자료에 나오는 것이 아니라 이 글에서 정리한 절차입니다.
이어서 읽기
- 이 블로그의 관련 글: 기술 부채를 비즈니스 언어로
- 이 블로그의 관련 글: 리팩터링의 경제학, 언제 비용이 회수되나
- 이 블로그의 관련 글: SLI/SLO/Error Budget 기반 신뢰성 엔지니어링
- 이 블로그의 관련 글: Strangler Fig 패턴 완벽 가이드
- 이 블로그의 관련 글: JWT와 세션, 무엇을 언제
- 관련 도구: SLO 에러 버짓 계산기
완전 가이드 시리즈
The Complete Guide to Technical Debt: Identify, Measure, Repay
- Introduction
- 1. What the metaphor originally meant, and how it gets misused
- 2. Things that are not debt
- 3. A typology of debt
- 4. Finding where it is — change frequency and complexity
- 5. How to calculate the interest
- 6. Building and maintaining the debt list
- 7. Ways to put repayment into the roadmap
- 8. Deciding not to repay is also a decision
- 9. Mechanisms that stop it accumulating again
- Quiz: Check your understanding
- Wrapping up
- References
- Further reading
Introduction
This blog already has Technical Debt in Business Language. That post covers how to explain debt to stakeholders and negotiate its priority — it is an article about persuasion.
This post is the step before that. To have something to persuade anyone about, you first have to find where the debt is and attach a number to it. "The code is messy" with no evidence behind it never wins at the negotiating table. So the subject here is identification, measurement, and the procedure for maintaining a list.
Reduced to one sentence: claiming debt exists and showing what it costs per month are different acts, and only the second one moves a budget.
1. What the metaphor originally meant, and how it gets misused
1-1. Fowler's quadrant
Martin Fowler splits technical debt along two axes: was it deliberate or inadvertent, and was it reckless or prudent?
Reckless Prudent
Deliberate "We don't have time for design" "Ship now, repay later"
skipped without knowing chose it knowing the cost
the price
Inadvertent "What's layering?" "Now we know how we should
did not know better have done it"
learned it by building it
Fowler emphasises the inadvertent-prudent quadrant in particular: it can take a year of programming on a project before you understand what the best design approach should have been. And his point is that the metaphor is useful across all four quadrants, not just the deliberate ones.
1-2. What the distinction does in practice
The quadrant is not a classification game; it matters because the prescription differs.
- Deliberate and prudent: fix the repayment schedule and conditions on the spot. Skip that and it looks like inadvertent debt next quarter.
- Deliberate and reckless: a process problem. Instead of blaming a person, look at why that choice was possible — deadlines and review standards.
- Inadvertent and reckless: a learning and review problem. When the same mistake recurs it is onboarding and guidance that are missing, not competence.
- Inadvertent and prudent: normal. A team ashamed of this stops feeding what it learns back into the code.
1-3. This is contested
Whether the "technical debt" metaphor is useful at all is a genuine argument. The objection is that the phrase launders bad engineering in financial vocabulary. Debt presupposes a deliberate choice and a repayment plan, yet in practice the same word gets attached to bad code produced with no plan whatsoever. The defence is that it is close to the only shared language available for talking with non-technical stakeholders.
Three axes: whether there really was intent, whether a repayment plan exists in writing, and what behaviour the word induces in the listener. Used without a plan, the objection is right; used with one, the defence is.
2. Things that are not debt
Filter before you build the list. There is one test: does it charge extra for future changes? If it does not, it is not debt.
- Differences of taste: code written in a different style is not debt. Put style arguments on the debt list and the whole list loses credibility.
- Old code that does not change: a module nobody has touched in three years that works correctly earns zero interest, and therefore has zero priority.
- Abstraction you do not need yet: not generalising is a judgement, not a debt. Duplication before the third instance is usually fine.
- Bugs: defects, not debt. Defects get fixed; debt gets repaid or explicitly not repaid.
- Unfinished features: that is the backlog.
- Deliberately simple design: lacking extensibility and having decided to forgo extensibility are different things.
Conversely there are things rarely recognised as debt that are: slow CI, manual pre-deployment checks, environments that do not reproduce locally, and a state where a new hire needs two weeks to land a first commit. None of them show up in the code, and all of them charge interest on every change.
3. A typology of debt
The point of typing debt is that discovery and repayment differ by type.
| Type | Where the interest shows up | How to find it |
|---|---|---|
| Code | Time per change, repeated review comments | Hotspot analysis, review comment counts |
| Design/architecture | One feature touches many modules at once | Change coupling, files that change together |
| Test | CI time, flaky failures, fear of deploying | Failure rate, duration, coverage gaps |
| Dependency/security | Cannot upgrade, spec violations still present | Vulnerability scans, end-of-support dates |
| Data/schema | Migration risk, deployments you cannot reverse | Un-contracted columns, leftover dual writes |
| Operations | Incident frequency, recovery time, manual steps | Incident retros, manual steps in runbooks |
| Knowledge/docs | Onboarding time, the same question repeated | Days to onboard, question logs |
3-1. Debt with an external clock
We usually choose when to repay, but some items have their deadline set from outside. Mark these separately on the list.
- Changes in security specifications: RFC 9700, the OAuth 2.0 security best current practice, states that the resource owner password credentials grant "MUST NOT be used", and that clients "SHOULD NOT" use the implicit grant unless access token injection in the authorization response is prevented. Items like these are bound to audit and review calendars, not to ours.
- Storage location problems: the OWASP Session Management Cheat Sheet says not to store authentication tokens, session IDs, JWTs or refresh tokens in
localStorageorsessionStorage, because those APIs are accessible to any JavaScript executing in the origin, so a single XSS vulnerability discloses every token. The trade-offs are laid out in JWT vs. Sessions: Which, and When.
3-2. Operational debt only bills you during an incident
The Google SRE Book defines a cascading failure as a failure that grows over time as a result of positive feedback, and recommends that you always use randomised exponential backoff when scheduling retries. Its example retry budget is 60 retries per minute in a process. It also points out that retries multiply across layers: three layers retrying four times each turn one user action into 64 attempts.
Having no backoff, no budget and no load shedding costs nothing on an ordinary day. Because the interest is billed all at once during an incident, the item keeps losing on priority. For this type, write the interest as "probability of occurrence times cost per incident" or it will not survive on the list.
3-3. Knowledge debt
The Design Docs at Google article notes that design docs, like all documentation, tend to get out of sync with reality over time. The response is to update the original, amend it, or link the follow-ups. More dangerous than a stale document is a state where nobody knows whether it is stale.
4. Finding where it is — change frequency and complexity
4-1. Look for expensive code, not bad code
The most common mistake in debt discovery is starting from the messiest file. A messy file nobody touches earns zero interest. What you want is code that changes often and is hard to change.
# Example — pulling the most frequently changed files out of version control history
# Last 12 months, top 30 files by commit count
git log --since='12 months ago' --name-only --pretty=format: \
| grep -v '^$' | sort | uniq -c | sort -rn | head -30
# Multiply this list by complexity or line count to get a hotspot score
# score = change count x complexity metric
# The top 10 are this quarter's candidates
4-2. Traps in the calculation
- Generated files and lockfiles: overwhelming commit counts that no human reads. Exclude them first.
- Mass formatting commits: one style application raises every file's change count equally. Exclude that commit.
- File renames: history breaks, and an old hotspot looks like a new file.
- Size bias: large files simply change more often. Also look at change count divided by line count.
4-3. Signals outside the code
Hotspot analysis only sees inside the code. These signals nominate candidates from outside it.
- Repeated incidents in the same file: count file names across retro documents.
- Repeated review comments: the same comment appearing three times is a structural problem, not a personal one.
- Repeated onboarding questions: the most honest indicator of knowledge debt.
- The number of manual pre-deployment steps: count the steps a human performs in the runbook.
- "Don't touch that one": any module that sentence attaches to is a candidate, without exception.
4-4. Do static-analysis debt scores mean anything — this is contested
Whether you can trust a tool's debt score or its "time to remediate" estimate is disputed. The useful camp argues that it is good enough for tracking a trend and gives the discussion a starting point. The meaningless camp argues that the number is just a count of rule violations times arbitrary weights, and that it cannot see design coupling or operational debt — the two most expensive kinds.
Two axes: whether you use the score as an absolute or as a trend, and whether it correlates with your actual change costs. If the score and change lead time do not move together in your repository, that score means nothing for you.
5. How to calculate the interest
5-1. The interest matters, not the principal
An item that says only "three weeks to fix" states the principal. Principal alone cannot prioritise anything. What you need is what it costs every month while unpaid — the interest.
[Estimating interest — write only observable quantities]
interest(month) = (extra time this debt adds to one change)
x (number of changes in that area per month)
+ (monthly outage hours caused by it x cost of one outage hour)
Examples
- Dual schema in the payment module: +4h per change, 6 changes/month → 24h/month
- Flaky E2E tests: 1.5h waiting on re-runs x 5 times/week → 30h/month
- Manual pre-deploy checks: 40 min per deploy x 20 deploys/month → 13h/month
Write the principal (repayment estimate) as a range, e.g. 10-15 person-days
Payback period = principal / interest. For the payment module above, about 4-6 months
5-2. Using DORA metrics as a proxy for interest
The easiest way to observe interest at team level is through deployment metrics. DORA defines change lead time as the amount of time it takes for a change to go from committed to version control to deployed in production, change fail rate as the ratio of deployments that require immediate intervention following a deployment, and failed deployment recovery time as the time it takes to recover from a deployment that fails and requires immediate intervention. Rework rate is the ratio of unplanned deployments caused by a production incident.
When debt accumulates these metrics move first. Change fail rate and recovery time in particular respond directly to test debt and operational debt.
5-3. Why "repaying debt slows us down" is the wrong frame
DORA reports that its research has repeatedly demonstrated that speed and stability are not trade-offs, and that for most teams the metrics are correlated. In DORA's words, the real trade-off over long periods of time is between better software faster and worse software slower.
That result directly refutes the "pick either quality or speed" frame that dominates debt conversations, and it is the strongest single piece of evidence available in a negotiation. The persuasion side is covered in Technical Debt in Business Language.
5-4. Cautions when measuring
- Watch the trend, not the absolute value. Comparisons against other teams or repositories almost always reach a wrong conclusion.
- Never use the metrics for individual evaluation. The moment you do, the records distort.
- Fill interest estimates with observations only. One unfounded number contaminates the credibility of the whole list.
- If the payback period exceeds the team's planning horizon, that item is not one to repay now.
6. Building and maintaining the debt list
6-1. Without a list, debt becomes an emotional argument
The purpose of the list is not record-keeping but comparison. Only when items can be compared does prioritisation become arithmetic rather than argument.
# Example — the minimum fields of one debt item
id: DEBT-114
title: Dual writes remain on the orders currency column
type: data # code | design | test | dependency | data | ops | knowledge
location: services/order/**
quadrant: deliberate-prudent # Fowler quadrant
symptom: every order change must edit both columns; rollback risks inconsistency
interest: +4h per change, 6 changes/month → 24h/month # observed
principal: 10-15 person-days # estimate, as a range
payoff: run the contract phase (drop the old column after reads have moved)
risk_if_unpaid: schema changes become irreversible, rollback impossible
external_clock: none # if any, put the date here
owner: order-team
review_by: 2026-11-01 # re-evaluation date
6-2. Maintenance rules
- Cap the number of items. A 200-item list is a warehouse, not a list. Past the cap, close the lowest-interest items.
- Make the re-evaluation date a field. If you cannot re-observe the interest on that date, close the item.
- Items with no observation stay candidates. "Feels bad" and "24 hours a month" must not sit in the same list.
- Keep the history of closed items. Why you decided not to repay is the starting point of the next discussion.
6-3. Code comments are not a list
TODO and FIXME in code have the advantage of being discoverable, but they carry no priority, no owner and no expiry. The practical compromise is to connect the two: leave only the list item's identifier in the comment, and keep the interest and the plan in the list. Then you can navigate from code to item, and when the item closes the comment goes with it.
7. Ways to put repayment into the roadmap
[Four ways in — assumptions and failure modes]
1. Fixed percentage allocate N% of every sprint's capacity to debt
Assumes: debt is spread across many areas
Fails when: the percentage is read as "spare time" and becomes zero
2. Dedicated period one or two weeks per quarter on debt only
Assumes: debt is concentrated in one area and comes in large lumps
Fails when: normal speed resumes afterwards and the root cause is untouched
3. Bundled with features repay only the debt on the path a feature crosses
Assumes: debt and roadmap areas overlap
Fails when: areas no feature crosses keep their debt forever
4. Gating make repaying a specific debt a precondition for new work
Assumes: that debt genuinely blocks the next task
Fails when: overused — every feature becomes a hostage and trust erodes
7-1. This is contested
Whether dedicated debt sprints or a fixed capacity percentage works better is unresolved. The dedicated-period camp argues that large structural changes are impossible in fragmented time. The fixed-percentage camp argues that a dedicated period turns debt into a special event, teaching everyone that it is optional the rest of the time.
Three axes: whether debt is concentrated or spread, how rigid the organisation's planning cycle is, and whether the team is trusted to allocate its own time. In practice a mix shows up often: a fixed percentage for spread-out debt, a dedicated period for a big concentrated lump. That combination recommendation is this article's, not the sources'.
7-2. Repayment must also come in deployable pieces
Repayment usually takes the shape of refactoring, so the rules in The Complete Guide to Refactoring apply directly. Stay deployable no matter when you stop, split interface changes into expand-migrate-contract, and cut large repayments into reversible pieces. A three-week debt-repayment branch creates new debt instead of clearing old debt.
If repayment is needed at system scale, The Complete Guide to the Strangler Fig Pattern covers that structure, and the investment decision itself is in The Economics of Refactoring.
8. Deciding not to repay is also a decision
8-1. When you do not have to repay
- Items with no observed interest: if there were zero changes in that area over six months, the interest is zero.
- Code about to be retired: with a confirmed retirement date, repaying is waste. Check that "soon" has not been repeating for two years.
- Payback period longer than the planning horizon: if payback takes three years and the service has a one-year lifespan, not repaying is correct.
- Items where repayment risk exceeds the interest: a large refactoring in an area with no safety net belongs here.
8-2. Ignoring and accepting are different
Ignoring leaves no record. Accepting means it is written down who decided, when, on what grounds, and until when not to repay. Do not delete accepted items from the list; change their state. When the re-evaluation date arrives, observe the interest again, and if it grew, reopen.
8-3. What cannot be accepted
Items with an external clock are not eligible for acceptance. An unsupported runtime, an authentication method prohibited by a specification, or an already-published vulnerability is not something our judgement can defer. The spec items in section 3-1 belong here. Making this distinction a field on the list shortens the discussion.
9. Mechanisms that stop it accumulating again
9-1. Put the contract phase in the definition of done
The most common path by which debt re-accumulates is doing expand and migrate but never contract. Widen an interface in parallel, never remove the old path, and interest starts accruing from that moment. Adding "a removal ticket exists and has an expiry date" to the definition of done is the cheapest available defence.
9-2. Use review as the baseline
Google's code review standard says that reviewers should favour approving a change once it definitely improves the overall code health of the system being worked on, even if it is not perfect. Adopt that criterion verbatim and review becomes the gate against debt inflow. The same document recommends prefixing non-mandatory polish with "Nit: " and fixes one business day as the maximum time to respond to a review request.
Review speed matters for a separate reason. The same source notes that slow reviews discourage code cleanups, refactorings, and further improvements to existing changes. In an organisation with slow reviews, debt repayment structurally does not happen.
9-3. What to lock down with automation
- Lint exception lists: fail the build when the count grows. Only shrinking is allowed
- Deprecation warnings: commit in advance to a date when warnings become errors
- Dependency updates: turn on automated PRs and show the backlog count on a dashboard
- Flag expiry: fail CI on feature flags past their expiry date
- Coverage floor: gate on the coverage of changed files, not the global number
- Manual deploy steps: track the number of manual steps in the runbook as a metric
9-4. Blocking it up front with design docs
The Design Docs at Google article defines a design doc as an informal document written before coding that captures the high level implementation strategy and key design decisions with emphasis on the trade-offs. It also says to skip the doc when the solution is obvious and has no meaningful trade-offs; a document that has become an implementation manual without trade-off analysis is a sign you should just start coding.
That criterion transfers straight to debt prevention. Require a document only for hard-to-reverse decisions and the place where you spend documentation effort coincides with the place where the most expensive debt would otherwise appear.
Quiz: Check your understanding
Quiz 1: A team picked "the 10 most complex files" and built a repayment plan around them. What is missing?
Answer: Change frequency. A complex file nobody touches earns zero interest.
Explanation: Debt priority is set by interest, not principal. Interest is roughly "extra time this debt adds to one change" times "number of changes in that area". Pull the last 12 months of change counts from version control history and multiply by complexity to get a hotspot score, and the genuinely expensive places appear. Exclude generated files and mass formatting commits first.
Quiz 2: A debt item reads only "code is messy, three weeks to fix". What do you ask for?
Answer: Observed interest, the risk of not repaying, and a re-evaluation date.
Explanation: "Three weeks" is the principal, and principal alone cannot be compared with other items. The symptom has to be written observably — "+4 hours per change, 6 changes a month" yields a comparable 24 hours a month. Add the risk of leaving it unpaid and a re-evaluation date and the item has earned its place on the list.
Quiz 3: A service retries with no backoff and no budget, and has had no incidents in six months. How do you write its interest?
Answer: As an expected value: probability of occurrence times cost per incident. State explicitly that day-to-day interest is zero.
Explanation: Operational debt shows no cost on ordinary days and bills everything during an incident, so writing only observed time costs keeps it losing on priority. The SRE Book recommends always scheduling retries with randomised exponential backoff and offers a retry budget such as 60 per minute in a process. It also notes that three layers retrying four times each turn one user action into 64 attempts — that amplification factor is usable as the basis of the interest calculation.
Quiz 4: Leadership says "we need speed this quarter, so let's defer debt repayment". What can you answer with?
Answer: DORA's finding that speed and stability are not trade-offs and that for most teams the metrics are correlated.
Explanation: DORA states that over long periods the real trade-off is between better software faster and worse software slower. That is a general result, though, so pair it with your own team's change lead time and change fail rate trends to be persuasive. Deferring a specific item can be a legitimate decision — record it as accepted and set a re-evaluation date.
Quiz 5: A service runs on an unsupported runtime. Can that item be marked "accepted"?
Answer: No. Items with an external clock are not something our judgement can defer.
Explanation: Items whose repayment date we choose and items whose deadline is set from outside are different in kind. Unsupported runtimes, authentication methods prohibited by a specification, and already-published vulnerabilities are the latter. For example RFC 9700, the OAuth 2.0 security best current practice, states that the resource owner password credentials grant must not be used. Keeping an external-clock field with a date on the list stops such items mixing in with ordinary ones.
Wrapping up
The hardest part of technical debt is not fixing it. It is finding where it lives, attaching comparable numbers to it, and keeping those numbers current. With that work done, repayment decisions are mostly easy; without it, no persuasion technique lasts long.
Compressed into one line: filter out what is not debt, find what changes often and is hard to change, write the interest from observations, compare through a list, cut repayment into deployable pieces, and record what you decided not to repay along with the grounds and the re-evaluation date. Then put the contract phase into the definition of done so it does not pile up again.
References
- Technical Debt Quadrant — Martin Fowler — cited for the deliberate/inadvertent and reckless/prudent axes, the character of the four quadrants, the observation that it can take a year of programming before you understand the best design approach, and the point that the metaphor holds across all four. Checked 2026-08-15.
- DORA metrics: the four keys — DORA — cited for the definitions of change lead time, change fail rate, failed deployment recovery time and rework rate, and for the conclusion that speed and stability are not trade-offs. Checked 2026-08-15.
- Addressing Cascading Failures — Google SRE Book — cited for the definition of cascading failure, the randomised exponential backoff recommendation, the 60-retries-per-minute budget example, and the three-layers-times-four-retries calculation. Checked 2026-08-15.
- Design Docs at Google — Industrial Empathy — cited for the definition of a design doc and its trade-off emphasis, the advice to skip it when the solution is obvious, and the observation that docs get out of sync with reality. Checked 2026-08-15.
- Code Review Developer Guide — Google — cited for approving a change that definitely improves overall code health even when imperfect, and the "Nit: " convention; and the review speed document for slow reviews discouraging cleanups and refactorings and the one-business-day standard. Checked 2026-08-15.
- RFC 9700 — OAuth 2.0 Security Best Current Practice — cited for the prohibition on the resource owner password credentials grant and the recommendation on the implicit grant. Checked 2026-08-15.
- OWASP Session Management Cheat Sheet — cited for the recommendation not to keep authentication tokens and session identifiers in browser storage, and the reason. Checked 2026-08-15.
- The debt test in section 2, the type table in section 3, the hotspot calculation and trap list in section 4, the interest formula in section 5, the item template and maintenance rules in section 6, the four roadmap approaches in section 7, the acceptance criteria in section 8 and the automation list in section 9 are procedures assembled in this article rather than taken from the sources above.
Further reading
- Related post on this blog: Technical Debt in Business Language
- Related post on this blog: The Economics of Refactoring: When Does It Pay Off?
- Related post on this blog: SLI/SLO/Error Budget-based Reliability Engineering
- Related post on this blog: The Complete Guide to the Strangler Fig Pattern
- Related post on this blog: JWT vs. Sessions: Which, and When
- Related tool: SLO & Error Budget Calculator
The Complete Guide series