What We Ask Before Proposing Microservices to an SMB
When a small or mid-sized business asks us about migrating to microservices, the first meeting almost never ends up being about microservices. It ends up being about the team that will operate them, the domain that would need to be split, and the pace at which the business actually changes. Migrating without settling those three questions first tends to produce a system more complex than the one it was meant to replace: the same business logic, the same underlying limitations, now spread across more processes, more network calls, and more new places where something can break. That is why the first technical conversation with a new client does not start with the codebase. It starts with questions that, in several cases, end with a recommendation to stay on the monolith.
The first question: who is going to operate this
Before talking about domain boundaries or separate databases, we ask about the team. How many people touch the backend, how much experience they have running distributed systems, and who gets paged if a service goes down at two in the morning on a Saturday. A well-built monolith fails in limited ways: the process crashes, it restarts, and in the worst case there is a single deploy to roll back. A microservices system fails in ways a monolith never has to deal with: a call between services that hangs with no timeout, a message processed twice because the consumer is not idempotent, a retry storm that overloads a service that was already struggling. Operating that well requires distributed observability, traces correlated across services, and someone who can read them under pressure.
When a client's backend team is three or four people with no prior experience running distributed systems, the conversation is not whether the monolith can technically be split. It almost always can. The conversation is whether that team, in production, will be able to diagnose why checkout is failing intermittently when the cause sits in the communication between two services rather than inside one. If the honest answer is no, recommending microservices at that point does not solve the client's actual problem — it trades it for a harder one to debug, with the same team that already has its hands full.
The second question: where the domain's real boundaries are
Microservices requires splitting the domain into pieces that change and scale independently. That sounds straightforward until you try to do it against a real domain. We ask to see the current data model, and we go field by field through the largest tables asking who reads and who writes each one. The usual answer at an SMB with only a few years of operation is that nearly everything is read and written by the same small handful of use cases, because the business does not yet have the scale or the variety of flows that would justify separate boundaries.
A typical case: a client with an orders-and-inventory system asked us to split the two into separate services because "that's what you're supposed to do." Reviewing the model, we found that every business operation touching orders also touched inventory in the same transaction, without exception, and that no use case existed where one changed without the other. Splitting them into two services would have meant turning a local transaction into a distributed one — with all the consistency problems that come with it — without any real independence benefit to offset it, since in practice the two "services" always deployed and changed together. The domain boundary worth respecting there was not orders-versus-inventory; it was the whole checkout flow separated from the product catalog, which did change at a different pace and was owned by a different team.
This kind of review also helps us catch the opposite case: when a real boundary does exist but is buried inside the monolith because of how the code happened to be organized, not because of how the business is organized. That is when the recommendation goes the other way.
The third question: how fast each part actually changes
The last question is about the pace of change, not traffic volume. Microservices pays off when different parts of a system need to ship on different cycles: the catalog team wants to release changes several times a week, while the billing module is barely touched because it depends on regulatory integrations that change once a year. That asymmetry in pace is what justifies paying the cost of splitting processes, because the concrete benefit is that a catalog deploy no longer requires coordinating a full release of the billing system alongside it.
When the whole system changes at the same pace — because the team is small and every change flows through the same two or three people — that asymmetry does not exist yet, and splitting processes without it adds the coordination cost between services without the benefit that is supposed to pay for it.
What did not work: starting from a code analysis
On our earliest migration assessments, we started on the technical side: we read the repository, identified modules with low coupling between them, and proposed a decomposition based on that code structure. The technical analysis came out fine, but the proposal fell apart in practice because we had not first asked whether the team could operate the result, or whether those code boundaries actually matched real business boundaries. We ended up handing over architectures that were correct on paper but that the client could not sustain six months later, because the operational problem we had skipped over in the first conversation showed up anyway — just later, and already in production.
Reordering the process — team, domain, pace of change, and only then the code — did not change the quality of the technical analysis itself. It changed which questions we asked before committing to a recommendation, and it kept us from proposing something technically correct but operationally unworkable for that particular client.
When the recommendation is to stay on the monolith
The recommendation not to migrate comes up more often than you would expect starting out in this line of work. It comes up when the team does not yet have the operational experience to run a distributed system, when the domain shows no natural boundaries that can be separated without duplicating transactions that are local today, or when every part of the system changes at the same pace and none of them would benefit from shipping on its own. In those cases, the alternative we usually propose is not "do nothing" — it is modularizing the monolith internally, with explicit code boundaries between the parts that could one day be split off, so the option stays open without paying, today, the full operational cost of a distributed system the team cannot yet sustain.