The Question I Get Before the Product Even Ships

A founder called me last quarter about an internal tool used by eleven employees, with a growth plan for maybe forty more over the next two years. His first question was not about the roadmap or the budget. It was whether his team should "start with microservices so we don't have to rebuild later." I have had a version of this conversation more times than I can count, and the honest answer is almost always the same: no, not yet, and possibly not ever.

Architecture decisions at growing SMBs are too often driven by what a conference talk from a company fifty times their size recommended, not by what their actual traffic, team, and budget can support. Netflix and Uber run distributed systems that fit their scale and their engineering headcount in the thousands. That does not mean the same architecture fits a twelve-person SaaS company processing a few thousand transactions a day. Copying the architecture of a company you are not, at a scale you do not have, is one of the most expensive mistakes I watch SMBs make.

What Each Architecture Actually Buys You

A monolith is a single deployable application: one codebase, one build pipeline, one deployment, usually one database. Every part of the system can call every other part directly, in-process, without a network hop in between. Deployment is simple. Debugging is simple. A single developer can hold most of the system in their head.

Microservices split that same functionality into independently deployable services, each typically owning its own database and communicating over the network through APIs or message queues. Each service can be built, deployed, and scaled on its own schedule. That independence is genuinely valuable — once you have the team size and traffic volume to justify the overhead it creates.

The modular monolith is the option I recommend most often for growing SMBs, and it rarely gets airtime in architecture debates because it is less exciting to talk about. It is a single deployable application, internally organized into well-defined modules with enforced boundaries between them. Operationally it behaves like a monolith — one deployment, one process, one on-call rotation. Internally it is structured like microservices — clear ownership per module, low coupling, explicit interfaces. I go into how to build those internal boundaries properly in my article on clean architecture in .NET. Done right, a modular monolith is cheap to run today and genuinely easy to split apart later, because the seams already exist in the code.

The Real Cost of Splitting Too Early

Every service you add to a distributed system brings its own CI/CD pipeline, its own monitoring and logging, its own on-call surface, and its own failure modes. A function call that used to take microseconds in-process now becomes a network request that can time out, retry, or fail partway through. Data that used to live in one consistent database now gets duplicated across services, and keeping it in sync introduces eventual consistency problems that did not exist before. None of this is theoretical — it is the daily reality of running a distributed system, and I consider it a form of hidden technical debt that most teams underestimate until they are living inside it.

Two well-known examples make the point better than I can. In 2018, Segment published a widely read account of consolidating roughly 140 microservices back into a single monolithic service, citing the operational burden of maintaining that many independently deployed pieces as the primary driver. In 2023, Amazon's own Prime Video engineering team described moving a video-quality-monitoring component off a distributed, serverless microservices setup and into a single monolithic process, reporting a 90% reduction in infrastructure cost for that specific workload. Neither of these companies is anti-microservices — both still run plenty of them elsewhere. The lesson is narrower and more useful: match the architecture to the actual workload, not to the architecture that is fashionable that year.

For an SMB, the added cost usually shows up as a part-time platform or DevOps hire you did not budget for, a cloud bill that grows faster than your user base, and feature velocity that slows down rather than speeds up, because every change now touches contracts between services instead of just one codebase.

A Framework: Five Questions Before You Split

Before recommending a split, I ask clients to honestly answer these:

  • Do different parts of the system actually need to scale independently, backed by real usage data rather than a hypothesis about future growth?
  • Do you have, or can you afford, a dedicated person who owns operational complexity — deployment pipelines, monitoring, incident response — separate from the people building features?
  • Are there genuine organizational boundaries: separate teams that need to ship independently without stepping on each other's deployments?
  • Is your current deployment pain actually caused by the architecture, or by process gaps — no CI/CD, no feature flags, no staging environment?
  • Would splitting solve a measurable technical bottleneck, or mostly make the org chart look more sophisticated to investors or new hires?

If the answer to two or more of these is "no," stay with a monolith — ideally a modular one. The pain you are trying to prevent is usually smaller than the pain you would create.

A Scenario I See Often

A B2B fintech client came to me wanting to split a reconciliation engine into a ledger service, a reporting service, and a notifications service before writing a single line of production code, out of fear that they would "hit a wall" later. I recommended a modular monolith instead: one deployable application with the ledger, reporting, and notification logic living in clearly separated modules, each with its own interface and no direct access to another module's data. We shipped the MVP in about ten weeks instead of the twenty-plus a distributed build would have required, with one team, one deployment pipeline, and one database to reason about.

Eighteen months later, the signal to split finally showed up: reporting queries were competing with transactional writes for the same database resources during month-end close, and that specific bottleneck was measurable, not hypothetical. Because the module boundaries already existed in the code, extracting reporting into its own service took a few weeks rather than a rewrite.

The Migration Path When You Do Need to Split

When a real, measured need for microservices does arrive, the safest path is the Strangler Fig pattern: extract one bounded module at a time behind a stable interface, run the old and new implementations side by side, redirect traffic gradually, and retire the old code only once you trust the new service under real load. This is the same incremental discipline I recommend for teams working through broader cloud migrations — big-bang rewrites fail far more often than staged transitions, regardless of whether the destination is a new cloud provider or a new service boundary.

So, Which One Should You Choose?

Start with a modular monolith unless you already have concrete, measured evidence that specific parts of your system need to scale, deploy, or fail independently of the rest. Architecture should follow your actual constraints — team size, traffic, and organizational structure — not your aspirations for where the company might be in five years. I would rather help you avoid an expensive architectural detour than watch you take one.

Let's talk through your situation.