The service you should not have written
A checklist for the moment before you create a new deployable, when saying no is still free.
Creating a new service feels like progress. It has a clean repository, no legacy, a fresh CI pipeline, and none of the compromises of the thing it is splitting away from.
It is also a permanent commitment that somebody will still be paying for in eight years. Here is the conversation worth having first.
what a service costs, in full#
Not the code. The code is the cheap part.
- A repository, a build, a deploy pipeline, a rollback path.
- A place to run, sized, with capacity headroom.
- Monitoring, alerting, dashboards, an on-call runbook.
- Secrets, credentials, and their rotation.
- Dependency upgrades and security patching, forever.
- A place in the request path that can now time out, and the circuit breaker and retry policy that implies.
- Documentation, and an owner who is still at the company.
- One more thing a new engineer must learn about.
That list is the same whether the service is four hundred lines or forty thousand. The overhead is fixed, which is why small services are the ones whose economics are worst.
the questions, in order#
1. Could this be a module?
The default answer for new functionality is a well-bounded module in something that already exists. It gets all of the above for free.
The follow-up that matters: if this were a module with an enforced boundary, what would we lose? If the honest answer is "nothing, it would just feel less tidy," write the module.
2. What is the deployment argument?
The strongest reason to extract is that a team needs to release on its own cadence and currently cannot. That is real and it scales with organisation size.
But check it: are they actually blocked, or do they merely deploy together? Two teams that could deploy independently and choose not to have a process problem, not an architecture problem, and a new service will not fix it.
3. What is the scaling argument, with numbers?
"It might need to scale differently" is not an argument. "This component is CPU-bound and spiky while the rest is I/O-bound and steady, and we are currently provisioning for the peak of both" is.
If you cannot state the resource profile that differs, there is no scaling argument.
4. Where does the data live?
The question that sinks most extractions. If the new service needs the same tables the old one does, you have not split anything — you have created two writers to one database, which is worse than one writer, and you now have to coordinate migrations across two deploy cycles.
A service that does not own its data is a distributed monolith with extra latency.
5. What happens when it is down?
If the answer is "the main flow breaks," you have added a failure mode and bought nothing in return. Fault isolation is not free; it is the result of deliberate timeouts, fallbacks and degradation, and if you are going to build those anyway you could have built them around a module.
6. Who owns it in two years?
Name the team. Not the person — the team. Services outlive their authors, and an ownerless service is the one that runs three major versions behind until it is a security incident.
the cases where the answer is yes#
Being fair, because the reflex against splitting can be as unexamined as the reflex toward it.
- A genuinely different resource profile, with numbers.
- A compliance or data-residency boundary that must be enforced structurally.
- A component that has to be written in a different language for a real reason.
- A team that is demonstrably blocked on someone else's release cadence.
- Something with wildly different availability requirements — a batch job that can be down for an hour next to a checkout path that cannot.
In every one of those, the service is buying something specific that a module cannot.
the reversibility test#
Before you create it, ask: if this turns out to be wrong, how do we merge it back?
If the answer is "we would not, we would just live with it," you are making a one-way decision on a hunch. Those deserve more scrutiny than they usually get, and the moment before the repository exists is the last time the scrutiny is free.
— Dom, August 13, 2026