DEV Community

Cover image for Stop Using Microservices Wrong - It is About Teams, Not Code
Darwin Manalo
Darwin Manalo

Posted on • Originally published at yourdomain.com

Stop Using Microservices Wrong - It is About Teams, Not Code

Table of Contents

Why Microservices Often Make Things Worse

Microservices have become the default architecture for many developers, especially those influenced by large-scale systems at companies like Netflix or Amazon. The promise sounds compelling: better scalability, cleaner modularity, and systems that mirror real-world domains.

But this is where many teams go wrong.

Most developers adopt microservices far too early—before they actually need them. They treat microservices as a technical upgrade rather than an architectural trade-off. If you look closely, this usually comes from a set of common misunderstandings about microservices (see: The Biggest Misconceptions About Microservices). And in doing so, they introduce a level of complexity their system—and more importantly, their team—is not ready to handle.

Instead of making systems better, microservices often make them slower, harder to debug, and significantly more fragile. What was once a straightforward application becomes a distributed system overnight.


The Hidden Cost of Microservices

Microservices do not reduce complexity—they redistribute it.

In a monolithic architecture, complexity is mostly contained within a single process. Communication between components is fast, predictable, and easy to reason about. You’re dealing with function calls, shared memory, and a single deployment unit.

But once you move to microservices, that same complexity doesn’t disappear—it spreads across the network.

Now, every interaction between components becomes a network call. And networks are inherently unreliable.

You now have to deal with:

  • Latency that can slow down user requests
  • Partial failures where one service works but another doesn’t
  • Retry mechanisms that can introduce duplicate actions
  • Timeouts that must be carefully tuned
  • Distributed debugging across multiple logs and systems
  • Data consistency challenges across separate databases

What used to be a simple local operation is now a coordination problem between independent systems.

In a monolith, most things stay simple and local:

+------------------------------+
|         Application          |
|------------------------------|
| Auth | Billing | Users | API |
| Orders | Notifications | UI  |
+------------------------------+
               |
            Database
Enter fullscreen mode Exit fullscreen mode

In microservices, that same system becomes distributed:

+---------+    +----------+    +----------+
|  Auth   |    |  Users   |    |  Orders  |
+---------+    +----------+    +----------+
     |              |               |
     v              v               v
 DB(Auth)      DB(Users)      DB(Orders)

         \        |        /
          \       |       /
           +------------------+
           |   API Gateway    |
           +------------------+
Enter fullscreen mode Exit fullscreen mode

Scenario 1: Solo Developer (Worst Case)

If you're a solo developer, microservices are almost always a bad idea.

In a monolith, your workflow is simple and efficient. You call a function, it executes, and you move on. Everything is local, predictable, and easy to debug.

But in a microservices setup, that same function call becomes a network request. Suddenly, you’re dealing with infrastructure concerns instead of just business logic.

A simple createUser() call now involves:

  • Network latency
  • Potential request failures
  • Retry logic
  • Timeout handling
  • Logging across services

Instead of focusing on building features, you’re spending time managing distributed systems complexity. That’s a massive overhead for a single developer.

In a monolith:

[ App ] -> createUser()
Enter fullscreen mode Exit fullscreen mode

In microservices:

[ Service A ] -> network -> [ Service B ]
Enter fullscreen mode Exit fullscreen mode

A simple function call becomes a distributed systems problem.


Scenario 2: Small Team (Still Painful)

Even with a small team, microservices can still cause more harm than good.

At this stage, team boundaries are usually not well-defined. Developers often work across multiple parts of the system, and responsibilities tend to overlap. Introducing microservices in this environment creates artificial boundaries that don’t match how the team actually operates.

Instead of reducing coordination, you increase it.

Now developers need to:

  • Coordinate API contracts between services
  • Manage versioning and backward compatibility
  • Handle cross-service dependencies
  • Synchronize deployments across multiple services

What used to be an internal refactor becomes a cross-team negotiation—even if the “teams” are just two or three people.


The Realization: It's Not About Code

This is the key insight most people miss:

Microservices are not about organizing code. They are about organizing
teams.

The structure of your architecture should reflect the structure of your organization. This idea is closely related to Conway’s Law: systems tend to mirror the communication patterns of the teams that build them.

If you have a single team, a monolith is often the most efficient architecture. Everyone works in the same codebase, shares context, and moves quickly.

But when you have multiple teams working independently, microservices start to make sense. Each team can own a service, deploy independently, and evolve their part of the system without blocking others.

In other words, microservices are a scaling solution for organizations—not just applications.

Single Team

[ Team ]
    |
    v
[ Monolith ]
Enter fullscreen mode Exit fullscreen mode

Multiple Teams

[Team A]   [Team B]   [Team C]
    |         |         |
    v         v         v
[Service A] [Service B] [Service C]
Enter fullscreen mode Exit fullscreen mode

When Microservices Actually Make Sense

        +-------------------+
        |    API Gateway    |
        +-------------------+
          /       |       \
         v        v        v
    [Users]   [Orders]  [Payments]
       |          |          |
    Team A     Team B     Team C
Enter fullscreen mode Exit fullscreen mode

Microservices shine when you have clear team boundaries and enough organizational complexity to justify them.

If you have multiple teams, each responsible for a distinct domain (e.g., users, orders, payments), microservices allow those teams to operate autonomously. They can:

  • Deploy independently
  • Scale their services based on demand
  • Choose technologies suited to their domain
  • Avoid stepping on each other’s toes

At this stage, the overhead of distributed systems becomes a worthwhile trade-off because it enables team scalability.

Without this context, microservices are just unnecessary complexity.


What You Should Do Instead

/modules
  /users
  /billing
  /auth
  /orders
Enter fullscreen mode Exit fullscreen mode

Start with a modular monolith.

This approach gives you the best of both worlds: simplicity and structure.

Organize your codebase into clear modules—users, billing, auth, orders—but keep everything within a single application. This allows you to:

Maintain fast, local communication between components
Avoid network-related complexity
Refactor easily as your understanding of the domain evolves

As your system and team grow, these modules can naturally evolve into separate services if needed. But you’re not forced into that complexity prematurely.

Think of it as “microservices later, if necessary.”


TL;DR

  • Microservices often make things worse early
  • They introduce distributed complexity
  • They only make sense for multiple teams
  • Start with a modular monolith

Top comments (0)