Split View: 좋은 티켓 쓰기: 사람과 AI 에이전트 모두를 위한 이슈 작성의 기술
좋은 티켓 쓰기: 사람과 AI 에이전트 모두를 위한 이슈 작성의 기술
"코드는 한 번 쓰고 백 번 읽힌다. 티켓은 한 번 쓰고 그것이 만든 코드만큼 오래 산다."
프롤로그 — 티켓이 곧 스펙이다
소프트웨어 팀에서 가장 자주 쓰지만 가장 적게 가르치는 글이 있습니다. 바로 티켓입니다. Jira 이슈, GitHub Issue, Linear 태스크 — 이름은 달라도 본질은 같습니다. 누군가 무엇을 왜 만들어야 하는지를 적은 글입니다.
티켓을 가볍게 보는 사람이 많습니다. "어차피 말로 설명하면 되지", "코드 보면 알겠지" 하면서요. 하지만 현실은 다릅니다. 티켓은 스펙입니다. 개발자가 PR을 열 때 머릿속에 들고 있는 유일한 계약서이고, 리뷰어가 "이게 맞나?"를 판단하는 기준이며, 6개월 뒤 누군가 git blame을 타고 들어왔을 때 마주치는 마지막 단서입니다.
나쁜 티켓은 나쁜 결과를 만듭니다. 모호한 티켓은 모호한 PR을, 범위가 숨겨진 티켓은 리뷰 불가능한 디프를, 인수 조건이 없는 티켓은 "다 됐어요"와 "아직 멀었는데요"가 충돌하는 회의를 만듭니다.
그리고 2026년, 이 문제는 두 배로 커졌습니다. 이제 티켓을 읽는 것은 사람만이 아니기 때문입니다.
AI 에이전트가 티켓을 읽는다
코딩 에이전트에게 작업을 시킬 때, 입력은 결국 티켓입니다. 사람 동료는 모호한 티켓을 받으면 슬랙으로 되묻거나, 맥락을 추측하거나, 옆자리에 가서 물어봅니다. 에이전트는 그렇게 하지 않습니다. 에이전트는 티켓에 적힌 것만 가지고 일합니다. 적히지 않은 것은 환각하거나, 무시하거나, 가장 그럴듯한 디폴트로 채웁니다.
즉, 티켓 품질이 결과 품질의 상한선입니다. 사람에게는 "물어보면 되는" 안전망이 있었지만, 에이전트에게는 그 안전망이 없습니다. 좋은 티켓을 쓰는 기술은 이제 협업 스킬을 넘어 AI를 다루는 핵심 역량이 되었습니다.
좋은 소식은, 사람에게 좋은 티켓이 에이전트에게도 좋다는 점입니다. 명시적인 맥락, 검증 가능한 인수 조건, 좁은 범위 — 이 세 가지는 사람과 AI 모두에게 똑같이 작동합니다. 이 글은 그 세 가지를 어떻게 글로 옮기는지에 대한 실전 가이드입니다.
이 글에서 다룰 내용:
| 장 | 주제 |
|---|---|
| 1장 | 좋은 티켓의 해부학 — 다섯 가지 구성 요소 |
| 2장 | 체크박스형 인수 조건 |
| 3장 | 범위 설정 — 하나의 티켓 = 하나의 PR |
| 4장 | 버그 티켓 템플릿 |
| 5장 | 기능 티켓 템플릿 |
| 6장 | AI-ready 티켓 vs 사람 전용 티켓 |
| 7장 | 링킹, 추적성, 이슈 그래프 |
| 8장 | 티켓 작성 안티패턴 |
| 9장 | 복사해서 쓰는 템플릿 모음 |
| 에필로그 | 체크리스트 + 안티패턴 + 다음 글 예고 |
1장 · 좋은 티켓의 해부학
좋은 티켓에는 다섯 가지 구성 요소가 있습니다. 외워두면 어떤 트래커에서든 통합니다.
1.1 다섯 가지 구성 요소
| 구성 요소 | 답해야 할 질문 | 없으면 생기는 일 |
|---|---|---|
| 맥락(Context) | 왜 지금 이걸 하는가? | 잘못된 문제를 푼다 |
| 문제(Problem) | 무엇이 잘못됐거나 빠졌는가? | 증상만 고치고 원인을 놓친다 |
| 인수 조건(Acceptance Criteria) | 무엇이 충족되면 "완료"인가? | "다 됐어요" 분쟁이 발생한다 |
| 제약(Constraints) | 무엇을 건드리면 안 되는가? | 범위를 벗어난 변경이 들어온다 |
| 참조(References) | 관련 코드, 문서, 디자인은 어디 있는가? | 작업자가 30분간 탐색만 한다 |
이 다섯 가지가 채워진 티켓은 그 자체로 자급자족합니다. 작업자가 슬랙을 뒤지지 않아도, 옆자리에 묻지 않아도 시작할 수 있습니다. 그리고 자급자족하는 티켓은 정확히 AI 에이전트가 필요로 하는 것입니다.
1.2 맥락: "왜"를 먼저
맥락은 가장 자주 생략되고 가장 비싸게 대가를 치르는 부분입니다. "버튼 색을 파란색으로 바꿔주세요"는 지시이지 맥락이 아닙니다. 작업자가 알아야 할 것은 왜입니다.
브랜드 리뉴얼로 1차 액션 색상이 보라색에서 파란색으로 바뀌었습니다(디자인 시스템 v3 참조). 이 버튼은 결제 플로우의 핵심 CTA라 새 색상을 따라야 합니다.
이 한 문장이 있으면 작업자는 비슷한 다른 버튼들도 같이 봐야 하는지 판단할 수 있고, 에이전트는 "1차 액션 색상"이라는 토큰 기반 변경인지 단발성 하드코딩인지 추론할 수 있습니다.
1.3 문제: 증상이 아니라 사실
문제 진술은 관찰 가능한 사실이어야 합니다. 해석이나 추측을 섞지 마세요.
- 나쁨: "로그인이 가끔 이상해요"
- 좋음: "비밀번호에
+기호가 포함된 사용자가 로그인하면 401을 받습니다. URL 인코딩 누락으로 추정되지만 미확인."
좋은 버전은 재현 조건(+ 포함 비밀번호), 관찰된 결과(401), 그리고 추측과 사실을 분리("추정", "미확인")했습니다.
2장 · 체크박스형 인수 조건
인수 조건(Acceptance Criteria, AC)은 티켓에서 가장 중요한 한 부분입니다. AC가 하는 일은 단순합니다. "완료"의 정의를 작업 시작 전에 못 박는 것.
2.1 왜 체크박스인가
AC는 산문이 아니라 체크박스 목록으로 써야 합니다. 이유가 분명합니다.
## 인수 조건
- [ ] `+` 가 포함된 비밀번호로 로그인하면 200을 반환한다
- [ ] `&`, `=`, 공백이 포함된 비밀번호도 동일하게 동작한다
- [ ] 잘못된 비밀번호는 여전히 401을 반환한다 (회귀 방지)
- [ ] 이 케이스를 커버하는 통합 테스트가 추가된다
체크박스가 작동하는 이유:
| 관점 | 체크박스가 주는 것 |
|---|---|
| 작업자 | 할 일 목록. 하나씩 지우면 끝 |
| 리뷰어 | 검수 체크리스트. PR이 각 항목을 만족하는지 1:1 대조 |
| PM/보고 | 진행률이 그대로 보임 (3/4 완료) |
| AI 에이전트 | 검증 가능한 목표. 각 줄이 테스트 케이스로 번역됨 |
마지막 줄이 핵심입니다. 에이전트는 산문에서 "이 정도면 됐겠지"를 추론하지 못합니다. 하지만 - [ ] X가 Y를 반환한다는 그대로 검증 루프가 됩니다. 코드를 짜고, 그 줄이 참인지 확인하고, 아니면 다시 짭니다.
2.2 좋은 AC의 조건
좋은 AC 한 줄은 다음을 만족합니다.
- 검증 가능하다 — 참/거짓을 객관적으로 판정할 수 있다
- 관찰 가능한 행동을 말한다 — 구현이 아니라 결과를 말한다
- 하나의 사실만 담는다 — "그리고"로 두 개를 묶지 않는다
예시 비교:
| 나쁜 AC | 좋은 AC |
|---|---|
| 로그인이 잘 동작한다 | 유효한 자격 증명으로 로그인하면 /dashboard로 리다이렉트된다 |
| 성능을 개선한다 | 상품 목록 API의 p95 응답 시간이 200ms 이하다 |
| 에러 처리를 추가한다 | 네트워크 실패 시 토스트 "다시 시도해 주세요"가 노출된다 |
| 코드를 깔끔하게 한다 | (AC가 아님 — 인수 조건에서 제거) |
마지막 행을 보세요. "코드를 깔끔하게 한다"는 검증 불가능합니다. AC가 아니라 작업 노트나 별도 리팩터링 티켓으로 빼야 합니다.
2.3 Given-When-Then 변형
복잡한 동작은 Given-When-Then 형식이 모호함을 줄여줍니다.
## 인수 조건
- [ ] **Given** 장바구니에 재고 없는 상품이 있을 때
**When** 결제 버튼을 누르면
**Then** "재고가 부족한 상품이 있습니다" 모달이 뜨고 결제는 진행되지 않는다
- [ ] **Given** 모든 상품의 재고가 충분할 때
**When** 결제 버튼을 누르면
**Then** 결제 확인 페이지로 이동한다
이 형식은 사람에게는 시나리오를 명확히 하고, 에이전트에게는 거의 그대로 테스트 코드 골격을 제공합니다.
3장 · 범위 설정 — 하나의 티켓 = 하나의 PR
좋은 티켓 작성에서 가장 강력하지만 가장 자주 무시되는 원칙입니다.
하나의 티켓은 하나의 리뷰 가능한 PR로 끝나야 한다.
3.1 "리뷰 가능한"의 의미
리뷰 가능한 PR이란 한 사람이 한 자리에서 끝까지 읽고 자신 있게 승인할 수 있는 디프입니다. 경험칙으로 변경 줄 수 400줄 안팎, 핵심 파일 10개 안팎입니다. 이 선을 넘으면 리뷰어는 읽기를 포기하고 "LGTM"을 누릅니다 — 그러면 리뷰는 무의미해집니다.
티켓이 이 크기에 맞춰지면 연쇄적으로 좋은 일이 생깁니다.
| 티켓이 작으면 | 결과 |
|---|---|
| PR이 작다 | 리뷰가 빠르고 꼼꼼하다 |
| 변경이 격리된다 | 문제 생기면 되돌리기 쉽다 |
| 범위가 명확하다 | "이것도 해야 하나?" 고민이 없다 |
| 진행이 보인다 | 큰 작업이 작은 완료들로 쪼개진다 |
| 에이전트가 다룬다 | 컨텍스트 윈도우 안에 다 들어간다 |
마지막 행이 AI 시대의 새로운 이유입니다. 거대한 티켓은 에이전트의 컨텍스트를 넘쳐 흐릅니다. 작업 절반에서 앞부분을 "잊고" 일관성을 잃습니다. 작은 티켓은 시작부터 끝까지 한 맥락에 들어옵니다.
3.2 큰 티켓을 쪼개는 법
"사용자 인증 구현"은 티켓이 아니라 에픽입니다. 쪼개야 합니다.
에픽: 사용자 인증
├─ #101 이메일/비밀번호 회원가입 (DB 스키마 + 엔드포인트)
├─ #102 로그인 + 세션 토큰 발급
├─ #103 비밀번호 재설정 플로우 (이메일 발송 포함)
├─ #104 로그인 UI 컴포넌트
└─ #105 인증 미들웨어로 보호 라우트 적용
쪼개는 기준:
- 수직 슬라이스 우선 — "전체 백엔드" 같은 수평 분할보다 "회원가입 기능 끝까지"가 낫다
- 각 조각이 독립 배포 가능한가 —
#102가#101없이 의미 없다면 의존성을 명시 - 각 조각에 자체 AC가 있는가 — 없으면 아직 너무 크다
3.3 "숨은 범위"라는 함정
티켓 제목은 작아 보이는데 본문이 슬며시 커지는 경우가 가장 위험합니다.
제목: 헤더에 다크 모드 토글 추가 본문: ...토글을 추가하고, 김에 디자인 토큰 시스템도 정리하고, 색상 변수를 전부 CSS 변수로 마이그레이션해 주세요.
토글 하나가 전사 색상 마이그레이션으로 둔갑했습니다. 이런 티켓은 둘로 갈라야 합니다. 토큰 마이그레이션은 별도 티켓, 토글은 그 위에 의존성으로 얹습니다.
4장 · 버그 티켓 템플릿
버그 티켓에는 정해진 형태가 있습니다. 빠진 칸이 하나라도 있으면 작업자는 추측을 시작하고, 추측은 시간을 태웁니다.
4.1 버그 티켓의 필수 칸
| 칸 | 내용 |
|---|---|
| 재현 절차 | 1, 2, 3 번호로 — 따라 하면 똑같이 재현되어야 함 |
| 기대 결과 | 무엇이 일어나야 했는가 |
| 실제 결과 | 무엇이 일어났는가 |
| 환경 | OS, 브라우저/런타임 버전, 앱 버전, 배포 환경 |
| 증거 | 스택 트레이스, 로그, 스크린샷, 네트워크 응답 |
| 빈도 | 항상 / 가끔 / 한 번 — 디버깅 전략이 갈림 |
4.2 Before / After
Before — 추측을 강요하는 버그 티켓:
제목: 결제가 안 됨 결제 페이지에서 에러가 나요. 확인 부탁드립니다.
작업자가 모르는 것: 어떤 결제 수단? 어떤 금액? 어떤 에러? 어떤 환경? 항상? 재현 절차는? 이 티켓은 사실상 "당신이 알아내세요"입니다.
After — 바로 디버깅 가능한 버그 티켓:
## 요약
신용카드 결제 시 5만 원 초과 금액에서 500 에러 발생
## 재현 절차
1. 장바구니에 6만 원어치 상품 담기
2. 결제 페이지 진입, 신용카드 선택
3. 카드 정보 입력 후 "결제하기" 클릭
## 기대 결과
결제 승인 후 주문 완료 페이지로 이동
## 실제 결과
"일시적인 오류가 발생했습니다" 토스트, 결제 미완료
## 환경
- 배포 환경: production
- 브라우저: Chrome 124, Safari 17 모두 재현
- 발생 시작: 5월 12일 14:00 배포 이후로 추정
## 증거
- 서버 로그: `PaymentError: amount exceeds limit (50000)`
- 요청 ID: req_8f2a91c
- 스크린샷: 첨부
## 빈도
5만 원 초과 시 항상 재현 (100%)
After 버전은 작업자가 30초 만에 원인 가설을 세울 수 있습니다 — 5월 12일 배포에서 들어온 금액 한도 검증이 의심됩니다. 에이전트라면 로그의 에러 메시지와 요청 ID를 단서로 곧장 해당 코드 경로를 찾아갑니다.
4.3 스택 트레이스는 코드 블록에
스택 트레이스나 로그를 붙일 때는 반드시 코드 블록 안에 넣으세요. 산문에 그대로 붙이면 읽기 어렵고, 트래커에 따라 마크다운으로 깨집니다.
## 증거
```
TypeError: Cannot read properties of undefined (reading 'id')
at resolveUser (src/auth/resolver.ts:42:18)
at processRequest (src/server/handler.ts:118:9)
```
파일 경로와 줄 번호(src/auth/resolver.ts:42)가 들어간 트레이스는 사람에게도 에이전트에게도 최고의 진입점입니다.
5장 · 기능 티켓 템플릿
기능 티켓은 버그 티켓과 구조가 다릅니다. 버그는 "무엇이 깨졌나"를 다루지만, 기능은 "무엇을 새로 만드나"를 다룹니다. 핵심은 인수 조건과 범위 경계입니다.
5.1 기능 티켓의 필수 칸
| 칸 | 내용 |
|---|---|
| 배경/문제 | 어떤 사용자 또는 비즈니스 문제를 푸는가 |
| 제안하는 변경 | 무엇을 만들 것인가 (구현 디테일이 아니라 동작) |
| 인수 조건 | 체크박스 목록 — "완료"의 정의 |
| 범위 밖(Out of scope) | 명시적으로 이번에 하지 않는 것 |
| 제약 | 성능 예산, 호환성, 건드리면 안 되는 것 |
| 참조 | 디자인, 관련 코드, 선행 티켓 |
범위 밖 칸을 특히 강조합니다. 무엇을 안 하는지 적는 것은 무엇을 하는지 적는 것만큼 강력합니다. 이 칸이 숨은 범위 증식을 막습니다.
5.2 Before / After
Before — 모호한 기능 티켓:
제목: 검색 기능 개선 검색이 더 똑똑해졌으면 좋겠어요. 사용자들이 원하는 걸 잘 못 찾아요.
"똑똑하다", "잘"은 검증 불가능합니다. 작업자는 자동완성을 만들지, 오타 교정을 넣을지, 랭킹을 바꿀지 알 수 없습니다.
After — 작업 가능한 기능 티켓:
## 배경
상품 검색에서 사용자가 정확한 상품명을 모르면 결과가 0건이다.
지난주 검색 로그상 38%가 0건 결과로 끝났다.
## 제안하는 변경
검색어에 대해 상품명 부분 일치 + 오타 교정(편집 거리 1)을 적용한다.
## 인수 조건
- [ ] "맥북"으로 검색하면 "맥북 프로 16"이 결과에 포함된다
- [ ] "맥북" 오타인 "맥붓"으로 검색해도 동일한 결과가 나온다
- [ ] 결과가 없을 때 "이런 상품은 어떠세요" 추천 5건이 노출된다
- [ ] 검색 API p95 응답 시간이 300ms 이하를 유지한다
## 범위 밖
- 음성 검색
- 검색 결과 랭킹 알고리즘 변경 (별도 티켓 #210)
- 카테고리 필터 UI
## 제약
- 기존 `/api/search` 엔드포인트 응답 스키마는 바꾸지 않는다
- 검색 인덱스 재색인은 무중단으로 가능해야 한다
## 참조
- 디자인: Figma 링크
- 0건 결과 분석: 노션 문서 링크
- 관련 코드: `src/search/query-builder.ts`
After 버전은 작업자에게 명확한 종료선을 주고, 에이전트에게는 각 AC가 그대로 검증 단계가 됩니다. 범위 밖 칸은 "랭킹도 같이 해야 하나?"라는 30분짜리 고민을 0초로 만듭니다.
6장 · AI-ready 티켓 vs 사람 전용 티켓
모든 티켓이 에이전트에게 넘기기 좋은 것은 아닙니다. 어떤 티켓은 사람만 다뤄야 하고, 어떤 티켓은 약간만 손보면 에이전트가 끝낼 수 있습니다. 이 구분을 라벨로 명시하면 팀 전체가 같은 기준을 공유합니다.
6.1 무엇이 티켓을 AI-ready로 만드는가
| AI-ready 티켓의 특징 | 사람 전용으로 남겨야 할 신호 |
|---|---|
| 인수 조건이 검증 가능하다 | "느낌상 더 나아야 한다" 류의 주관적 목표 |
| 범위가 좁고 명확하다 | 여러 시스템에 걸친 모호한 탐색성 작업 |
| 참조 코드 경로가 명시됨 | 어디를 건드릴지 자체가 조사 대상 |
| 기존 패턴을 따르는 작업 | 새로운 아키텍처 결정이 필요한 작업 |
| 테스트로 결과 확인 가능 | 프로덕션 데이터/외부 시스템 의존이 큼 |
| 되돌리기 쉬운 변경 | 마이그레이션, 보안, 결제 등 고위험 영역 |
핵심 통찰: AI-ready 티켓은 사실 잘 쓴 티켓입니다. 별도의 종족이 아닙니다. 사람을 위해 명확하게 쓰면 에이전트도 다룰 수 있습니다. 거꾸로, 에이전트가 헤매는 티켓은 보통 사람 신입도 헤맵니다.
6.2 라벨 전략
트래커 라벨로 의도를 신호하세요. 예시 체계:
| 라벨 | 의미 |
|---|---|
agent-ready | AC가 검증 가능하고 범위가 좁음 — 에이전트에 위임 가능 |
agent-assisted | 에이전트가 초안을 만들고 사람이 마무리/판단 |
human-only | 아키텍처 결정, 고위험 영역, 모호한 탐색 |
needs-refinement | 아직 AC가 없거나 범위가 큼 — 그루밍 필요 |
needs-refinement가 중요합니다. 이 라벨은 "이 티켓은 아직 누구에게도 줄 수 없다"는 솔직한 신호입니다. 그루밍 회의에서 이 라벨이 붙은 티켓을 다듬어 agent-ready 또는 human-only로 졸업시킵니다.
6.3 에이전트에게 넘기기 전 체크
티켓에 agent-ready를 붙이기 전 확인할 것:
- [ ] 인수 조건이 모두 체크박스이고 검증 가능한가
- [ ] 관련 코드 경로/파일이 참조에 적혀 있는가
- [ ] "범위 밖" 칸으로 경계가 그어져 있는가
- [ ] 기존 코드의 어떤 패턴을 따라야 하는지 명시했는가
- [ ] 결과를 확인할 테스트 방법이 있는가
- [ ] 고위험 영역(인증, 결제, 마이그레이션)이 아닌가
이 여섯 줄을 통과하지 못하면 아직 needs-refinement입니다.
7장 · 링킹, 추적성, 이슈 그래프
티켓은 외딴 섬이 아닙니다. 좋은 팀의 트래커는 그래프입니다 — 티켓들이 서로, 그리고 코드와 연결되어 있습니다.
7.1 연결해야 할 다섯 가지 링크
| 링크 방향 | 예시 | 왜 |
|---|---|---|
| 티켓 → 상위 에픽 | #102는 "사용자 인증" 에픽 소속 | 큰 그림 안에서의 위치 |
| 티켓 → 의존 티켓 | #102는 #101 완료에 의존 | 작업 순서가 보임 |
| 티켓 → PR | #102 본문에 PR 링크 | 무엇이 이 티켓을 해결했나 |
| PR → 티켓 | PR 설명에 Closes #102 | 머지 시 자동 종료 |
| 티켓 → 문서/디자인 | RFC, Figma, 포스트모템 | 결정의 근거 |
Closes #102 같은 키워드는 GitHub/GitLab에서 PR 머지 시 이슈를 자동으로 닫아줍니다. 이 작은 습관 하나가 "닫는 걸 깜빡한" 좀비 티켓을 없앱니다.
7.2 추적성의 가치
6개월 뒤 누군가 이상한 코드를 발견했다고 합시다. 추적성이 살아 있다면 경로는 이렇습니다.
이상한 코드 한 줄
→ git blame → 커밋
→ 커밋 → PR
→ PR → 티켓 #102
→ 티켓 → 에픽 + RFC 링크
→ "아, 이래서 이렇게 짰구나"
이 사슬이 끊겨 있으면 그 코드는 영원히 미스터리로 남고, 아무도 손대기 무서워합니다. 티켓-PR-커밋 링크는 미래의 자신에게 보내는 편지입니다.
7.3 AI 에이전트와 이슈 그래프
에이전트에게 #102를 시킬 때, 의존성 링크가 있으면 에이전트는 #101의 결과물(스키마, 엔드포인트)을 맥락으로 끌어올 수 있습니다. 링크가 없으면 에이전트는 #102만 외딴섬으로 보고, 이미 존재하는 것을 다시 만들거나 일관성을 깹니다. 이슈 그래프는 에이전트의 컨텍스트 지도입니다.
8장 · 티켓 작성 안티패턴
좋은 티켓의 반대편을 알면 더 빨리 좋아집니다. 현업에서 가장 자주 보는 안티패턴들입니다.
8.1 모호한 동사
"개선한다", "최적화한다", "정리한다", "더 낫게 한다" — 이 동사들은 검증 불가능합니다. 항상 측정 가능한 결과로 바꾸세요.
| 모호한 동사 | 측정 가능한 버전 |
|---|---|
| 검색을 개선한다 | 0건 결과 비율을 38%에서 10% 이하로 낮춘다 |
| 페이지를 최적화한다 | LCP를 4.1초에서 2.5초 이하로 낮춘다 |
| 에러 처리를 정리한다 | 모든 API 실패에 사용자용 에러 메시지를 노출한다 |
8.2 숨은 범위
3장에서 다뤘습니다. 제목은 작은데 본문이 슬며시 커지는 패턴. "~하는 김에", "겸사겸사"가 본문에 보이면 거의 항상 별도 티켓 신호입니다.
8.3 인수 조건 누락
"완료"의 정의 없이 시작하는 티켓. 끝에 가서 "이게 다 된 거 맞나요?" 회의가 열립니다. AC 없는 티켓은 시작하면 안 됩니다.
8.4 한 티켓에 여러 작업
"로그인 버그 고치고, 회원가입 UI도 다듬고, 로깅도 추가"처럼 묶인 티켓. 리뷰가 불가능하고, 하나가 막히면 전부 막힙니다.
8.5 해결책을 지시하고 문제를 숨김
"users 테이블에 인덱스 추가" — 왜? 어떤 쿼리가 느린가? 문제가 없으면 작업자는 그게 맞는 해결책인지 검증할 수 없고, 더 나은 해결책을 제안할 수도 없습니다. 문제를 적고, 해결책은 제안 정도로.
8.6 안티패턴 요약표
| 안티패턴 | 증상 | 처방 |
|---|---|---|
| 모호한 동사 | "개선/최적화/정리" | 측정 가능한 결과로 |
| 숨은 범위 | 제목보다 큰 본문 | 티켓 분할 |
| AC 누락 | "완료" 정의 없음 | 체크박스 AC 추가 |
| 다중 작업 | 한 티켓에 여러 PR거리 | 하나씩 분할 |
| 해결책 지시 | "왜"가 없음 | 문제 진술 추가 |
| 맥락 부재 | "왜 지금"이 없음 | 배경 한 문단 추가 |
9장 · 복사해서 쓰는 템플릿 모음
아래 템플릿들을 리포지토리의 .github/ISSUE_TEMPLATE/ 또는 트래커 템플릿에 그대로 넣으세요.
9.1 버그 리포트 템플릿
## 요약
{한 문장으로 무엇이 깨졌는지}
## 재현 절차
1.
2.
3.
## 기대 결과
{무엇이 일어나야 했는가}
## 실제 결과
{무엇이 일어났는가}
## 환경
- 배포 환경: {production / staging / local}
- 브라우저/런타임: {버전}
- 앱 버전/커밋: {버전 또는 SHA}
## 증거
```
{스택 트레이스 / 로그 / 요청 ID}
```
{스크린샷 첨부}
## 빈도
{항상 / 가끔(N회 중 M회) / 한 번}
9.2 기능 요청 템플릿
## 배경
{어떤 사용자/비즈니스 문제를 푸는가, 가능하면 데이터와 함께}
## 제안하는 변경
{무엇을 만들 것인가 — 구현이 아니라 동작 수준으로}
## 인수 조건
- [ ] {검증 가능한 결과 1}
- [ ] {검증 가능한 결과 2}
- [ ] {회귀 방지 항목}
- [ ] {테스트 추가 항목}
## 범위 밖
- {이번에 하지 않는 것 1}
- {이번에 하지 않는 것 2}
## 제약
- {성능 예산 / 호환성 / 건드리면 안 되는 것}
## 참조
- 디자인:
- 관련 코드:
- 선행 티켓:
9.3 작업/태스크 템플릿 (리팩터링·잡무용)
## 무엇을
{한 문장 작업 설명}
## 왜
{왜 지금 이게 필요한가}
## 인수 조건
- [ ] {결과 1}
- [ ] {기존 동작이 깨지지 않음을 확인하는 항목}
## 범위 밖
- {확장 유혹을 명시적으로 차단}
## 참조
-
9.4 에이전트 위임 체크리스트 (PR 설명 또는 티켓 코멘트용)
## 에이전트 위임 전 점검
- [ ] AC가 모두 체크박스이고 검증 가능
- [ ] 관련 코드 경로가 참조에 명시됨
- [ ] "범위 밖"으로 경계가 그어짐
- [ ] 따라야 할 기존 패턴을 지정
- [ ] 결과 검증 방법(테스트)이 있음
- [ ] 고위험 영역 아님 (인증/결제/마이그레이션)
라벨: {agent-ready / agent-assisted / human-only / needs-refinement}
9.5 템플릿 운영 팁
- 템플릿은 출발점이지 족쇄가 아닙니다. 안 쓰는 칸은 지우되, 다섯 가지 구성 요소(맥락·문제·AC·제약·참조)는 남기세요.
- 칸 안의
{placeholder}안내문은 작성자가 채우면서 지워야 합니다. 빈 채로 제출된{placeholder}는 그 자체로 "이 티켓은 아직 미완성"이라는 신호입니다. - 팀 위키에 좋은 티켓 예시 3개를 박제해 두세요. 신입과 에이전트 모두 그것을 기준점으로 삼습니다.
에필로그 — 좋은 티켓은 미래에 보내는 투자
티켓은 소프트웨어에서 가장 레버리지가 큰 글입니다. 한 번 잘 쓰면, 그 티켓이 만든 코드만큼 오래 가치를 냅니다. 한 번 못 쓰면, 그 비용을 모든 후속 단계가 나눠 냅니다 — 그리고 AI 시대에는 그 비용이 두 배입니다. 사람에게는 "물어보면 되는" 안전망이 있었지만, 에이전트에게는 티켓이 전부이기 때문입니다.
하지만 결론은 단순합니다. 사람을 위해 명확하게 쓰면 에이전트도 다룰 수 있습니다. 별도의 기술이 아니라, 같은 기술의 두 배 보상입니다.
좋은 티켓 체크리스트
- 맥락 — "왜 지금 이걸 하는가"가 한 문단으로 적혀 있다
- 문제 — 증상이 아니라 관찰 가능한 사실로 적혀 있다
- 인수 조건 — 검증 가능한 체크박스 목록이다
- 하나의 사실 — AC 한 줄에 "그리고"로 두 개를 묶지 않았다
- 범위 — 하나의 리뷰 가능한 PR로 끝날 크기다
- 범위 밖 — 이번에 하지 않는 것이 명시되어 있다
- 제약 — 건드리면 안 되는 것, 성능 예산이 적혀 있다
- 참조 — 관련 코드 경로, 디자인, 선행 티켓이 링크되어 있다
- 추적성 — 에픽·의존 티켓·PR이 양방향으로 연결되어 있다
- 라벨 —
agent-ready/human-only등 의도가 신호되어 있다
피해야 할 안티패턴
- 모호한 동사 — "개선/최적화/정리"는 측정 가능한 결과로 바꾼다
- 숨은 범위 — "~하는 김에"가 보이면 티켓을 쪼갠다
- AC 누락 — "완료"의 정의 없이 작업을 시작하지 않는다
- 다중 작업 — 한 티켓에 여러 PR거리를 묶지 않는다
- 해결책 지시 — 문제를 숨기고 해결책만 적지 않는다
- 빈 placeholder — 템플릿의 안내문을 지우지 않은 채 제출하지 않는다
다음 글 예고
다음 글은 **"좋은 PR 설명 쓰기 — 리뷰어와 에이전트가 5분 만에 승인하게 만드는 법"**입니다. 티켓이 작업의 입력이라면 PR 설명은 작업의 출력입니다. 무엇을·왜 바꿨는지, 어떻게 검증했는지, 리뷰어가 어디부터 봐야 하는지를 적는 기술 — 그리고 그것이 어떻게 AI 코드 리뷰어와도 맞물리는지를 다룹니다.
좋은 티켓을 쓰는 데 드는 10분은, 잘못 만든 것을 되돌리는 10시간을 막습니다. 그 10분은 미래의 팀과 미래의 자신에게 보내는 투자입니다.
Writing Great Tickets: The Craft of Issues for Humans and AI Agents
"Code is written once and read a hundred times. A ticket is written once and lives as long as the code it produced."
Prologue — The Ticket Is the Spec
Every software team has one artifact it writes most often and teaches least: the ticket. Jira issue, GitHub Issue, Linear task — the names differ, the essence is the same. It is a piece of writing that says what someone should build, and why.
Many people treat tickets casually. "I'll just explain it in chat." "They'll figure it out from the code." But the reality is harsher. A ticket is the spec. It is the only contract a developer holds in their head when they open a PR. It is the standard a reviewer uses to decide "is this right?" It is the last clue someone hits six months later, riding git blame into the dark.
A bad ticket produces a bad outcome. A vague ticket produces a vague PR. A ticket with hidden scope produces an unreviewable diff. A ticket with no acceptance criteria produces a meeting where "it's done" and "no it isn't" collide.
And in 2026, this problem doubled in size. Because tickets are no longer read only by humans.
AI agents read tickets too
When you hand a task to a coding agent, the input is, in the end, a ticket. A human teammate handed a vague ticket will ask in Slack, guess at context, or walk over to someone's desk. An agent does none of that. An agent works with only what the ticket says. Whatever is not written, it hallucinates, ignores, or fills with the most plausible default.
In other words, ticket quality is the ceiling on outcome quality. Humans had a safety net — "you can always ask." Agents do not. The skill of writing great tickets has crossed from a collaboration skill into a core skill for working with AI.
The good news: a ticket that is good for humans is good for agents. Explicit context, verifiable acceptance criteria, narrow scope — these three work identically for people and for AI. This article is a practical guide to getting those three onto the page.
What this article covers:
| Chapter | Topic |
|---|---|
| 1 | The anatomy of a great ticket — five components |
| 2 | Acceptance criteria as checkboxes |
| 3 | Scoping — one ticket = one PR |
| 4 | The bug ticket template |
| 5 | The feature ticket template |
| 6 | AI-ready tickets vs human-only tickets |
| 7 | Linking, traceability, the issue graph |
| 8 | Ticket-writing anti-patterns |
| 9 | Copy-paste templates |
| Epilogue | Checklist + anti-patterns + next-post teaser |
Chapter 1 · The Anatomy of a Great Ticket
A great ticket has five components. Memorize them and they work in any tracker.
1.1 The five components
| Component | The question it answers | What happens without it |
|---|---|---|
| Context | Why are we doing this now? | You solve the wrong problem |
| Problem | What is broken or missing? | You patch the symptom, miss the cause |
| Acceptance criteria | What must be true to call it "done"? | "It's done" disputes erupt |
| Constraints | What must not be touched? | Out-of-scope changes creep in |
| References | Where is the related code, doc, design? | The worker spends 30 minutes just searching |
A ticket with these five filled in is self-sufficient. The worker can start without digging through Slack, without asking the next desk over. And a self-sufficient ticket is exactly what an AI agent needs.
1.2 Context: lead with "why"
Context is the most frequently omitted component and the most expensive one to lose. "Change the button color to blue" is an instruction, not context. What the worker needs is the why.
A brand refresh moved the primary action color from purple to blue (see Design System v3). This button is the key CTA in the checkout flow, so it must follow the new color.
With that one sentence, the worker can judge whether other similar buttons need attention too, and the agent can infer whether this is a token-based change to "primary action color" or a one-off hardcode.
1.3 Problem: facts, not symptoms
A problem statement should be an observable fact. Do not mix in interpretation or guesswork.
- Bad: "Login is kind of weird sometimes"
- Good: "Users whose password contains a
+character get a 401 on login. Suspected missing URL encoding, but unconfirmed."
The good version separates the reproduction condition (password with +), the observed result (401), and guess from fact ("suspected", "unconfirmed").
Chapter 2 · Acceptance Criteria as Checkboxes
Acceptance criteria (AC) are the single most important part of a ticket. What AC does is simple: it nails down the definition of "done" before work begins.
2.1 Why checkboxes
AC should be written as a checkbox list, not prose. The reasons are concrete.
## Acceptance Criteria
- [ ] Logging in with a password containing `+` returns 200
- [ ] Passwords containing `&`, `=`, or a space behave identically
- [ ] An incorrect password still returns 401 (regression guard)
- [ ] An integration test covering this case is added
Why checkboxes work:
| Perspective | What checkboxes provide |
|---|---|
| Worker | A to-do list. Cross them off and you're done |
| Reviewer | A review checklist. Match the PR against each item 1:1 |
| PM / reporting | Progress is visible (3/4 complete) |
| AI agent | Verifiable goals. Each line translates into a test case |
The last row is the key. An agent cannot infer "this is probably enough" from prose. But - [ ] X returns Y becomes a verification loop directly: write the code, check if that line is true, and if not, write it again.
2.2 What makes a good AC
A good AC line satisfies the following:
- Verifiable — true/false can be judged objectively
- States observable behavior — the result, not the implementation
- Holds exactly one fact — does not bundle two with "and"
A comparison:
| Bad AC | Good AC |
|---|---|
| Login works well | Logging in with valid credentials redirects to /dashboard |
| Improve performance | The product list API's p95 response time is under 200ms |
| Add error handling | On network failure, a toast "Please try again" is shown |
| Clean up the code | (not an AC — remove it from acceptance criteria) |
Look at the last row. "Clean up the code" is unverifiable. It is not an AC; pull it out into a work note or a separate refactoring ticket.
2.3 The Given-When-Then variant
For complex behavior, the Given-When-Then format reduces ambiguity.
## Acceptance Criteria
- [ ] **Given** the cart contains an out-of-stock item
**When** the checkout button is pressed
**Then** a modal "Some items are out of stock" appears and checkout does not proceed
- [ ] **Given** all items have sufficient stock
**When** the checkout button is pressed
**Then** the user moves to the checkout confirmation page
This format clarifies the scenario for humans and hands the agent an almost ready-made test skeleton.
Chapter 3 · Scoping — One Ticket = One PR
The most powerful and most frequently ignored rule in ticket writing.
One ticket should resolve into one reviewable PR.
3.1 What "reviewable" means
A reviewable PR is a diff one person can read end to end in one sitting and approve with confidence. As a rule of thumb, around 400 changed lines, around 10 core files. Cross that line and the reviewer gives up reading and clicks "LGTM" — at which point the review is meaningless.
When a ticket is sized to fit this, good things cascade.
| When the ticket is small | Result |
|---|---|
| The PR is small | The review is fast and thorough |
| The change is isolated | If something breaks, it is easy to revert |
| The scope is clear | No "should I do this too?" agonizing |
| Progress is visible | Big work splits into small completions |
| An agent can handle it | It all fits inside the context window |
The last row is the new reason in the AI era. A giant ticket overflows the agent's context. Halfway through the work it "forgets" the beginning and loses consistency. A small ticket fits in one context from start to finish.
3.2 How to split a large ticket
"Implement user authentication" is not a ticket — it is an epic. It must be split.
Epic: User Authentication
├─ #101 Email/password signup (DB schema + endpoint)
├─ #102 Login + session token issuance
├─ #103 Password reset flow (including email send)
├─ #104 Login UI component
└─ #105 Apply auth middleware to protected routes
Criteria for splitting:
- Vertical slices first — "signup feature end to end" beats a horizontal split like "the whole backend"
- Is each slice independently shippable — if
#102is meaningless without#101, state the dependency - Does each slice have its own AC — if not, it is still too big
3.3 The "hidden scope" trap
The most dangerous case: the ticket title looks small but the body quietly grows.
Title: Add a dark mode toggle to the header Body: ...add the toggle, and while you're at it, also tidy up the design token system and migrate all color variables to CSS variables.
A single toggle has morphed into a company-wide color migration. A ticket like this must be split in two. The token migration becomes a separate ticket; the toggle sits on top of it as a dependency.
Chapter 4 · The Bug Ticket Template
Bug tickets have a fixed shape. Leave even one field empty and the worker starts guessing — and guessing burns time.
4.1 The required fields of a bug ticket
| Field | Content |
|---|---|
| Repro steps | Numbered 1, 2, 3 — following them must reproduce it identically |
| Expected result | What should have happened |
| Actual result | What did happen |
| Environment | OS, browser/runtime version, app version, deploy environment |
| Evidence | Stack trace, logs, screenshots, network response |
| Frequency | Always / sometimes / once — the debugging strategy diverges |
4.2 Before / After
Before — a bug ticket that forces guessing:
Title: Payment doesn't work There's an error on the payment page. Please check.
What the worker doesn't know: which payment method? what amount? what error? what environment? always? what are the repro steps? This ticket effectively says "you figure it out."
After — a bug ticket you can debug immediately:
## Summary
A 500 error occurs for credit card payments above 50,000 KRW
## Repro Steps
1. Add 60,000 KRW worth of items to the cart
2. Go to the payment page, select credit card
3. Enter card details and click "Pay"
## Expected Result
After payment approval, move to the order complete page
## Actual Result
A toast "A temporary error occurred", payment not completed
## Environment
- Deploy environment: production
- Browser: reproduces on both Chrome 124 and Safari 17
- Started: suspected since the May 12 14:00 deploy
## Evidence
- Server log: `PaymentError: amount exceeds limit (50000)`
- Request ID: req_8f2a91c
- Screenshot: attached
## Frequency
Always reproduces above 50,000 KRW (100%)
The After version lets the worker form a root-cause hypothesis in 30 seconds — the amount-limit validation that came in with the May 12 deploy is the suspect. An agent would follow the log's error message and request ID straight to the relevant code path.
4.3 Stack traces go in code blocks
When pasting a stack trace or logs, always put them inside a code block. Pasting them raw into prose is hard to read and breaks as markdown in some trackers.
## Evidence
```
TypeError: Cannot read properties of undefined (reading 'id')
at resolveUser (src/auth/resolver.ts:42:18)
at processRequest (src/server/handler.ts:118:9)
```
A trace with file paths and line numbers (src/auth/resolver.ts:42) is the best entry point for both humans and agents.
Chapter 5 · The Feature Ticket Template
A feature ticket is structured differently from a bug ticket. A bug deals with "what broke"; a feature deals with "what to build new." The core is acceptance criteria and scope boundaries.
5.1 The required fields of a feature ticket
| Field | Content |
|---|---|
| Background / problem | What user or business problem does this solve |
| Proposed change | What to build (behavior, not implementation detail) |
| Acceptance criteria | A checkbox list — the definition of "done" |
| Out of scope | What you explicitly will not do this time |
| Constraints | Performance budget, compatibility, what not to touch |
| References | Design, related code, predecessor tickets |
Emphasize the Out of scope field in particular. Writing down what you will not do is as powerful as writing down what you will. This field is what stops hidden scope from multiplying.
5.2 Before / After
Before — a vague feature ticket:
Title: Improve search I want search to be smarter. Users can't find what they want.
"Smarter" and "well" are unverifiable. The worker cannot tell whether to build autocomplete, add typo correction, or change ranking.
After — a workable feature ticket:
## Background
In product search, if a user doesn't know the exact product name, results come back empty.
Per last week's search logs, 38% ended in zero results.
## Proposed Change
Apply product-name partial matching plus typo correction (edit distance 1) to the query.
## Acceptance Criteria
- [ ] Searching "macbook" includes "MacBook Pro 16" in the results
- [ ] Searching "makbook", a typo of "macbook", returns the same results
- [ ] When there are no results, 5 "you might like these" suggestions are shown
- [ ] The search API p95 response time stays under 300ms
## Out of Scope
- Voice search
- Changes to the search result ranking algorithm (separate ticket #210)
- Category filter UI
## Constraints
- Do not change the response schema of the existing `/api/search` endpoint
- Search index re-indexing must be possible with zero downtime
## References
- Design: Figma link
- Zero-result analysis: Notion doc link
- Related code: `src/search/query-builder.ts`
The After version gives the worker a clear finish line, and for the agent, each AC becomes a verification step directly. The Out of scope field turns a 30-minute "should I do ranking too?" deliberation into zero seconds.
Chapter 6 · AI-Ready Tickets vs Human-Only Tickets
Not every ticket is good to hand to an agent. Some tickets should be handled only by humans; some need only a light touch before an agent can finish them. Make this distinction explicit with labels and the whole team shares one standard.
6.1 What makes a ticket AI-ready
| Traits of an AI-ready ticket | Signals to keep it human-only |
|---|---|
| Acceptance criteria are verifiable | Subjective goals like "it should just feel better" |
| Scope is narrow and clear | Vague exploratory work spanning multiple systems |
| Reference code paths are specified | Where to touch is itself the investigation |
| Work that follows existing patterns | Work that needs a new architecture decision |
| Result can be confirmed by tests | Heavy dependence on production data / external systems |
| Easy-to-revert change | High-risk areas like migrations, security, payments |
The core insight: an AI-ready ticket is really just a well-written ticket. It is not a separate species. Write it clearly for humans and an agent can handle it too. Conversely, a ticket an agent gets lost in usually loses a human new hire too.
6.2 Label strategy
Signal intent with tracker labels. An example scheme:
| Label | Meaning |
|---|---|
agent-ready | AC is verifiable and scope is narrow — can be delegated to an agent |
agent-assisted | An agent drafts it, a human finishes and judges |
human-only | Architecture decisions, high-risk areas, vague exploration |
needs-refinement | No AC yet or scope too big — needs grooming |
needs-refinement matters. This label is an honest signal that "this ticket cannot yet go to anyone." In grooming, you refine the tickets carrying this label and graduate them to agent-ready or human-only.
6.3 The check before handing it to an agent
What to confirm before putting agent-ready on a ticket:
- [ ] Are all acceptance criteria checkboxes and verifiable
- [ ] Are the relevant code paths/files written in references
- [ ] Is the boundary drawn with an "out of scope" field
- [ ] Did you specify which pattern in the existing code to follow
- [ ] Is there a test method to confirm the result
- [ ] Is this not a high-risk area (auth, payments, migration)
If it does not pass these six lines, it is still needs-refinement.
Chapter 7 · Linking, Traceability, the Issue Graph
A ticket is not an island. A good team's tracker is a graph — tickets connected to each other and to the code.
7.1 The five links you should connect
| Link direction | Example | Why |
|---|---|---|
| Ticket to parent epic | #102 belongs to the "User Authentication" epic | Its place in the big picture |
| Ticket to dependency ticket | #102 depends on #101 being complete | Work order becomes visible |
| Ticket to PR | A PR link in the body of #102 | What resolved this ticket |
| PR to ticket | Closes #102 in the PR description | Auto-close on merge |
| Ticket to doc / design | RFC, Figma, postmortem | The basis of the decision |
A keyword like Closes #102 auto-closes the issue on PR merge in GitHub/GitLab. This one small habit eliminates the zombie ticket that "someone forgot to close."
7.2 The value of traceability
Suppose six months later someone finds strange code. If traceability is alive, the path is this.
One line of strange code
-> git blame -> the commit
-> the commit -> the PR
-> the PR -> ticket #102
-> the ticket -> epic + RFC link
-> "ah, that's why it was written this way"
If this chain is broken, that code stays a mystery forever and nobody dares touch it. The ticket-PR-commit link is a letter to your future self.
7.3 AI agents and the issue graph
When you give #102 to an agent, if a dependency link exists the agent can pull #101's output (schema, endpoint) in as context. Without the link, the agent sees #102 as an isolated island, rebuilds what already exists, or breaks consistency. The issue graph is the agent's context map.
Chapter 8 · Ticket-Writing Anti-Patterns
Knowing the opposite of a great ticket makes you better faster. The anti-patterns most often seen in the field.
8.1 Vague verbs
"Improve", "optimize", "clean up", "make better" — these verbs are unverifiable. Always swap them for a measurable result.
| Vague verb | Measurable version |
|---|---|
| Improve search | Lower the zero-result rate from 38% to under 10% |
| Optimize the page | Lower LCP from 4.1s to under 2.5s |
| Clean up error handling | Show a user-facing error message on every API failure |
8.2 Hidden scope
Covered in Chapter 3. The pattern where the title is small but the body quietly grows. If "while we're at it" or "as a bonus" appears in the body, it is almost always a separate-ticket signal.
8.3 Missing acceptance criteria
A ticket that starts with no definition of "done." At the end, a "is this actually done?" meeting opens. A ticket with no AC should not be started.
8.4 Multiple tasks in one ticket
A ticket bundled like "fix the login bug, also polish the signup UI, also add logging." It is impossible to review, and if one part stalls, all of it stalls.
8.5 Prescribing the solution and hiding the problem
"Add an index to the users table" — why? which query is slow? Without the problem, the worker cannot verify it is the right solution, nor propose a better one. Write the problem; keep the solution at the level of a suggestion.
8.6 Anti-pattern summary table
| Anti-pattern | Symptom | Prescription |
|---|---|---|
| Vague verb | "improve / optimize / clean up" | Turn into a measurable result |
| Hidden scope | Body bigger than the title | Split the ticket |
| Missing AC | No definition of "done" | Add checkbox AC |
| Multiple tasks | Several PRs' worth in one ticket | Split into one each |
| Prescribing the solution | No "why" | Add a problem statement |
| Absent context | No "why now" | Add a background paragraph |
Chapter 9 · Copy-Paste Templates
Drop the templates below straight into your repository's .github/ISSUE_TEMPLATE/ or your tracker templates.
9.1 Bug report template
## Summary
{In one sentence, what broke}
## Repro Steps
1.
2.
3.
## Expected Result
{What should have happened}
## Actual Result
{What did happen}
## Environment
- Deploy environment: {production / staging / local}
- Browser/runtime: {version}
- App version/commit: {version or SHA}
## Evidence
```
{Stack trace / logs / request ID}
```
{Attach screenshot}
## Frequency
{Always / sometimes (M out of N) / once}
9.2 Feature request template
## Background
{What user/business problem does this solve, with data if possible}
## Proposed Change
{What to build — at the level of behavior, not implementation}
## Acceptance Criteria
- [ ] {Verifiable result 1}
- [ ] {Verifiable result 2}
- [ ] {Regression guard item}
- [ ] {Test-added item}
## Out of Scope
- {What you will not do this time, 1}
- {What you will not do this time, 2}
## Constraints
- {Performance budget / compatibility / what not to touch}
## References
- Design:
- Related code:
- Predecessor ticket:
9.3 Task template (for refactoring / chores)
## What
{One-sentence task description}
## Why
{Why this is needed now}
## Acceptance Criteria
- [ ] {Result 1}
- [ ] {Item confirming existing behavior is not broken}
## Out of Scope
- {Explicitly block the temptation to expand}
## References
-
9.4 Agent delegation checklist (for a PR description or ticket comment)
## Pre-agent-delegation check
- [ ] All AC are checkboxes and verifiable
- [ ] Relevant code paths are specified in references
- [ ] The boundary is drawn with "out of scope"
- [ ] The existing pattern to follow is specified
- [ ] There is a way to verify the result (tests)
- [ ] Not a high-risk area (auth / payments / migration)
Label: {agent-ready / agent-assisted / human-only / needs-refinement}
9.5 Tips for running templates
- A template is a starting point, not a shackle. Delete fields you do not use, but keep the five components (context, problem, AC, constraints, references).
- The
{placeholder}guidance text inside a field should be deleted as the author fills it in. A{placeholder}submitted still empty is itself a signal that "this ticket is not yet finished." - Pin three examples of good tickets in the team wiki. New hires and agents alike use them as a reference point.
Epilogue — A Great Ticket Is an Investment Sent to the Future
The ticket is the highest-leverage written artifact in software. Write it well once and it pays out as long as the code it produced lives. Write it badly once and every downstream stage splits the cost — and in the AI era, that cost is doubled. Humans had a safety net of "you can always ask"; for agents, the ticket is everything.
But the conclusion is simple. Write it clearly for humans and an agent can handle it too. It is not a separate skill — it is double the reward on the same skill.
The great-ticket checklist
- Context — "why we are doing this now" is written in one paragraph
- Problem — written as an observable fact, not a symptom
- Acceptance criteria — a verifiable checkbox list
- One fact — no AC line bundles two with "and"
- Scope — sized to finish as one reviewable PR
- Out of scope — what you will not do this time is stated
- Constraints — what not to touch, the performance budget, is written
- References — relevant code paths, design, predecessor tickets are linked
- Traceability — epic, dependency tickets, PR are connected bidirectionally
- Labels — intent is signaled with
agent-ready/human-onlyand the like
Anti-patterns to avoid
- Vague verbs — turn "improve / optimize / clean up" into a measurable result
- Hidden scope — if "while we're at it" appears, split the ticket
- Missing AC — do not start work with no definition of "done"
- Multiple tasks — do not bundle several PRs' worth into one ticket
- Prescribing the solution — do not hide the problem and write only the solution
- Empty placeholders — do not submit with the template's guidance text undeleted
Next post teaser
The next article is "Writing a Great PR Description — How to Make Reviewers and Agents Approve in 5 Minutes." If the ticket is the input to the work, the PR description is the output. The craft of writing what changed and why, how it was verified, and where the reviewer should look first — and how that meshes with AI code reviewers too.
The 10 minutes it takes to write a great ticket prevents the 10 hours it takes to undo the wrong thing built. Those 10 minutes are an investment sent to the future team and the future you.