How to Guarantee You Never Ship
First published at Wednesday, 2 September 2026
How to Guarantee You Never Ship
This is a blog article out of a series where I reflect on what I learned during funding, growing (, and selling) SaaS product companies. While I write those things down for myself, since I believe self-reflection helps me learn, I also want to share my thoughts with anybody who might be interested. An overview of all related topics can be found below. I have a bunch of these blog posts lined up, so you also might want to follow me if you like this one.
In a run of posts I have argued against a handful of things one at a time: splitting code across many repositories, long-lived feature branches, reaching for microservices to model separation, dedicated quality assurance, one fixed test coverage target for every change, and pull-request review as your quality gate. Each of those is a problem on its own. This post is about adopting all of them at once, which is more common than it should be, because each arrives wearing the badge of a best practice.
So let me write the inverse of everything else in this series. If your goal were to build an organisation that never delivers anything, while every individual decision still looked responsible in the meeting where it was made, here is how you would do it.
The recipe
Start by splitting your system across many repositories, one per service or team, for clean ownership. Then let work happen on long-lived feature branches, because that is how serious features get built. Make sure those branches span several repositories at once, so a feature is never contained in one place. Cut the system into as many microservices as you can, ideally small ones, so every interaction crosses the network. Require a reviewed pull request before any merge, in every repository, and gate each one on 90% test coverage, so nothing merges until it is covered. And put a dedicated QA stage at the end, to catch what all of that let through.
Every step there is defensible in isolation. Together they are a delivery-proof machine.
Coordination boundaries multiply, they do not add
Here is the part that makes it lethal rather than merely annoying. Each of those choices adds a coordination boundary, and coordination boundaries do not add up. They multiply.
Take one small change: you add a field to a concept that several parts of the system share. Watch it travel. It touches four repositories, so it is four pull requests, not one, each reviewed by someone with only their slice in view, each merged in an order that matters. Each of them has to clear the coverage gate on its own, so a one-field change needs tests written around it in four places before any of it merges. Those repositories are on long-lived branches, so each pull request also has to survive a merge against weeks of drift. The parts talk over the network, so the field has to be added on both sides of every boundary it crosses, and the two sides are deployed separately, so for a while production is running a mix that has the field in some places and not others. Now QA is handed the result and asked to test it, and the first question is the honest one: which combination of versions is even deployed where, and what, exactly, are we testing?
Nobody can answer that, because the number of combinations is the product of all the axes you added, not their sum. Four repositories times several in-flight branches times a dozen services times a per-repository review queue times a coverage gate on every pull request multiplies the cost of shipping one coherent change.
And that is only the mechanical part. The field also needs a name, and everyone whose slice it crosses has an opinion informed by implicit knowledge of that slice and by their own taste in naming. Left alone they do not converge: the same concept ends up as customerType in one repository, accountCategory in the next, and an enum with different members in the third. So the four of them have to talk, and if the field shows up in what customers see, the chief technology officer (CTO) and the chief product officer (CPO) have a stake in what it is called too.
None of that is waste. In one checkout those same people still have the same discussion, in the pull request or standing at a whiteboard, and it converges because there is one place where the answer lands. Across four repositories the discussion turns into a merge prerequisite in four queues: nothing moves until it resolves, it resolves at the speed of the slowest calendar involved, and the first name anybody committed is now the one three other repositories have to be changed to match. Nobody in that room is the problem. The problem is that one line of code waits for the room.
You did not slow delivery down. You designed it to be impossible and called each step an improvement.
Why it happens
The unsettling thing is that no one sits down to build this. In 1944 somebody did: the US Office of Strategic Services, the wartime predecessor of the CIA, wrote a sabotage manual for occupied Europe, and its chapter on organisations reads like a process document. Insist on doing everything through channels. Refer every matter to a committee, and make the committee as large as possible. Haggle over precise wordings. Advocate caution. See that three people have to approve everything where one would do. The difference between that and a delivery pipeline nobody can ship through is intent, and only intent.
Ours accretes. Each layer is added by a reasonable person solving a real, local problem with a practice they read was correct. Separate repositories for clean ownership. Branches for isolation. Services for scalability. Reviews for quality. A coverage number so quality is measurable. A QA stage for safety. The failure is that nobody is watching the product of them: each was justified on its own terms, and the interaction effect has no owner.
And once it is in place, noone can see a change whole. The system that was split for clarity is now the system no one can hold in their head, or in one checkout, at all.
The way out is subtraction
Every post in this series has been, quietly, the same argument from a different angle, and this is the place to say it plainly. The principle is: minimise coordination surfaces, and make every boundary you add pay for itself.
That is what each piece of advice was. One repository removes the cross-repository axis. Trunk-based development with feature flags removes the long-lived-branch axis. In-process boundaries instead of reflexive microservices removes the network axis. Quality owned by the team removes the hand-off-to-QA axis. Synchronous review removes the pull-request-queue axis. Coverage decided by stage removes the gate that taxes every change regardless of whether the code will still exist next month. None of them is a productivity trick. Each one deletes a multiplier.
Boundaries are not free, and sometimes one earns its keep: a service that must scale on its own, a repository for code you genuinely publish to strangers, a flag for a customer you are stuck supporting. Keep those. The discipline is refusing every boundary that does not pay, because the ones that do not are not neutral. They multiply against all the others.
Summary
The scary part is not any single one of these practices. It is the compounding. Adopt multi-repo, long-lived cross-repo branches, reflexive microservices, dedicated QA, a blanket coverage target, and per-repo pull-request review together, and you have built, from nothing but respectable decisions, an organisation that cannot ship a coherent change. The fix is not a better process on top. It is subtraction: count the coordination surfaces a single change has to cross, and start removing the ones that do not earn their place.
Subscribe to updates
There are multiple ways to stay updated with new posts on my blog: