Skip to content

✍️ 필사 모드: Writing Great Tickets: The Craft of Issues for Humans and AI Agents

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

"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:

ChapterTopic
1The anatomy of a great ticket — five components
2Acceptance criteria as checkboxes
3Scoping — one ticket = one PR
4The bug ticket template
5The feature ticket template
6AI-ready tickets vs human-only tickets
7Linking, traceability, the issue graph
8Ticket-writing anti-patterns
9Copy-paste templates
EpilogueChecklist + 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

ComponentThe question it answersWhat happens without it
ContextWhy are we doing this now?You solve the wrong problem
ProblemWhat is broken or missing?You patch the symptom, miss the cause
Acceptance criteriaWhat must be true to call it "done"?"It's done" disputes erupt
ConstraintsWhat must not be touched?Out-of-scope changes creep in
ReferencesWhere 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:

PerspectiveWhat checkboxes provide
WorkerA to-do list. Cross them off and you're done
ReviewerA review checklist. Match the PR against each item 1:1
PM / reportingProgress is visible (3/4 complete)
AI agentVerifiable 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 ACGood AC
Login works wellLogging in with valid credentials redirects to /dashboard
Improve performanceThe product list API's p95 response time is under 200ms
Add error handlingOn 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 smallResult
The PR is smallThe review is fast and thorough
The change is isolatedIf something breaks, it is easy to revert
The scope is clearNo "should I do this too?" agonizing
Progress is visibleBig work splits into small completions
An agent can handle itIt 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 #102 is 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

FieldContent
Repro stepsNumbered 1, 2, 3 — following them must reproduce it identically
Expected resultWhat should have happened
Actual resultWhat did happen
EnvironmentOS, browser/runtime version, app version, deploy environment
EvidenceStack trace, logs, screenshots, network response
FrequencyAlways / 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

FieldContent
Background / problemWhat user or business problem does this solve
Proposed changeWhat to build (behavior, not implementation detail)
Acceptance criteriaA checkbox list — the definition of "done"
Out of scopeWhat you explicitly will not do this time
ConstraintsPerformance budget, compatibility, what not to touch
ReferencesDesign, 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 ticketSignals to keep it human-only
Acceptance criteria are verifiableSubjective goals like "it should just feel better"
Scope is narrow and clearVague exploratory work spanning multiple systems
Reference code paths are specifiedWhere to touch is itself the investigation
Work that follows existing patternsWork that needs a new architecture decision
Result can be confirmed by testsHeavy dependence on production data / external systems
Easy-to-revert changeHigh-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:

LabelMeaning
agent-readyAC is verifiable and scope is narrow — can be delegated to an agent
agent-assistedAn agent drafts it, a human finishes and judges
human-onlyArchitecture decisions, high-risk areas, vague exploration
needs-refinementNo 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.

Link directionExampleWhy
Ticket to parent epic#102 belongs to the "User Authentication" epicIts place in the big picture
Ticket to dependency ticket#102 depends on #101 being completeWork order becomes visible
Ticket to PRA PR link in the body of #102What resolved this ticket
PR to ticketCloses #102 in the PR descriptionAuto-close on merge
Ticket to doc / designRFC, Figma, postmortemThe 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 verbMeasurable version
Improve searchLower the zero-result rate from 38% to under 10%
Optimize the pageLower LCP from 4.1s to under 2.5s
Clean up error handlingShow 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-patternSymptomPrescription
Vague verb"improve / optimize / clean up"Turn into a measurable result
Hidden scopeBody bigger than the titleSplit the ticket
Missing ACNo definition of "done"Add checkbox AC
Multiple tasksSeveral PRs' worth in one ticketSplit into one each
Prescribing the solutionNo "why"Add a problem statement
Absent contextNo "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

  1. Context — "why we are doing this now" is written in one paragraph
  2. Problem — written as an observable fact, not a symptom
  3. Acceptance criteria — a verifiable checkbox list
  4. One fact — no AC line bundles two with "and"
  5. Scope — sized to finish as one reviewable PR
  6. Out of scope — what you will not do this time is stated
  7. Constraints — what not to touch, the performance budget, is written
  8. References — relevant code paths, design, predecessor tickets are linked
  9. Traceability — epic, dependency tickets, PR are connected bidirectionally
  10. Labels — intent is signaled with agent-ready / human-only and 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.

현재 단락 (1/276)

Every software team has one artifact it writes most often and teaches least: the **ticket**. Jira is...

작성 글자: 0원문 글자: 20,537작성 단락: 0/276