BlogTechnical
Technical

Microservices vs Monolith: Lessons from 26 Years of Builds

VS
Vikram Malihan
Fractional CTO
October 30, 2026
8 min read
Illustration comparing one well-organised monolith with a tangle of small microservices

Microservices solve problems of scale and team size. If you don't have those problems yet, you pay the costs without the benefits. Here is what early microservices cost, the service-ready modular monolith I recommend instead, and the signals that tell you it's time to split.

Most startups split too early

Microservices are the default answer in too many architecture meetings. A three-person team with no paying users draws a diagram with twelve services, a message queue and a Kubernetes cluster, because that's how the big tech companies do it.

In 26+ years, I've built both: classic monoliths in PHP and Laravel, and microservices platforms in Spring Boot, Node.js and Nest.js. The lesson is consistent. Microservices solve problems of scale and team size. If you don't have those problems yet, you pay the costs without getting the benefits.

This post covers what early microservices cost, the middle ground most startups should start with, and the signals that tell you it's time to split.

What early microservices cost a small team

Every service you add brings work that has nothing to do with your product:

A small team overwhelmed by many services, pipelines, logs and cloud bills
  • More things to run. Each service needs its own deployment pipeline, monitoring, logs, alerts and scaling rules.
  • Network calls instead of function calls. A simple feature now crosses several services, each call can fail or slow down, and you need retries and timeouts everywhere.
  • Data split across databases. Reports and joins that took one query now need several APIs or a separate data pipeline. Keeping data consistent across services is hard.
  • Slower debugging. One user complaint means tracing a request through many services and log streams.
  • Higher cloud bills. Many small services, each with its own containers, databases and load balancers, cost more than one well-run application at low traffic.
  • Change gets harder, not easier. Early on, you don't know where the right boundaries are. Splitting in the wrong place means every new feature touches several services and needs coordinated releases.

For a team of three to ten engineers, this overhead can eat a large part of every sprint.

The middle ground: a modular monolith

A modular monolith is one application, deployed as one unit, but built as clearly separated modules. It gives you most of the structure of microservices with almost none of the operating cost.

  • Clear module boundaries. Payments, users, orders, notifications: each module owns its code and its tables.
  • Modules talk through defined interfaces, never by reaching into each other's data. This is the rule that makes a later split easy.
  • One deployment, one database, with separate schemas or table prefixes per module.
  • Background jobs and queues for slow work, so the main application stays fast without becoming distributed.

When a module later needs to scale, deploy or change on its own, you lift it out along a boundary that already exists. That's a planned extraction, not a rewrite.

The hybrid approach I recommend: a service-ready modular monolith

The best of both worlds is to run as one application today, but build every module as if it might become a service tomorrow. You get the speed and low cost of a monolith now, and a clean, low-risk path to microservices the day you have a real need.

What that looks like in practice:

  1. One codebase, many modules. Organise the code as a monorepo of self-contained modules, such as Nest.js modules, Spring Modulith modules or Laravel packages. Each has its own folder, tests and public interface.
  2. Modules talk through contracts, not shortcuts. Calls go through a defined interface or API, with shared request and response types. No module reads another module's tables.
  3. A schema per module. One database server, but each module owns its own schema. Splitting later means moving a schema, not untangling joins.
  4. Events through an in-process event bus. Modules publish events like "order placed" behind an interface. Later, you swap the in-process bus for a real queue (such as RabbitMQ, Kafka or a cloud queue) without changing the module code.
  5. One API gateway from day one. Clients call the gateway, not the modules. When a module becomes a service, the gateway routes to it and no app or partner integration changes.
  6. Deployable as one, or many. Package the app in containers, and make each module able to start on its own through configuration. Today it runs as one process; tomorrow the same code runs the payments module as its own service.
  7. Observability built in. Request IDs, structured logs and tracing across modules from the start, so the first split doesn't leave you blind.
