Hacker News new | ask | show | jobs
by RyanHamilton 884 days ago
I can recommend a Philosophy of Software Design Paperback by John Ousterhout. I've been programming for 15 years and it closely parallels my own current beliefs about programming. He stands above the lower aspects of programming/code/modules, raising the discussion to a conceptual level, that you seem to be wanting. I think there were only 2 areas out of approximately 10 that I thought I had a few better ideas but a)He may have simplifying it to more easily allow explanation. b) His explanations are much better than mine and if I tried to explain it, maybe I'd fail.
3 comments

Haven't read this, but based on looking through the table of contents (so take what I'm saying with a grain of salt), I agree with your choice. You mentioned that you've been programming for awhile, you might have heard of Grady Booch and "Object-Oriented Analysis and Design with Applications." Object oriented techniques has fallen a little out of fashion now, which may be part of the reason why this book isn't as popular, but it was big around 20 years ago. It also discusses on the many topics of your choice: - Complexity, what it is and how to manage it - Encapsulation (Information Hiding). - Interfaces - contracts for your encapsulated code - Abstraction

And am certain that Grady Booch's book was itself mostly based on older material.

I also really like any really good book on doing requirements (they all have 80% of the same info). Getting the true needs of all your stakeholders (your users, the business people, the devs themselves, and the technical needs of the system) is probably the step that is done the poorest in most organizations, and the one that could save project the most time and money if done well.

His idea to keep interfaces small and powerful bothered me - I don’t think it expresses any truth about programming despite being a good rule of thumb. I kept feeling frustrated while reading. The lambda calculus has the best interface to functionality ratio possible but you don’t see humans using it to program. This objection led me to the conclusion that a better heuristic would be: programs should try to communicate intent primarily to another reader, and then to the computer. He does touch on this a little by saying aim for ease of reading over ease of writing, but I don’t think it was a main point. I agreed with almost everything else he had to say including the “taking it too far” chapter (I don’t actually think we should be using lambda calculus to program lol). Curious to know which parts you disagreed with. I am only 2yoe
Most so the book is meant to be taken as a rule of thumb. I personally think simple interface hiding deep functionality is a really good one. I was looking at the langchain codebase last year after having read the book and the point was struck home. Many of the functions in Langchain are one line wrappers of other functions. I try to think of functions as atomic units of knowledge. Very short functions are a good indicator that you're not organizing that knowledge effectively. When you have a simple interface with deep logic, you're encapsulating logic in a single place. This is very good for reducing the cognitive load of a developer. Shallow functions fragment that knowledge across multiple classes/functions/files. The developer needs to hold more of the world in their head to make sense of things. This increases complexity.

Additionally, you can sort of intuit this by looking at very successful commercial products like Google Search, the iPhone, and ChatGPT. Search is a single text input. The iPhone is a screen with a very small number of buttons. Chatgpt has a simple chat interface. All of these systems are incredibly complex, but they're able to present a simple interface to the public to allow most people to leverage that complexity without having to think about it.

Just my 2 cents.

To people who are experienced in writing out the full interface of modules, ie their assumptions and guarantees, it's quite clear that being a "deep module" in Ousterhout's sense is quite rare and often undesirable, and that Ousterhout's examples of deep modules are actually shallow. See https://www.pathsensitive.com/2018/10/book-review-philosophy...

He gets a lot of other stuff right though. Love his writing on comments

I think I might be missing something from that article, but I don't think they're in so much disagreement with Ousterhout.

Ousterhout's core argument is this: consider a module as its interface (measured, say, in # of edge cases) multiplied by its effect (measured, say, in # of features achieved). When comparing two modules, we should prefer modules with smaller interfaces and larger implementations, i.e. of two modules with equally sized interfaces, prefer the one which does more, and of two modules which do the same thing, choose the one with the simpler interface.

Narrow vs wide in this context seems to me to mainly be a point of comparison - in practice, narrow and wide don't make much sense when talking about a module on its own, because the dimensions of interface and implementation are not comparable.

However, this article seems to mainly be about comparing interface to implementation and arguing that, because interfaces can be very complex, often as complex or more as the implementation itself, then Ousterhout is wrong. Or in other words, if we convert "# of edge cases" and "# of features achieved" to a single comparable unit like "# of lines of code", then for Ousterhout's advice to hold, then the implementation must be more lines of code than the interface.

To me, that's kind of missing the point of the advice, which is less to do with the amount of code needed to implement something, and more to do with the capability of code vs its interface. Yes, lines of code can be a useful proxy for both dimensions, but the dimensions aren't meant to be directly comparable.

I might be missing something, though.

As one example, I think his formula for overall complexity is mostly correct: Total Complexity = Sum[ componentComplexity x timeSpentWorkingOnThatPart] However I would replace the timeSpentWorkingOnThatPart with the sum of the square roots of all separate times. i.e. Working on a component for 2 days straight versus working on it 1 hour every 2 weeks are quite different. But heh I'm sure there's other refinements and if I was writing a book I wouldn't necessarily include them. It slightly goes against the point of the book to nitpick these things as it's trying to help us think abstractly at a higher level.
If you've never used lambda calculus I would highly recommend trying haskell.
I was about to recommend the same book - lots of good nuggets of wisdom in here, without the dogma you sometimes find in this kind of literature.