Live data from Hacker News

What makes code hard to read: Visual patterns of complexity (2023)

seeinglogic.com

31–40 of 383 posts

Re: What makes code hard to read: Visual patterns of complexity (2023)

#31
I would like to add something to the point made here:

"For long function chains or callbacks that stack up, breaking up the chain into smaller groups and using a well-named variable or helper function can go a long way in reducing the cognitive load for readers. [my emphasis]

  // which is easier and faster to read?
  function funcA(graph) {
    return graph.nodes(`node[name = ${name}]`)
      .connected()
      .nodes()
      .not('.hidden')
      .data('name');
  }

  // or:
  function funcB(graph) {
    const targetNode = graph.nodes(`node[name = ${name}]`)
    const neighborNodes = targetNode.connected().nodes();
    const visibleNames = neighborNodes.not('.hidden').data('name')

    return visibleNames;
  }
The names of the functions being called are rather generic, which is appropriate and unavoidable, given that the functions they compute are themselves rather generic. By assigning their returned values to consts, we are giving ourselves the opportunity to label these computations with a hint to their specific purpose in this particular code.

In general, I'm not a fan of the notion that code can always be self-documenting, but here is a case where one version is capable of being more self-documenting than the other.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#32
You really should try to pack an unbroken thought as a single line of code as much as possible. That’s the idea behind chaining multiple functions together on one line instead of spreading it out over several lines. Eyes go horizontally more naturally than up and down, it fits our vision’s natural aspect ratio. And stop making deep nestings.

Making a single function call per line assigning output to a variable each time is really just for noobs who don’t have great code comprehension skills and appreciate the pause to have a chance to think. If the variable’s purpose for existence is just to get passed on to a next function immediately, it shouldn’t exist at all. Learn to lay pipe.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#33
The article's good, but misses my most mentally-fatiguing issue when reading code: mutability.

It is such a gift to be able to "lock in" a variable's meaning exactly once while reading a given method, and to hold it constant while reasoning about the rest of the method.

Your understanding of the method should monotonically increase from 0% to 100%, without needing to mentally "restart" the method because you messed up what the loop body did to an accumulator on a particular iteration.

This is the real reason why GOTOs are harmful: I don't have a hard time moving my mind's instruction-pointer around a method; I have a hard time knowing the state of mutable variables when GOTOs are in play.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#34
post #21

Towards the end he had an example of splitting a sequence of "graph.nodes(`node[name = ${name}]`).connected().nodes().not('.hidden').data('name');" adding variable between some of the . in there and claimed it was marginally less efficient. This is sometimes true, but if it ever is you need to talk to your tool vendors about a better optimizes. If you are working in a language without an optimizer than the marginal d…

I’d add that I follow that approach because the optimizers are more likely to optimize readable code than weird hacks.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#35
Maybe it's just me, but TypeScript makes code hard to read.

It's fine if the data model is kept somewhat "atomic" and devs are diligent about actually declaring and documenting types (on my own projects, I'm super diligent about this).

But once types start deriving from types using utility functions and then devs slack and fall back to type inference (because they skip an explicit type), it really starts to unravel because it's very hard to trace fields back to their origin in a _deep_ stack (like 4-5 levels of type indirection; some inferred, some explicit, some derived, some fields get aliased...).

    type Dog = {
      breed: string
      size: "lg" | "md" | "sm"
      // ...
    }

    type DogBreedAndSize = Pick

    function checkDogs(dogs: Dog[]) : DogBreedAndSize[] {
      return dogs.map(d => /* ... */)
    }

    const checkedDoggos = checkDogs([])
Versus:

    function checkDogs(dogs: Dog[]) {
      // ...
    }
Very subtle, but for large data models with deep call stacks, the latter is completely unusable and absolutely maddening.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#36

Why not apply a programming methodology which allows one to leverage a rich set of tools which were created for making text more readable and visually pleasing? http://literateprogramming.com/ For a book-length discussion of this see: https://www.goodreads.com/book/show/39996759-a-philosophy-of... previously discussed here at: https://news.ycombinator.com/item?id=27686818 (and other times in a more passing mention --…

I find Literate Programming interesting partly because it’s almost the opposite of the much-advocated “many small functions” style. You could literally be writing a book that explains your program, and the code becomes almost secondary material to illustrate the main text rather than the main asset itself.

I did once write a moderately substantial application as a literate Haskell program. I found that the pros and cons of the style were quite different to a lot of more popular/conventional coding styles.

More recently, I see an interesting parallel between a literate program written by a developer and the output log of a developer working with one of the code generator AIs. In both cases, the style can work well to convey what the code is doing and why, like good code comments but scaled up to longer explanations of longer parts of the code.

