← The Journal

A merge is not a deploy

Closing a pull request tells you the code got accepted. It tells you nothing about whether it is actually live. Here is why we gate "shipped" on the artifact users can reach, not the merge.

There is a satisfying moment when a pull request goes green and merges. It feels finished. It isn't. A merge only tells you the code got accepted into a branch. Whether it reached production, and whether the version your users are loading right now is the one you just merged, is a separate question with a separate answer.

We learned this the hard way on a system we run ourselves. Our deploy platform would, every so often, quietly drop the production deploy of a merge commit. The pull request closed. The dashboard said "merged." And the old code kept serving, with no error and no alert. The first sign anything was wrong was a user reporting a bug we were certain we had already fixed.

The fix was not to trust the pipeline harder. It was to stop treating the merge as proof of anything. Now, when a change merges, it does not get marked done. It moves to a "deploying" state and records the commit. A separate check watches the live site's version endpoint and only marks the change shipped once the version actually being served matches the commit we merged. If it does not match in time, the change flips to "deploy dropped," with a note on exactly how to retrigger it.

That is a small amount of plumbing for a large amount of calm. The idea underneath it is one we lean on everywhere: the state a system reports is not always the state it is in. "Merged," "deployed," "done" are claims. The served artifact is the fact. Gate on the fact.

If your definition of "shipped" stops at the merge button, you have left a blind spot in the worst possible place: between the work being finished and the work being live.