[HN Gopher] Cognitive loads in programming
       ___________________________________________________________________
        
       Cognitive loads in programming
        
       Author : ajdude
       Score  : 149 points
       Date   : 2022-08-31 18:40 UTC (4 hours ago)
        
 (HTM) web link (rpeszek.github.io)
 (TXT) w3m dump (rpeszek.github.io)
        
       | suzzer99 wrote:
       | I must be weird because I love YAML. I love the brevity of no
       | closing brackets and the finality of the indentation being the
       | code, rather than just window dressing that can get out of whack.
        
       | prohobo wrote:
       | Competent*
        
       | googlryas wrote:
       | I've often thought of this as "developer empathy" when I was
       | working at Amazon. Generally regarding people who create APIs for
       | other developers to use. The idea being that you can make your
       | API simple or complex, and most developers tend to make their API
       | complex, because they think their system is really powerful and
       | cool and want to expose all the power to end users. But, you need
       | to remember that your users aren't just using your API. They are
       | using maybe 100 other ones as well. And there is no way for a
       | person to learn the ins and outs of all 101 complex APIs. Add to
       | this the fact that the more complexity you expose, the more
       | unintentionally coupled your API becomes to your implementation
       | (generally). So, make your APIs simple, and provide backdoors if
       | you really want to expose complexity.
       | 
       | I will say, this did not get me promoted at Amazon.
        
         | jackblemming wrote:
         | That just sounds like the designer didn't layer the API
         | properly. A good API has a sensible top level that does all the
         | common use cases, but also allows access to the lower level
         | stuff for power users.
        
       | krawczstef wrote:
       | Yes! As one of the creators of
       | https://github.com/stitchfix/hamilton this was one of the aims.
       | Simplifying the cognitive burden for those developing and
       | managing data transforms over the course of years, and in
       | particular for ones they didn't write!
       | 
       | For example in Hamilton -- we force people to write "declarative
       | functions" which then are stitched together to create a dataflow.
       | 
       | E.g. example function -- my guess is that you can read and
       | understand/guess what it does very easily. # in
       | client_features.py
       | 
       | @tag(owner='Data-Science', pii='False')
       | 
       | @check_output(data_type=np.float64, range=(-5.0, 5.0),
       | allow_nans=False)
       | 
       | def height_zero_mean_unit_variance(height_zero_mean: pd.Series,
       | height_std_dev: pd.Series) -> pd.Series:             """Zero mean
       | unit variance value of height"""             return
       | height_zero_mean / height_std_dev
       | 
       | To use `height_zero_mean_unit_variance` somewhere else, you'd
       | then find it as a parameter to some other function, and that's
       | the basic gist of how you'd develop a dataflow. To then execute
       | the dataflow, you'd then in a separate file, decoupled from your
       | transform logic, write some code to (1) create the DAG, and then
       | (2) request what you want executed from it.
       | 
       | In terms of reducing cognitive burden for maintainers, by forcing
       | things into a function there's all these nice properties to be
       | had for maintenance:
       | 
       | - unit testing is always possible
       | 
       | - integration testing is super easy, since you can add code, and
       | then _only_ test that path without having to run your entire
       | transform workflow.
       | 
       | - documentation has a natural place & and we can build a DAG
       | visualizing the dataflow that you're looking at
       | 
       | - debugging is methodical since it's straightforward to map an
       | output to a function, check the logic there, and then
       | methodically traverse its dependencies because they are declared.
       | 
       | - you can wrap functions/inject things at run time easily
        
       | baby wrote:
       | People write code for computers, but they should also write code
       | for humans. Humans have different parsers and the evolution of
       | the code is dictated by how quickly and efficiently can these
       | human parse the code.
        
         | karmakaze wrote:
         | I write code for humans, first of all me. Ensuring correctness,
         | getting the syntax right, and following style conventions, etc
         | is what follows. It's much easier for a human to verify the
         | logic if it's easy to read and comprehend. Good notes in a
         | pull-request is also handy and easy to find with a git-blame
         | and commit hash lookup on GitHub.
        
         | luciusdomitius wrote:
         | Programming code is for humans. Machine code is for machines.
         | The compiler translates from human to machine.
        
       | troelsSteegin wrote:
       | Would GOMS [0] be a useful framework for thinking about cognitive
       | load in programming? GOMS is a framework for analyzing user
       | workload in system interaction. Bad or gnarly code creates a much
       | more complex interface for the programmer. A quick look did not
       | surface a reference here, but I'd look further at work by David
       | Kieras [1].
       | 
       | [0] https://en.wikipedia.org/wiki/GOMS [1]
       | https://www.researchgate.net/profile/David-Kieras
        
       | AceJohnny2 wrote:
       | Offtopic:
       | 
       | I'm saddened to see YAML get a bad rap for effectively being
       | shoehorned to fit a task that it's just bad at.
       | 
       | In my experience, YAML is a great format for legibly declaring a
       | dict/array structure, with added benefits over JSON like
       | anchors/references (effectively pointers, which thus allow it to
       | describe a graph), comments, and overloads (great for defaults!).
       | 
       | But YAML has no facilities for conditionals, loops, or any logic.
       | So these get tacked-on, ad-hoc, by systems that need them, and
       | that pulls YAML towards Greenspun's Tenth Rule ( _" Any
       | sufficiently complicated [system] contains an ad hoc, informally-
       | specified, bug-ridden, slow implementation of half of Common
       | Lisp"_)
       | 
       | I'm glad better languages like Dhall exist for the problem-space
       | that YAML is just not designed for.
        
       | giantg2 wrote:
       | Multiple levels of abstraction kills me, at least without
       | adequate documentation. Conceptually, it's veey easy to grasp,
       | but in code it becomes a mess making jumps through multiple
       | unfamiliar files in a large code base. Essentially, you end up
       | having to hold all the pertinent information on that code in your
       | head, which largely defeats some of the purposes of breaking them
       | into multiple files.
        
         | baby wrote:
         | You'd probably say the same if no abstractions were there. It's
         | a trade off. Don't necessarily abstract something, but do it if
         | it makes everything else better and more maintainable and more
         | secure. Probably a good philosophy is to never start with
         | abstraction and leave that to refactors.
        
           | giantg2 wrote:
           | Yeah, we seem to have gotten better at this by using noSQL
           | databases with one big JSON object for each item instead of
           | relational based on OOP objects.
        
         | rileymat2 wrote:
         | I find that this type of code is much easier to debug in a
         | debugger, while harder to debug looking at source code.
         | 
         | When in a debugger, I can throw a breakpoint in, inspect inputs
         | and outputs, if they are what I expect, there is (generally[1])
         | no reason to dive into those other files.
         | 
         | When reading the code from source outside of a debugger, as a
         | new person to the code base, I agree with you and the exact
         | problem you are describing.
         | 
         | [1] Reasonably written code without a bunch of hidden global
         | state.
        
       | keyle wrote:
       | One of the goals of any code base of significant size should be
       | to reduce the cognitive load (not lines of code per function).
       | 
       | Assume the developer working here just had a 45 mins sync, has a
       | meaningless repeat meeting about vapourware in 10 mins and a 2
       | hours town hall meeting after lunch... and still have to deliver
       | that mess of a requirement you made him promise to deliver before
       | any of these interruptions were even on his calendar!
       | 
       | - Always aim for declarative programming (abstract out the how it
       | does it),
       | 
       | - limit the depth of the function calls (rabbit hole
       | developers...),
       | 
       | - separate business logic from framework internals and
       | 
       | - choose composition over inheritance.
       | 
       | - Also avoid traits and mixins (run from the dark magic)
       | 
       | - don't over document the language or framework, only the eyebrow
       | raising lines, the performance sensitive stuff and the context
       | surrounding the file if it isn't obvious.
       | 
       | - name stuff to be easily grepable
       | 
       | Easy rules to go by, (there are probably more), they can make or
       | break your ability to work, so that you can get interrupted 12
       | times an hour and still commit work.
       | 
       | I don't find these in books, just decades of sweating and pulling
       | my hair "why does it have to be so hard!?" I have met plenty of
       | senior devs who naturally do the same thing now.
       | 
       | The code size fallacy is a prime example of the wrong way to look
       | at it. Plenty of extremely large code base in C++ are far more
       | manageable than small JavaScript apps.
       | 
       | Mixing boilerplate framework junk with your intellectual property
       | algorithms "what makes your software yours" is a sure way to
       | hinder productivity in the long term.
       | 
       | You write code 3-4 times. You read it 365 times a year.
       | 
       | One last thing I recommend if you deal with a lot of
       | interruptions and maybe multiple products, various code bases...
       | keep a                   // @next reintegrate with X
       | 
       | The @next comment 'marker' is a great way to mark exactly which
       | line of which file you were at before you left this project, for
       | a meeting, for lunch, for the day, etc. And it allows you jump
       | back into context by searching for @next and go. Also since it's
       | visual and located, your brain has a much better time remembering
       | the context, since we're good with places and visual landmarks.
       | 
       | It's far more efficient than roughly remembering what I was
       | doing, looking at the last commit, scrolling endlessly through
       | open files. Don't commit @next though :)
        
         | [deleted]
        
         | magicalhippo wrote:
         | Nice list. I would add, near the top, "don't be clever unless
         | it makes the code significantly easier to read for others".
         | 
         | For example, if your code involves a lot of linear algebra,
         | then operator overloading the sensible operators is probably a
         | good thing. But don't use operator overloading just to save
         | some keystrokes.
         | 
         | Over-abstraction is another such case. Try to avoid adding
         | abstraction layers until you need them.
         | 
         | I'd also add "make the code as self-documenting as possible".
         | That means being verbose at the right times, such as writing
         | meaningful identifier names.
         | 
         | And of course "avoid global variables". I've seen people use
         | singletons with a read/write member, which is as much a global
         | variable as any other.
        
       | ABS wrote:
       | it's going to take quite some time to read it all since it's long
       | and deserves the time but since it's soliciting early feedback
       | here it is: research and quote all the works done over the last
       | 10 or so years by others in this space!!
       | 
       | The topic of cognitive load in software development is far from
       | rarely considered and in fact it's been somewhat "popular" for
       | several years depending on what communities and circles you
       | participate it on- and off-line.
       | 
       | I'm surprised not to find any mentions to things like:
       | 
       | - the Team Topologies book by Skelton and Pais, published in 2019
       | where they cover the topic. Particularly of note here is the fact
       | that Skelton has a Computer Science BSc and a Neuroscience MSc
       | 
       | - the many, many, many articles, posts, discussions and
       | conference sessions on congnitive load from the same authors and
       | connected people in subsequent years (I'd say 2021 was a
       | particularly rich year for the topic)
       | 
       | - Dan North sessions, articles and posts from around 2013/2014 in
       | which he talks about code that fits in your head but no more,
       | referencing James Lewis original... insight. E.g. his GOTO 2014
       | session "Kicking the Complexity Habit"
       | https://www.youtube.com/watch?v=XqgwHXsQA1g&t=510s a quick search
       | returns references to it even in articles from 2020
       | https://martinfowler.com/articles/class-too-large.html
       | 
       | - Rich Hickey's famous 2011 Simple Made Easy talk
       | https://www.infoq.com/presentations/Simple-Made-Easy/
        
         | Lwepz wrote:
         | >research and quote all the works done over the last 10 years
         | or so by researchers in this space!!
         | 
         | I totally understand your point and appreciate you linking
         | those resources, however I think it's important to remember
         | that the author's post is from a personal blog, not from a
         | scientific journal or arxiv.
         | 
         | Perhaps OP would've never posted this if he felt that his
         | "contribution" wasn't novel enough. Additionally, there's a
         | chance that the wording and tone the author used might speak to
         | people who found the articles you mentioned opaque(and vice
         | versa, obviously).
         | 
         | If the author, feeling the urge to write something up, had
         | looked very hard for "prior work" instead of following the flow
         | of their insights gained through experience, perhaps they
         | would've felt compelled to use the same vocabulary as the
         | source, which has its pros(forwarding instead of reinventing
         | knowledge) and cons(propagating opaque terms, self censoring
         | because of a feeling of incompetence in the face of the
         | almighty researchers).
         | 
         | That's one of the great things about blog posts: to be able to
         | write freely without being blamed for incompleteness or prior
         | art omission.
         | 
         | On a different note, I think this may also highlight the fact
         | that the prior work you mentioned isn't easy enough to find.
         | Perhaps knowledge isn't circulating well enough outside of
         | particular circles.
        
       | UweSchmidt wrote:
       | One measure for code quality should be the effort to make a minor
       | change to code, or to fix a simple, common bug.
       | 
       | Can a developer who is familiar with the code, effortless make a
       | change in an area where change was expected at the beginning?
       | 
       | Assuming a typical, minor bug that turns out to originate from
       | the usual suspect places in the code (database query, logical
       | error, incomplete implementation of a requirement, off-by-
       | one/calculation error). Is it usually easy to narrow things down,
       | and to spot the error?
       | 
       | That means the lead architect should regularly try implementing
       | minor features or fix minor bugs and/or pair up with colleagues
       | who do, and draw conclusions about the code accordingly.
        
         | hu3 wrote:
         | That's a good one.
         | 
         | Another measure I use for code quality is: how afraid am I of
         | breaking something when I make changes? This is where typing
         | and tests shine.
        
           | wtetzner wrote:
           | Typing and tests definitely help, but nothing works better
           | than having well-factored, decoupled code, so a change in one
           | place doesn't impact the rest of the code base in unexpected
           | ways. Unfortunately I don't know of a good way to do that
           | outside of experience + vigilance.
        
             | baby wrote:
             | Good abstractions + asserts + minimal assumptions
             | everywhere in the code (also called refactoring-resistant
             | code)
        
             | bcrosby95 wrote:
             | Unexpected is an interesting word. This probably means
             | consistency is important, because expected in one codebase
             | could be unexpected in another.
        
             | [deleted]
        
       | mikewarot wrote:
       | I tend to think of this as an _impedance matching_ [1] (in the EE
       | sense) problem. The best frameworks match the way we think about
       | problems once we've gotten used to them. There has to be some
       | give on the part of the programmer, because of Godel's
       | incompleteness theorems.[2]
       | 
       | I whole heartedly agree that we have to minimize extra steps when
       | reviewing code, but you can only push so much of that burden back
       | through space-time to the green field programmer.
       | 
       | For instance, in Pascal, you have to declare everything first.
       | I'm used to that, so it matches my expectations. However, it also
       | sucks because if you're looking in the middle of code the
       | declarations aren't proximal to the first time they're used.
       | There's an expectation that all the code will be read, which was
       | fine in the academic world which gave birth to Pascal, but not in
       | the world of million line systems.
       | 
       | There are tradeoffs that will take decades to get right, simply
       | because we need to have time to gain enough perspective to adjust
       | things collectively, as a profession.
       | 
       | [Edit -- Tweak link format per suggestions below]
       | 
       | [1] - https://en.wikipedia.org/wiki/Impedance_matching
       | 
       | [2] -
       | https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_...
       | 
       | [3] - https://en.wikipedia.org/wiki/Off-by-one_error
        
         | Jtsummers wrote:
         | Is there a reason you deliberately make it hard for people to
         | follow the links you put in your comments?
        
           | mikewarot wrote:
           | I number them, and space them out to make it easy to select
           | and copy... there's no way to inline hyperlinks here that I'm
           | aware of.
           | 
           | Option #1 -- formatted as a code block                 [1] -
           | https://en.wikipedia.org/wiki/Impedance_matching       [2] - 
           | https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_t
           | heorems       [3] - https://en.wikipedia.org/wiki/Off-by-
           | one_error
           | 
           | Option 2 -- jumble of links
           | 
           | [1] - https://en.wikipedia.org/wiki/Impedance_matching [2] - 
           | https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_.
           | .. [3] - https://en.wikipedia.org/wiki/Off-by-one_error
           | 
           | -
           | 
           | Option 3 -- Extra line breaks everywhere
           | 
           | [1] - https://en.wikipedia.org/wiki/Impedance_matching
           | 
           | [2] - https://en.wikipedia.org/wiki/G%C3%B6del%27s_incomplete
           | ness_...
           | 
           | [3] - https://en.wikipedia.org/wiki/Off-by-one_error
           | 
           | -
           | 
           | None of those really seems right
           | 
           | [Edit] Sorry, I didn't know about the issues on mobile. it
           | definitely wasn't trying to make it worse on purpose. I added
           | the off by one link to make the examples long enough
           | 
           | The 2 biggest problems in computing... Naming things, Cache
           | Invalidation, and off by 1 errors.
        
             | layer8 wrote:
             | I'd also suggest to leave out the hyphens, they are just
             | visual noise.
        
             | Jtsummers wrote:
             | No, but if you didn't put the white space at the front of
             | the line they'd be, you know, _links_. So people don 't
             | have to copy/paste them. Which is sort of the point of
             | hypertext.
             | 
             | EDIT: Most of the parent comment wasn't there when I wrote
             | my initial reply.
             | 
             | Of the three options, the third is the best of a bad
             | situation since we don't get to cleanly inline links like
             | on Reddit and other places. The first is just
             | inconsiderate, especially to mobile users. The second is,
             | as you noted, a mess especially when there are multiple
             | links. The third lets you keep your original intent
             | (clearly listing each link) while letting them still
             | function as links. Which is better than the first option
             | because it's not disrespectful of other people even if it
             | does add some vertical whitespace.
        
             | wtetzner wrote:
             | If you don't put them in a code block, the URLs are
             | automatically linkified by HN.
        
       | OneLessThing wrote:
       | If we improve our programming languages and practices to make
       | code bases more easily understood we will simply increase the
       | scope of what the projects attempt to solve, not reduce their
       | complexity. While this is a good for software it still means
       | we're going to be moaning about working on complex software.
       | 
       | It's like how the increased economic productivity due to
       | technology has not made the work week shorter.
        
       | luciusdomitius wrote:
       | This is insanely good! I always thought I am the only person in
       | the world taking that into account when designing/coding
       | something.
        
       | z9znz wrote:
       | > We ask "How long will it take?"
       | 
       | That is the first problem. We should be asking several questions,
       | and the value of their answers would be weighted against business
       | goals.
       | 
       | How long will it take?
       | 
       | How will it affect future development (pace).
       | 
       | How will it affect the forced total rewrite date (a broader view
       | of the previous question).
       | 
       | And my favorite, how difficult will it affect provability of
       | system behavior? This point is greatly affected by frameworks
       | which impose their own conventions (which change over time).
        
       | Lwepz wrote:
       | Splendid article.
       | 
       | I was thinking that perhaps walking the readers of our code
       | through our architectural decisions(and not just through what our
       | code does) is a good way to lessen their cognitive load. This
       | helps identify decisions that have been taken to look smart or
       | because the foie-gras the author ate on that day went down really
       | well with the Chardonnay and made them feel extra stylish.
       | 
       | This also helps us understand how well we know the tools we're
       | using versus how much we do simply through pattern repetition.
        
       | CraigJPerry wrote:
       | This is an enjoyable read. The topic of abstraction needs to be
       | explored more here. I wonder if the author would agree that In
       | practice almost all abstractions can be made to leak. Given that,
       | then even experienced developers who understand how to avoid
       | mixing different layers of abstraction will seldom succeed in
       | creating abstractions without leaks. If an abstraction leaks,
       | it's not germane, it's incidental complexity.
       | 
       | This applies to blueprints as equally as it does to
       | implementations. The author notes the caveat in soundness of
       | blueprints - not something a common developer can do much about -
       | but if the developer designs blueprints with unintended
       | behaviours - for example the author's recursive let example -
       | then they're being very generous with the helpfulness of
       | blueprints since they are no silver bullet to avoid shooting
       | yourself in the foot.
       | 
       | The common case in software development is not a PLT enthusiast.
       | Blueprints are just another way you can shoot yourself in the
       | foot if you hold the tool wrongly. And the common case is not to
       | understand the tool much more than at a fairly superficial level,
       | so mishandling is all but assured.
       | 
       | This means pragmatically, there's no substitute for an acceptable
       | level of testing in a project.
        
       | he0001 wrote:
       | My pet peeve with this, is that people write code that doesn't
       | reflect the problem. A messy problem should have a messy solution
       | as that would reflect the problem. As soon you start to write
       | code that's either for the sake of something else, clean code
       | etc, you deviate from what you are trying to solve. That creates
       | a solution which hard to follow, as the problem is abstract and
       | others may be able to interpret that. But then if you look at
       | some code that does something entirely different, due to language
       | constructs, framework or some diligent programmer trying to write
       | good looking code but has nothing to do with the problem. That
       | creates cognitive load as you need to not just only understand
       | and solve original problem and changes, you need to understand
       | the actual code too.
        
         | michaelwww wrote:
         | > A messy problem should have a messy solution as that would
         | reflect the problem
         | 
         | I've never heard this expressed before and it goes against my
         | experience. My entire goal is to find a simple solution to a
         | messy problem. I can think of a lot of simple solutions to
         | messy problems. If programming required me to come up with
         | messy solutions to messy problems I wouldn't want to do it.
         | Most programmers like that "a-ha" moment of a discovering a
         | simple and elegant solution to a messy problem.
        
           | Aperocky wrote:
           | Exactly, solution to a messy problem is 3 simple solutions
           | working together, not one messy solution.
        
             | michaelwww wrote:
             | I'm a bit old so when I was growing up in the 60's a
             | cartoonist named Rube Goldberg made his living by amusing
             | people with overly-complicated solutions to simple
             | problems. I don't hear him mentioned anymore so I'll
             | mention him now
             | 
             | https://en.wikipedia.org/wiki/Rube_Goldberg_machine
        
         | atoav wrote:
         | I once wrote an extremely general and elegant piece of code for
         | an extremely messy problem (some obscure CSV from a source
         | system I cannot influence with a ton of inconsistencies and
         | mistakes that I have to straighten out automatically).
         | 
         | I started out with straightforward and somewhat messy code, but
         | after adjusting that 3 times, I ended up writing something that
         | is more or less a toolset to deal with the problems that data
         | had. The code of the tools is convoluted, but when I have to
         | adjust some things every now and then I just use the tools I
         | built anyways so who cares.
         | 
         | Abstraction in programming should be seen like certain devices,
         | helpers etc. someone would make use of in woodworking. Of
         | course it costs you some time to built them, but it can make
         | your life a lot easier, because ot makes results more
         | consistent and testable.
        
         | layer8 wrote:
         | Yes, I would formulate this as the distance between the mental
         | model of the functionality being implemented and the structure
         | of the code. The interesting question is how to design
         | programming languages, libraries and tooling so that gap
         | remains small.
        
       | wikitopian wrote:
       | Alan Kay is a very nice and bright guy and I don't want to hurt
       | his feelings.
       | 
       | But this is a perfectly soluble problem and it is entirely and
       | exclusively Alan's fault that everybody stampeded in the wrong
       | direction to our current situation.
        
       ___________________________________________________________________
       (page generated 2022-08-31 23:00 UTC)