Code Agency
5 min read

Microservices for custom web applications: when they make sense — and when they don't

Microservices solve an organisational problem, not a technical one. How we decide between a modular monolith and distributed services for custom builds, and the signals that justify the split.

Somewhere in almost every intake for a custom web application, the question surfaces: "Shouldn't this be microservices?" It's a fair question — the pattern powers Netflix, Amazon and every conference talk of the last decade. But the honest answer for most custom applications is: not yet, and possibly never.

Microservices are not a quality level you unlock. They're a trade: you exchange code complexity for operational complexity. Whether that trade pays off depends far less on your software than on your organisation.

What microservices actually buy you

Stripped of the hype, splitting an application into independently deployed services gives you four concrete things:

  1. Independent deployment — team A ships the invoicing service without waiting for team B's release train.
  2. Independent scaling — the PDF-rendering service gets 12 replicas at month-end while the admin panel idles on one.
  3. Fault isolation — a memory leak in the reporting service can't take the checkout down with it.
  4. Team autonomy — each service has clear owners, its own repo, its own on-call.

Notice the pattern: three of the four are about teams and operations, not code. That's the part of the sales pitch that usually goes unsaid — microservices solve coordination problems between multiple teams. If you don't have multiple teams, you're buying the solution without having the problem.

What they cost you

Every arrow in a microservices diagram is a function call that became a network call. That single change fans out into a long bill:

  • Distributed failure modes. A function call either works or throws. A network call can be slow, time out, half-succeed, or succeed after you gave up and retried. Now you need timeouts, retries, idempotency keys and circuit breakers — everywhere.
  • Eventual consistency. The order exists but the invoice doesn't yet. Your support team will meet this in week one; your architecture must handle it forever.
  • Operational surface. One app means one pipeline, one log stream, one thing to monitor. Eight services mean eight pipelines, distributed tracing, and a debugging session that starts with "which service was it?"
  • Local development. A new developer used to run docker compose up and get everything. Now they run eleven containers to change a button.

For a team of two to five developers — the realistic size for most custom business applications — this tax doesn't slow you down by ten percent. It can double the cost of every feature that crosses a service boundary. And on day one of a greenfield project, you don't yet know where the real boundaries are; the modules you'd guess at kickoff are rarely the seams the business actually has. Getting a boundary wrong inside one codebase is a refactor. Getting it wrong across two services is a distributed migration.

Our default: the modular monolith

The way we build custom applications captures most of the microservices benefits without the distributed-systems bill: one deployable application, strictly separated modules inside it.

Each module owns its domain — orders, invoicing, planning — with explicit interfaces between them and no reaching into each other's tables. The discipline is enforced in code review and tooling, not by a network boundary. What that gets you:

  • One pipeline, one deploy, one log stream — boring operations, in the best sense.
  • Function calls instead of network calls: fast, transactional, debuggable with a stack trace.
  • Real module boundaries that make the code navigable and testable.
  • And crucially: an exit path. Because modules don't share internals, any one of them can be extracted into its own service later — when there's evidence it needs to be.

That last point defuses the fear driving most premature microservices decisions: "if we don't start distributed, we'll be trapped." You won't be. A well-bounded module is a service waiting to happen. A tangled monolith is not — but the fix for tangled is boundaries, not networks.

When the split does make sense

We do build and run distributed services — on purpose, with evidence. The honest signals:

  • A workload with a genuinely different profile. The report generator that pins a CPU for 40 seconds doesn't belong in the web process. Move it behind a queue with its own workers and scaling rules — that's not "doing microservices", that's just good architecture, and it's usually the only split a business application ever needs.
  • Multiple teams shipping independently. When the release train itself becomes the bottleneck — teams blocked on each other's deploys — the coordination cost is now real and measured, and a service boundary pays for itself.
  • Different availability or compliance requirements. The public API that must survive a bad deploy of the back office; the payment component that needs a stricter audit scope.
  • A proven, stable boundary. You've run the system, the module's interface hasn't churned in months, and it's the thing that keeps needing independent scaling or deployment. Extract that one module — not the whole application.

Note what's not on the list: "we might need to scale someday." Scaling a monolith horizontally behind a load balancer works remarkably far — most business applications never outgrow it. When one part does, you extract that part.

The rule of thumb

Start with one well-modularised application. Split when — and only when — you can name the specific module, the specific problem (measured, not imagined), and the team that will own the resulting service. If any of those three is missing, the split is architecture theatre.

It's the same philosophy behind why every project here starts with a fit-gap analysis: decisions this expensive should be made on evidence, not on defaults. Microservices are a great answer to a problem you can point at — and an expensive answer to a problem you only suspect.

Want us to publish something specific?

Tell us what you'd like to read and we'll add it to our writing queue.

Get the next one in your inbox

New articles, videos and the occasional engineering note — a short mail when there’s something worth reading, nothing else.