Live data from Hacker News

Simple Ways of Reducing the Cognitive Load in Code

chrismm.com

71–80 of 203 posts

Re: Simple Ways of Reducing the Cognitive Load in Code

#71
post #25

Earlier quoted context omitted.

I completely disagree, every method can be split in private methods. In that way you don't need awful and unhelpful comments in the middle because you can understand what it does simply from the method name.

Methods can be split into private methods. But what that sometimes means is, when you're trying to understand how a piece of information flows through a method to fix a bug, instead of it being all self-contained in the method, instead you have to jump around to 10 places in a file to see where the information goes. That severely increases the cognitive overhead.

There is also a cognitive overhead when trying to find local variables defined in huge functions, at least when they are split into smaller private (static) methods (with variables passed as arguments) it's easier to track.

Both in the parent method and in the encapsulated child.

Re: Simple Ways of Reducing the Cognitive Load in Code

#73
post #26

Earlier quoted context omitted.

I completely disagree, every method can be split in private methods. In that way you don't need awful and unhelpful comments in the middle because you can understand what it does simply from the method name.

sorry, but given this pre-condition (from gp): > ... a large function cannot be broken up usefully, because a lot of state needs to be shared between the different parts ... there is no way to break that up with multiple smaller functions without stowing away the state somewhere. sometimes reading a large function is not half as bad as reading 10 different ones with each altering the shared state.

state can be passed through as arguments...

Re: Simple Ways of Reducing the Cognitive Load in Code

#74
post #14

This article is a good start, but I found it much too light on detail. Each section ended just when I was ready for it to dive into details! For example, in the final section "Make it easy to digest": > Using prefixes in names is a great way to add meaning to them. It’s a practice that used to be popular, and I think misuse is the reason it hasn’t kept up. Prefix systems like hungarian notation were initially meant t…

is_emtpy and make_empty are just going to irritate every C++ programmer in the business, since all the STL containers use empty and clear.

Some C++ programmers, perhaps. But I've just explained why `empty` & `clear` are ambiguous. Even if `empty` & `clear` can never be removed from the STL containers, there's no requirement that these ambiguous names must be propagated to new code.

But focusing exclusively on these two names is missing the forest for the trees. These two names are just a particularly striking example that illustrates the benefit of prefixes. A codebase that applies useful prefix naming will be an easier codebase to understand.

And applying prefix naming consistently will also make it easier for a new developer to contribute to a codebase, since there will be no ambiguity about what to name new functions, nor what to expect them to be named. `is_empty` & `make_empty` would simply be part of that consistency.

Re: Simple Ways of Reducing the Cognitive Load in Code

#75

"Use names to convey purpose. Don't take advantage of language features to look cool." I can't say enough about this. Please write code that is easy to read and understand, not the most compact code, and not the most "decorated" code, or "pretty" code or neat because it uses that giant list expression or ridiculous map statement thats an entire paragraph long. Similarly what bugs me is when I receive a pull request w…

Using intermediate variables is one of the most underrated tools to make code more understandable. It's the definition of something completely unnecessary from a technical standpoint that is all about conveying meaning and clarity to other programmers. And it can be used to help group and "modularize" chunks of code within a routine without necessarily going to the extreme of pulling out a separate subroutine, which can be overkill in some circumstances.

Re: Simple Ways of Reducing the Cognitive Load in Code

#76
post #17

As a junior dev I can confirm the advice about junior devs is very accurate. An anecdote: I recently started working with a team on their half completed web app. They had so many dependencies, and tools for managing dependencies, it took me far longer than it should have to become productive. It's obviously not my place to question which technologies they use, but it can be frustrating.

> They had so many dependencies, and tools for managing dependencies

This is not just junior developer's thing. Everyone would be hindered in this case.

Re: Simple Ways of Reducing the Cognitive Load in Code

#77
post #12
post #4

I really like the advice from "Perl Best Practices" to code in paragraphs. Sometimes, a large function cannot be broken up usefully, because a lot of state needs to be shared between the different parts, or because the parts don't have a meaning outside of the very specific algorithm. In that case, code in paragraphs: Split the function body into multiple steps, put a blank line between these and, most importantly, a…

A easy way of making code into "paragraphs" with comments is to just move it into a function. So in the end, this large function you're talking about, is just calling the other ones, creating a paragraph while the functions are just "words". Makes it easy to test and no need for comments :)

