Isaac Emmanuel

2025.08.21

You probably don’t need microservices. You need an index.

Measure first, rearchitect later (maybe never)

Also published on Medium on 2026-10-23.

I’ve been asked to help with slow systems more times than I can count. The conversation usually starts the same way. The API is slow, users are complaining, and someone has already drawn a diagram with the monolith split into six services and a message queue in the middle.

Sometimes that’s the right answer. Most of the time, it isn’t. Most of the time, the problem is a missing index.

The pattern

Here’s how it usually plays out:

  • An endpoint gets slow
  • Someone proposes splitting the system into services
  • Someone else proposes adding a cache
  • A third person suggests a bigger database instance
  • Nobody has looked at the query plan

Every one of those proposals is expensive. Splitting a system takes months and adds operational complexity forever. Caching introduces invalidation bugs. Bigger hardware costs money every month and often just delays the problem.

Looking at the query plan takes five minutes.

Start with the database

Before anything else, I want answers to a few boring questions.

How many queries does this request run? If a single API call triggers dozens or hundreds of queries, you probably have an N+1 problem. It often hides inside ORM code that looks innocent: a loop over orders that lazily loads each customer, each address and each line item.

What does EXPLAIN say? Run the slow query with EXPLAIN (or EXPLAIN ANALYZE). Look for full table scans on large tables, sorts that spill to disk and joins that touch far more rows than they return.

Which columns are we filtering and sorting on? If you filter by tenant and sort by created date, an index on just one of those columns may not help. A composite index that matches how you actually query often makes a dramatic difference.

How much are we loading? Selecting every column, including large text fields, when the endpoint needs three of them wastes I/O, memory and network time.

Common fixes that take minutes

In my experience, the majority of “we need to rearchitect” situations are solved by something on this list:

  • Adding a composite index that matches the query’s filter and sort
  • Replacing a loop of queries with one query and a join, or batch loading
  • Selecting only the columns you need
  • Adding pagination to an endpoint that returns everything
  • Moving a slow, non-urgent piece of work to a background job
  • Removing a query that’s no longer used but still runs on every request

None of these are glamorous. All of them are cheap, low risk and easy to roll back.

Why we jump to architecture

If the fix is usually simple, why do teams reach for big changes first?

Partly it’s because architecture is more interesting. Designing services feels like engineering. Reading a query plan feels like admin.

Partly it’s visibility. Many teams don’t have good query-level observability, so they can see that an endpoint is slow but not why. Without that information, big structural changes feel like the only lever available.

And partly it’s that “the architecture is wrong” is an easier story than “we forgot an index.” But the second one is much cheaper to fix.

When architecture is the answer

To be fair, there are real reasons to split systems: separate teams that keep blocking each other, components with very different scaling needs, strict isolation requirements, or genuinely independent domains. I’ve built and migrated distributed systems for those reasons.

But “this endpoint is slow” is rarely one of them. Performance problems are usually local. Architectural changes are global. Using a global fix for a local problem means paying a lot for something you could have had cheaply.

A simple rule

Here’s the rule I try to follow and teach:

  • Measure. Find out exactly where the time goes.
  • Fix the local problem. Index, query shape, payload size, background jobs.
  • Measure again.
  • Only then consider structural changes, with data to justify them.

Architecture decisions are expensive and close to permanent. An index takes minutes to add and seconds to drop.

Measure first. Rearchitect later. Maybe never.