It seemingly develops a theory of software architecture that is getting at some reasonable stuff, but does so without any reference _at all_ to the already rich theories for describing and modeling things.
I find software design highly related to scientific theory development and modeling, and related to mathematical theories like model theory, which give precise accounts of what it means to describe something.
Just taking the notion of "complexity". Reducing that to _just_ cognitive load seems to be a very poor analysis, when simple/complex ought to deal with the "size" of a structure, not how easy it is to understand.
The result of this poor theoretical grounding is that what the author of A Philosophy of Software Design presents feels very ad-hoc to me, and I feel like the summary presented in this article similarly feels ad-hoc.
Preface: I'm likely nitpicking here; the use of "_just_" is enough for me to mostly agree with your take.
Isn't the idea that the bulk of complexity IS in the understanding of how a system works, both how it should work and how it does work? We could take the Quake Fast Inverse Square Root code, which is simple in "size" but quite complex on how it actually achieves its outcome. I'd argue it requires comments, tests, and/or clarifications to make sense of what its actually doing.
How do we measure that complexity? No idea :) But I like to believe that's why the book takes a philosophical approach to the discussion.
I agree the arguments in the book largely "make sense" to me but I found myself finding it a little hand-wavey on it actually proving its points without concrete examples. I don't recall there being any metrics or measurements on improvement either, making it a philosophical discussion to me and not a scientific exercise.
For instance, a binary tree that contains just a root node is clearly simpler than a binary tree with three nodes, if we take "simple" to mean "with less parts" and complex to mean "with more parts". Similarly, a "molecule" is more complex than an "atom".
This is a useful definition, I think, because when we write computer programs they always written in some programming language, with a syntax that yields some kind of abstract tree, so ultimately we'll always have _some_ kind of graph-like nature to the computer program, both syntactically and semantically, and surely graphs also permit the same kind of complexity metrics.
I'm not saying measuring the number of nodes is _the_ way of getting at complexity, I'm just pointing out that there's no real difficulty in defining it.
Complexity means more stuff, and we simply take it as a premise that we can only fit so much stuff in our head at the same time.
Perhaps too precise? APoSD is about the practical challenges of large groups of people creating and maintaining extensive written descriptions of logic. Mathematical formalisms may be able to capture some aspects of that, but I'm not sure they do so in a way that would lend real insight.
"How can I express a piece of complicated logic in an intuitive and easy-to-understand way" is fundamentally closer to writer's craft than mathematics. I don't think a book like this would ever be mathematically grounded, any more than a book about technical writing would be. Model theory would struggle to explain how to write a clear, legible paragraph.
The relation between an interface and an implementation to me is very much the same as between a formal theory and a model of that theory.
I agree that in practice you'd want to use heuristics for this, but I think the benefits would be similar to learning a little bit of formal verification like TLA+, it's easier to shoot from your hip if you've studied how to translate some simpler requirements into something precise.
For a book like this you'd probably not need more than first order logic and set theory to get a sense of how to express certain things precisely, but I think making _reference to_ existing mathematics as what grounds our heuristics would've been beneficial.
I worry that it doesn't much matter if it's perfect or mediocre, though, because there's a huge contingent of project managers who mock _any_ efforts to improve code and refuse to even acknowledge that there's any point to doing so - and they're still the ones running the asylum.
The generally accepted roles are Product decides what we need to build, Design decides how it should work from user perspective, Engineering decides how to build it at a reasonable upfront and maintenance cost. This involves a fair amount of influence, because Engineering is better equipped to describe the cost tradeoffs than any other function. Of course this comes with the responsibility of understanding the big picture and where the business wants to go. IMHO you should not be speaking to project management about code quality, you should maintain ground level quality as you go, for bigger refactoring/cleanup this needs to be presented to Product leadership (not project managers) in terms of shoring up essential product complexity so it's easier for customers to use, less support, and simpler foundation for the next wave of features. Never talk about code with non-technical stakeholders.
It didn't feel like a thought through whole, and I felt somewhat punished for trying to read along attentively.
I also found there to be a frequent conflation of e.g. the notion of modules and a classic OOP-class, to me it seemed like the author thought of them interchangeably.
To me there's enough theoretical computer science that can be used to help ground the terminology, even if it's just introduced cursory and with a reference for further reading. But at least then there'd be more consistency.
I'm not sure I think the book is invaluable, but I think it's a good contribution to the subject.
Also, have you considered writing on this subject yourself? I get the feeling that your perspective here would be valuable to others.
My basic pitch is that, to a large degree writing sensible computer programs is about modeling some real life activity that the computer program will be part of, and describing things accurately has been done in other fields than programming for many hundreds if not thousands of years, so there's a deep well to draw from.
Despite my appetite for a dry and mathematical treatment of writing computer programs, I still think the book is good for what it is. I think I would go easier on the book if it were not for the title, because philosophy is precisely one of those subjects that tend to favor being very precise about things, something I distinctly think the book lacks. What the book is, however, is an excellent _sketch_ on what we'd want out of program design. I definitely agree about the author's notion of "deep modules" being desirable.
It's funny reading the "key contributors to dependency-complexity" -- Duplication, Exceptions, Inheritance, Temporal Decomposition -- because those qualities seem like the standard for AI-generated code.
Because long-term productivity was never about the generated lines of code. You can increase your system features through expansion, or by a combination of expansion and contraction.
Generating new code without spending time to follow through with the contraction step, or alternatively contracting first as a way of enabling the new expansion, will always make the code more complex and harder to sustain and continue to improve wrt the feature set.
For me it's not uncommon for AI to draft an initial solution in X minutes, which I then spend 3*X minutes refactoring. Here's a specific example for a recent feature I coded: https://www.youtube.com/watch?v=E25R2JgQb5c
Organizationally enforcing strategy would be the issue. And also that the people most interested in making rules for others in an organization may not be the ones best qualified to program. And automatic tools (linters) by necessity focus on very surface level, local stuff.
That's how you get the argument for the small teams productivity camp.
Like making state machines easier than channels. (Rust is sort-of good at state machines compared to C++ but it has one huge issue because of the ownership model, which makes good SMs a little clumsy)
Or making it slightly inconvenient to do I/O buried in the middle of business logic.
Doing IO in the middle of business logic is just bad coding. It’s usually the developer not caring about the architecture (tornado coding or slum coding) or the architecture not existing.
So perhaps we should ask ourselves: What can we learn from the internet architecture?
And no that does not automatically mean micro-services. The core idea of the internet is to agree on API's (protocols like HTTP) and leave the rest as implementation details. You can do the same with modules, libraries, classes, files etc.
Even if you do use micro-services you still need a protocol!
LLMs are trained on data from humans. That means these “code ergonomics” apply equally to coding with AI. So this advice will continue to be good, and building with it in mind will continue to pay off.
?
The problem I usually encounter is that whoever was there before me didn't do this. They just made a solution then added quick fixes until it became a monster, and then left. Now I first have to figure out what the code actually does, try to distinguish what it needs to do from what it happens to actually do, usually write tests because the people who write the kind of code I end up fixing usually don't write good tests if any at all, then refactor the code to something more reasonable.
This can easily take me days or weeks whereas the person responsible could likely have cleaned it up much quicker because they don't first have to figure out requirements by reading messy code. Then again if they were competent and cared about the quality of their work they wouldn't have left me that mess in the first place so I tend to just write it off as incompetence, fix it and move on.