Maintainability, or the ability to effect change in the codebase, also improves significantly because of this. When code is simple and modular, it’s easier to build upon and extend. Maintainability is valuable regardless of team size: even in a team of one, if we leave a piece of code untouched for a few months and then come back to it, it might be hard to improve upon or even understand if we didn’t consider writing maintainable code the first time around.

    Components in modular applications are defined by their interfaces. The implementation of those components is not their essence, but their interfaces are. When interfaces are well-designed, they can be grown in non-breaking ways, augmenting the amount of use cases they can satisfy, without compromising existing usage. When we have a mindfully designed interface, the implementation behind that interface becomes easy to tweak or swap entirely. Strong interfaces are effective at hiding away weak implementations, that can be later refactored into more robust implementations provided the interface holds. Strong interfaces are also excellent for unit testing, because we won’t have to worry about the implementation and we can test the interface — the inputs and outputs of a component or function. If the interface is well-tested and robust, we can surely consider its implementation in a secondary plane.

    We’ll discuss the trade-offs between flexibility, simplicity, composability, and the right amount of future-proofing in the following couple of chapters.