Video summary

Why Shopify Rejected Microservices (And What They Did Instead)

Main summary

Key takeaways

Educational

Main ideas, concepts, and lessons

Why Shopify matters

  • Shopify is described as a high-scale e-commerce platform, with:
    • millions of lines of code,
    • billions in sales,
    • extremely high request rates.
  • Even with this success, Shopify’s original architecture evolved over time due to maintainability and delivery speed problems.

Shopify’s architectural evolution

  • Started with a layered monolith (Rails/MVC style).
  • Over time, code for major business domains (billing, shipping, checkout, orders, taxes, etc.) became tightly intertwined.
    • This is framed as technical debt and the result of no clear domain boundaries.
  • Consequences included:
    • fragile code
    • slow, friction-filled changes

Core architecture lesson: “Monoliths fail in two ways”

Monoliths typically end up as one of two patterns:

  • a “majestic monolith” (well-structured), or
  • a “big ball of mud” (high coupling, unclear boundaries).

Key lesson:

  • The problem isn’t coupling itself—it’s uncontrolled coupling caused by missing/weak boundaries, leading to:
    • side effects across domains/components,
    • brittle behavior (one change breaks another area),
    • reduced ability to ship features quickly.

Pain points Shopify experienced

  • Fragile code: changing one domain (e.g., tax) could break shipping logic or related tests.
  • No enforced boundaries: internal component/domain calls were not restricted.
  • High onboarding cost: new teammates needed to understand many moving parts.
  • Very large test suite (>100,000 tests): tests existed, but slow execution and architectural friction prevented improvement.

Decision: Modular monolith over microservices

Shopify surveyed developers and chose to change direction.

Options considered:

  1. Do nothing (stay with the existing monolith)
  2. Migrate to a modular monolith (chosen)
  3. Adopt microservices (considered but rejected due to trade-offs)

Why microservices weren’t chosen:

  • multiple deployment pipelines (one per service),
  • network overhead/latency for cross-service calls,
  • database isolation per service complicating cross-domain querying,
  • refactors being riskier and often requiring coordinated multi-service deploys,
  • API versioning complexity (version drift vs coordinated versioning),
  • observation that Big Tech has sometimes shifted away from microservices as the default and become more open to monoliths.

What a modular monolith is (as explained)

Goal

  • Maintain logical boundaries while keeping one deployment unit.

How it works

  • Boundaries are enforced inside a single codebase/deployment.
  • Each module exposes a public API, while keeping the module’s internals encapsulated.

Conceptual comparison

  • Microservices: boundaries are physically enforced via separate deployment units; communication usually involves network calls.
  • Big ball of mud monolith: boundaries are not enforced; bounded contexts mix together.
  • Modular monolith: “monolith done right”—more modularity, controlled coupling, and no network overhead.

Methodology / steps described for Shopify’s modular monolith migration (detailed)

Stage 1: Componentization (reorganize by business domain)

  • Initial state: code organized by technical capability (e.g., controllers/models/data access), resulting in low cohesion and weak boundaries.
  • Action:
    • reorganize into a structure grouped by business domain rather than technical folders.
    • example domain components mentioned: apps, billing, checkouts, taxes, orders, shipping.
  • How Shopify performed the refactor:
    • listed ~6,000 classes,
    • manually labeled each class into a domain (with stakeholder input),
    • created an automated script to move files into the appropriate domain/component folders,
    • used their full test suite to verify correctness after the move.

Stage 2: Enforce boundaries via module public APIs

  • Action:
    • each component/module exposes a public API that other modules can call,
    • module internals remain hidden via encapsulation:
      • e.g., using C# internal for implementation details,
      • placing each module in its own C# project so internals remain isolated.
  • Why this matters:
    • without a defined public API, any module could depend on another module’s internal details,
    • with a public API + encapsulation, boundaries become enforceable at the code level.

Optional / further tightening: enforce boundaries at the database level

  • Action:
    • create separate database schemas per component/module,
    • store each module’s tables in its own schema,
    • create a dedicated database user per schema,
    • configure access so only the owning module can access its tables.
  • Optional exception:
    • extra permissions/views may be allowed for cross-module needs,
    • but the preferred approach is routing access through the module’s public API and application code, not broad database access.

Additional architectural guidance / principles emphasized

  • The primary design target is to improve cohesion and control coupling, increasing change locality (things that change together should live together).
  • Vertical slice architecture is referenced as a pattern that helps enforce functional grouping/cohesion.
  • Clean architecture is also referenced as compatible with grouping around use cases and dependency direction.
  • Warning sign: if a small feature change unintentionally impacts multiple modules, boundaries are likely wrong.
  • Practical benefit: unlike microservices, correcting boundaries in a modular monolith is “only refactoring” and preserves one deployment unit.

Speakers / sources featured

  • Speaker/creator of the video: unnamed (the transcript uses “we” and “I”, but no name is provided).
  • Referenced sources:
    • Shopify (including a Shopify tech blog article referenced in the transcript).
  • Books/frameworks mentioned:
    • Clean Architecture
    • Vertical Slice Architecture

Rate this summary

Your feedback will help improve summaries.

Improve this summary

Reprocess with a stronger model when the summary feels incomplete or inaccurate.

Pro

Translate summary in another language

Pro

Ask questions to this video

Chat for follow-up questions, clarifications, and source-backed answers.

Coming soon

Share this summary

Original video