Under Deconstruction: The State of Shopify’s Monolith

It’s been 18 months since we talked about our Rails modular monolith. We lay out the current state of the work, and things we’d do differently if we started again.

Philip Müller
24 min readadvanced
--
View Original

Overview

The article discusses Shopify's efforts to modularize its Ruby on Rails monolith, which consists of over 2.8 million lines of code. It outlines the challenges faced and the strategies implemented to improve developer productivity and system complexity through componentization.

What You'll Learn

1

How to implement modular design principles in a Ruby on Rails application

2

Why understanding developer behavior is crucial for architectural changes

3

When to apply the SOLID principles at both class and component levels

4

How to use Rails Engines for better modularity

5

How to leverage static analysis tools like Packwerk for dependency management

Prerequisites & Requirements

  • Understanding of Ruby on Rails and its architecture
  • Familiarity with static analysis tools like Sorbet(optional)

Key Questions Answered

What are the benefits of modularizing a Ruby on Rails monolith?
Modularizing a Ruby on Rails monolith allows for easier onboarding of new developers, faster test suite execution, and more manageable feature implementations. By breaking down the monolith into smaller components, developers can work more independently and efficiently, leading to improved overall system design and quality.
How does Shopify manage dependencies in its monolith?
Shopify uses tools like Packwerk to analyze static constant references and enforce component boundaries. This helps in managing dependencies effectively, allowing developers to identify and resolve issues before they affect the main codebase, thus maintaining a cleaner architecture.
What challenges arise when refactoring a large monolith?
Refactoring a large monolith presents challenges such as managing complex interdependencies and ensuring that changes do not degrade existing functionality. Developers must be aware of the broader system architecture and the implications of their changes on the overall codebase.
What role does developer behavior play in architectural changes?
Developer behavior is critical in implementing architectural changes because a centralized team cannot enforce changes against the momentum of hundreds of developers. Engaging developers in the process and fostering a mindset shift towards modular design is essential for successful transformation.

Key Statistics & Figures

Lines of code in Shopify's monolith
2.8 million
This extensive codebase presents significant challenges in managing complexity and implementing modular design.
Number of commits in the monolith
500,000
The high number of commits indicates continuous development and the need for effective management strategies.
Number of components currently in the main monolith
37
These components are designed to encapsulate functionality and improve the modularity of the application.

Technologies & Tools

Some links below are affiliate links. We may earn a commission if you make a purchase.

Backend
Ruby On Rails
The primary framework used for building Shopify's monolith.
Tool
Packwerk
A static analysis tool used for managing dependencies and enforcing component boundaries.
Tool
Sorbet
A static type checker for Ruby, used to express input and output contracts on component boundaries.
Backend
Rails Engines
A modularity mechanism within Rails that allows for encapsulating components.

Key Actionable Insights

1
Engage developers in the modularization process by fostering a grassroots movement within the organization.
This approach encourages collaboration and ensures that solutions are aligned with the real challenges developers face, leading to more effective architectural changes.
2
Utilize Rails Engines to encapsulate components and manage dependencies effectively.
Rails Engines provide a built-in mechanism for modularity, allowing for easier transitions to standalone applications if needed, thus enhancing the overall architecture.
3
Implement static analysis tools like Packwerk to maintain clean component boundaries.
By integrating Packwerk into the development workflow, teams can proactively manage dependencies and ensure that changes do not violate component encapsulation.

Common Pitfalls

1
Failing to consider the dependency graph when modularizing components can lead to circular dependencies and tightly coupled systems.
This often happens when developers focus solely on creating public interfaces without analyzing how components interact, resulting in a system that is difficult to maintain and evolve.
2
Imposing too many rules and patterns can demotivate developers and hinder their ability to innovate.
When developers feel constrained by rigid guidelines, they may resist adopting new practices that could improve the system, leading to stagnation in architectural evolution.

Related Concepts

Componentization In Software Architecture
Solid Principles In Software Design
Behavioral Design Models In Software Development