A draft pull request is an ordinary pull request with one flag set: it is marked as not ready, and GitHub refuses to merge it.
Everything else works. Checks run. People can comment and review. The diff renders. It links to Issues. The only differences are that the merge button is disabled and automatic reviewer routing does not fire.
The problem drafts solve
Section titled “The problem drafts solve”Before drafts existed, people signalled the same thing by writing WIP: in the title. That worked
socially and not at all mechanically: nothing stopped a merge, nothing knew the difference, and any
automation had to parse titles for a convention that varied between teams.
Draft state makes readiness a property of the pull request rather than a note in its title. Because it is structured data, tooling can act on it — merge automation can skip drafts, dashboards can exclude them, and reviewer routing can wait.
gh pr create --draft --title "Spike: alternative retry strategy" \ --body "Exploring approach B. Not for merge — looking for direction on the interface."What changes, precisely
Section titled “What changes, precisely”| Draft | Ready | |
|---|---|---|
| Checks run | Yes | Yes |
| Can be reviewed | Yes | Yes |
| Can be merged | No | Yes |
| CODEOWNERS review requested automatically | No | Yes |
| Counts toward required reviews | Reviews are recorded but merging is blocked regardless | Yes |
| Visible in the pull request list | Yes, marked as draft | Yes |
The CODEOWNERS row is the one that catches people. Marking a pull request ready is what triggers automatic review requests, so a draft that sits for a week has not been silently ignored by code owners — they were never asked.
When a draft is the right choice
Section titled “When a draft is the right choice”Early direction-seeking. You have an approach and want to know whether it is the right one before polishing it. Opening a draft with a clear question in the body is far more efficient than describing the approach in chat.
Long-running work that should be visible. A change spanning several days benefits from being visible — colleagues can see it exists, avoid duplicating it, and notice conflicts with their own work early.
Waiting on something external. The code is finished but depends on a dependency release, another pull request, or a decision. Draft state says “not yet” without implying “not done”.
Exercising CI on unfinished work. Checks run on drafts, so you get the pipeline’s opinion while still working.
Stacked changes. When a change depends on another pull request, keeping the dependent one as a draft until its base merges prevents accidental early merging.
When a draft is the wrong choice
Section titled “When a draft is the wrong choice”As a permanent state. A draft open for three weeks with no activity is not signalling progress; it is a stalled change occupying attention. Either finish it, close it, or say what it is waiting for.
To avoid review. If you want feedback, ask for it explicitly — draft state suppresses automatic routing, so a draft with no direct request usually gets no review at all.
For everything, reflexively. Opening every pull request as a draft and converting immediately adds a step that signals nothing. The flag only carries meaning if it is sometimes absent.
Working with drafts
Section titled “Working with drafts”gh pr create --draft --fillgh pr ready PULL_NUMBERgh pr list --draftgh pr view PULL_NUMBER --json isDraft --jq .isDraftgh pr ready converts to ready for review, which is the moment CODEOWNERS routing fires. Converting
back to draft is also possible:
gh pr ready PULL_NUMBER --undoConverting back is useful when review reveals the change needs substantial rework — it is a clearer signal than leaving it open with unaddressed change requests.
Drafts and automation
Section titled “Drafts and automation”Because draft state is structured, automation should respect it. Two patterns worth knowing:
Skip drafts in merge automation. Any bot that merges approved pull requests must check
isDraft, otherwise it will attempt merges GitHub rejects and generate noise.
gh pr list --json number,isDraft,reviewDecision \ --jq '[.[] | select(.isDraft | not) | select(.reviewDecision == "APPROVED") | .number]'Save CI on drafts, carefully. Some teams skip expensive test suites on drafts to save runner time. This is a real saving and a real trade: the checks then first run at the moment the pull request is marked ready, which is exactly when people want to merge. If you do it, run the fast checks on drafts and reserve only the slow ones.
Team conventions worth agreeing
Section titled “Team conventions worth agreeing”Draft state means whatever your team decides it means, so decide:
- Does a draft mean “do not review” or “review the direction, not the details”? Both are reasonable; ambiguity is not.
- How long may a draft stay open? A soft limit prevents accumulation.
- Who marks it ready? Normally the author, but on some teams a reviewer does after a design pass.
- Do drafts run the full pipeline? Decide once rather than per pull request.
Stacked changes
Section titled “Stacked changes”Draft state is what makes stacked pull requests workable.
A stack is a sequence where each pull request targets the previous one’s branch rather than main:
main └── refactor-storage-interface PR #1 → main └── add-retry-logic PR #2 → refactor-storage-interface └── expose-retry-config PR #3 → add-retry-logicEach is reviewable on its own — a reviewer sees only that step’s changes rather than a combined diff. The risk is merging out of order, which produces a confusing history and occasionally a broken build.
Keeping #2 and #3 as drafts until their base merges removes that risk mechanically. When #1 merges,
GitHub retargets #2 to main automatically, and you mark it ready.
Two rules keep a stack manageable. Each pull request must be independently correct — a stack where #1 breaks the build until #3 lands is one change split across three pages, not three changes. And keep it shallow: past three or four, rebasing after each merge costs more than the review clarity is worth.
Drafts and notifications
Section titled “Drafts and notifications”Draft state changes who hears about the pull request, which is the part people find surprising.
Opening a draft does not request review from code owners. Nobody is notified except watchers of the repository. That is the point — but it means a draft opened with no explicit request and no comment is genuinely invisible to the people who might help.
If you want eyes on a draft, ask directly:
gh pr create --draft --title "Spike: alternative retry strategy" \ --body "Exploring approach B — not for merge. @alice, does the interface look right to you?"Marking ready is the moment routing fires, which has a practical consequence: a draft that sat for a week and is then marked ready enters the review queue as new work, with no credit for the time it spent open. If you want review before it is finished, ask; do not wait for the system to ask for you.
Team conventions worth writing down
Section titled “Team conventions worth writing down”Draft state means whatever your team decides, so decide explicitly. Four questions cover it:
What does a draft mean here? “Do not review” and “review the direction, not the details” are both reasonable and produce different behaviour.
How long may one stay open? A soft limit — a week, two weeks — prevents accumulation. Enforce it by asking, not by closing.
Who marks it ready? Usually the author. On some teams a reviewer does after a design pass, which makes the transition a shared decision.
Does CI run fully on drafts? Skipping expensive suites saves runner minutes and moves the first real signal to the moment someone wants to merge. Running everything costs more and surfaces problems earlier.
Write the answers in CONTRIBUTING.md. Two sentences prevent the ambiguity where one person’s draft
means “please look” and another’s means “leave me alone”.
Common mistakes
Section titled “Common mistakes”Using a draft to avoid feedback. It suppresses automatic review requests, so this works — badly.
Leaving drafts open indefinitely. They stop signalling anything.
Not explaining why it is a draft. The badge says “not ready”; only you can say why.
Automation that ignores isDraft. Produces failed merge attempts and noise.
Assuming code owners have seen it. They have not been asked until it is marked ready.
Marking ready before checks pass. Wastes a reviewer’s attention on something that will change.
Exercise
Section titled “Exercise”- Open a draft pull request with
gh pr create --draft, with a body stating what would make it ready. - Confirm checks still run with
gh pr checks. - Attempt
gh pr mergeand observe that it is refused. - Run
gh pr readyand note whether any review requests appear that were not there before. - Convert back with
gh pr ready --undoand confirm the state changed. - List drafts with
gh pr list --draft.
Step 4 is the one to watch if the repository has a CODEOWNERS file — the routing firing at that moment is the behaviour most people are unaware of.
What you learned
Section titled “What you learned”- A draft is an ordinary pull request with merging disabled and automatic review routing suppressed.
- Checks run on drafts, so CI feedback is available throughout.
- Marking ready is what triggers CODEOWNERS review requests.
- Drafts suit direction-seeking, long-running work and external dependencies; they suit permanent residence badly.
- Automation must check
isDraftor it will attempt merges that cannot succeed. - The flag only carries meaning if your team agrees what it means.
Draft state and CI cost
Section titled “Draft state and CI cost”Whether to run the full pipeline on drafts is a real trade rather than an obvious call.
Running everything gives the author complete feedback from the first push, catches integration problems while the work is still malleable, and costs runner minutes on code that will change repeatedly.
Running only fast checks saves minutes and moves the first real signal to the moment the pull request is marked ready — which is exactly when someone wants to merge it, and the worst moment to discover a failure.
A middle position works well: run linting and unit tests on drafts, reserve integration and end-to-end suites for ready pull requests.
jobs: quick: runs-on: ubuntu-latest steps: [...]
full: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: [...]The draft == false condition is the whole mechanism. It is worth knowing that this makes the full
suite’s first run happen at the ready transition — so if it is slow, the transition from draft to
ready is where people will feel it.
Drafts and auto-merge
Section titled “Drafts and auto-merge”Auto-merge and draft state interact in a way worth knowing, because the combination is genuinely useful and slightly surprising.
Auto-merge can be enabled on a draft. It will not merge while the pull request remains a draft, because draft is itself a blocking condition. Marking it ready then lets the normal requirements apply, and it merges when they are satisfied.
gh pr merge 128 --auto --squash --delete-branch # queued, not mergedgh pr ready 128 # now eligibleThat sequence is a reasonable pattern for work you want to land unattended once it is finished and green: enable auto-merge while still drafting, mark ready when done, and the merge happens without another visit.
The failure mode to be aware of is the same combination used carelessly. Auto-merge on a draft that someone else marks ready will merge without the author returning — which is fine if that was the intent and surprising if it was not. On a shared repository, saying so in the description costs nothing.
gh pr view 128 --json isDraft,autoMergeRequest \ --jq '{draft: .isDraft, autoMerge: (.autoMergeRequest.mergeMethod // "off")}'Checking both together is the way to see the actual state, since neither field alone tells you what will happen.