K
Knacode Engineering Team
March 2026 ยท 8 min read

What Microservices Actually Cost

Microservices distribute your system across multiple services, each deployable independently. This is genuinely powerful at scale. But it comes with overhead that's often invisible until you're deep in it: distributed tracing across services, network latency between services that were previously in-process calls, managing multiple deployment pipelines, handling partial failures and circuit breakers, data consistency across service boundaries, and the cognitive overhead of understanding a distributed system. For a team of 3โ€“8 engineers shipping a new product, this overhead is often crippling.

The Specific Pain Points We Hit

On one project, a simple user registration flow touched 4 microservices: auth, user profile, notification, and billing. A bug in the notification service could silently fail without the user or other services knowing. Debugging required correlating logs across 4 different services. Deploying a feature that touched multiple services required coordinating releases. The team spent more time managing infrastructure than building product.

The Modular Monolith Alternative

A modular monolith is a single deployable unit internally structured into modules with clear boundaries โ€” the same separation of concerns as microservices, but without the distribution overhead. Auth code doesn't know about billing code. The notification module has a clear interface. But they all run in the same process, share the same database (with schema separation), and deploy together. The boundaries are enforced by code conventions and module visibility rules, not network calls.

When We Migrated Back

The migration itself was typically 4โ€“8 weeks: collapsing inter-service network calls into in-process function calls, merging separate databases (with schema namespacing to maintain logical separation), consolidating deployment pipelines into one, and removing distributed tracing infrastructure in favour of simpler structured logging. The result: deployment time dropped from 20 minutes to 4. Debugging became dramatically simpler. The team could reason about the entire system again.

When Microservices Are Right

Microservices make sense when: different parts of your system have genuinely different scaling requirements (your video processing service needs 100 GPU instances, your user service needs 2); you have multiple independent teams each owning a service end-to-end (Conway's Law โ€” your architecture should match your org structure); you need different technology stacks for different components (ML inference in Python, web tier in Go); or you're operating at a scale where the coordination overhead is worth the deployment independence. None of these conditions apply to most early-stage products.

Our Current Default

We now default to a modular monolith for any new product with fewer than 3 engineering teams. We design the module boundaries carefully โ€” as if they could be extracted into services one day โ€” but we don't actually distribute them until there's a clear, measured need. This gives us the architectural flexibility of microservices without the operational complexity. The best time to split a monolith is when you have a specific, measurable problem that distribution would solve โ€” not as a starting assumption.

Dealing with architectural complexity?

Tell us about your project and we'll respond within 24 hours with a clear, honest plan.

More from our blog