[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)
 
web link (rpeszek.github.io)
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)