Skip to main content

Write code that is easy to replace ✈️

Software survives by being easy to replace.

This may sound counterintuitive, but if there’s one thing we know, it’s that things change. Technology evolves. Requirements shift. Sooner or later your system, or parts of it, will be replaced. Often sooner than you think.

Ignore replaceability and the system will become harder to maintain. The dependency on one library will slowly bleed into other parts of the system, and supposedly unrelated parts become entangled and hard to split apart.

A line-art illustration showing an airplane switching an engine mid-air.
Switching engines will happen sooner or later, possibly mid-air.

To build a resilient system that survives, build it to be replaced gradually.

  • Keep it simple. It is much easier to replace a small, simple module that an large, over-engineered one.
  • Define narrow interfaces between modules. Even imperfect seams help. It will be much easier to replace one part if there is some structure to rely on, rather than a huge web of dependencies.
  • Isolate third-party code behind an anti-corruption layer.1 Don’t allow dependencies to bleed into the rest of your system, or it will become much harder to replace. For example, your HTTP framework’s types shouldn’t appear in core domain code.
  • Prefer libraries over frameworks. While a good framework may provide a lot of benefit, they are typically very hard to replace. The framework you choose may seem like it will last forever, but the industry is full of Silverlight and AngularJS projects that didn’t age well.
  • Separate policy from mechanism. Logic for “what to do” should not depend on “how it’s done.” For example, business logic should be free of infrastructure concerns such as storage, transport, and frameworks.
  • Prefer composition over inheritance. Composition makes it easier to change behavior without inheritance chains collapsing. It is virtually always easier to replace a has-a relationship than an is-a relationship.
  • Keep data portable. Replaceable code is useless if data is trapped. Prefer explicit schemas, migration tools, and open formats over opaque vendor storage.
  • Minimize global assumptions. Every global singleton, static configuration, or implicit coupling is a hidden anchor. Pass dependencies explicitly and avoid shared mutable state.

Replaceability is about preparing for change. If every part of your system can be replaced without panic, you’ve built something that will last.

The tricky part is doing it without premature generalization. Focus abstractions where change is most likely, near external boundaries, volatile integrations, or unstable requirements. Keep the rest simple and concrete.


  1. Anti-corruption layer is a design pattern borrowed from Domain-Driven Design. It acts as a protective barrier, preventing the external system’s concepts and models from “corrupting” your domain. Any information passed through the barrier is translated between the two different models. ↩︎