[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)