Monoliths and microservices have both powered serious production systems for years, and the choice between them is one of those questions engineering teams keep returning to as they grow. Many teams move between architectures over time as headcount shifts, traffic patterns change, or the team's infrastructure needs change. The right choice usually depends on your domain's shape, your deployment automation, and how many engineers are touching the code today.
Here's how each architecture works, where they differ in day-to-day engineering, when each one fits, examples from teams running both, and what a migration to services usually involves.
Copy link to headingWhat is monolithic architecture?
A monolithic application is a single deployable unit in which all request-handling logic runs in a single process. Martin Fowler describes it as a server-side application that handles HTTP requests, runs domain logic, reads and writes data, and renders HTML views, all within a single logical executable. Any change, however small, means rebuilding and redeploying the whole thing, and horizontal scaling works by running more instances of that same executable behind a load balancer.
Copy link to headingExamples of monolithic applications
Shopify's core commerce app is the canonical modern monolith. It runs around 2.8 million lines of Ruby across roughly 500,000 commits; hundreds of engineers contribute to it daily, and the team ships it many times a day. Boundaries within the codebase are enforced by a static analysis tool the engineering team built in-house, but the deployable unit is still a single application.
The Guardian's website followed a similar path, starting as a monolith and later adding new features as standalone services that called into the monolith's API. That shape is common once a single application outgrows what one team wants to coordinate around in a single deploy.
Copy link to headingWhat is microservices architecture?
Microservices structure a single application as a suite of small services, each running in its own process and communicating with the others over lightweight protocols such as HTTP or message queues. The canonical James Lewis and Martin Fowler definition puts three properties at the centre: services are organized around business capabilities, they're independently deployable, and they can use different programming languages and storage technologies where it makes sense. Most production systems also give each service its own data store so that two services don't fight over the same tables.
Running services independently comes with extra day-to-day work. Trading one deployable unit for many adds automated deployment pipelines, distributed tracing, per-service monitoring, and patterns such as circuit breakers and retries that keep request paths reliable when a downstream service is slow or unavailable.
Copy link to headingExamples of microservices applications
Netflix is the example most engineers reach for. After an August 2008 database corruption took down DVD shipments for three days, the engineering team decided to rebuild on cloud infrastructure as a collection of services. The migration ran for about seven years and ended with hundreds of services backing the streaming product. Uber followed a similar trajectory as its rider and driver platforms grew, eventually running thousands of services with the platform tooling to operate them.
Copy link to headingMonolithic vs microservices: key differences
The two models diverge across day-to-day engineering work in ways that surface the moment you ship code. A monolith encapsulates a system's complexity within a single process and deploy. Microservices spread that same complexity across many processes, teams, and the network connecting them.
Each dimension is worth looking at on its own, since the trade-offs that make sense for one team rarely align cleanly with another's.
Copy link to headingDevelopment and codebase
Refactoring within a monolith remains at the code level, and the compiler usually catches broken references when you rename a function or move a method. Doing the equivalent across services means working with versioned APIs, deploy ordering, and migration work for every consumer of the API.
A common failure mode here is the distributed monolith, where a system gets split into services but the deploys still happen together, and the schemas stay tightly coupled across them. You end up with the infrastructure cost of services and the coordination cost of a single deploy.
Copy link to headingDeployment
Every change to a monolith requires a full rebuild and redeploy, while microservices let you ship one service without touching the others. The advantage of independent deploys really only shows up once the supporting infrastructure is in place. Fowler's microservice prerequisites make this explicit: a deployment pipeline finishing in no more than a couple of hours is the bar before any team should adopt microservices, since shipping services independently without that pipeline tends to introduce more failure modes than it removes.
On Vercel, the git-push flow with preview deployments gives a monolith most of the deployment ergonomics that engineering teams reach for in microservices, with far lower coordination cost.
Copy link to headingScalability
Monoliths scale as a unit, while microservices scale per service. The case for per-service scaling is strongest when a component has genuinely heterogeneous load, such as a video transcoder or a search index. When traffic shape is roughly uniform across components, scaling per service usually adds infrastructure work without giving you much in return.
A Next.js application can mix rendering strategies per route within a single codebase, and fluid compute handles concurrency with Active CPU billing, so a monolith on Vercel isn't paying for idle wait time even before any decomposition.
Copy link to headingPerformance and latency
Inside a monolith, function calls remain in process. Microservices add serialization and a network hop at every service boundary, and that overhead compounds once a single user request fans out across five or six services.
With deliberate work on caching, batching, and async patterns, a microservices system can approach the perceived latency of a monolith, though that work is non-trivial and rarely free.
Copy link to headingReliability and fault isolation
Microservices can improve fault isolation when the team implements disciplined controls: circuit breakers, retries with backoff, timeouts, and graceful degradation. If those controls aren't in place, a slow downstream service propagates failures back through the call graph, bringing the whole system down with it.
A monolith has a single process boundary, so a memory leak or unhandled exception affects every code path running within the same instance. The engineering discipline ends up mattering in both models.
Copy link to headingTechnology flexibility
Monoliths constrain the stack, simplifying hiring, onboarding, and shared tooling because every developer uses the same language, build system, and dependency graph.
Microservices allow polyglot development in principle, though in practice, most production systems use a curated, governed subset of approved technologies. A handful of languages and databases stays manageable across an org; an unconstrained polyglot setup creates hiring and on-call work that compounds over time.
Copy link to headingDebugging and observability
Keeping execution in a single process makes a monolith easier to debug, since you can attach a single debugger and watch a request move through the system on a single stack trace. Microservices need an observability investment before you can answer basic questions about where a request went and why it was slow.
A microservices observability stack typically includes:
Per-service dashboards: up/down status, throughput, and latency per instance.
Distributed tracing: request paths stitched across service boundaries.
Circuit breaker telemetry: retry counts and breaker state per dependency.
Per-service alerting: thresholds tied to each service's failure modes.
Once a system grows beyond a handful of services, the observability stack becomes mandatory, since logs alone aren't enough to find root causes. Observability on Vercel covers the runtime side of frontend and full-stack apps, regardless of whether the backend behind them is a single service or multiple services.
Copy link to headingSecurity
Monoliths present a smaller attack surface, with internal communication happening entirely in-process and no service-to-service traffic to intercept. Microservices multiply the network channels you have to authenticate, encrypt, and rotate keys for.
The OWASP cheat sheet highlights the practical mTLS work this entails, including key provisioning, trust bootstrapping, certificate revocation, and rotation, each of which needs an owner and a runbook before the architecture is production-ready.
Copy link to headingCost and infrastructure overhead
Running a monolith requires less infrastructure early on, with a single deployment pipeline, runtime, and observability footprint to maintain. Microservices add API gateways, container orchestration, distributed tracing backends, per-service pipelines, and on-call rotations to staff.
Fowler describes this cost as the Microservice Premium, noting that services impose a productivity tax that's only justified once a system becomes complex enough to require them.
Copy link to headingTeam structure and velocity
Conway's Law has a lot to do with how this works in real organizations. The velocity benefit of microservices shows up when team boundaries line up with service boundaries, so each team can deploy and operate its own services with limited cross-team coordination.
Under roughly 10 engineers, a monolith keeps coordination simple and iteration quick. As headcount grows beyond what a single team can hold together, splitting along service lines becomes a more useful organizational tool than a single shared codebase.
Copy link to headingWhen to choose monolithic vs microservices
There's no universal answer here. Engineering teams usually get more out of keeping the architecture simple while they're still learning the domain, then taking on more infrastructure once a specific scaling, deployment, or ownership need shows up.
Copy link to headingWhen a monolith is the right fit
A monolith works well when domain boundaries are still evolving, the team is small, or deployment automation isn't yet mature. Splitting a system into services prematurely usually costs more than refactoring boundaries inside a single codebase.
A few signals point toward keeping things in a single deployable unit:
Domain boundaries still moving: wrong module boundaries inside a monolith are cheaper to fix than wrong service boundaries.
Engineering team under ~10 people: coordination stays cheap when everything ships together.
CI/CD or observability still maturing: per-service deploys add risk faster than they remove it without a fast pipeline.
Product still finding its shape: one codebase keeps domain learning cheap before locking boundaries into service contracts.
Fowler makes a similar argument in MonolithFirst, pointing out that almost every successful microservice system started as a monolith that grew up. Working in one codebase gives the team room to learn the domain before committing to service contracts and network boundaries.
Copy link to headingWhen microservices are the right fit
Microservices become worth the cost when scaling, deployment, or ownership needs can't be handled inside a single application, and the team has the delivery infrastructure to run them without slowing everything down.
A different set of conditions tends to point toward services:
Heterogeneous scaling needs: parts of the system have load profiles that the rest can't match.
Mature delivery infrastructure: continuous delivery, distributed tracing, and on-call rotations are already running.
Independent deployment requirements: components need to ship on schedules that conflict within one deployable unit.
Distinct team ownership: separated teams own separate business capabilities and want service-level boundaries.
Adoption goes better when it targets a specific outcome the team can name in advance. The infrastructure overhead of running services compounds quickly, and without a clear bottleneck the split is meant to solve, that overhead tends to outweigh whatever flexibility the architecture provides.
Copy link to headingWhen a modular monolith is the right middle ground
A modular monolith maintains a single deployment pipeline while enforcing internal modularity and clear domain boundaries, so you avoid the infrastructure overhead associated with services. Deployments remain straightforward, and the internal lines for later extraction are already drawn if needed. ThoughtWorks describes microservices as a destination teams reach over time, recommending engineers start with a well-modularized application and extract services only when a clear reason appears.
Copy link to headingReal-world examples of monolithic and microservices architectures
Engineering teams in production usually don't treat this as a one-way progression. The examples below cover companies that moved from a monolith to services, stayed with a modular monolith, and consolidated services back into a single application.
Copy link to headingNetflix: scaling streaming with microservices
Netflix's migration started after an August 2008 database corruption interrupted DVD shipments for three days. Rather than lifting and shifting the monolith into the cloud, the engineering team rebuilt the system as a collection of services running on cloud infrastructure with denormalized data models. The migration took about seven years, with customer-facing services completed prior to 2015.
Copy link to headingAmazon: breaking down the monolith
Amazon's original architecture was a monolith called Obidos backed by shared databases. Starting with the 1998 Distributed Computing Manifesto, the company broke the system into services and reorganized into two-pizza teams, each small enough that two pizzas could feed them and each owning a product or service end-to-end.
The organizational restructuring happened in parallel with the architectural work, and that pairing is often understated when teams plan their own decompositions; service boundaries hold up much better when ownership boundaries match.
Copy link to headingSegment: rolling back to a monolith
Segment broke its system into one worker service per destination to get fault isolation between integrations. Infrastructure overhead grew along with the service count, and developer velocity dropped as the cost of maintaining hundreds of pipelines compounded. The team eventually consolidated back to a monolith and built Centrifuge, an event-delivery system that routes events into the consolidated worker. The team's reversal is a useful reminder that the architecture must match how the team coordinates and operates day-to-day.
Copy link to headingShopify: succeeding with a modular monolith
Shopify's engineering team chose a modular monolith over microservices, keeping all the code in one codebase while enforcing boundaries between components with Packwerk, an in-house static analysis tool that checks dependency and privacy rules at compile time. Services get extracted only for two specific cases: storefront rendering, which is read-only and high-throughput, and credit card vaulting, which handles sensitive data that shouldn't flow through the rest of the system.
Copy link to headingHow to migrate from a monolith to microservices
Most successful migrations are incremental and tied to clear business value. The parts of a system that benefit from independent deployment, scaling, or ownership move first, and the rest of the monolith stays in place until a similar reason appears for it too.
The Strangler Fig pattern is the most common incremental approach: a proxy sits in front of the monolith and redirects calls to migrated functionality, while unmigrated features keep running in the original system. Each migration step should deliver independent value, since long-running migrations almost never complete according to their original plan. Bounded Contexts can help with where to draw service lines, though a bounded context can also live inside a modular monolith. For teams coordinating multiple apps or packages during the transition, microfrontends and Turborepo help keep ownership boundaries clear without forcing a full rebuild of everything at once.
Copy link to headingChoosing between monolithic and microservices architecture
For most engineering teams, the practical path is to start with a monolith, enforce clean module boundaries inside the codebase, and extract services only when a specific outcome demands it. The modular monolith has become a reasonable default for organizations that aren't yet at the scale where service independence offsets its infrastructure cost.
How you apply that depends on your context. A team with stable domain boundaries, mature deployment automation, and clear scaling bottlenecks may benefit from microservices sooner. A smaller team still learning its product usually gets more value from keeping everything in one codebase for longer.
Copy link to headingBuild and deploy any architecture with Vercel
Vercel runs both architectural patterns, so teams don't have to commit to one upfront. A monolithic Next.js application can mix rendering strategies per route, including static generation, server-side rendering, and Incremental Static Regeneration, and Rolling Releases plus instant rollback handle gradual rollouts and quick reverts as systems evolve.
For groups moving toward service boundaries, microfrontends let multiple Next.js apps run under a single domain, and Turborepo handles incremental builds in a monorepo, so only the changed code is rebuilt. You can start a new project on Vercel or browse templates to see a working baseline for either architecture.
Copy link to headingFrequently asked questions about monolithic and microservices
Copy link to headingIs microservices better than monolithic architecture?
Neither is universally better. Microservices give you independent scaling and per-service deploys, which is useful for systems complex enough to justify the extra infrastructure overhead. Smaller systems usually ship faster on a monolith or modular monolith with less infrastructure, and on Vercel both patterns deploy through the same primitives, with fluid compute handling concurrency in either model.
Copy link to headingCan monolithic and microservices architectures coexist?
Yes, and coexistence is the standard migration path. The Strangler Fig pattern routes traffic between the monolith and new services through a proxy, so functionality moves over piece by piece rather than in a single large rewrite. On Vercel, microfrontends allow multiple apps to share a single domain during the migration period.
Copy link to headingHow long does a monolith-to-microservices migration take?
Migrations usually span multiple years; Netflix's took about seven. Each step should deliver independent value, since long-running architecture programs tend to drift from their original plans as priorities change. Running an incremental migration on Vercel Functions keeps the monolith and new services on the same deployment workflow throughout the transition.
Copy link to headingAre microservices more secure than monoliths?
Not inherently. A monolith has a smaller, well-defined perimeter, whereas a microservices system introduces service-to-service traffic that requires protection via mTLS, microsegmentation, and per-service secrets. On Vercel, the platform handles WAF, DDoS protection, and BotID at the edge of the application, while resolver-level controls within the services themselves remain the responsibility of the engineering team.