SaaS & Architecture

The Role of Microservices in Modern Web Applications

A
Abdul Rahaman
2 September 2026
11 min read
microservicessoftware architecturemonolithbackend developmentSaaS & Architecture

The Role of Microservices in Modern Web Applications

Microservices get recommended a lot. They're presented as the architecture of serious, at-scale products — what Netflix uses, what Amazon uses, what mature engineering organisations do. The implication is clear: if you want to build something that scales, you should build with microservices.

This framing is incomplete in a way that causes real harm to product teams. Amazon, Netflix, and Airbnb didn't start with microservices. They started with monoliths, outgrew them, and migrated — after they understood exactly which parts of their systems needed to scale independently and had the engineering teams large enough to operate distributed infrastructure. The success stories are about microservices at the end of a growth journey, not the beginning of one.

The actual question worth asking is not "should we use microservices?" but "does our specific situation have the characteristics that microservices solve?" The answer to that question is context-specific, and for most product teams, it is "not yet."


What Microservices Actually Are

A microservices architecture decomposes an application into a collection of small, independently deployable services, each responsible for a single business capability. A payment service, a notification service, a user authentication service, a product catalogue service — each runs as a separate process, communicates over a network (typically REST APIs, gRPC, or message queues), and maintains its own data store.

The contrast with a monolith is architectural, not just about code organisation. In a monolith, all the application logic runs in a single process. The payment module and the notification module call each other directly in memory — fast, reliable, and debuggable. In a microservices architecture, that same call crosses a network boundary, introducing latency, failure modes, and the need for explicit handling of network errors, timeouts, and partial failures.

The key insight: microservices move complexity from the code level to the operational and networking level. The code in each individual service may be simpler. The overall system is significantly more complex to operate, debug, and maintain.


The Monolith Is Not a Legacy Architecture

The industry has spent a decade treating "monolith" as a pejorative. It isn't. A well-structured monolith is the correct architecture for the majority of products at the majority of stages of their growth.

The arguments in favour of starting with a monolith are concrete:

Development velocity. Adding a feature to a monolith means writing code and deploying one application. Adding the same feature across a microservices architecture may mean modifying three services, updating inter-service API contracts, deploying each service independently, and updating integration tests. The overhead is real and compounds across every sprint.

Simplicity of debugging. When something goes wrong in a monolith, the stack trace tells you exactly what happened and where. When something goes wrong in a microservices architecture, tracing a user request as it flows through six services requires distributed tracing tooling, centralised log aggregation, and significant debugging overhead. The phrase "distributed systems are hard" is not rhetorical — it describes a genuine increase in the cognitive and operational burden of understanding what your system is doing.

Cost. Running one application is cheaper than running ten. Each microservice needs its own containerised infrastructure, its own CI/CD pipeline, its own monitoring setup, its own service discovery registration. For a team of five or ten engineers at an early-stage product, this overhead is disproportionate to the benefit.

The modular monolith as a middle path. A well-structured monolith organises code into clean, domain-aligned modules — a payments module, a users module, a notifications module — with clearly defined interfaces between them, even though they all run in the same process. This gives you the domain boundary discipline of microservices without the operational overhead. And critically, if a specific module genuinely needs to be extracted into an independent service later, the clean boundaries make that extraction tractable rather than surgical.


When Microservices Are Actually the Right Answer

Microservices solve specific, real problems. The question is whether those problems exist in your system yet.

Independent scaling requirements. If your image processing pipeline needs 20x more compute during peak hours than your user authentication service, running them in the same process means scaling both together — wasteful and expensive. Microservices allow you to scale the image processing service independently, allocating compute precisely where traffic demands it. This only matters when the scaling pressure is genuinely asymmetric and meaningful at your traffic level.

Team autonomy at scale. This is the argument that Amazon and Netflix are actually making when they describe their microservices architecture. When you have ten teams of eight engineers, each working on different business domains, a single shared codebase creates coordination overhead — teams step on each other's changes, deployment schedules conflict, and a bug in one team's code blocks another team's release. Microservices give each team a bounded, independently deployable domain they own completely. This is an organisational solution to an organisational problem. It makes no sense for a team of twelve.

Fault isolation requirements. If a failure in your recommendation engine should absolutely not take down your checkout flow, running them in the same process is a liability. Independent services with circuit breakers mean a failing service degrades gracefully rather than cascading. For products where specific domains have strict availability requirements independent of others, this isolation has genuine value.

Different technology requirements per service. A machine learning inference service might legitimately need Python and GPU access, while the web API is best served by Node.js. Running them as separate services makes this possible. In a monolith, every component shares the same runtime.


The Hidden Costs That Kill Premature Microservices Adoptions

The most common failure mode is adopting microservices for the benefits but underestimating the costs. The costs are not optional complexity — they are inherent to distributed systems.

Network calls replace function calls. Every inter-service call is a network request. Network requests fail, timeout, and return unexpected errors in ways that in-process function calls do not. Your code must explicitly handle these failure modes everywhere — retries, exponential backoff, circuit breakers, fallback behaviour. This is non-trivial code that adds up across dozens of service boundaries.

Distributed transactions are hard. In a monolith with a single database, wrapping a multi-step operation in a transaction gives you atomicity — either everything succeeds or everything rolls back. In a microservices architecture with distributed databases, there is no transaction. You need patterns like the Saga pattern to coordinate state changes across services, and these patterns are genuinely complex to implement and debug correctly.

Observability requires investment. You cannot understand what a distributed system is doing without distributed tracing (OpenTelemetry, Jaeger), centralised logging (across services, into a searchable aggregate), and service mesh visibility. Setting up and operating this infrastructure is real work. Without it, debugging production issues in a microservices architecture is effectively impossible.

