This was my main takeaway from A Philosophy Of Software Design by John Ousterhout. It is the best book on this subject and I recommend it to every software developer. Basically, you should aim to minimise complexity in software design, but importantly, complexity is defined as "how difficult is it to make changes to it". "How difficult" is largely determined by the amount of cognitive load necessary to understand it.
It’s a pain in the ass to source a copy of this book without giving Jeff Bezos all the money. If anyone reading this thread knows John, could you bring this to his attention? I even tried calling the bookstore on his campus and they said try back at the beginning of a semester, they didn’t have any copies. My local book store could not source me a copy, and neither IIRC could Powell’s.
Cognitive load is what matters
431–440 of 552 posts
Re: Cognitive load is what matters
#432Earlier quoted context omitted.
This is a trap junior devs fall into DRY isn't free it can be premature optimization since in order to avoid copying code you often add both an abstraction AND couple components together that are logically separate. The issues are at some point they may have slightly different requirements and if done repeatedly you can get to a point that you have all these small layers of abstraction that are cross cutting concerns…
The reverse of that is people introducing bugs because code that wasn't DRY enough was only changed in some of the places that needed to be changed instead of all the places. To me, it's the things that are specifically intended to behave the same should be kept DRY.
Re: Cognitive load is what matters
#433Earlier quoted context omitted.
It's one message I struggle to convey to people I do code reviews for: don't make me understand it, make it more self explanatory so every reader does. (And, yes, I ask for it explicitly too) (I sometimes "ask" questions for something it took me a few back and forths through code to get so they'd think about how it could be made clearer) Unfortunately, most people focus on explaining their frame of mind (insecurity?)…
Yeah, not easy, but it helps to build some rapport first, so people learn what you’re after. The way I tend to do that is by leaving a review comment with an example code snippet that makes me understand it better, and a question “what do you think about this version? I tried to clarify a few things here.”. + Explain what was clarified. I find the effort usually pays off.
Re: Cognitive load is what matters
#434Earlier quoted context omitted.
I think the most common way to approach that problem would be to have a "default config", and overrides. Could you go into more detail about why you didn't do this instead? Downsides with your approach is: 1. Now whenever you want to change something both in Europe and (assuming) USA you have to do it in 2 places. If the change is the same for both, in my system, you could just update the default/shared config. If th…
I don’t do this because with a complete copy I get progressive rollouts across regions without the complexity of if statements and feature flags. That is to say, making the change twice is a feature not a bug when the changes are staggered in time. From an operational perspective it’s much more important to ensure the code is clear and readable during an incident. Overrides are like inheritance. They are themselves c…
By way of analogy for why the two configs are different, for example Two beaches are not the same because they both have very similar sand.
You really have two different configs.. You also have one set of configs. You didn't set up an application that also fetches some config that is already provided. It would be like having a test flag in both config and database, sane flag - two places.
Where config duplication goes bad is when repeatedly the same change is made across all N, local variations have to be reconciled each time and it is N sets of testing you need to do. Something like that in code is potentially more complex, more obviously a duplication of a module, just more likely to be a problem overall.
Re: Cognitive load is what matters
#435This was my main takeaway from A Philosophy Of Software Design by John Ousterhout. It is the best book on this subject and I recommend it to every software developer. Basically, you should aim to minimise complexity in software design, but importantly, complexity is defined as "how difficult is it to make changes to it". "How difficult" is largely determined by the amount of cognitive load necessary to understand it.
I'm struggling with the amount of complexity. As an inexperienced SWE, I found it difficult to put everything into my head when the # of function calls (A) + # of source code files (B) to navigate reach N. In particular, if B >= 3 or A >= 3 -- because, B equals the number of screens I need to view all source code files without Command+Tab/Alt+Tab, and cognitive load increases when A increases, especially when some "p…
Re: Cognitive load is what matters
#436Earlier quoted context omitted.
> Every rule can be used to argue anything. Unless it's a rule prohibiting complexity by removing technologies. Here's a set of rules I have in my head. 1. No multithreading. (See Mozilla's "You must be this high" sign) 2. No visitor pattern. (See grug oriented development) 3. No observer pattern. (See django when signals need to run in a particular order) 4. No custom DSL's. (I need to add a new operator, damnit, an…
Visitor pattern is extremely useful in some areas, such as compiler development.
Re: Cognitive load is what matters
#437It's always interesting that many people who push the cognitive load argument also push for simpler languages. To me once I have learned a language well the features it has don't add to the cognitive load. they become basically second nature. It even has a great benefit, many things that are explicit in simple languages because there is no language support fall away in more complex languages. So more complex language…
Even C++ and all it's crazy features of last 5-10 years?
Re: Cognitive load is what matters
#438Earlier quoted context omitted.
DRY isn't about not reimplementing things; it's about not literally copying and pasting code. Which I have seen all the time, and which some might find easier now but will definitely make the system harder to change (correctly) at some point later on.
I’ll bite. We’re expanding into Europe. I literally copied and pasted our entire infrastructure into a new folder named “Europe” Now there’s a new requirement that only applies to Europe and nowhere else and it’s super easy and straight forward to change the infrastructure. I don’t see how it was a poor choice to literally copy and paste configs that result in hundreds of thousands of lines of yaml and I have 25 yoe.
Perhaps one day you will. I'm a dev who worked with infra people who had your philosophy: many copy pasted config files.
Sometimes I needed to add an env var to a service. Expressing "default to false and only set it to true in these three environments" took changing about 30 files. I always made mistakes (usually of omission), and the infra people only ever caught them at deployment time. It was hell.
Re: Cognitive load is what matters
#439This was my main takeaway from A Philosophy Of Software Design by John Ousterhout. It is the best book on this subject and I recommend it to every software developer. Basically, you should aim to minimise complexity in software design, but importantly, complexity is defined as "how difficult is it to make changes to it". "How difficult" is largely determined by the amount of cognitive load necessary to understand it.
I'm struggling with the amount of complexity. As an inexperienced SWE, I found it difficult to put everything into my head when the # of function calls (A) + # of source code files (B) to navigate reach N. In particular, if B >= 3 or A >= 3 -- because, B equals the number of screens I need to view all source code files without Command+Tab/Alt+Tab, and cognitive load increases when A increases, especially when some "p…
With that said , get good at jumping into definitions, finding all implementations, then jumping back where you were. With a good IDE you can do that at the speed of thought (in IntelliJ that’s Cmb+b, Cmd+Alt+b, Cmd+[ on Mac). I only open more than one file at the same time when comparing them. Otherwise it’s much easier to jump around back and forth (you can even open another function inline if you just want to take a Quick Look, it’s Alt+Space). Don’t use the mouse to do that, things you do all the time can be made an order of magnitude faster via shortcuts. Too many developers I see struggle with that and are embarrassingly slow moving around the code base!
Re: Cognitive load is what matters
#440I think most programmers agree that simpler solutions (generally matching "lower cognitive load") are preferred, but the disagreements start about which ones are simpler: often a lower cognitive load comes with approaches one is more used to, or familiar with; when the mental models one has match those in the code. For instance, the article itself suggests to use early/premature returns, while they are sometimes comp…
I like premature returns and think they reduce complexity, but as exclipy writes (I think quoting Ousterhout) 'complexity is defined as "how difficult is it to make changes to it"'.
If premature returns are the only premature exit your language has then they add complexity in that you can't then add code (in just one place) that is always executed before returning.
A good language will also have "break" from any block of code, such that the break can also carry a return value, AND the break can be from any number of nested blocks, which would generally mean that blocks can be labelled / named. And also of course that any block can have a return value.
So you don't actually need a distinguished "return" but only a "break" that can be from the main block of the function.
A nice way to do this is the "exit function", especially if the exit function is a first class value and can also exit from called functions. (of course they need to be in a nested scope or have the exit function passed to them somehow).
It is also nice to allow each block to have a "cleanup" section, so that adding an action to happen on every exit doesn't require wrapping the block in another block, but this is just a convenience, not a necessity.
Note that this is quite different to exception handling try / catch / finally (Java terms) though it can be used to implement exception handling.