A service boundary should keep paying rent.
Here is a state most systems reach eventually and nobody ever chooses.
Two services, separate repositories, separate pipelines, separate runtimes. Every network cost that implies is still being paid: the serialization, the timeouts, the versioned contract, the distributed trace. And yet A cannot reach production safely unless B goes with it. The release runbook says deploy A, wait for validation, deploy B, verify together.
That is the full distributed-systems bill with none of the release independence the boundary was bought for. The bill is not abstract. Two services at 99.9% on a serial path give you about 99.8%, which turns 43 minutes of monthly downtime budget into 86. Add the versioned contract, the distributed trace, the retry policy, and the second on-call runbook. All of it is still charged monthly.
This state is not a compromise architecture to settle into. It is a decision point with two legitimate exits, and most teams take neither, because there is no meeting where either proposal would belong.
You can see it in deploy records without instrumenting anything new. Count the share of releases that could not go out alone and you have a Release Coupling Ratio. Teams running that query for the first time are usually surprised, which is the point. Nobody decided on this.
One clarification before the exits, because this gets conflated constantly. Shipping two features on the same day is not release coupling. You can deploy A and B independently for weeks and flip a flag for both at once, and that is a coordinated product launch. The ratio is about what it takes to reach production, not about when a customer sees the result.
The Choice Is Not Two Services or One
Most of this conversation gets stuck because people frame it as a binary. On the release and runtime axis there are three operating states worth distinguishing, plus a deeper recombination that is a separate decision.