The "distributed monolith" anti-pattern. Teams that split into microservices without genuine domain independence often create a distributed monolith — the worst of both worlds. Services that can only be deployed together because they share a database or are tightly coupled through synchronous API calls give you the operational overhead of microservices with none of the independence benefits. The prerequisite for microservices is genuinely independent domains. If you can't identify your bounded contexts cleanly, you're not ready.


How to Decide: A Practical Framework

The decision between a monolith and microservices should be driven by the following questions, in order.

Do you understand your domain boundaries clearly? Microservices require you to split your system along domain boundaries that are stable and genuinely independent. If you're still figuring out what your product does — which is true of most pre-PMF startups — your domain boundaries will change and your services will need to change with them. Every boundary change in microservices is expensive. In a monolith, you refactor. This question alone rules out microservices for most early-stage products.

Do you have teams large enough to own independent services? The two-pizza-team rule (each service team should be small enough to be fed by two pizzas) is not a coincidence — it reflects the organisational prerequisite for microservices. If you don't have enough engineers to staff independent, autonomous teams per service, you will share people across services and lose the autonomy benefit entirely.

Is there genuine asymmetric scaling pressure? Can you identify specific parts of your system that need to scale at fundamentally different rates? Not "we think payments might need more compute eventually" but "our image processing service currently handles 10,000 operations per hour and needs to scale to 500,000 while our user service stays flat." Real, measurable, asymmetric scaling is a concrete argument for splitting those services out.

What is the operational maturity of your team? Running microservices in production requires DevOps maturity — container orchestration, service discovery, distributed tracing, multi-service deployment pipelines, incident response across services. If your team has not operated distributed systems at scale before, the learning curve will materially slow your product development.

SignalRecommendation
Pre-PMF, team < 15 engineersModular monolith — strong default
Post-PMF, team 15–40, clear domain boundaries emergingModular monolith with selective extraction of high-pressure services
Team 40+, multiple independent squads, clear asymmetric scalingMicroservices viable with proper DevOps investment
Any size, unclear domain boundariesMonolith — do not split until boundaries are stable

At StartupSphare, we default to a well-structured modular monolith for early-stage product builds. The codebase is organised by domain — modules with clear interfaces and minimal coupling — so that when a specific service genuinely needs extraction, it's a defined engineering task rather than an architectural surgery. For the Ridgeline Health telehealth platform, this meant a single Next.js + Node.js application with clear domain separation between appointments, billing, and patient records. The architecture handled 1,400 patient signups in month one without any service extraction required.


What "Carving Out" a Service Actually Looks Like

If you start with a modular monolith and eventually need to extract a service, the process is significantly cleaner when the module was written with separation in mind.

The canonical steps:

  1. Identify the extraction candidate — which module has the asymmetric scaling pressure, the team autonomy requirement, or the technology difference that justifies independence?
  2. Define the API contract — before splitting, define the explicit interface between this module and the rest of the monolith. What data does it expose? What events does it emit?
  3. Introduce an abstraction layer — refactor the monolith to call the module through the defined interface, as if it were already a separate service. Run it in-process initially.
  4. Extract and deploy independently — move the module into its own deployable unit, replace the in-process call with a network call, run both in parallel, validate.
  5. Remove the monolith's copy — once the extracted service is verified in production, remove the module from the monolith.

This approach is far less risky than the "big bang" split that many teams attempt. You understand the interface before you depend on the network. You test the network call in production before you remove the fallback.


FAQ

What is the difference between microservices and a monolith? A monolith runs all application logic in a single process. Microservices decompose an application into independently deployable services that communicate over a network. A monolith has one deployment, one codebase, one shared database. Microservices have many deployments, many codebases, and typically independent data stores per service. The complexity moves from the code level to the operational and networking level.

Should a startup build with microservices from day one? Almost never. The prerequisites for microservices — clear domain boundaries, teams large enough for service ownership, DevOps maturity for distributed infrastructure — are rarely present at the start. Most successful microservices stories (Amazon, Netflix, Airbnb) started as monoliths that were selectively decomposed as the team and traffic scaled. Starting with microservices prematurely adds distributed systems complexity before you understand what you're building.

What is a modular monolith and why is it recommended for early products? A modular monolith organises code into clearly defined, domain-aligned modules with minimal coupling between them — but runs as a single deployable unit. It gives you the architectural discipline of clear domain boundaries without the operational overhead of distributed services. Modules can be extracted into independent services later if specific scaling or team autonomy needs arise.

What is a "distributed monolith" and why is it the worst outcome? A distributed monolith happens when teams split into separate services but the services remain tightly coupled — they share a database, or they can only be deployed together, or one service fails to function without another being available. It combines the operational complexity of microservices with none of the independence benefits. Avoiding this requires genuinely independent domain boundaries before splitting.

How many microservices is too many? There's no fixed number, but the right sizing is one service per bounded domain, owned by a team small enough to understand the entire service. When a service is owned by multiple teams or needs to be modified every time an adjacent service changes, it is either too broad or the boundaries are wrong. The signal that you have too many is when deploying a feature requires coordinating changes across four services that conceptually belong to the same domain.


If you're planning a complex platform and want architecture guidance from engineers who have built and operated both monolithic and distributed systems in production, talk to our team before committing to an approach. The architectural decision made on week one determines the operational reality for years.


Custom Software & SaaS development · Scalable AWS cloud architecture · API-first development guide

Author: Abdul Rahaman
Last updated: August 2026

Ready to Build This for Your Business?

Talk to our product team — we'll scope your idea and turn it into web, mobile, or custom software, then help it grow.

Start a Project