I stumbled across Patterns of Software: Tales from the Software Community by Richard Gabriel. (You can download the book as a pdf.) Gabriel approaches software design using principles from architecture, especially the work of Christopher Alexander.
Like other good writing on software architecture, Patterns of Software focuses on the tradeoffs involved. Rather than encouraging always more and stronger abstractions, Gabriel is more pragmatic and discusses what actually seems to work in software projects and the human reasons why.
Alexander described why a detailed “master plan” usually fails to create an aesthetically coherent community, and Gabriel applies this to software: first, we cannot know all the details of how the construction will be used and fit into its environment up front. Those details become apparent only when it is actually used. Software designers recognize this as the principle of “use before reuse”: you can’t create a good abstraction before you have several concrete use cases, because you do not know which details are important. These ideas are related to the “agile” development approach. In addition, the “users” subjected to a master plan (in the case of software, the implementers) often cannot feel ownership in a plan where they have zero influence. The master plan is a monolith, stuck in the past and owned by the original designer. Lack of ownership by programmers, over time, will translate to a disconnect between the whole and the parts of a design.
Gabriel warns of trying to create “perfect” or ultimately “clear” software from the start—a dubious task that leads to software that is not flexible in the ways that you need it. Why? Because humans cannot conceive all of the details and implications of a master plan all at once. It’s like trying to play an opening chess move by thinking about all of the possible endgames. Given our limited ability to reason, we have to focus on those things that we know and can reason about from the beginning, and keeping software understandable so we can make changes down the road. Gabriel calls this quality “habitability”, “the characteristic of source code that enables programmers, coders, bug-fixers, and people coming to the code later in its life to understand its construction and intentions and to change it comfortably and confidently”. I like this metaphor for software design. Make your software a place where future programmers (including yourself) can feel at home, so they can take ownership of the “home” and grow it as needed.