I have now finished the book and am following up on feedback for other chapters. I have spotted many good stuff that I agree with, but I'm only writing topics I found worth a constructive conversation.
Sorry for my relatively raw notes below, they are first hand comments without much rewriting not re-structuring.
--
Section 6.3 uses open and closed principle, which is a clearer way to frame the improvement. The property of "having fewer methods" doesn't capture the benefits of the new design, it's a better split of responsibilities that in this case happens to have fewer methods.
Section 6.4 I wouldn't say generality leads to better information hiding, but the "correct module split and interfaces" lead to that. This is a case where splitting modules and adding a dependency between them is super beneficial, because it removes the need for changing the interfaces in future use cases. A good design minimizes the need of changing it in the future.
Section 6.5: I didn't think less methods imply more general purpose interface, you could have a very specific method being the single one in the interface. A better definition would be how many use cases it covers.
Section 6.6 It's hard to see an up and down direction for a general dependency graph. I wouldn't say up or down here, but to the edges of the graph. A good usage of "final class" Java keyword class is an example of being aware of edges vs core as distinct types of modules.
Section 7.1 "distinct and coherent set of responsibilities" is one way to say "the correct module split and interfaces" I refer to in my comments, I think that's central to good software design. This is the core the book seem to avoid defining in a more crisp way, it talks briefly in chapter 21 about it.
Chap. 7 I don't think decorators add much complexity, their interface is exactly the same as the original one, so that callers are already dealing with that interface. I'm not too allergic to shallow modules if they are at the edge and have few dependers. Indeed it's best to always rethink the module responsibility scope, and after rethinking it's quite common to reach to a conclusion a decorator is a good solution. Decorators only add interface duplication in a Java codebase because Java libraries don't offer a good way to handle it. Python decorators (for functions) have a lot of boilerplate hidden away in more generic libraries than Java.
Section 8.3 again an easy principle to overdo (aligning with my previous email in this thread). Pulling complexity downwards is really another case of designing "the right module split" and or "the right responsibility split". Analogous to information leakage concept, this can be viewed as Problem leakage. Which I think is addressed by a principle similar to "what is the responsibility split that minimizes future changes", this principle, in my experience, often correlates with "single responsibility" and "open for extension and closed for modification" principles.
Section 9.7 that's exactly single responsibility principle. Maybe we need a concept of "atomic responsibility" to avoid breaking a single atom into nonsense.
Chapter 10 I'm also allergic to excessive exception handling! Have you thought about Monadic patterns here like Optional or Result? I think they offer cleaner ways of handling errors. It does exactly what your example of TCL unset does in section 10.3
Section 12.1 I disagree with the statement that adding a bunch of private shallow methods to "name" blocks of code adds complexity. I've seen multiple cases similar to "a complex non-obvious REGEX wrapped around an understandable function name" that significantly decreased cognitive load, even though makes code inner dependencies tiny slightly more complex.
Chapter 21 pull that entire chapter up to early in the book. I think "deciding what matters" is the single most important topic in software design. It guides engineers on "what is the correct module/problem split".
Stuff I wish I saw in the book:
- Functional programming examples. Monads can solve error handling in a similar way you propose. Some other functional patterns like immutability, pure functions, are concretions of principles described in the book.
- A richer variety of problems, I can almost count on my fingers the few examples that were repeated throughout the book.
- Problem decomposition techniques. There's a lot of content on the surroundings of that, such as, examples of how a system becomes more complex if you make bad choices. But the core, as preface tells, of problem decomposition is not developed much. I think that's crucial to produce "the correct module split".
Thank you!
Diego Tsutsumi