Split View: 리뷰 가능한 PR 쓰기: 작은 PR, 좋은 설명, 스택 PR로 리뷰어를 빠르게 만드는 법
리뷰 가능한 PR 쓰기: 작은 PR, 좋은 설명, 스택 PR로 리뷰어를 빠르게 만드는 법
"좋은 PR은 리뷰어에게 주는 선물이다. 나쁜 PR은 리뷰어에게 떠넘기는 숙제다."
프롤로그 — 작성자가 리뷰 지연을 통제한다
"왜 내 PR은 항상 늦게 머지될까?" 많은 개발자가 이 질문의 답을 리뷰어에게서 찾습니다. 리뷰어가 바쁘다, 우선순위가 낮다, 팀이 느리다. 일부는 사실입니다. 하지만 가장 큰 변수는 다른 곳에 있습니다. PR의 리뷰 지연 시간(review latency)은 대부분 작성자가 결정합니다.
리뷰어가 PR을 열었을 때 마주치는 것이 800줄짜리 디프에 설명 한 줄("기능 추가")이라면, 그 PR은 "나중에"로 미뤄집니다. 리뷰어도 사람이고, 큰 인지 부하를 요구하는 작업은 자연스럽게 뒤로 밀립니다. 반대로 120줄짜리 디프에 "무엇을·왜·어떻게 검증했는지"가 또렷하게 적혀 있다면, 리뷰어는 커피를 기다리는 5분 동안 그것을 끝냅니다.
이 글은 코드 리뷰를 하는 법에 관한 글이 아닙니다. 그건 리뷰어의 기술이고, 리뷰 에티켓·머지 큐·리뷰 우선순위는 받는 쪽의 이야기입니다. 이 글은 PR을 쓰는 법 — 작성자가 리뷰어의 일을 쉽고 빠르게 만들기 위해 할 수 있는 모든 것에 관한 글입니다.
좋은 PR은 협상이 아니라 배려다
PR을 "내 코드를 통과시키는 관문"으로 보는 사람과 "리뷰어와 함께 코드를 검증하는 자리"로 보는 사람은 전혀 다른 PR을 만듭니다. 전자는 디프를 크게 만들고, 설명을 짧게 쓰고, 질문에 방어적으로 답합니다. 후자는 디프를 작게 자르고, 맥락을 충분히 적고, 리뷰어가 어디부터 봐야 할지 안내합니다.
배려는 추상적인 덕목이 아닙니다. 측정 가능한 행동입니다 — PR을 작게 자르는 것, 설명에 검증 방법을 적는 것, 포매팅 변경을 분리하는 것, 셀프 리뷰를 먼저 도는 것. 이 글은 그 행동들의 목록입니다.
이 글에서 다룰 내용:
| 장 | 주제 |
|---|---|
| 1장 | 작은 PR이 이기는 이유 — 인지 절벽 |
| 2장 | 하나의 PR = 하나의 아이디어 |
| 3장 | PR 설명 — 가장 중요한 산출물 |
| 4장 | 읽히는 디프 만들기 — 커밋 위생 |
| 5장 | 스택 PR / 스택 디프 |
| 6장 | 리뷰 요청 전 셀프 리뷰 |
| 7장 | 리뷰 피드백에 잘 답하는 법 |
| 8장 | 드래프트 PR과 준비 완료 신호 |
| 9장 | AI 시대의 PR 작성 |
| 에필로그 | 체크리스트 + 안티패턴 + 다음 글 예고 |
1장 · 작은 PR이 이기는 이유
PR 작성의 모든 조언 중 가장 효과가 큰 단 하나를 고르라면, 작게 만들어라입니다. 다른 모든 기술은 이것 위에 쌓입니다.
1.1 리뷰 품질은 PR 크기에 반비례한다
리뷰어의 집중력은 무한하지 않습니다. 디프가 커질수록, 한 줄당 들이는 주의력은 줄어듭니다. 그래서 큰 PR에서는 역설이 생깁니다 — 변경이 많을수록 각 변경은 덜 검토됩니다.
| PR 크기 | 리뷰어의 실제 행동 | 결과 |
|---|---|---|
| ~50줄 | 모든 줄을 읽고 생각한다 | 버그·설계 문제를 잡는다 |
| ~200줄 | 핵심만 읽고 나머지는 훑는다 | 표면적인 것만 잡는다 |
| ~500줄 | "큰 문제 없어 보이네" 하고 승인 | 사실상 검토 안 됨 |
| 1000줄+ | 죄책감과 함께 승인하거나 무한정 미룬다 | 리뷰가 형식이 된다 |
500줄짜리 PR에 달린 "LGTM"은 리뷰가 아니라 항복입니다. 작성자가 정말 원하는 것이 검토라면, 검토 가능한 크기로 줘야 합니다.
1.2 인지 절벽
PR 크기와 리뷰 품질의 관계는 선형이 아닙니다. 어느 지점을 넘으면 급격히 떨어지는 절벽(cliff) 이 있습니다. 정확한 숫자는 팀과 코드베이스마다 다르지만, 경험적으로 그 절벽은 대략 200~400줄 사이에 있습니다.
절벽 아래에서 리뷰어는 PR 전체를 머릿속에 한 번에 담을 수 있습니다. 절벽 위에서는 그게 불가능해지고, 리뷰어는 PR을 "조각으로" 읽기 시작합니다. 조각으로 읽으면 조각 사이의 상호작용 — 가장 버그가 많이 숨는 곳 — 을 놓칩니다.
목표는 단순합니다. 모든 PR을 절벽 아래에 두는 것. 작업이 크면 작업을 자르는 것이지, PR을 키우는 것이 아닙니다.
1.3 작은 PR의 복리 효과
작은 PR은 리뷰 품질만 올리는 게 아닙니다.
- 빠른 피드백 — 작은 PR은 빨리 리뷰되고, 빨리 머지되고, 빨리 배포됩니다. 잘못된 방향이면 일찍 알게 됩니다.
- 쉬운 롤백 — 문제가 생겼을 때 되돌릴 단위가 작습니다. 800줄 중 어디가 문제인지 찾는 대신, 80줄짜리 PR 하나를 되돌립니다.
- 적은 충돌 — 작은 PR은 오래 살지 않으므로
main과 멀어질 시간이 없습니다. 리베이스 지옥이 줄어듭니다. - 명확한 히스토리 —
git log가 "기능 추가" 같은 거대 커밋이 아니라, 의미 단위의 변경 기록이 됩니다.
작게 만드는 것은 비용이 아니라 투자입니다. 자르는 데 드는 10분이, 큰 PR이 만드는 며칠의 지연을 막습니다.
2장 · 하나의 PR = 하나의 아이디어
작게 만드는 것만큼 중요한 것이 하나만 담는 것입니다. 크기가 작아도 두 개의 무관한 변경이 섞이면 리뷰는 어려워집니다.
2.1 "섞인 PR 금지" 규칙
가장 흔하고 가장 해로운 안티패턴은 리팩터링과 동작 변경을 한 PR에 섞는 것입니다.
리뷰어가 변경된 함수를 봅니다. 이름이 바뀌었고, 위치가 옮겨졌고, 들여쓰기가 달라졌고, 그리고 그 안에서 if 조건 하나가 바뀌었습니다. 리뷰어는 이제 질문할 수 없습니다 — "이 동작 변경이 의도된 것인가, 아니면 리팩터링하다 실수한 것인가?" 노이즈가 시그널을 덮었습니다.
규칙은 단순합니다.
하나의 PR은 하나의 아이디어만 담는다. 리팩터링과 동작 변경은 절대 같은 PR에 넣지 않는다.
순서도 정해져 있습니다. 리팩터링 먼저, 동작 변경 나중. 리팩터링 PR은 "동작은 동일함, 구조만 변경"이라고 선언하면 리뷰어가 빠르게 훑을 수 있습니다. 그다음 동작 변경 PR은 깨끗한 토대 위에서 작은 디프로 들어옵니다.
2.2 무엇이 "하나의 아이디어"인가
| 잘못된 묶음 | 올바르게 자른 PR |
|---|---|
| 기능 A + 기능 A를 위한 리팩터링 + 무관한 오타 수정 | PR 1: 리팩터링 / PR 2: 기능 A / PR 3: 오타 |
| 버그 수정 + "하는 김에" 한 의존성 업그레이드 | PR 1: 의존성 업그레이드 / PR 2: 버그 수정 |
| 새 API 엔드포인트 + 전체 파일 포매팅 | PR 1: 포매팅 / PR 2: 엔드포인트 |
| 기능 + 그 기능의 테스트 | 한 PR에 둘 다 — 테스트는 기능의 일부다 |
마지막 줄이 핵심입니다. "하나의 아이디어"는 "하나의 파일"이나 "하나의 커밋"이 아닙니다. 기능과 그 기능의 테스트는 같은 아이디어이므로 함께 갑니다. 반면 기능과 무관한 오타는 작아도 다른 아이디어이므로 분리합니다.
2.3 "하는 김에"는 경고음이다
PR을 만들다 보면 유혹이 옵니다 — "이 파일 보는 김에 저 죽은 코드도 지우자", "여기 온 김에 저 변수명도 고치자". 이 "하는 김에"가 바로 섞인 PR이 태어나는 순간입니다.
원칙: "하는 김에"가 떠오르면 멈추고, 그것을 별도 PR이나 티켓으로 만든다. 좋은 발견을 버리라는 게 아닙니다. 그 발견을 별도의 리뷰 가능한 단위로 옮기라는 것입니다.
3장 · PR 설명 — 가장 중요한 산출물
PR에서 가장 많이 쓰이고 가장 적게 공들여지는 부분이 설명(description) 입니다. 많은 작성자가 디프를 쓰는 데 두 시간을 쓰고 설명에 20초를 씁니다. 이건 거꾸로입니다.
3.1 설명은 디프가 답하지 못하는 것을 답한다
디프는 무엇이(what) 바뀌었는지를 보여줍니다. 디프가 절대 보여주지 못하는 것은 왜(why) 입니다. 그리고 리뷰의 핵심 질문은 거의 항상 "왜"입니다 — "왜 이렇게 했는가", "왜 다른 방법이 아닌가", "왜 지금인가".
설명이 없으면 리뷰어는 "왜"를 추측해야 하고, 추측은 틀리고, 틀린 추측 위에 달린 리뷰 코멘트는 작성자와 리뷰어 모두의 시간을 태웁니다.
3.2 좋은 PR 설명의 여섯 가지 요소
| 요소 | 답해야 할 질문 | 없으면 생기는 일 |
|---|---|---|
| 무엇(What) | 이 PR이 무엇을 바꾸는가? | 리뷰어가 디프 전체를 읽고 요약해야 한다 |
| 왜(Why) | 왜 이 변경이 필요한가? | 리뷰어가 의도를 추측한다 |
| 어떻게(How) | 어떤 접근을 택했고 왜 그것인가? | 설계 의도가 전달되지 않는다 |
| 검증(Verification) | 어떻게 확인했는가? | "테스트는 했나요?" 왕복이 발생한다 |
| 범위 밖(Out of scope) | 이번에 하지 않는 것은? | "이것도 고쳐야 하는 거 아닌가요?" 코멘트가 달린다 |
| 시각 자료(Screenshots) | UI 변경이 어떻게 보이는가? | 리뷰어가 직접 브랜치를 받아 띄운다 |
이 여섯 가지 중 "범위 밖" 이 가장 자주 빠지고 가장 효과가 큽니다. "이 PR은 X를 하지 않습니다 — 그건 후속 PR입니다"라는 한 줄이, 리뷰어가 달려던 코멘트 다섯 개를 미리 막습니다.
3.3 리뷰어를 위한 읽기 가이드
큰 변경이 불가피할 때(예: 자동 생성된 파일이 포함될 때), 설명에 읽는 순서를 적어줍니다.
## 리뷰 가이드
- 핵심 로직은 `src/auth/session.ts` 입니다. 여기부터 보세요.
- `src/generated/*` 는 codegen 산출물입니다. 훑고 넘어가도 됩니다.
- `package-lock.json` 변경은 의존성 추가의 부수 효과입니다.
이 한 블록이 "디프가 600줄"이라는 사실의 체감 무게를 "실제로 봐야 할 건 80줄"로 바꿉니다.
3.4 복사해서 쓰는 PR 설명 템플릿
아래는 그대로 복사해 .github/pull_request_template.md 로 쓰거나, 팀 위키에 박제해 둘 수 있는 템플릿입니다. 바깥 펜스가 네 개의 백틱인 이유는 안쪽에 코드 블록과 ## 헤더가 들어 있기 때문입니다.
## 무엇 (What)
{이 PR이 바꾸는 것을 한두 문장으로}
## 왜 (Why)
{이 변경이 필요한 이유. 연결된 이슈: #123}
## 어떻게 (How)
{택한 접근과 그 이유. 고려했지만 버린 대안이 있으면 한 줄}
## 검증 (How to verify)
- [ ] {리뷰어가 따라 할 수 있는 확인 단계}
- [ ] {추가한/수정한 테스트}
- [ ] {수동 확인 절차 — 있으면}
```bash
# 재현/검증 명령 — 있으면
npm test -- src/auth
```
## 범위 밖 (Out of scope)
- {이번에 하지 않는 것 — 그리고 어디서 다룰지}
## 스크린샷 (UI 변경 시)
| 변경 전 | 변경 후 |
| --- | --- |
| {img} | {img} |
## 리뷰 가이드 (디프가 클 때)
- {어디부터 보면 되는지}
- {훑고 넘어가도 되는 파일}
칸을 다 채울 필요는 없습니다. UI 변경이 없으면 스크린샷 칸은 지웁니다. 하지만 무엇·왜·검증 세 칸은 어떤 PR에도 남깁니다. 이 세 칸이 리뷰 지연의 90%를 줄입니다.
3.5 제목도 설명이다
PR 제목은 git log에 영원히 남습니다. "fix", "update", "wip" 같은 제목은 6개월 뒤 아무 정보도 주지 않습니다.
- 나쁨:
update - 나쁨:
버그 수정 - 좋음:
auth: 세션 만료 시 토큰 자동 갱신 추가 - 좋음:
fix: 빈 장바구니에서 결제 버튼 비활성화
규칙: 제목은 "이 PR이 머지되면 무엇이 달라지는가"를 한 줄로 말한다. 접두어(auth:, fix:)로 영역과 종류를 신호하면 더 좋습니다.
4장 · 읽히는 디프 만들기 — 커밋 위생
PR을 작게, 하나의 아이디어로 만들었어도, 그 안의 커밋이 엉망이면 리뷰는 여전히 어렵습니다. 디프는 읽히도록 구성되어야 합니다.
4.1 논리적 커밋
리뷰어는 PR을 두 가지 방식으로 봅니다 — 전체 디프를 한 번에, 또는 커밋을 하나씩. 후자가 가능하려면 커밋이 논리적 단위여야 합니다.
| 나쁜 커밋 히스토리 | 좋은 커밋 히스토리 |
|---|---|
wip | auth: SessionToken 타입 추가 |
fix | auth: 토큰 만료 검사 로직 구현 |
fix again | auth: 만료 임박 시 자동 갱신 연결 |
oops | test: 세션 갱신 시나리오 테스트 추가 |
address review | (리뷰 반영은 4.3 참고) |
좋은 히스토리에서 리뷰어는 커밋 하나하나를 따라가며 "이 변경이 왜 이 순서로 일어났는지"를 이해할 수 있습니다. 각 커밋은 그 자체로 빌드·테스트를 통과하는 것이 이상적입니다.
4.2 포매팅 변경은 따로
자동 포매터(Prettier, gofmt, black 등)를 돌리면 의미 없는 줄들이 디프를 가득 채웁니다. 이 줄들이 진짜 변경을 덮습니다.
원칙: 포매팅·이름 변경 같은 기계적 변경은 별도 커밋, 가능하면 별도 PR로 분리한다. "전체 파일 포매팅" 커밋 하나는 리뷰어가 git diff -w(공백 무시)로 1초 만에 훑고 넘어갈 수 있습니다. 하지만 그게 동작 변경과 같은 커밋에 섞이면, 리뷰어는 한 줄 한 줄 봐야 합니다.
4.3 리뷰 전 히스토리 정리
PR을 올리기 전, 작업 중 쌓인 wip / fix / oops 커밋을 정리합니다.
# 작업 브랜치에서: 마지막 5개 커밋을 논리 단위로 재구성
git rebase -i HEAD~5
# 또는 전부 합쳐서 다시 논리적으로 쪼개기
git reset --soft main
git add -p # 의미 단위로 골라 담기
주의: 이미 리뷰가 시작된 PR에서는 함부로 히스토리를 갈아엎지 않습니다(4.3 후반과 7장 참고). 정리는 첫 리뷰 요청 전에 끝내는 것이 원칙입니다.
4.4 한 줄짜리 무관한 변경을 섞지 않기
디프에 import 순서만 바뀐 줄, 안 쓰는 공백이 지워진 줄이 끼어 있으면 리뷰어는 "이건 의도된 건가?"를 매번 멈춰서 판단해야 합니다. 에디터가 자동으로 만든 변경은 커밋 전에 git add -p로 걸러냅니다.
5장 · 스택 PR / 스택 디프
작업을 작게 자르려는데 변경들이 서로 의존할 때가 있습니다. PR 2가 PR 1의 코드 위에서만 동작하는 경우입니다. 이때 스택 PR(stacked PRs) 이 답입니다.
5.1 스택 PR이란
스택 PR은 PR을 일렬로 쌓는 것입니다. 각 PR은 main이 아니라 바로 아래 PR의 브랜치를 베이스로 합니다.
main
└─ PR 1: auth 타입 추가 (base: main)
└─ PR 2: 토큰 검증 로직 (base: PR 1)
└─ PR 3: 자동 갱신 UI 연결 (base: PR 2)
리뷰어는 PR 1부터 차례로 봅니다. 각 PR은 작고, 하나의 아이디어만 담고, 디프는 바로 아래 PR과의 차이만 보여줍니다. 800줄짜리 단일 PR이 200줄짜리 PR 네 개가 됩니다.
5.2 언제 스택을 쓰는가
| 상황 | 스택? |
|---|---|
| 큰 기능을 의존성 있는 단계로 나눌 수 있다 | 예 — 전형적인 스택 사용처 |
| 리팩터링 → 그 위에 동작 변경 | 예 — PR 1 리팩터, PR 2 동작 |
| 변경들이 서로 독립적이다 | 아니오 — 그냥 별도 PR을 병렬로 |
| 단계가 다섯 개를 넘어간다 | 주의 — 너무 깊은 스택은 관리 비용이 큼 |
스택은 의존성이 있을 때만 씁니다. 독립적인 변경을 굳이 쌓으면, 아래 PR이 막혔을 때 위 PR도 같이 막힙니다.
5.3 스택 운영의 현실
스택의 가장 큰 비용은 베이스 PR이 바뀌면 위의 모든 PR을 리베이스해야 한다는 것입니다.
# PR 1이 리뷰 반영으로 바뀜 → PR 2를 PR 1 위로 다시 리베이스
git checkout pr-2-branch
git rebase pr-1-branch
# 스택 전체 갱신은 도구의 도움을 받는 게 낫다
# (Graphite, Sapling, spr 등 스택 전용 도구)
손으로 스택을 관리하면 리베이스가 금방 지옥이 됩니다. 스택을 자주 쓰는 팀은 스택 전용 도구(Graphite, Sapling, spr, ghstack 등)를 도입하는 것이 거의 필수입니다. 도구가 스택 전체의 리베이스·재제출을 한 번에 처리해 줍니다.
5.4 스택을 쓸 수 없을 때의 차선책
스택 도구가 없거나 팀이 스택에 익숙하지 않다면, 차선책은 하나의 PR 안에서 논리적 커밋으로 단계를 나누는 것입니다(4.1 참고). 리뷰어에게 "커밋 단위로 봐주세요"라고 설명에 적습니다. 스택만큼 깔끔하지는 않지만, 거대 단일 디프보다는 훨씬 낫습니다.
6장 · 리뷰 요청 전 셀프 리뷰
리뷰 요청 버튼을 누르기 전에 해야 할 일이 하나 있습니다. 자기 PR을 리뷰어의 눈으로 한 번 보는 것.
6.1 왜 셀프 리뷰가 효과적인가
작업 중에는 코드를 "쓰는 사람"의 시점에 갇혀 있습니다. PR을 올리고 디프 화면에서 다시 보면 시점이 바뀝니다. 에디터에서는 안 보이던 것들이 디프 뷰에서 보입니다 — 두고 온 console.log, 주석 처리한 코드, 디버그용 하드코딩, 안 지운 TODO, 빠진 에러 처리.
리뷰어가 이런 것을 잡아주길 기대하면 안 됩니다. 그건 리뷰어의 주의력을 사소한 것에 쓰게 만들고, 정작 설계 를 볼 여력을 빼앗습니다.
6.2 셀프 리뷰 체크리스트
리뷰 요청 전, GitHub/GitLab의 "Files changed" 탭을 열고 한 줄씩 봅니다.
- 디버그 흔적이 없는가 (
console.log,print, 주석 처리된 코드, 하드코딩된 값) - 무관한 변경이 섞이지 않았는가 (포매팅, import 순서, 우연한 공백)
- 모든 새 분기에 테스트가 있는가
- 에러·엣지 케이스가 처리되었는가 (빈 값, null, 실패 경로)
- PR 설명의 "검증" 칸을 내가 실제로 따라 할 수 있는가
- 디프가 절벽 아래인가 — 아니라면 자를 수 있는가
- 커밋 히스토리가 논리적인가
- 리뷰어가 처음 마주칠 줄이 가장 중요한 줄인가
마지막 항목이 미묘합니다. 디프 안에서 가장 중요한 변경이 가장 잘 보이도록 파일 순서나 커밋 순서를 조정할 수 있습니다.
6.3 셀프 리뷰는 시간을 절약한다
셀프 리뷰에 5분을 쓰면, 리뷰 왕복이 한 번 줄어듭니다. 리뷰 왕복 한 번은 보통 반나절에서 하루입니다(리뷰어가 다시 시간을 낼 때까지의 대기 포함). 5분 대 반나절. 이보다 수익률 높은 투자는 드뭅니다.
7장 · 리뷰 피드백에 잘 답하는 법
PR을 올리면 코멘트가 달립니다. 여기서 작성자의 태도가 다음 PR의 리뷰 속도까지 결정합니다.
7.1 모든 코멘트에 응답한다
리뷰 코멘트는 두 가지로 끝나야 합니다 — 반영했거나, 반영하지 않은 이유를 설명했거나. 무응답으로 남은 코멘트는 리뷰어에게 "내 의견이 무시됐다"는 신호를 줍니다.
| 코멘트 유형 | 좋은 응답 |
|---|---|
| 명백한 버그 지적 | 고치고 "수정했습니다 (커밋 abc123)" |
| 더 나은 접근 제안 | 동의하면 반영, 아니면 "이 방법을 택한 이유는 X입니다. 어떻게 생각하세요?" |
| 취향 차이 | 사소하면 그냥 반영(논쟁 비용 > 반영 비용), 또는 팀 컨벤션을 근거로 |
| 질문 | 코드가 아니라 코멘트로 답하고, 필요하면 코드에 주석 추가 |
| 범위 밖 제안 | "좋은 지적입니다. 이번 PR 범위 밖이라 #456 으로 만들었습니다" |
7.2 방어하지 말고 이해하라
리뷰 코멘트를 공격으로 받아들이면 응답이 방어적이 됩니다. 방어적인 응답은 리뷰어를 지치게 하고, 다음번에 그 작성자의 PR을 꼼꼼히 보고 싶지 않게 만듭니다.
코멘트가 틀린 것 같을 때조차, 첫 반응은 "왜 리뷰어가 이렇게 봤을까" 입니다. 리뷰어가 오해했다면, 그 오해 자체가 정보입니다 — 코드가 그렇게 오해될 만큼 불명확하다는 뜻이니까요. 코드를 명확하게 고치거나, 주석을 추가합니다.
7.3 리뷰 반영 커밋을 어떻게 쌓을까
리뷰가 이미 시작된 PR에서는 새 커밋으로 반영하는 것이 기본입니다. address review feedback 같은 커밋을 추가하면, 리뷰어가 "1차 리뷰 이후 무엇이 바뀌었는지"만 따로 볼 수 있습니다. 이미 본 부분을 다시 볼 필요가 없습니다.
머지 직전, 팀 컨벤션에 따라 이 커밋들을 squash 할 수 있습니다. 핵심은 리뷰가 진행 중인 동안에는 히스토리를 보존하고, 머지 시점에 정리하는 것입니다. 리뷰 도중 force-push로 히스토리를 갈아엎으면 리뷰어가 자기 코멘트의 맥락을 잃습니다.
8장 · 드래프트 PR과 준비 완료 신호
PR에는 두 가지 상태가 있습니다 — "아직 보지 마세요"와 "이제 봐주세요". 이 둘을 명확히 신호하지 않으면 리뷰어의 시간이 낭비됩니다.
8.1 드래프트 PR의 용도
드래프트(Draft) PR은 "코드는 올라왔지만 아직 리뷰 요청은 아니다"라는 명시적 신호입니다. 쓰는 경우:
- CI를 미리 돌려보고 싶을 때 — 빌드·테스트 결과만 먼저 확인
- 방향에 대한 조기 피드백을 받고 싶을 때 — "전체를 보지 말고, 이 접근이 맞는지만 봐주세요"라고 명시
- 작업이 진행 중임을 팀에 보이고 싶을 때 — 중복 작업 방지
드래프트의 핵심은 리뷰어의 기대를 관리하는 것입니다. 드래프트에 "방향만 봐주세요"라고 적으면, 리뷰어는 변수명이나 테스트 누락을 지적하지 않습니다 — 그건 아직 볼 단계가 아니니까요.
8.2 "준비 완료"를 명확히 신호하라
드래프트를 "Ready for review"로 전환하는 것 자체가 신호입니다. 하지만 그것만으로는 부족할 때가 있습니다.
- 드래프트에서 받은 조기 피드백을 반영했다면, 무엇을 바꿨는지 코멘트로 남깁니다.
- 리뷰 요청 시 누구에게 요청하는지 명확히 합니다 — "아무나"는 보통 "아무도"가 됩니다.
- PR이 다른 PR이나 배포에 블로킹이라면 그 긴급성을 적습니다.
8.3 드래프트를 방치하지 마라
오래된 드래프트 PR은 노이즈입니다. 며칠째 멈춰 있는 드래프트는 팀에게 "이 작업이 살아 있는가 죽었는가"를 헷갈리게 합니다. 드래프트는 활성 작업일 때만 열어두고, 멈췄으면 닫거나 상태를 코멘트로 남깁니다.
9장 · AI 시대의 PR 작성
2026년, PR 작성의 풍경이 바뀌었습니다. 코드의 상당 부분을 AI 에이전트가 생성하고, PR 설명도 AI가 초안을 잡을 수 있습니다. 하지만 핵심 원칙은 바뀌지 않았습니다 — 오히려 더 중요해졌습니다.
9.1 AI가 잘하는 것: 설명 초안과 셀프 리뷰
AI는 PR 작성의 특정 부분에서 탁월합니다.
- PR 설명 초안 — 디프를 입력으로 주면 "무엇·어떻게"를 요약해 줍니다. 단, "왜" 는 사람만 압니다 — AI 초안의 "왜" 칸은 작성자가 반드시 다시 씁니다.
- 셀프 리뷰 보조 — "이 디프에서 두고 온 디버그 코드, 빠진 에러 처리, 테스트 없는 분기를 찾아줘"는 AI가 잘하는 작업입니다. 6장의 체크리스트를 AI에게 1차로 돌리게 할 수 있습니다.
- 커밋 메시지 정리 — 논리적 커밋으로 재구성할 때 메시지 초안을 잡아줍니다.
9.2 AI가 못하는 것: 책임과 맥락
AI가 절대 대신할 수 없는 것이 있습니다.
- "왜"의 진짜 맥락 — 이 변경이 어떤 사업적·기술적 결정에서 나왔는지는 사람의 머릿속에만 있습니다.
- 검토되지 않은 출력에 대한 책임 — PR에 올린 코드는, AI가 썼든 사람이 썼든, 작성자가 보증하는 코드입니다.
9.3 절대 하지 말 것: 검토되지 않은 에이전트 출력 덤프
AI 시대의 가장 해로운 안티패턴은 이것입니다 — 에이전트가 생성한 코드를 작성자가 읽지도 않고 PR로 올리는 것.
이건 리뷰어에게 "내가 안 본 코드를 당신이 봐주세요"라고 말하는 것과 같습니다. 리뷰의 사회적 계약이 깨집니다. 리뷰어는 "작성자가 한 번 검토한 코드"를 검토한다고 가정하는데, 그 가정이 거짓이 되면 리뷰어는 작성자가 했어야 할 1차 검토까지 떠안게 됩니다.
규칙: AI가 생성한 코드도 사람이 쓴 코드와 똑같은 PR 기준을 통과해야 한다. 작게 자르고, 하나의 아이디어만 담고, 작성자가 셀프 리뷰를 돌고, 작성자가 모든 줄을 이해하고 보증한다. AI는 디프를 빨리 만들어 줄 뿐, 리뷰 가능하게 만드는 책임은 여전히 작성자의 것입니다.
9.4 AI 시대에 작은 PR이 더 중요해진 이유
AI는 코드를 빠르게, 많이 만들 수 있습니다. 이것은 작은 PR 원칙을 더 중요하게 만듭니다 — 생성 속도가 빨라질수록, 그것을 검토 가능한 단위로 자르는 규율이 없으면 리뷰어는 더 빨리 익사합니다.
AI는 작성 속도를 올렸을 뿐, 리뷰 대역폭을 올리지 않았습니다. 그 격차를 메우는 것이 작성자의 PR 작성 기술입니다.
에필로그 — 좋은 PR은 리뷰어에게 주는 선물이다
PR의 리뷰 지연 시간은 작성자가 통제합니다. 리뷰어가 느린 게 아니라, 리뷰 불가능한 PR이 느린 것입니다. 작게 자르고, 하나의 아이디어만 담고, 설명에 "왜"와 "검증"을 적고, 디프를 읽히게 구성하고, 올리기 전에 셀프 리뷰를 돌면 — 같은 리뷰어가 같은 코드를 몇 배 빠르게 승인합니다.
이건 리뷰어를 위한 호의이자, 결국 작성자 자신을 위한 일입니다. 빠른 리뷰는 빠른 머지이고, 빠른 머지는 빠른 피드백이며, 빠른 피드백은 잘못된 방향을 일찍 잡습니다. 좋은 PR을 쓰는 기술은 협업 스킬인 동시에, 자기 작업의 사이클을 짧게 만드는 기술입니다.
리뷰 가능한 PR 체크리스트
- 크기 — 디프가 인지 절벽 아래다 (대략 200~400줄 미만)
- 하나의 아이디어 — 리팩터링과 동작 변경을 섞지 않았다
- 무엇 — PR 설명이 바뀐 것을 한두 문장으로 요약한다
- 왜 — 이 변경이 필요한 이유가 적혀 있다 (AI 초안이라면 사람이 다시 썼다)
- 검증 — 리뷰어가 따라 할 수 있는 확인 단계가 있다
- 범위 밖 — 이번에 하지 않는 것이 명시되어 있다
- 시각 자료 — UI 변경이면 스크린샷이 붙어 있다
- 커밋 위생 — 커밋이 논리적 단위이고, 포매팅이 분리되어 있다
- 셀프 리뷰 — "Files changed"를 한 줄씩 봤고 디버그 흔적이 없다
- 신호 — 드래프트/준비 완료 상태와 리뷰 대상자가 명확하다
피해야 할 안티패턴
- 거대 PR — 800줄에 "기능 추가" 한 줄. LGTM은 항복이지 리뷰가 아니다
- 섞인 PR — 리팩터링 + 동작 변경 + 오타가 한 디프에. 시그널이 노이즈에 묻힌다
- "하는 김에" — 무관한 변경을 슬쩍 끼워 넣지 않는다. 별도 PR/티켓으로
- 빈 설명 — "왜"와 "검증" 없이 디프만 던지지 않는다
- 포매팅 혼입 — 자동 포매터 결과를 동작 변경과 같은 커밋에 섞지 않는다
- 방어적 응답 — 리뷰 코멘트를 공격으로 받지 않는다. 오해는 코드가 불명확하다는 정보다
- 리뷰 중 force-push — 리뷰가 진행 중일 때 히스토리를 갈아엎지 않는다
- 에이전트 출력 덤프 — 자기가 안 읽은 AI 코드를 PR로 올리지 않는다
다음 글 예고
다음 글은 **"리뷰어의 기술 — 빠르고 친절하고 엄격한 코드 리뷰를 하는 법"**입니다. 이번 글이 PR을 쓰는 쪽의 기술이었다면, 다음 글은 PR을 읽는 쪽의 기술입니다. 무엇을 먼저 보는지, 어떤 코멘트가 도움이 되고 어떤 코멘트가 노이즈인지, 승인과 변경 요청의 기준선을 어디에 두는지 — 그리고 AI 리뷰 봇과 사람 리뷰어의 역할을 어떻게 나누는지를 다룹니다.
좋은 PR을 쓰는 데 드는 10분은, 리뷰 왕복 한 번의 반나절을 막습니다. 그 10분은 리뷰어에게 주는 선물이자, 미래의 자신에게 보내는 투자입니다.
Authoring Reviewable Pull Requests: Small PRs, Good Descriptions, and Stacked Diffs
"A good PR is a gift to the reviewer. A bad PR is homework dumped on them."
Prologue — The author controls review latency
"Why does my PR always take so long to merge?" Many engineers look for the answer in the reviewer: they're busy, it's low priority, the team is slow. Some of that is true. But the biggest variable lives somewhere else. A PR's review latency is mostly decided by its author.
When a reviewer opens a PR and finds an 800-line diff with a one-line description ("add feature"), that PR gets pushed to "later." Reviewers are people too, and work that demands a heavy cognitive load naturally slides to the back of the queue. Conversely, a 120-line diff that clearly states "what, why, and how to verify" gets finished in the five minutes a reviewer waits for coffee.
This is not a post about how to do code review. That's the reviewer's craft — review etiquette, merge queues, review prioritization belong to the receiving side. This is a post about how to author a PR: everything the author can do to make the reviewer's job easy and fast.
A good PR is consideration, not negotiation
People who see a PR as "the gate my code has to pass through" and people who see it as "a place to verify code together with a reviewer" produce completely different PRs. The former makes the diff big, writes a short description, and answers questions defensively. The latter cuts the diff small, supplies enough context, and guides the reviewer on where to start.
Consideration is not an abstract virtue. It is a set of measurable behaviors — cutting the PR small, writing the verification steps in the description, separating formatting changes, doing a self-review first. This post is a list of those behaviors.
What this post covers:
| Chapter | Topic |
|---|---|
| 1 | Why small PRs win — the cognitive cliff |
| 2 | One PR = one idea |
| 3 | The PR description — your most important artifact |
| 4 | Making the diff readable — commit hygiene |
| 5 | Stacked PRs / stacked diffs |
| 6 | Self-review before requesting review |
| 7 | Responding well to review feedback |
| 8 | Draft PRs and signaling readiness |
| 9 | Authoring PRs in the AI era |
| Epilogue | Checklist + anti-patterns + next post teaser |
Chapter 1 · Why small PRs win
If you had to pick the single most effective piece of PR-authoring advice, it would be: make it small. Every other technique stacks on top of this one.
1.1 Review quality is inversely proportional to PR size
A reviewer's focus is not infinite. As the diff grows, the attention spent per line shrinks. So big PRs produce a paradox — the more you change, the less each change is examined.
| PR size | What the reviewer actually does | Result |
|---|---|---|
| ~50 lines | Reads and thinks about every line | Catches bugs and design issues |
| ~200 lines | Reads the core, skims the rest | Catches only surface-level issues |
| ~500 lines | "Looks fine, no big problems" → approve | Effectively unreviewed |
| 1000+ lines | Approves with guilt, or defers indefinitely | Review becomes a formality |
An "LGTM" on a 500-line PR is not a review — it's a surrender. If what the author truly wants is scrutiny, they have to hand over a scrutinizable size.
1.2 The cognitive cliff
The relationship between PR size and review quality is not linear. Past a certain point there is a cliff where it drops off sharply. The exact number varies by team and codebase, but empirically that cliff sits somewhere around 200 to 400 lines.
Below the cliff, the reviewer can hold the whole PR in their head at once. Above it, that becomes impossible, and the reviewer starts reading the PR "in pieces." Reading in pieces means missing the interactions between the pieces — which is exactly where bugs hide most.
The goal is simple: keep every PR below the cliff. If the work is large, you cut the work — you do not grow the PR.
1.3 The compounding effect of small PRs
Small PRs don't just raise review quality.
- Fast feedback — Small PRs get reviewed fast, merged fast, deployed fast. If the direction is wrong, you find out early.
- Easy rollback — When something breaks, the unit you revert is small. Instead of hunting through 800 lines for the culprit, you revert one 80-line PR.
- Fewer conflicts — Small PRs don't live long, so they have no time to drift from
main. Rebase hell shrinks. - Clear history —
git logbecomes a record of meaningful units of change, not a giant "add feature" commit.
Going small is not a cost; it's an investment. The ten minutes spent cutting prevents the multi-day delay a big PR creates.
Chapter 2 · One PR = one idea
Just as important as going small is carrying only one thing. Even a small PR becomes hard to review when two unrelated changes are mixed into it.
2.1 The "no mixed PRs" rule
The most common and most harmful anti-pattern is mixing a refactor with a behavior change in one PR.
The reviewer looks at a changed function. The name changed, the location moved, the indentation is different, and somewhere inside it one if condition changed. The reviewer now can't ask the question — "Is this behavior change intentional, or a mistake made while refactoring?" The noise has buried the signal.
The rule is simple.
One PR carries one idea. Never put a refactor and a behavior change in the same PR.
The order is fixed too: refactor first, behavior change second. A refactor PR can declare "behavior identical, structure only changed," and the reviewer can skim it fast. Then the behavior-change PR arrives as a small diff on a clean foundation.
2.2 What counts as "one idea"
| Wrong bundle | Correctly split PRs |
|---|---|
| Feature A + a refactor for Feature A + an unrelated typo fix | PR 1: refactor / PR 2: Feature A / PR 3: typo |
| Bug fix + a dependency upgrade done "while we're at it" | PR 1: dependency upgrade / PR 2: bug fix |
| New API endpoint + whole-file reformatting | PR 1: formatting / PR 2: endpoint |
| Feature + the tests for that feature | Both in one PR — the tests are part of the feature |
The last row is the key. "One idea" is not "one file" or "one commit." A feature and its tests are the same idea, so they go together. An unrelated typo, however small, is a different idea, so it gets separated.
2.3 "While I'm at it" is a warning siren
While building a PR, temptation arrives — "while I'm in this file, let me delete that dead code," "while I'm here, let me rename that variable." That "while I'm at it" is the exact moment a mixed PR is born.
The principle: when "while I'm at it" pops up, stop, and turn it into a separate PR or ticket. This is not about throwing away a good discovery. It's about moving that discovery into its own reviewable unit.
Chapter 3 · The PR description — your most important artifact
The most-used and least-cared-for part of a PR is the description. Many authors spend two hours on the diff and twenty seconds on the description. That's backwards.
3.1 The description answers what the diff cannot
The diff shows what changed. What the diff can never show is why. And the central question of review is almost always "why" — "why this way," "why not another way," "why now."
Without a description the reviewer has to guess the "why," the guess is wrong, and review comments built on a wrong guess burn the time of both author and reviewer.
3.2 The six elements of a good PR description
| Element | Question it answers | What happens without it |
|---|---|---|
| What | What does this PR change? | The reviewer has to read the whole diff and summarize it |
| Why | Why is this change needed? | The reviewer guesses the intent |
| How | What approach did you take, and why that one? | The design intent doesn't get communicated |
| Verification | How did you confirm it? | A "did you test it?" round trip happens |
| Out of scope | What is not being done this time? | "Shouldn't this be fixed too?" comments pile up |
| Screenshots | How does the UI change look? | The reviewer pulls the branch and runs it themselves |
Of these six, "out of scope" is the most often missing and the most effective. A single line — "this PR does not do X — that's a follow-up PR" — preempts five comments the reviewer was about to write.
3.3 A reading guide for the reviewer
When a large change is unavoidable (for example, when generated files are included), write the reading order in the description.
## Review guide
- The core logic is in `src/auth/session.ts`. Start here.
- `src/generated/*` is codegen output. Skim and move on.
- The `package-lock.json` change is a side effect of the dependency addition.
This one block changes the felt weight of "the diff is 600 lines" into "you actually need to look at 80 lines."
3.4 A copyable PR description template
Below is a template you can copy directly into .github/pull_request_template.md or pin in your team wiki. The outer fence uses four backticks because it contains code blocks and ## headers inside.
## What
{What this PR changes, in one or two sentences}
## Why
{Why this change is needed. Linked issue: #123}
## How
{The approach taken and why. One line on any alternative considered and dropped}
## How to verify
- [ ] {A check step the reviewer can follow}
- [ ] {Tests added / modified}
- [ ] {Manual verification steps — if any}
```bash
# Reproduction / verification command — if any
npm test -- src/auth
```
## Out of scope
- {What is not being done this time — and where it will be handled}
## Screenshots (for UI changes)
| Before | After |
| --- | --- |
| {img} | {img} |
## Review guide (when the diff is large)
- {Where to start}
- {Files you can skim and move past}
You don't have to fill every field. If there's no UI change, delete the screenshots field. But keep the three fields — What, Why, Verification — in every PR. Those three cut 90% of review latency.
3.5 The title is a description too
The PR title lives in git log forever. Titles like "fix," "update," "wip" give zero information six months later.
- Bad:
update - Bad:
bug fix - Good:
auth: add automatic token refresh on session expiry - Good:
fix: disable the checkout button on an empty cart
The rule: the title states, in one line, what changes once this PR is merged. Signaling the area and the kind with a prefix (auth:, fix:) is even better.
Chapter 4 · Making the diff readable — commit hygiene
Even if you made the PR small and single-idea, review is still hard if the commits inside it are a mess. The diff has to be structured to be read.
4.1 Logical commits
A reviewer reads a PR in two ways — the whole diff at once, or commit by commit. For the latter to be possible, the commits have to be logical units.
| Bad commit history | Good commit history |
|---|---|
wip | auth: add the SessionToken type |
fix | auth: implement the token expiry check |
fix again | auth: wire up auto-refresh near expiry |
oops | test: add tests for session refresh scenarios |
address review | (review responses: see 4.3) |
In a good history the reviewer can walk commit by commit and understand "why this change happened in this order." Ideally each commit, on its own, passes the build and tests.
4.2 Formatting changes go separate
Running an auto-formatter (Prettier, gofmt, black, etc.) fills the diff with meaningless lines. Those lines bury the real change.
The principle: mechanical changes like formatting and renaming go in a separate commit, and ideally a separate PR. A single "whole-file formatting" commit lets the reviewer skim it in one second with git diff -w (ignore whitespace). But mix it into the same commit as a behavior change, and the reviewer has to inspect line by line.
4.3 Cleaning up history before review
Before opening the PR, clean up the wip / fix / oops commits that piled up during work.
# On the work branch: restructure the last 5 commits into logical units
git rebase -i HEAD~5
# Or squash everything and re-split it logically
git reset --soft main
git add -p # pick changes into meaningful units
Caution: do not casually rewrite history on a PR where review has already started (see the rest of 4.3 and Chapter 7). As a rule, finish the cleanup before the first review request.
4.4 Don't mix in one-line unrelated changes
When the diff contains a line where only import order changed, or a line where unused whitespace was removed, the reviewer has to stop every time and judge "is this intentional?" Filter out editor-generated changes with git add -p before committing.
Chapter 5 · Stacked PRs / stacked diffs
Sometimes you want to cut work small but the changes depend on each other — PR 2 only works on top of PR 1's code. This is where stacked PRs are the answer.
5.1 What stacked PRs are
Stacked PRs are PRs stacked in a line. Each PR is based not on main but on the branch of the PR directly below it.
main
└─ PR 1: add auth types (base: main)
└─ PR 2: token validation (base: PR 1)
└─ PR 3: wire auto-refresh UI (base: PR 2)
The reviewer goes through PR 1 onward in order. Each PR is small, carries one idea, and the diff shows only the difference from the PR directly below. A single 800-line PR becomes four 200-line PRs.
5.2 When to use a stack
| Situation | Stack? |
|---|---|
| A big feature can be split into dependent steps | Yes — the classic use of a stack |
| Refactor → behavior change on top of it | Yes — PR 1 refactor, PR 2 behavior |
| The changes are independent of each other | No — just open separate PRs in parallel |
| The steps go beyond five | Caution — a too-deep stack has high management cost |
Use a stack only when there's a dependency. If you stack independent changes for no reason, then when a lower PR is blocked, the upper PR is blocked with it.
5.3 The reality of operating a stack
The biggest cost of a stack is that when a base PR changes, you have to rebase every PR above it.
# PR 1 changed from review feedback → rebase PR 2 back onto PR 1
git checkout pr-2-branch
git rebase pr-1-branch
# Updating the whole stack is better done with tool help
# (Graphite, Sapling, spr, and other stack-specific tools)
Manage a stack by hand and the rebases quickly become hell. For teams that use stacks often, adopting a stack-specific tool (Graphite, Sapling, spr, ghstack, etc.) is nearly essential. The tool handles rebasing and re-submitting the whole stack in one shot.
5.4 The fallback when you can't use a stack
If you have no stack tooling or the team isn't comfortable with stacks, the fallback is splitting the steps into logical commits within a single PR (see 4.1). Tell the reviewer in the description, "please review commit by commit." It isn't as clean as a stack, but it's far better than one giant monolithic diff.
Chapter 6 · Self-review before requesting review
There is one thing to do before pressing the request-review button: look at your own PR through the reviewer's eyes.
6.1 Why self-review works
While working, you're trapped in the viewpoint of the person "writing" the code. Push the PR and look again on the diff screen, and the viewpoint shifts. Things invisible in the editor become visible in the diff view — a left-behind console.log, commented-out code, a debug hardcode, an undeleted TODO, a missing error handler.
You should not expect the reviewer to catch these. That makes the reviewer spend their attention on trivia and strips away the bandwidth to look at the design.
6.2 Self-review checklist
Before requesting review, open the "Files changed" tab on GitHub/GitLab and look line by line.
- No debug traces (
console.log,print, commented-out code, hardcoded values) - No unrelated changes mixed in (formatting, import order, accidental whitespace)
- Every new branch has a test
- Errors and edge cases are handled (empty values, null, failure paths)
- The "Verification" field in the PR description is something I can actually follow
- The diff is below the cliff — if not, can I cut it?
- The commit history is logical
- The first line the reviewer hits is the most important line
The last item is subtle. You can adjust file order or commit order so that the most important change is the most visible within the diff.
6.3 Self-review saves time
Spend five minutes on self-review and you cut one review round trip. One review round trip is usually half a day to a full day (including the wait until the reviewer frees up again). Five minutes versus half a day. There are few investments with a higher return.
Chapter 7 · Responding well to review feedback
Once you open a PR, comments arrive. Here the author's attitude decides even the review speed of their next PR.
7.1 Respond to every comment
A review comment should end in one of two ways — you applied it, or you explained why you didn't. A comment left with no response signals to the reviewer that "my opinion was ignored."
| Comment type | A good response |
|---|---|
| A clear bug pointed out | Fix it and say "fixed (commit abc123)" |
| A better approach suggested | Apply it if you agree; if not, "I chose this way because X. What do you think?" |
| A matter of taste | If trivial, just apply it (cost of arguing > cost of applying), or cite the team convention |
| A question | Answer with a comment, not just code, and add a code comment if needed |
| An out-of-scope suggestion | "Good catch. It's out of scope for this PR, so I filed it as #456" |
7.2 Don't defend — understand
Take a review comment as an attack and your response turns defensive. A defensive response wears the reviewer down and makes them not want to look closely at that author's PR next time.
Even when a comment seems wrong, the first reaction is "why did the reviewer see it this way?" If the reviewer misunderstood, the misunderstanding itself is information — it means the code is unclear enough to be misread that way. Fix the code to be clear, or add a comment.
7.3 How to stack review-response commits
On a PR where review has already started, applying changes as new commits is the default. Adding a commit like address review feedback lets the reviewer look separately at "what changed since the first review." They don't have to re-read parts they've already seen.
Right before merge, you can squash these commits per the team convention. The key is to preserve history while review is in progress and clean up at merge time. Rewrite history with a force-push mid-review and the reviewer loses the context of their own comments.
Chapter 8 · Draft PRs and signaling readiness
A PR has two states — "don't look yet" and "please look now." Fail to signal these clearly and the reviewer's time is wasted.
8.1 What draft PRs are for
A Draft PR is an explicit signal that "the code is up, but this is not a review request yet." Use it when:
- You want to run CI early — just check the build and test results first
- You want early feedback on direction — explicitly say "don't review the whole thing, just whether this approach is right"
- You want to show the team work is in progress — to prevent duplicate work
The core of a draft is managing the reviewer's expectations. Write "just review the direction" on a draft, and the reviewer won't point out variable names or missing tests — that's not the stage to look at yet.
8.2 Signal "ready" clearly
Converting a draft to "Ready for review" is itself a signal. But sometimes that alone isn't enough.
- If you applied early feedback from the draft, leave a comment on what you changed.
- When requesting review, be clear about who you're requesting from — "anyone" usually becomes "no one."
- If the PR is blocking another PR or a deployment, write down that urgency.
8.3 Don't leave drafts to rot
An old draft PR is noise. A draft stuck for days makes the team unsure "is this work alive or dead?" Keep a draft open only while it's active work; if it's stopped, close it or leave a status comment.
Chapter 9 · Authoring PRs in the AI era
In 2026 the landscape of PR authoring has changed. A large share of the code is generated by AI agents, and AI can draft the PR description too. But the core principles haven't changed — if anything, they matter more.
9.1 What AI is good at: drafting descriptions and self-review
AI is excellent at specific parts of PR authoring.
- Drafting the PR description — give it the diff as input and it summarizes "what and how." But "why" is known only to a human — the author must rewrite the "why" field of an AI draft.
- Assisting self-review — "find the left-behind debug code, missing error handling, and untested branches in this diff" is a task AI does well. You can run the Chapter 6 checklist through AI as a first pass.
- Cleaning up commit messages — it can draft messages when restructuring into logical commits.
9.2 What AI can't do: accountability and context
There are things AI can never substitute for.
- The real context of "why" — what business or technical decision this change came out of lives only in a human's head.
- Accountability for unreviewed output — code put up in a PR, whether AI wrote it or a human did, is code the author vouches for.
9.3 Never do this: dumping unreviewed agent output
The most harmful anti-pattern of the AI era is this — putting up agent-generated code as a PR without the author even reading it.
This is equivalent to telling the reviewer, "please look at code I haven't looked at." The social contract of review breaks. The reviewer assumes they're reviewing "code the author has examined once," and when that assumption becomes false, the reviewer ends up shouldering the first-pass review the author should have done.
The rule: AI-generated code has to pass the exact same PR bar as human-written code. Cut it small, carry one idea, run a self-review as the author, and have the author understand and vouch for every line. AI only makes the diff faster — the responsibility to make it reviewable still belongs to the author.
9.4 Why small PRs matter more in the AI era
AI can produce code fast and in large volume. This makes the small-PR principle matter more — the faster generation gets, the faster the reviewer drowns without the discipline to cut it into reviewable units.
AI raised authoring speed; it did not raise review bandwidth. Closing that gap is the author's PR-authoring craft.
Epilogue — A good PR is a gift to the reviewer
A PR's review latency is controlled by the author. The reviewer isn't slow — the unreviewable PR is slow. Cut it small, carry one idea, write the "why" and "verification" in the description, structure the diff to be read, and run a self-review before pushing — and the same reviewer approves the same code several times faster.
This is a favor for the reviewer and, in the end, a thing you do for yourself. Fast review is fast merge, fast merge is fast feedback, and fast feedback catches the wrong direction early. The craft of writing a good PR is a collaboration skill and, at the same time, a skill for keeping your own work cycle short.
The reviewable-PR checklist
- Size — the diff is below the cognitive cliff (roughly under 200–400 lines)
- One idea — you did not mix a refactor with a behavior change
- What — the PR description summarizes the change in one or two sentences
- Why — the reason this change is needed is written (if an AI draft, a human rewrote it)
- Verification — there are check steps the reviewer can follow
- Out of scope — what is not being done this time is stated
- Visuals — if it's a UI change, screenshots are attached
- Commit hygiene — commits are logical units, and formatting is separated
- Self-review — you looked at "Files changed" line by line and there are no debug traces
- Signal — the draft/ready state and the review target are clear
Anti-patterns to avoid
- The giant PR — 800 lines with a one-line "add feature." An LGTM is a surrender, not a review
- The mixed PR — refactor + behavior change + typo in one diff. The signal drowns in noise
- "While I'm at it" — don't slip in unrelated changes. Make them a separate PR/ticket
- The empty description — don't toss the diff with no "why" and no "verification"
- Formatting contamination — don't mix auto-formatter output into the same commit as a behavior change
- Defensive responses — don't take a review comment as an attack. A misunderstanding is information that the code is unclear
- Force-push mid-review — don't rewrite history while review is in progress
- Agent output dump — don't put up AI code you haven't read as a PR
Next post teaser
The next post is "The Reviewer's Craft — How to Do Fast, Kind, and Strict Code Review." If this post was the craft of the side that writes a PR, the next is the craft of the side that reads one. What to look at first, which comments help and which are noise, where to set the baseline for approval versus change request — and how to divide the roles of an AI review bot and a human reviewer.
The ten minutes spent writing a good PR prevents the half-day of a single review round trip. Those ten minutes are a gift to the reviewer and an investment sent to your future self.