Architecture

Splitting a Monolith into Microservices: When, Why, and How

July 9, 2024 · 9 min read · Dillo.Tech Team

Let's start with the advice that loses us consulting revenue: most teams asking about microservices should keep their monolith. A well-factored monolith with a clean module structure is faster to develop, easier to test, cheaper to run, and simpler to debug than a distributed system — and a distributed system built from a badly factored monolith is that same mess with network calls between the pieces. The question isn't whether microservices are good. It's whether your constraints have genuinely outgrown a single deployable — and if so, how to split without betting the company.

When not to split (which is most of the time)

  • Because the code is messy. Messy code needs refactoring, not distribution. Service boundaries drawn through a big ball of mud produce a distributed ball of mud.
  • Because the team is small. Under roughly twenty engineers, in our experience, the operational tax of microservices — deployment pipelines, service discovery, distributed tracing, on-call complexity — outweighs the autonomy gains.
  • Because it's the modern thing. Résumé-driven architecture is real and expensive.
  • Because one endpoint is slow. Profile it. Cache it. Scale the monolith horizontally. Extraction is the most expensive performance fix available.

Signals you've actually outgrown it

  • Teams block each other constantly. Multiple teams queueing on one release train, merge conflicts as a daily ritual, a broken build freezing everyone.
  • Parts need genuinely different scaling or availability. A compute-heavy pricing engine and a CRUD admin panel sharing one deployment means paying for peak of both, everywhere.
  • Regulatory isolation. When audit scope (PCI, banking regulation) covers the whole monolith because payment logic lives inside it, extraction can shrink compliance cost dramatically.
  • Technology dead ends. One domain needs a runtime, language, or data model the monolith can't accommodate.

Notice these are organizational and operational signals, not aesthetic ones. That's Conway's Law working as intended: your architecture ends up mirroring your communication structure, so the right service boundaries are the ones that let teams ship independently — not the ones that look tidy on a whiteboard.

How: strangler fig, domain seams, one service at a time

Never do a big-bang decomposition. The pattern that works is the strangler fig: new services grow around the monolith, traffic shifts gradually, and the monolith shrinks until what remains is either gone or small enough to stop mattering. The playbook:

  • 1. Find the domain seams. Service boundaries should follow business capabilities — orders, payments, identity — not technical layers. Domain-driven design's bounded contexts are the map. If two "services" would need to deploy together, they're one service.
  • 2. Modularize in place first. Enforce the future boundary inside the monolith — separate modules, no cross-imports, calls through an interface. If the boundary doesn't work in-process, it definitely won't work over a network.
  • 3. Extract the first service. Pick one that changes often (so the payoff is real) but isn't the scariest (so the learning is survivable). Route traffic to it behind a facade; keep the monolith's code path as fallback until confidence is earned.
  • 4. Decompose the data — the actually hard part. Splitting code is easy; splitting the database is the project. Shared tables must become owned-by-one-service, cross-entity transactions become sagas or well-designed eventual consistency, and joins become API calls or replicated read models. Budget more time for this than for everything else combined; in our experience, teams that underestimated data decomposition are the ones that abandoned the migration halfway.
  • 5. Integrate through events where you can. An event backbone (Kafka or equivalent) keeps services loosely coupled: publish "OrderPlaced" rather than synchronously calling four downstream services. Reserve synchronous calls for genuine request/response needs — chains of them just rebuild the monolith with latency.

Stop-when-done rule: the goal is not "microservices," it's independent deployability where it pays. Many successful decompositions end with four services and a smaller monolith — and that's a win, not an unfinished project.

Where AI-assisted analysis changes the work

The riskiest step — finding real seams in a codebase nobody fully understands — used to depend on tribal knowledge. AI coding agents change that: pointed at a decade-old monolith, an agent maps which modules touch which tables, traces call graphs across the boundary you're proposing, and flags the hidden couplings (shared mutable state, sneaky cross-module queries) that would have surfaced as production incidents in month six. It also accelerates the safety net — characterization tests around the seam before extraction — the same way it does in any modernization effort. The judgment calls — where the bounded contexts are, what consistency the business can tolerate — stay with your architects.

A real-world shape

If you want to see this pattern under maximum pressure — regulatory isolation, zero-downtime requirements, and a database at the center of everything — read our core-banking decomposition case study, where a strangler-fig extraction ran against a live core-banking monolith. Engagements like that typically run as a dedicated augmented pod working alongside the client's architects, though a fully bounded extraction can also be a managed outsourced deliverable.

Key takeaways

  • Default to the monolith: split only on organizational, scaling, regulatory, or technology signals — never on aesthetics.
  • Boundaries follow business domains and team structure (Conway's Law), not technical layers.
  • Strangler fig, one service at a time, with the monolith as fallback — never big-bang.
  • Data decomposition is the hard part; budget more for it than for everything else combined.
  • Prefer event-driven integration; chains of synchronous calls rebuild the monolith with latency added.
  • AI-assisted code analysis de-risks seam-finding and test backfill, but architects still own the boundary decisions.

Wrestling with a monolith that's outgrown its skin — or unsure whether it actually has? Talk to us. We'll tell you honestly if the answer is "keep the monolith, fix the modules."

← Back to all articles

Ready to build your team?

Tell us what you need and get your first highly relevant candidate CVs within 24–48 hours.