Position 1 is what healthy release independence looks like. Two services, two runtimes, and either one can ship without the other. That is one axis rather than a clean bill of health, since a pair can release independently and still share a database or propagate every failure.
Position 2 is where this post started, and it usually arrives by drift rather than design. Nobody proposes coupling their releases. A compatibility shortcut here, a shared migration there, a rollout that has to be sequenced once and then always, and eighteen months later the runbook is the architecture.
So position 2 is a diagnosis, and it forks two ways.
Exit one: restore release independence
If runtime isolation or independent scaling still matter, or if team autonomy depends on being able to deploy without waiting, fix whatever made the releases dependent and get back to position 1. Backward-compatible contracts. Expand-and-contract schema changes. Consumer-driven contract tests in CI. A flag that separates rollout from deploy. Merging here throws away a boundary that is still earning.
This is the more common right answer, and it gets less attention because it is unglamorous. Nobody gives a conference talk called “We Fixed Our Contracts.” But most position 2 boundaries got there through ordinary contract rot rather than through a domain that turned out to be one thing, and rot is repairable.
Exit two: consolidate
If those benefits no longer justify a distributed boundary, and consolidation is acceptable on cost and reversibility, one consolidation path is position 3. Consolidation is a family rather than a single move, with internal libraries and tighter modules at the reversible end and structural merges at the other.
Position 3 is the one people usually mean by merge, and the details are the whole point. The network boundary disappears. The domain boundary does not. The modules stay separate. The relevant schemas and contracts stay explicit. The tests stay. The observability stays. What you stop paying is the distributed-systems tax on a boundary that stopped justifying distribution.
Two kinds of enforcement do different jobs here. Module visibility and architecture tests keep the structural coupling honest. Semantic contracts keep the behavioral agreement honest: ordering guarantees, staleness tolerance, idempotency scope, the invariants a type signature never captured. Removing the network does nothing to the second category.
Segment’s 2018 write-up is still the most useful public account of this path. They had a separate service per data destination, more than a hundred of them, and operational cost grew with each one. The failure mode was not latency. A shared library change had to be rolled across a hundred repositories, so it usually was not, and the versions drifted. After consolidating, they reported more improvements to those libraries in six months than they had managed in a full year under the old structure.
One diagnostic cuts across both exits. A boundary with no consumer-driven contract tests and no schema gates in CI is not really being maintained as a boundary, whatever the topology says. That makes it a consolidation candidate on its own. It also makes it the cheapest thing on this page to fix, if what you actually want is to keep the split.
Fusing the code and the data completely is where most people’s mental image of “merge” lives. It is a separate domain-modeling decision rather than the goal of consolidation, and it only arises once the module boundary has also stopped carrying useful responsibility. That is a different finding, and acting on it is far less reversible. Jumping straight there from a coupled pair is how a merge becomes the six-month project someone eventually writes a cautionary blog post about.
When Merging Is the Wrong Answer
I have argued for a direction the industry underuses. Here is where the argument breaks.
Module boundaries are weaker than network boundaries. This is the strongest objection to everything above, and it deserves a direct answer rather than a footnote. A network call is a crude enforcement mechanism. It is also one a developer three weeks from a deadline cannot casually bypass, and an import rule is not. Plenty of teams split precisely because their module boundaries had already rotted. Consolidating puts those modules back within reach of the same pressure that rotted them the first time.
The answer is that enforcement has to be mechanical rather than cultural. Shopify built Packwerk for exactly this, a static check in CI that fails the build when one package reaches past another’s public surface.
Read their own retrospective before treating it as solved. Adoption inside their own monolith was partial rather than universal, and driving a single package to zero violations required enough indirection that the code got harder to read. So enforcement is available and it is not free. If you are not going to fund it, this objection stands and you should keep the network.
Data is often where consolidation gets expensive. Separate schemas with separate migration histories and different consistency requirements make this a data migration project, and the code merge is the easy half. A shared database removes one migration step, but read that as a diagnosis rather than good news: the data boundary eroded some time ago. The post that tells you to merge without mentioning the database is selling you something.
Scaling profiles do not average. A CPU-bound service and a memory-bound one in a single process scale on the worse dimension. If one has a much steeper scaling curve, merging forces both to scale as a unit, and every replica added for the hot component carries the footprint of the quiet one. That alone can justify keeping a boundary that fails every other test.
Compliance and blast radius. Data residency, PCI or PHI scope, tenant isolation, or credential separation can make a boundary mandatory regardless of how chatty it is. A merged service drops the edge between A and B but inherits the union of their external dependencies, so check that the result is not simply a new hub.
Segment named their losses too, which is why their account is worth more than most. Consolidating cost them fault isolation, made in-memory caching less effective, and turned dependency upgrades into a change with a much wider blast radius. Those were not surprises they discovered later. They were the price of the trade, and they said so.
Two more to price before you start. Availability requirements can differ legitimately, and merging fuses the fate of a component that must survive a partial outage with one that can be down for an hour unnoticed. And the regressions in build time, test duration, cold start, and rollback size are never dealbreakers and always surprises.
Creating a boundary is expensive and removing one is expensive too. The asymmetry is as much psychological as technical: we underprice the ongoing tax of a new boundary because it arrives in installments nobody signs for, then overprice the reversal once undoing it feels like a loss.
Make the Question Routine
Neither exit gets taken if nobody is asked to choose, and that is the actual failure here. It is organizational rather than technical. A team can know its Release Coupling Ratio is 0.7, agree that the boundary is not earning, and still do nothing for two years, because doing something requires a forum that does not exist.
So put it on a calendar as a Consolidation Review, and run it like a finance committee rather than a design discussion. Once a quarter, rank boundaries by weighted edges, traffic and change frequency and data sensitivity, then take the top few and ask five questions:
- What is this boundary buying right now? Independent deployability, failure isolation, independent scaling, clearer responsibility. If the answer takes more than a sentence, that is data.
- Does the telemetry agree? Release Coupling Ratio, Sync Hop, Dependency Degree, incidents that cross. Claimed and observed benefits diverge more than anyone expects.
- Would we draw this boundary today?
- What does keeping it cost for another quarter?
- What would it cost to undo?
Splitting those last three matters. “Would we draw it today” removes sunk cost, which is correct: the six months spent on the split is gone regardless of what you decide next. The other two are future costs, and neither is sunk. Keeping the boundary costs the operational tax plus the cost of delay on everything the coordination slows down. Undoing it costs a migration plus a risk premium, and that is a live expense the sunk-cost framing is very good at waving away.
So a boundary can be one you would never create today and still one you should keep, because the migration costs more than another quarter of friction. That is a defensible answer, and it needs saying out loud rather than arriving by avoidance.
The output does not need to be an architecture project. Keep it, measure it, or schedule the work. Most boundaries land in the first bucket, which is fine, because now they are there on purpose.
Whatever the outcome, attach a sunset to it. Three months, six, or twelve, depending on how fast the evidence should move. Release Coupling Ratio, P95 and P99, change failure rate, and time to recover should improve before that date, or the decision comes back to the same table. A review that only produces decisions is half a loop.
Then write down what happens if the review does not happen, because eventually it will not. Somebody leaves, a quarter gets eaten by an incident, the calendar invite quietly dies. A decision with a defined default expires into that default instead of into permanence, and the difference between those two is the whole mechanism.
Without something like this, every boundary becomes permanent by default, and default permanence is not a decision anyone made.
Split When the Boundary Buys Something
Microservices work because boundaries work. A service boundary should keep paying its rent, and independent deployability, failure isolation, scaling flexibility, and clearer responsibility are how it pays.
When those benefits stop arriving, the bill does not stop with them. The network hops remain. The distributed failure modes remain. The coordination overhead very much remains. The semantic contracts remain too, and that one is deliberate: consolidation removes topology that stopped earning without erasing a domain boundary that still means something.
We have gotten very good at taking systems apart. The other skill starts with something much smaller than a merge: a recurring meeting where “should this still be two things” is an ordinary question rather than a career risk.
This essay develops an idea from Chapter 3, “The Illusion of Small Services,” in Beyond Microservices: Building Systems That Align Architecture, Teams, and Flow. The measurements referenced above are covered in the companion post on the five signals.

Leave a Reply