I’ve been spending a good part of my career designing and building software systems that need to scale gracefully as teams and user bases grow. One of the most pivotal transitions I see—both in my own projects and in well-known companies—is the move from a single monolith to multiple microservices. While it can unlock astonishing productivity for large engineering organizations, a poorly desgined architecture can bring real challenges and pitfalls that aren’t always obvious from the outside.

Recently, I took a closer look at the journey DoorDash took in splitting their large Python monolith into a network of microservices. Their story underscores a theme I’ve observed repeatedly: adopting microservices is rarely about pure performance or traffic scaling. Instead, it often boils down to organizational challenges and developer velocity. Once you outgrow the simplicity of having everyone in one codebase, microservices start to look like a practical path forward—but that path comes with its own twists and turns.



Why Monoliths Live On (Longer Than You Think)

Monoliths are a single deployable unit holding all application logic, including business rules, data access, and user interfaces. It surprises many people to learn just how far a monolithic application can be pushed before it’s truly limiting. Modern hardware and cloud infrastructure can handle significant traffic loads, even when the system is all in one piece. A well-designed monolith also has the advantage of fewer moving parts: there’s one code repository, one deployment pipeline, and one place to configure observability. This is one reason monoliths remain a good first choice for smaller teams or projects that need to iterate quickly without worrying about distributed communication.

Monolith.png

Key Benefits of Monoliths

  1. Simplicity One codebase, one deployment pipeline, one place to debug issues.
  2. Faster Early-Stage Development Small teams can move quickly without worrying about cross-service communication or complex orchestration.
  3. Lower Operational Costs Monitoring one service is simpler. More services means more dashboards, logs, and alerts.

For an extended period, DoorDash relied on a Python monolith that powered its core functionality. As the company grew, they managed to scale this monolith further than many would have expected. The fact that it held up to large traffic volumes speaks to how far a monolithic design can go—especially when engineers carefully profile and optimize hotspots, rather than hastily fragmenting the code into microservices.

However, as organizations scale—both in terms of the number of developers and the number of features—a monolith can become unwieldy. Code merges conflict more often, deployment schedules clash, and any single defect can stall the entire release pipeline.


The Organizational Catalyst for Microservices

A real turning point often arrives when a single, monolithic codebase becomes a bottleneck—teams trip over each other’s changes, a single broken build can block everyone, and rolling back a bug forces unrelated features to be rolled back as well. DoorDash’s official blog highlights that monoliths can stretch surprisingly far, but with hundreds (or thousands) of engineers pushing code, one deployment train can’t reliably handle it all.

Microservices address these issues by decoupling the application’s components into services aligned with particular business domains (e.g., user profiles, payments, order tracking). Separate teams can then deploy independently, respond to user needs faster, and avoid blocking one another.

Microservices.png

Core Motivations for the Switch