·Architecture·2 min read·Senior developers

Microservices, Modular Monoliths, and 'Just Build a Monolith' — A Decision Framework

The pendulum has swung back. Most teams should start with a modular monolith. Here's how to tell when you actually need microservices, and when you're just cosplaying as one of the FAANGs.

I've watched three teams in three years pull their microservices back into a single deployable. None of them ran out of scale. They ran out of patience for the operational tax. That's the conversation we should be having: not "which is better," but "what does each cost you, and can you pay it?"

A simple framework I use:

flowchart TD
    Start[New service] --> A{Team size > 30<br/>shipping the same code?}
    A -- Yes --> B{Multiple deploy cadences<br/>or compliance domains?}
    A -- No --> M[Modular monolith]
    B -- Yes --> MS[Microservices]
    B -- No --> M
    M --> Later{Scale or org<br/>pressure later?}
    Later -- Yes --> Extract[Extract one service<br/>when the seam screams]
    Later -- No --> Stay[Stay monolith]

The boring answer is: start with a modular monolith. Use a single deployable, but enforce module boundaries inside it (separate projects, internal types, no cross-module DB access). The .NET ecosystem makes this easier than it's ever been. Minimal APIs grouped by feature, EF Core with separate DbContext per module, Aspire wiring it all up locally. You get most of the operational benefits of microservices (clear ownership, testability, independent evolution) without paying the network tax.

When to actually go microservices:

  • You have more than ~30 engineers stepping on each other in the same repo.
  • Two parts of the system have radically different scaling profiles (a 10k-qps lookup API and a 5-rps reporting service).
  • One slice needs different compliance treatment (HIPAA, PCI) and you don't want the rest of the codebase in scope.
  • You genuinely need a different runtime (Python ML service, Go networking service) and the language boundary is doing real work.

When not to:

  • Because Netflix did it. You aren't Netflix.
  • Because it sounds modern in interviews.
  • Because the senior engineer who pushed for it last quarter has already left.

The modular monolith isn't a compromise. It's the default that pays for itself for years, and then quietly grows up into a small set of services when the seams scream loudly enough to merit the surgery.

If I'm starting a new B2B SaaS tomorrow, it's a modular monolith with Aspire, deployed as a single container, with one Postgres instance and one Redis. The day I outgrow that is the day I'll have the team and the revenue to do microservices properly. Not before.