Ask ten engineers how to split a system into services, and most will start drawing boxes around data models or technical layers: this owns users, this owns payments, this owns notifications. That instinct is not wrong, but it is incomplete. The boundary that actually holds up over time is not the one that looks cleanest on a whiteboard. It is the one that matches how your teams actually work.
Conway’s Law is a rule, not just a quote
Conway’s Law says organizations build systems that mirror how they communicate. People often treat it as an interesting observation. In practice, it works more like a rule of physics for your architecture. Ignore it, and you will pay for it again and again, not just once.
If two teams have to coordinate on every deploy, every schema change, every incident, no amount of clean API design between their “services” will make them independent. You have drawn a technical line around something that is, organizationally, still one shared problem. The service boundary is not real until the team boundary backs it up.
The question that actually matters: who owns this?
Before drawing any box, ask this. If this breaks at 2 a.m., whose phone rings? If this needs a new feature next quarter, whose list does it land on? If the answer is “it depends” or “both teams, kind of,” that is not a service boundary yet. That is shared responsibility waiting to slow everyone down.
The splits that work well are the ones that make official what was already true. A team that already runs its own list of work, makes its own daily decisions, and has a clear way of talking to the rest of the org, pulling out a service for them just formalizes what already existed. A split forced onto a team that does not have that independence creates the look of microservices with the coordination cost of one shared codebase, minus the benefit of being easy to read in a single place.
What a split before you are ready actually costs
Every service boundary you add turns a function call into a network call. That is not free.
- Delay you did not have before, on every call that crosses the boundary
- Partial failure as a new kind of bug: the call did not fail, it just did not come back in time
- Versioning, since two things now need to agree on a contract instead of just compiling together
- Debugging that feels like detective work, since a single user action can now spread across services, and understanding what happened means matching up logs across systems instead of reading one clear trace
None of this rules out splitting a service. It is the price of independence. The mistake is paying that price for a boundary that does not actually give you independence, because the teams on either side still have to talk to each other constantly anyway.
Signs you are ready to split
- The team already runs on its own priorities and schedule, and talking to other teams is the exception, not the rule
- You have a real, steady interface, the kind of thing you could write down as a contract and not expect to change every week
- You need to scale, release, or handle failure on your own terms: different load, different release schedule, different level of risk
- You have the operational maturity to support it: monitoring, tracing, and on-call habits that work across services, not just inside one codebase
Signs you are not
- You are splitting along technical layers (all the “data access” in one service, all the “business logic” in another) instead of along what the business actually does. This almost always brings back tight coupling, just now over a network
- The team that would own the new service does not have the time or the authority to actually own it, which means someone else stays on the hook for it
- You are doing it because “that is what growing companies do,” not because you can point to a coordination problem it solves today
The org chart is the real first draft
If you are planning a split, sketch out team ownership before you sketch out service boundaries. If the org chart does not support independent ownership of the piece you want to pull out, the architecture will not support it either, no matter how well the API is designed. A single codebase owned by a team that communicates well and ships fast will beat a “microservices” setup where every service secretly still needs three other teams to sign off on a change.
Split when the independence is already real and the architecture just needs to catch up to it. Resist splitting to invent independence that is not there yet. That is a reorg dressed up as a system diagram, and the costume does not hold.