Making "done" mean something in GitHub
GitHub has no status called done, so teams silently adopt merged as a substitute. Here is how to build a real finish line out of required checks, linked issues and deployment records.
Somebody asks whether the feature is done. The pull request is merged, the issue closed itself, the board moved the card. Three systems agree, and none of them knows whether a single user has ever seen the change. Two weeks later it turns out the deploy failed on Friday afternoon and nobody noticed because everything downstream had already declared victory.
GitHub has no concept called done. It has merged, and it has closed, and those are repository facts. Shipping is a deployment fact. The whole job here is to stop letting the first stand in for the second.
Most of this is native, and routinely switched off
Rather a lot of this is native, and teams routinely leave it switched off.
Closing keywords. A pull request description containing a closing keyword and an issue reference closes that issue when the pull request merges into the default branch. This is well documented and reliable, with two conditions worth remembering: the keyword has to be there, and the merge has to be into the default branch. Release-branch workflows break the second condition constantly and it is almost never noticed.
Branch protection, and rulesets in newer versions. Required approving reviews, required status checks, required conversation resolution, code owner review, restrictions on who can push. This is your merge contract, and unlike a wiki page it is evaluated every time. Newer GitHub versions offer rulesets, which cover similar ground with different scoping. Check which your organisation is on, since guidance written for one does not always transfer to the other.
CODEOWNERS. The cleanest way to make review requirements follow the code rather than the team roster. If a change touches the payments directory, the payments owners get asked, whichever team the author sits in.
Actions as required checks. Any check you can write can be made mandatory. This matters more than it sounds, because it means your definition of done is not limited to what GitHub happens to model. A check that fails when the pull request body contains no issue reference is about twenty lines and turns a convention into a gate.
Environments and deployment records. This is the important one and the one most teams skip. GitHub can record that a specific commit was deployed to a named environment at a specific time, with a status. Environments can also carry protection rules: required reviewers before a deployment proceeds, a wait timer, restrictions on which branches may deploy there. Some versions also let you require a successful deployment before a merge is allowed.
If your pipeline deploys without ever creating a deployment record, GitHub’s knowledge of your software ends at the merge commit. Everything after that lives in your CI logs, which no board, report or stakeholder will ever read.
Projects. GitHub Projects supports custom fields and built-in automations that react to pull request and issue events. Configured properly, a card can move itself on merge without anybody touching it.
Merged as the finish line
- Records that code reached the main branch
- Says nothing about the deploy
- Fails silently on release-branch workflows
- A revert leaves everything looking shipped
Deployment as the finish line
- Ties a commit to an environment and a time
- Carries a status you can query
- Supports approval before it proceeds
- Distinguishes staging from production properly
Closing keywords depend on the author remembering
Closing keywords depend on the author remembering. There is no native enforcement. Your linking rate, meaning the share of merged pull requests that reference an issue, is the number that determines whether any of the reporting built on this is trustworthy, and almost nobody measures it. Do it once and be prepared for it to be lower than you assumed.
One pull request, five issues. A refactor that touches several pieces of work either closes all of them, which is wrong, or none of them, which is honest and unhelpful. GitHub cannot split a merge across issues, so somebody has to decide what the unit of work is and hold to it.
Reverts do not reopen anything. Revert the merge and the issue stays closed, the card stays in done, and the only record of the reversal is in the commit history. This is the single most common way a board becomes quietly wrong.
Required checks are per repository. A change spanning four repositories passes four separate bars and nothing evaluates the whole. GitHub has no native notion of a change set that spans repositories, and the larger your estate the more of your risk lives in exactly that gap.
Deployment records only exist if you create them. Nothing forces this. A team can run a mature pipeline for years and have GitHub believe nothing has ever shipped.
Merged into main is not the same as released. Trunk-based teams with feature flags have a further gap: the code is deployed and the behaviour is off. If flags govern your release, then your real finish line is a flag state, and GitHub does not know about it at all.
The free settings first, then CODEOWNERS
Turn on required conversation resolution. Free, immediate, and it removes the most common class of “I thought that was addressed”.
Add CODEOWNERS before you add reviewers. Requirements that follow the code survive reorganisations. Requirements that name a team do not.
Write the linkage check. A required check that fails when the pull request references no issue. Warn for a fortnight, then enforce. Include a documented escape phrase for genuine no-issue work and count how often it is used, because that count tells you whether your issue tracker matches how people actually work.
Create deployment records. If you change one thing after reading this, change this. Have the pipeline record a deployment against a named environment with a status. It converts every downstream question about whether something is live from an investigation into a query.
Then put the approval where the risk is. Environment protection rules mean the human check happens before production rather than at merge time, which is where it belongs for anything genuinely risky. Reviewing a diff and approving a release are different judgements and it is worth stopping conflating them.
Then let the board follow. Only once merges and deployments are both recorded is it worth automating the card. Automating a card off a signal you do not trust just makes the wrong answer arrive faster.
Configured this way, GitHub answers the question properly on its own. For a single-repository team this is the complete solution and there is nothing else you need to buy.
Where this breaks down
This makes shipping slower before it makes it better. Required checks, protection rules and deployment approvals all add friction, and some of that friction lands on people who were never the problem. If your incident rate is already low, be honest about what you are buying.
Deployment records can be as fictional as anything else. A pipeline that reports success on a deployment that half-failed is worse than no record, because now the wrongness is machine-generated and looks credible.
Feature flags defeat the whole model. If release is decoupled from deploy, then GitHub cannot see your finish line by design, and no amount of configuration changes that. The answer there is that your flag system has to emit the release event, not GitHub.
Small teams pay the cost and get little. Four people shipping continuously already know what is live. The value here scales with the distance between the person asking and the person who knows, and if that distance is one desk, skip most of this.