Skip to main content
← All posts
5 min read

The Case Against Microservices for Series A Startups

Microservices are an organizational solution to a coordination problem you don't have yet. Here's when they actually help — and when they're a foot-gun.

Share

A Series A startup with 12 engineers decides to "build microservices for scale." Two years later they have 47 services, 6 of which are owned by people who left, distributed tracing that mostly doesn't work, and feature delivery that's slowed by 40%.

This story is so common it's almost a meme. And yet teams keep doing it. Let's talk about why microservices are wrong for almost every startup before Series B — and what to do instead.

What microservices actually solve

Microservices are an organizational solution. They solve:

  • Independent deployment when teams own different services. Team A ships to service A without coordinating with team B.
  • Independent scaling. The 1% of traffic that needs 10x compute doesn't force the other 99% to scale up.
  • Technology heterogeneity. ML team uses Python, payments uses Go, frontend uses Node — different runtimes okay.
  • Failure isolation. Service A crashing doesn't take down service B.

Note what's not on that list: "performance," "scalability" of the system as a whole, "clean code." Microservices don't give you those. Often they take them away.

What microservices cost

Every service boundary adds:

  • Network calls instead of function calls. 100x slower minimum, plus failure modes (timeouts, retries, idempotency).
  • Distributed tracing to debug anything cross-service. Operational complexity.
  • Schema versioning between services. Breaking changes become 3-PR migrations.
  • Deployment complexity. N services × M environments × deploy pipelines.
  • Operational overhead. Each service needs alerts, dashboards, runbooks, on-call coverage.
  • Cognitive load. Engineers must hold a mental model of N services and their interactions.

The cost is roughly linear in number of services. The benefit is roughly logarithmic. Past a certain point, you're losing.

The right size for the team

The real heuristic: a service per team, give or take.

If you have 3 teams, you should probably have 3-5 services. If you have 50 teams, 50-100 services makes sense.

Series A with 12 engineers and 2 teams: 2-3 services. Not 47.

When teams are smaller than services, you have engineers context-switching between services constantly. Each service needs maintenance the team can't afford. Engineers don't really "own" a service — they own an owner-less bundle.

What to do instead: the modular monolith

A modular monolith is a single deployable artifact that's internally structured into clear modules with explicit boundaries. The shape:

/src
├── billing/
│   ├── api.ts          (public interface for other modules)
│   ├── service.ts      (business logic)
│   └── repository.ts   (data access)
├── orders/
│   ├── api.ts
│   ├── service.ts
│   └── repository.ts
└── auth/
    ├── api.ts
    ├── service.ts
    └── repository.ts

Modules talk to each other only through their api.ts. Anything else is a lint error.

You get most of the benefits of microservices:

  • Clear boundaries between domains
  • Independent reasoning about each module
  • Refactor confidence — change a module's internals without affecting callers
  • Easy to extract later — when a module truly needs independent scaling, split it into a service

Without the costs:

  • One deployment
  • One database (with schemas per module if you want)
  • Function calls instead of HTTP
  • Standard debugging
  • One CI pipeline

This shape carries you to ~50 engineers. At that point, you can extract services where the org structure justifies it.

When microservices are right earlier

A few legitimate cases for splitting before Series B:

1. ML pipeline — Python ML stack is genuinely different from your Node/Go business logic. Run it as a service.

2. Public API gateway — strict latency requirements, very different scaling profile, different security boundary.

3. Background workers — batch processing that needs different deployment cadence than the API.

4. Acquired company integration — codebases you can't merge without 6 months of work. Run them in parallel.

These are usually 1-3 services beyond the main monolith. Not 47.

How to extract a service when the time comes

Don't just rip it out. Use the strangler fig pattern:

  1. Define the boundary. What's the API of the new service?
  2. Make the monolith call this API internally. Even though the API is implemented in the monolith, refactor callers to use it.
  3. Implement the new service. It exposes the same API.
  4. Switch one caller at a time from the monolith implementation to the new service. Use a feature flag.
  5. Decommission the monolith implementation once all callers are migrated.

This takes weeks, not days. But each step is reversible. You don't have a "big bang migration" that ships broken on launch day.

The signs you should split

Real signals it's time to extract a service:

  • The module has its own deploy cadence (changes daily while others are weekly)
  • The module has different scaling requirements (always saturated when others idle)
  • The module has different SLA requirements (5x stricter latency)
  • The module has a different team that doesn't want to coordinate deploys

Note: "the codebase is getting big" is not a signal. "Some engineers want to use Rust" is not a signal.

The post-mortem you'll write later

If you split prematurely, here's the post-mortem you'll write at Series B:

"We adopted microservices in early 2024. By 2026, we had 47 services. Engineering velocity had dropped 40%. We're now spending Q3 consolidating 20 of those services back into the monolith, because they had no team and no clear ownership. The original goal — independent team velocity — never materialized because we never had multiple teams."

Skip this. Build a modular monolith. Split when there's a real reason.

The takeaway

Microservices solve organizational problems. Pre-Series B, you don't have those problems yet. Build a modular monolith with clear internal boundaries. Extract services only when team structure or specific technical needs demand it. You'll ship 2x faster and operate 10x more easily.

Work with me

I consult with engineering teams on AI adoption, cloud architecture, and engineering effectiveness. If this post surfaced a challenge you're facing, let's talk.

Get in touch →

Explore more on these topics:

Subscribe to new posts

Get an email when I publish something new. No spam, unsubscribe any time.