• Home
  • BVSSH
  • Engineering Enablement
  • Playbooks
  • Frameworks
  • Good Reads
Search

What are you looking for?

Practice : Modular Monoliths

Purpose and Strategic Importance

A Modular Monolith is a design approach where software is built as a single deployable unit but internally divided into well-encapsulated, independent modules. It combines the simplicity of a monolith with the structural discipline of microservices - making it easier to build, evolve, and scale software while avoiding premature distribution.

This approach supports a pragmatic evolution of architecture, enabling teams to maintain high cohesion, reduce coupling, and prepare for future decomposition without incurring the operational overhead of microservices too early.


Description of the Practice

  • A single codebase contains multiple modules, each representing a distinct business capability or bounded context.
  • Modules are isolated via explicit interfaces and do not directly depend on each other’s internals.
  • Internal boundaries are enforced through language features, tooling, or conventions.
  • Modules can be tested, built, and reasoned about independently, even if deployed together.
  • Refactoring to microservices becomes easier when modularity is in place.

How to Practise It (Playbook)

1. Getting Started

  • Identify logical business domains and boundaries within the existing monolith.
  • Create explicit modules in code, each owning its data model and use cases.
  • Establish interfaces between modules using public contracts or application services.
  • Prevent cross-module calls or shared state using compiler rules, dependency management, or peer reviews.

2. Scaling and Maturing

  • Align module boundaries with domain ownership and team responsibilities.
  • Build automated tests and CI pipelines for individual modules.
  • Track and reduce module coupling over time to improve independence.
  • Use architecture decision records (ADRs) to capture modular evolution.
  • Define policies for versioning, lifecycle, and observability at the module level.

3. Team Behaviours to Encourage

  • Treat modules as independently owned units of design and delivery.
  • Collaborate across teams to define clear module contracts and responsibilities.
  • Consider modular boundaries during backlog refinement, not just implementation.
  • Use modular insights to guide decisions about future decomposition or service extraction.

4. Watch Out For…

  • Logical modules that are tightly coupled in practice due to shared code or data.
  • Poorly defined interfaces that allow accidental dependencies.
  • Treating modularisation as a technical goal rather than a business enabler.
  • Neglecting testing, ownership, or observability within the monolith.

5. Signals of Success

  • Teams deliver and evolve features within their module independently.
  • Architecture decisions are reversible and based on usage, not trends.
  • Refactoring paths to microservices are visible and achievable.
  • Code quality and cohesion improve across the system.
  • Delivery speed increases without the need for distributed systems complexity.
Associated Standards
  • All infrastructure modules are versioned and backwards-compatible
  • Domains are integrated through stable, loosely coupled interfaces
  • High-risk changes are identified and routed appropriately
  • Platform capabilities include clear boundaries of responsibility
  • Strategy and execution are aligned through continuous feedback
  • Systems are architected to minimise the cost of change

Technical debt is like junk food - easy now, painful later.

Awesome Blogs
  • LinkedIn Engineering
  • Github Engineering
  • Uber Engineering
  • Code as Craft
  • Medium.engineering