| I fundamentally disagree with you. If the goal in writing software is to reduce it to a set of pieces that plug together, I'd agree. But it's an arbitrary metric and not an absolute measure of quality, not by a long shot. Note that I don't say "composable", because composability is something that needs to be designed in, and it isn't usually clear how to do it best until the third or so time around - the rule of threes. Furthermore, I don't say reusable, because reusability is something that also needs to be designed for. In particular, reusability in such a way that software can evolve over time without breaking clients (reusers) of the abstraction demands a tight and constrained contract that is broadened carefully, while testable components demand broad and flexible contracts, otherwise not everything would be available to be tested. Every part that testing has forced you to break out to be individually addressable is a part that you cannot remove in a refactoring that significantly changes the way a library solves its problem. A library that has been broken into parts that are neither composable nor reusable is simply over-complex. Almost every extant Java library is like this! Of course, if you just write end software in small teams, none of this is relevant to you. But it is crucial in library design, especially when client code is outside of your organization. |
But if instead of a monolith, you have a set of components with well defined interfaces that have simple semantics (that do not leak abstractions) -- whether or not these parts are re-usable in other contexts -- then you almost automatically have higher quality software:
* Easier to test means it will likely be better tested
* Well-defined interface and abstraction and a small implementation means that reviewing/correctness becomes easy. You only need to understand a small component to review it. "Obviously no bugs" rather than "no obvious bugs"
* Easier to split the work across developers
* Easier to comprehend the whole as a collection of its parts
The total number of lines of code, or even the total complexity may increase relatively to a monolithic design. But correctness becomes so much easier.
You mention refactoring, too, and IME, refactoring can be both easier or more difficult, depending on whether it is within a single small component or across multiple ones.
If you add architectural/design changes -- then it is night and day. A monolith will likely have to be rewritten to make an architectural change reliably.
A set of components can easily be split, for example, so that a few components are moved to run on a different system with a network protocol between them.