Live data from Hacker News

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

seeinglogic.com

81–90 of 383 posts

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

#81
post #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 u…

Disagree. There's an abstract "information space" that the code is modeling, and you have to move around your mind's instruction pointer in that space. This can be helped or hindered by both mutable and immutable vars--it depends on how cleanly the code itself maps into that space. This can be a problem w/ both mutable and immutable vars. There's a slight tactical advantage to immutable vars b/c you don't have to worry about the value changing or it changing in a way that's misleading, but IME it's small and not worth adopting a "always use immutability" rule-of-thumb. Sometimes mutability makes it way easier to map into that "information space" cleanly.

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

#82

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 valu…

_thank you_. this is the comment i came here desperately hoping somebody had already made.

It's not that names are bad - it's that when you use intermediate variables, my brain has to check whether any of the variables are used more than once - i.e., is the flow here completely linear, or is there a hidden branching structure?

the chain of methods approach makes it _completely_ clear that there is no 'tree' in the code.

If you want names (and that's a fine thing to want!) then _either_ comments or defining separate _functions_, e.g `function messageRecipient`, `function friends`, `function visibleToScroll`) is the way to go. Although with many languages that don't have a built-in pipe operator, it becomes harder to express the nice linear flow in a top-to-bottom arrangement if you take the function route. A good reason for languages to keep converging toward syntax for pipes!

For my money, you only define those functions if you want to reuse them later - additional indirection is not usually helpful - so comments would be my choice if there were no other uses of these selectors.

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

#83
post #63

Earlier quoted context omitted.

If the algorithm is well known, it's all good as long as the function name for it is somewhat understandable. I have to work with 200 line functions at work and it's a complete, excuse the language, shitshow.

> as long as the function name for it is somewhat understandable But does using a function, essentially a box with known inputs and outputs, constitute actually understanding the function? What happens if you need to debug or understand the implementation of it? Now the original name has gone and you're looking at a larger number of differently-named things that hopefully communicate their intent well. But if you nee…

My original comment never was about understanding the implementation details. It was about understanding the high level goal of the function.

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

#84
post #3

Earlier quoted context omitted.

> If a method is long to read from top to bottom, the answer isn't always splitting it into 5 smaller ones, sometimes life just has inherent complexity. Yes! This. I find it much easier to parse a long function where I can scroll down it and just read it top to bottom, then having a function which calls out to lots of other functions and I'm jumping around the code base, back and forward. Just reading the long functi…

> [than] having a function which calls out to lots of other functions and I'm jumping around the code base, back and forward. i agree with longer functions and less jumping around, but there's also some nuance i find. I sometimes find converting a complicated multi-line condition into something like the below is much easier for me to read, so long as the function is named in a clear way and the function definition is…

> can also do it the grug-brained way

This way reads like:

    x = 1 // set variable x equal to 1
in that gt_zero echoes what the > operator does and says nothing about intent. Comparing, e.g.

    gt_zero = space > 0     // there is some space I guess?

    space_for_logfile = space > 0   // oh, logfiles need space > 20 there's the mistake.

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

#85

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 b…

I agree that functions should probably specify their output type, MOSTLY to enforce that all paths that return from that function must adhere to that type I've seen plenty of regressions where someone added a new condition to a function and then returned a slightly different type than other branches did, and it broke things However, I don't think there is much value in putting types on variable declarations In your e…

I want it on the other side (on the function return) so that it's consistently displayed in type hints and intellisense so I don't have to navigate the code backwards 3-4 layers to find the root type (do you see what I'm saying?)

    function checkDogs(dogs: Dog[]) : DogBreedAndSize[] {
      return dogs.map(d => /* ... */)
    }
^^^ That's where it's important to not skip the type def because then I can see the root type in the editor hints and I don't need to dig into the call stack (I know the end result is the same whether it's on the assignment side or the declaration side, but it feels like ensuring it's always on the declaration side is where the value is)

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

#86
post #79

Do people really really agree with "Shorthand constructs that combine statements decreases difficulty"? The author even identifies a problem with the example from the original guide.

No, almost everybody disagree with it as a general statement.

Some people disagree to a point where they want languages to have only a handful different constructs. But most people will disagree at some amount of language complexity.

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

#87
post #58

Shoutout to the pipe operator in R. The code equivalent of "and then." It helps to unnest functions and put each action on one line. I know R is more for stats and data, but I just think it's neat.

For anyone interested in this as design, it’s called method chaining.

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

#88

Earlier quoted context omitted.

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 valu…

_thank you_. this is the comment i came here desperately hoping somebody had already made. It's not that names are bad - it's that when you use intermediate variables, my brain has to check whether any of the variables are used more than once - i.e., is the flow here completely linear, or is there a hidden branching structure? the chain of methods approach makes it _completely_ clear that there is no 'tree' in the co…

I agree that commenting appropriately is desirable (I do more that I used to.) I also like the idea of const being the default, and for syntax highlighting that clearly distinguishes mutables.

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

#89
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.

So it should take 5 minutes whether it's your language or choice or the assembly it compiles to? Or does it matter how it looks in _that_ case?

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

#90
post #61

My pet peeve: function getOddness4(n: number): if (n % 2 === 0): return "Even"; return "Odd"; While it is shorter, I prefer vastly prefer this one: function getOddness2(n: number): if (n % 2 === 0): return "Even"; else: return "Odd"; Reason: getOddness4 gives some sense of asymmetry, whereas "Even" and "Odd" are symmetric choices. getOddness2 is in that respect straightforward.

I personally prefer the former as you can visually see the return one level of indentation below function name. It shows a guaranteed result barring no early-exits. Something about having the return embedded lower just seems off to me.
Post reply on HN