Skip to content
Back to the blog

Monolith or microservices: team size sets the threshold

Two panels side by side: on the left a single block with sections inside and its database; on the right six boxes connected by straight arrows

The choice between a monolith and a service architecture is decided by how many people are going to touch the code at the same time. The traffic you expect barely enters into it. Unless you have at least one team able to deploy on its own behind each service, splitting the system gets you every cost of distribution and none of its benefits.

What each one really solves

It’s worth separating what each architecture solves from what gets attributed to it.

A monolith solves consistency. A transaction spans everything it needs, a query joins whatever tables it has to, a change deploys as a whole and either all of it goes in or none of it does. Debugging means reading one trace. Testing locally means starting one thing.

Microservices solve organizational autonomy. Each team deploys when it wants, sets its own pace and waits for nobody. That’s the main benefit, and it’s about how people coordinate.

What gets attributed to them and isn’t automatically true:

  • That they scale better. A monolith scales horizontally just as well as long as the database holds up, and the database is where the real limit sits in most products.
  • That they isolate failures. Only if you’ve designed for degradation. Without circuit breakers and timeouts, one slow service takes down everything that depends on it, and in a more confusing way than a monolith going down would.
  • That they let you use the right language for each job. True, and it tends to end with four languages that nobody on the team knows all of.

The criteria that actually decide

CriterionFavors a monolithFavors services
Teams that deployOneThree or more, with real autonomy
Deployment paceThe same for everythingVery different by area
Load profileUniformOne part uses far more than the rest
Compliance requirementsSharedOne part has requirements of its own
Data dependencyHigh, everything queries everythingLow, clear boundaries
Operational maturityNormalHigh: tracing, deployments, on-call

The first one rules. The rest add nuance.

The criterion everyone looks at and why it doesn’t matter

The number of users. It comes up in the first line of almost every discussion, and on its own it decides nothing.

A product with a hundred thousand users and a uniform usage pattern works perfectly well as a monolith. A product with two hundred users where one specific feature processes heavy files may need to pull that feature out from the start. What decides is the shape of the load and who touches it. Volume on its own says little.

The second useless criterion: what big companies use. The architectures of companies with hundreds of engineers solve a coordination problem among hundreds of engineers. Copying them with four people imports the solution without the problem.

The threshold

Here’s the concrete answer, the one almost nobody gives:

A service gets split off when there’s a team able to maintain it, deploy it and answer for it on its own. Without that team behind it, it’s just a part of your monolith that now fails over the network.

In numbers, for a product being built:

  • Fewer than 8 developers: a modular monolith. Nimboo knows of no exceptions.
  • From 8 to 25: a modular monolith with very sharp internal boundaries, and at most one or two services pulled out for a specific reason you can name.
  • More than 25 across several teams: the conversation starts to make sense, and even then it’s done piece by piece, with a reason for each piece.

And a rule that doesn’t depend on size: the number of services shouldn’t exceed the number of teams. Twelve services and two people give you twelve places to look for the same bug.

What you pay when you split

These are the costs that never make it into the slide decks. Every one of them is real, and every one of them comes due.

  • Transactions disappear. What used to be one atomic operation becomes a choreography with compensations. It’s the most expensive of them all, and the most underestimated.
  • Queries that joined tables no longer exist. Either you duplicate data, or you make several calls and compose the result in memory. Both options need maintaining.
  • Every call can fail. You need timeouts, retries, idempotency and circuit breakers at every boundary, and that’s code that has to be written and tested.
  • Debugging becomes a different job. Without distributed tracing already in place, finding where the milliseconds go turns from reading one trace into reconstructing a story across seven logs.
  • Local development gets complicated. Starting the whole product stops being trivial, and the cost is paid every morning, per person.
  • Deployments need ordering. When two services change at once, there’s a correct order to deploy them in, and discovering it in production is expensive.

None of them is a deal-breaker on its own. Together, they’re the reason a four-person team that splits its system into eight services moves more slowly six months later.

The modular monolith, which wins almost every time

The option that’s almost never named in the discussion and that’s the right answer most of the time.

A modular monolith is a single deployable with real internal boundaries: modules that talk through explicit interfaces, that don’t read another module’s tables, and that could be split off if it were ever needed.

What it gives you:

  • Transactional consistency and ordinary queries, the good part of a monolith.
  • Clear boundaries, the good part of services.
  • The option to split later with information you don’t have today: which part really needs to live on its own.

One deployable, six boundaries. If a module ever has to be split off, you already know where it cuts.

What it demands is no small thing: discipline. Without something stopping one module from reading another’s table, boundaries erode within months. The project structure itself should make it hard, and code review should check for it explicitly.

When this doesn’t apply

Two cases where the recommendation flips.

If one part of the system has a radically different resource profile (processing video, generating very heavy reports, running models), pulling it out from the start is right even if you’re a team of one. That’s a separate worker, and separate workers have been standard practice for decades. Nobody would call it a microservice architecture.

If one part has compliance requirements of its own (health data, or payment data you don’t want touching your main system), separating it shrinks the scope of what has to be audited, and that’s worth real money.

Outside those two cases, the useful question is “how many people will deploy this independently next year?”. If the answer is one or two, the architecture is already decided.


Nimboo builds whole products, and the architecture decision is made in the design phase, before any code is written, with the scope already fixed. How that phase works is in custom SaaS, and Vecinly is a product in production built on this principle.

← Back to the blog Tell us about your project →