If the methods you are creating are private and for the purpose of segmenting a block of code, you really shouldn't be testing it directly, as it almost certainly won't be part of your public API.

Re: Simple Ways of Reducing the Cognitive Load in Code

#78
post #4

I really like the advice from "Perl Best Practices" to code in paragraphs. Sometimes, a large function cannot be broken up usefully, because a lot of state needs to be shared between the different parts, or because the parts don't have a meaning outside of the very specific algorithm. In that case, code in paragraphs: Split the function body into multiple steps, put a blank line between these and, most importantly, a…

Situations like this are exactly where nested functions can be helpful. I've always thought that it was a shame that C didn't have them. Sometimes I almost wish that Algol flavored languages like Pascal anf Modula 2 would have won out for systems programming, instead of C and the languages it inspired. Actually, GNU C supports nested functions, and a new round if standardization is just starting up, so maybe there's…

In languages where braces define a scope even if there's no if, for, etc. keyword around, you can get a lightweight version of that just by sticking braces around your "paragraphs", as needed. They aren't the same as nested functions, in particular because you can't invoke a naked block multiple times, but if you've got a long function that hasn't got any useful break points in it, but you just want to chunk things, curly braces may help you isolate things nicely.

I tend to consider this a "last resort" over using actual functions, but there are certain classes of functions where this is helpful. The two biggest I know of are functions that represent a state machine, and functions that are the big top-level "plumb all the libraries together" where breaking that up into separate functions significantly complicates code due to all the intricate routing of values you have to do.

Re: Simple Ways of Reducing the Cognitive Load in Code

#79
post #74

Earlier quoted context omitted.

is_emtpy and make_empty are just going to irritate every C++ programmer in the business, since all the STL containers use empty and clear.

Some C++ programmers, perhaps. But I've just explained why `empty` & `clear` are ambiguous. Even if `empty` & `clear` can never be removed from the STL containers, there's no requirement that these ambiguous names must be propagated to new code. But focusing exclusively on these two names is missing the forest for the trees. These two names are just a particularly striking example that illustrates the benefit of pref…

I agree with you, but honkhonkpants raises a good point - sometimes you must bow to existing convention, even if it doesn't meet current best practice.

Re: Simple Ways of Reducing the Cognitive Load in Code

#80
post #31

"Use names to convey purpose. Don't take advantage of language features to look cool." I can't say enough about this. Please write code that is easy to read and understand, not the most compact code, and not the most "decorated" code, or "pretty" code or neat because it uses that giant list expression or ridiculous map statement thats an entire paragraph long. Similarly what bugs me is when I receive a pull request w…

I recently got a code review that in several places suggested I switch to the new Java 8 stream API [1]. I just flatly responded that it was far less readable, even if I could condense a half-dozen lines of code down to one. Where I can quickly scan over a foreach loop to get the jist of what it's doing, I have to closely examine each call in the new approach to have any idea what it's doing. [1] http://www.oracle.co…

Stream based programming is a paradigm that with some training and proper code indentation is much, much faster to read than a nested for loop. Once you get used to it,you can literally fast-scan code written in this style with the confidence that you are not missing anything.

Also, assuming you do not use mutable state, it also has the advantage of being easily parallelizable without any code changes. (As well, as easily combinable, throttlable, samplable etc)

But it _definitely_ has a learning curve (like Calculus) and one needs to invest time in it to get used to this paradigm. Time must be spent in learning all the various operators and how they can be effectively leveraged. Stream-based code is certainly not something you can read off the cuff.

The good news is that this is valuable time investment since all stream/observable libraries across languages have a large set of identical operators and shared API surface - the cross platform applicability is fantastic.

Once you know stream based code in language A, you can automatically read stream-based code in language B, without even knowing the syntax of language B.

I love observable stream libraries like RxJS. It makes async programming so easy! I could never write async JS code effectively with callbacks or even promises. Too much state to keep in my poor head.

We now even have standards like ReactiveX coming out that are attempting to define a cross-language/platform spec for observable flows and standardized patterns on how to create new stream operators, etc.

At risk of a terrible analogy, I consider Stream based code to be something of a mini-language like Regex. (but easier to read). No one who hasn't put in time understanding regex elements will ever follow regex patterns. Ditto for stream based stuff - you need AOT learning, but the rewards are worth it.

Post reply on HN