In both cases, I also question how well the approach would continue to scale up to much larger code bases with more developers concurrently working on them. The problems you need to solve writing “good code” at the scale of hundreds or maybe a few thousand lines are often different to the problems you need to solve coordinating multiple development teams working on applications built from many thousands or millions of lines of code. Good solutions to the first set of problems are necessary at any scale but probably insufficient to solve the second set of problems at larger scales on their own.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#37

I would like to add something to the point made here: "For long function chains or callbacks that stack up, breaking up the chain into smaller groups and using a well-named variable or helper function can go a long way in reducing the cognitive load for readers. [my emphasis] // which is easier and faster to read? function funcA(graph) { return graph.nodes(`node[name = ${name}]`) .connected() .nodes() .not('.hidden')…

I'm almost always a fan of more rather than less commenting and self-documentation...

...but in this example I find the first to be far faster and easier to read. The "labeled" versions don't add any information that isn't obvious from the function names in the first.

If you were giving business logic names rather than generic names (e.g. "msgRecipient", "recipientFriends", "visibleFriends" then I could see more value. But even then, I would find the following the easiest:

    function funcA(graph) {
        return (graph
          .nodes(`node[name = ${name}]`)  // message recipient
          .connected()  // recipient friends
          .nodes()
          .not('.hidden')  // inside current scroll area
          .data('name')
          );
      }
This keeps the code itself simple and obvious, prevents a ton of repetition of variable names, and allows for longer descriptions than you'd want in a variable name.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#38
post #28

There is a (large, I believe) aspect of good code that is fundamentally qualitative & almost literary. This annoys a lot of computer programmers (and academics) who are inclined to the mathematical mindset and want quantitative answers instead. I love dostoyevsky and wodehouse, both wrote very well, but also very differently. While I don't think coding is quite that open a playing field, I have worked on good code ba…

I consider code bad if it takes more then 5 seconds to read and understand the high level goal of a function. Doesn't matter how it looks. If its not possible to understand what a function accomplishes within a reasonable amount of time (without requiring hours upon hours of development experience), it's simply bad.

> I consider code bad if it takes more then 5 seconds to read and understand the high level goal of a function.

That's something that's possible only for fairly trivial logic, though. Real code needs to be built on an internal "language" reflecting its invariants and data model and that's not something you can see with a microscope.

IMHO obsessive attention to microscope qualities (endless style nitpicking in code review, demands to run clang-format or whatever the tool du jour is on all submissions, style guides that limit function length, etc...) hurts and doesn't help. Good code, as the grandparent points out, is a heuristic quality and not one well-defined by rules like yours.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#39
post #28

There is a (large, I believe) aspect of good code that is fundamentally qualitative & almost literary. This annoys a lot of computer programmers (and academics) who are inclined to the mathematical mindset and want quantitative answers instead. I love dostoyevsky and wodehouse, both wrote very well, but also very differently. While I don't think coding is quite that open a playing field, I have worked on good code ba…

I consider code bad if it takes more then 5 seconds to read and understand the high level goal of a function. Doesn't matter how it looks. If its not possible to understand what a function accomplishes within a reasonable amount of time (without requiring hours upon hours of development experience), it's simply bad.

There is a call-stack depth problem here that is specific to codebases though. For one familiar with the the conventions, key data abstractions (not just data model but convention of how models are structured and relate) and key code abstractions, a well formed function is easy to understand. But someone relatively new to the codebase will need to take a bunch of time switching between levels to know what can be assumed about the state or control flow of the system in the context of when that function/subroutine is running. Better codebases avoid side-effects, but even with good separation there, non-trivial changes require strong reasoning about where to make changes in the system to avoid introducing side-effects and not just passing extra state or around all over the place.

So, I'd take "good architecture" with ok and above readability, over excellent readability but "poor architecture" any day. Where architecture in this context means the broader code structure of the whole project.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#40
There's a cool plugin for vscode called Highlight[1] that lets you set custom regexes to apply different colors to your code. I think a common use of this is to make //TODO comments yellow, but I use it to de-emphasize logs, which add a lot of visual noise because I put them EVERYWHERE. The library I maintain uses logs that look like:

    this.logger?.info('Some logs here');
So I apply 0.4 opacity to it so that it kind of fades into the background. It's still visible, but at a glance, the actual business logic code pops out at you. This is my configuration for anyone who wants to modify it:

    //In vscode settings.json:
    "highlight.regexes": {
        "((?:this\\.)?(?:_)?logger(?:\\?)?.(debug|error|info|warn)[^\\)]*\\)\\;)": {
            "regexFlags": "gmi",
            "decorations": [{
                "opacity": "0.4"
            }]
        }
    },
---

[1] https://marketplace.visualstudio.com/items?itemName=fabiospa...

Post reply on HN