Day one: modular monolithWhen a real need appears: extract a service
DeploymentOne app, one pipelineSame code, the module deployed on its own
DataOne database, a schema per moduleThat module's schema moves to its own database
CommunicationFunction calls behind interfaces; in-process eventsSame interfaces over HTTP or gRPC; events over a queue
Client accessThrough the API gatewayStill through the gateway, now routed to the new service
TeamOne team owns everythingA team owns the extracted service

The extraction then follows a simple path: put the module behind its interface (already done), give it its own database, deploy it separately, route traffic to it through the gateway, and remove the old in-process wiring. Because the boundaries existed from day one, it's a planned project of routing changes and a data migration, not a months-long rewrite.

From monolith to microservices, one step at a time

Most products move through three stages. The mistake is jumping straight to the last one.

Path from a monolith to a modular monolith to selective services, with the signals to move between stages

In practice, many successful products never go past a modular monolith with two or three extracted services. That's a sign of good architecture, not a lack of ambition.

How I've split real systems

On the microservices platforms I've architected, each service split had a specific reason. None was split just to follow a pattern.

PlatformServices that earned their own deploymentWhy they were split
EV charging management SaaS (Nest.js)Charger communication (OCPP), billing, load balancingThousands of chargers send constant status messages, which must scale separately from billing and apps
Food delivery SaaS (Nest.js)Ordering, kitchen, delivery routingOrder spikes at meal times and festivals hit ordering far harder than the rest of the platform
iGaming talent marketplace (Node.js)Payments and escrow, KYC, matchingMoney and identity checks need strict security and audit trails, kept apart from fast-changing product features
Exam preparation platform (Spring Boot)Content, testing and analyticsExam-day traffic on mock tests needs to scale without slowing content management

The pattern: split where load, security or release speed is genuinely different from the rest of the system. Everything else stays together until it has its own reason to move.

Ops costs to budget for

If you do move to microservices, budget for the platform work around them, not just the services:

  • Container orchestration (Kubernetes or a managed container service) and someone who knows how to run it.
  • Centralised logging, tracing and monitoring across every service.
  • An API gateway, service-to-service security and secrets management.
  • A message queue or event bus for work that crosses services.
  • Automated testing and deployment pipelines for each service.

Where you deploy matters too. Customers in regulated markets often expect their data to stay in their region, and each extra region multiplies the infrastructure you run.

Cloud regions across Europe, the Middle East, Africa, India and South East Asia for a growing platform
RegionCommon cloud regionsWhy it matters
EuropeFrankfurt, Ireland, LondonGDPR and customer expectations of EU or UK hosting
Middle EastUAE, Bahrain, Saudi ArabiaPDPL rules and data residency for regulated clients
AfricaCape Town, JohannesburgPOPIA, and lower latency for African users
IndiaMumbai, HyderabadDPDP Act and latency for Indian users
South East AsiaSingapore, JakartaRegional hub, plus local data-localisation rules in some countries

A monolith deployed in two regions is far simpler to run than twenty services deployed in two regions.

Before you split: a checklist

  • You can name the specific problem the split solves: load, security, team ownership or release speed.
  • The module already has a clean boundary and its own data.
  • You have centralised logs, monitoring and tracing in place.
  • Deployments are automated and tested.
  • A team or person will own the new service.
  • You've estimated the added cloud and maintenance cost per month, in USD.
  • You've planned data residency for each region you serve.

Not sure your architecture fits your stage?

With 26+ years of building platforms, I help founders choose the architecture that fits their team and growth, and plan when and how to split.

Book an architecture consultation
#Architecture#Scaling#Startups
Share this article
VS

About Vikram Malihan

Fractional CTO with 26+ years of experience building and scaling technology teams across multiple continents. Specializing in helping startups and scale-ups navigate technical challenges, from architecture decisions to team building and fundraising support.

Learn more about Vikram

Need Technical Leadership for Your Startup?

Get expert guidance on technology strategy, team building, and scaling your product.