Live data from Hacker News

Clear is better than clever [pdf]

dave.cheney.net

181–190 of 335 posts

Re: Clear is better than clever [pdf]

#181
post #133

Earlier quoted context omitted.

Nothing kills a users experience like buggy code. [..]Fast but buggy code will lose to slower but correct code everytime. Remind me how Chrome took over the browser market with "Chrome Fast" as its slogan? How did nginx grow so popular against Apache? Was it promoted as "less buggy" or "faster"? How MySQL became so prominent, was that people choosing "slower but more correct"? How MongoDB became so popular - "slower…

Ten characters of unreadable code written by an expert will have fewer bugs than 100,000 lines of "readable, clear" C++ code written by a novice. Everytime this topic comes up someone brings up this point. The counter to it is always the same. In the lifetime of those ten characters someone who is not the original author will have to modify it. Because it is not clear what exactly the code is doing they will probably…

You want a large amount of code, because you're afraid of maybe having to rewrite a small amount of code? How does that make sense, and why won't the large amount go through the same iterative adjustment and subtle bugs until it needs rewriting?

Because it is not clear what exactly the code is doing

A ten character English which name vaguely describes what a function hidden in another file might have been doing (assuming you understand the word in the same way the author did when they chose it) at the time it was written - but it might not be doing the same thing now since it was edited so many times, is not exact. By contrast, ten dense characters right there under your nose is exact - as much as a programming language can be. There's nothing hidden, no surprises, no unseen side effects, no uncertainty about whether it handles edge cases.

Code is as exact as it gets - it does what the language does. Abstractions are less exact - they do some version of what the previous chain of developers intended the words to imply that they do, in the best case. In worse cases they also do unrelated and surprising things.

And you dodged every question about when slow but correct code won out over faster, more buggy code.

Re: Clear is better than clever [pdf]

#182

It's interesting that the comparator function is one of the examples, because that's something which shows how people often confuse verbose with clear ; I think it's best written in a single line: return a b ? 1 : 0; That's one line, compared with the 10(!) of his proposed method using a switch statement. Having worked with some "modern" codebases, I think verbosity is a bigger problem that's become especially preval…

Presenting '{' on newlines feels like a bit of a strawman to me, I'd expect the more verbose styles to still look something like

    if (a == b) {
        return 0;
    } else {
        return a 

Re: Clear is better than clever [pdf]

#183
post #147

Earlier quoted context omitted.

If you even need to indent them then why you don't write a full if else statement instead? Nested ternaries shouldn't be used at all.

For languages in which if/else is unfortunately only imperative, not an expression

IMO if if/else can be used as an expression, your language shouldn't have a ternary operator at all

Re: Clear is better than clever [pdf]

#184

It's interesting that the comparator function is one of the examples, because that's something which shows how people often confuse verbose with clear ; I think it's best written in a single line: return a b ? 1 : 0; That's one line, compared with the 10(!) of his proposed method using a switch statement. Having worked with some "modern" codebases, I think verbosity is a bigger problem that's become especially preval…

Compilers are very good at doing the ternaries for you. With good ol' Javascript you get things like the Google Closure Compiler that will return yet more Javascript rather than some bytecode, this informs me that my use of ternaries is a waste of time. I might as well go for the if/then/else type of approach and refactor it when it gets ugly into some functions.

My excuse for taking this approach in all languages is accessibility, with accessibility applying to people who can't really code. Whatever the language you can hack a few if statements even if you don't know the language. There are people who are learning and you want to make it 'accessible' for them.

Let the compiler do the work and stay off the bleeding edge features. That includes spaceship operators in PHP. I also think it is worth keeping code a few versions old, so in the case of the spaceship operator, I can do if/then/else things for the compiler to do the work and have my code work on someone else's box that is still on PHP 5.4 because the main software they use doesn't work on PHP 7.3.

Or in Javascript, it is obviously best practice to use the fancy new things but if your web page dies in the console on some Apple iPad running an old version of Safari then you can end up refactoring those best practice iterators into for loops. Invariably these refactoring exercises are hastily done, so you can come a cropper.

Another thing is that if I had to explain my code to someone then the nearer to natural language the better. The example ternary would require me to stop and think for a moment, if it was in boring if statements I could explain it to a not-so-technical person in terms they could understand, even if they could not write it themselves.

Re: Clear is better than clever [pdf]

#185

> To be clear, I don’t mean to dismiss the work of a lone programmer toiling on programs without anyone to pair with or learn from. I’ve been that person many times in my career as a programmer, it’s not fun. I'd say it's a lot of fun.

It completely depends on the quality of your collaborators. Are they good developers, do they have somewhat compatible views, and are they reasonably friendly? I prefer working in a highly capable group.

If you have collaborators you are not in that situation.

Re: Clear is better than clever [pdf]

#186
When you are talking about making code "clear," I think it's important to ask "clear about what?"

Is it:

* Clear about what the machine is doing?

* Clear about what the possible error cases are?

* Clear about the purpose of the code?

Go often prioritizes making it clear what the machine is doing over making the purpose of the code clear. In some languages you might write `newList = map(myFunc, myList)`. This is clear about what I intend, but not how it is accomplished[0]. The equivalent for-range loop in Go is clear about what it does at the expense of diluting the intent.

[0] for example, that map function could apply to the first element first, or the last element first, or it could apply to all the elements in parallel.

Re: Clear is better than clever [pdf]

#187
post #139

Earlier quoted context omitted.

I disagree, what is "saved" is your time when reading/understanding... why spend time to read 10 lines and figure out that it actually does what a simple ternary operation would accomplish?? On that same vein, if a ternary operation is going to throw someone for a loop, I've got some bad news about their career in programming.

> why spend time to read 10 lines and figure out that it actually does what a simple ternary operation would accomplish?? Because when something is expressed in 10 lines explicitly, generally you can simply skim over the code to get an idea of what's going on without having to go into the details. "Smart and clever" code, like using nested ternaries, demands you to focus and try to figure out what is going on.

> Because when something is expressed in 10 lines explicitly, generally you can simply skim over the code to get an idea of what's going on without having to go into the details.

Which is why important details get missed when reviewing verbose code.

Re: Clear is better than clever [pdf]

#188
post #39

Earlier quoted context omitted.

Fully agree! Combinations of if/else can seriously damage readability and maintainability. I (all the time) face verbose code that pretend to be readable and that requires lots of attention just to find out you are setting a single variable across 10-20 lines. Example: if (cond1) { myVar = 1; } else if (cond2) { if (cond3) { myVar = 10; } else { myVar = 100; } ... } else { myVar = defaultValue; } * huge number of lin…

Or break them out into a function, then you can "short-circuit" with a return: int logic() { if (cond1) { return 1; } if (cond2) { if (cond3) { return 10; } return 100; } // ... return defaultValue; } Since it's a function, it's also apparent that you are only touching one variable: myVar = logic(); I love the ternary operator, but I find it's harder to step it in a debugger though.

Short circuiting is poor for readability in my view because not only do you have to parse the syntax but you need to mentally walk through each case to understand what’s going on.

Traditional if/else conditionals express clarity by embedding the decision for myVar and visually show how it would be assigned in various cases.

As @hotBacteria said:

  if(cond1){
   myVar = 1;
  } else if(cond2 && cond3){
   myVar = 10;
  } else if(cond2 && !cond3){
   myVar = 100;
  } else {
   myVar = 4;
  }

  // Now return myVar
  return myVar;
I’ll take this syntax over short circuiting any day.

Re: Clear is better than clever [pdf]

#189
post #39

Earlier quoted context omitted.

Fully agree! Combinations of if/else can seriously damage readability and maintainability. I (all the time) face verbose code that pretend to be readable and that requires lots of attention just to find out you are setting a single variable across 10-20 lines. Example: if (cond1) { myVar = 1; } else if (cond2) { if (cond3) { myVar = 10; } else { myVar = 100; } ... } else { myVar = defaultValue; } * huge number of lin…

You can write it without resorting to one-line conditionals: if(cond1){ myVar = 1; } else if(cond2 && cond3){ myVar = 10; } else if(cond2 && !cond3){ myVar = 100; } else { myVar = 4; } But when I encounter this kind of situations I have other problems than code formatting anyway: with 3 conditions you have 2^3 possibilities to check: are you really really sure (!cond1 && !cond2 && cond3) should give you defaultValue…

That makes code formatting inconsistent because many prefer the other way. The VB.Net style is more compact and readable in my opinion:

        if cond1 then
            myVar = 1
        else if cond2 and cond3 then
            myVar = 10
        else if cond2 and not cond3 then
            myVar = 100
        else
            myVar = 4
        end if
The VB.Net style also makes it easier to identify mismatched blocks because the "enders" are named differently. (One can also put the assignment on the same line as the conditional, but I generally don't recommend it.)

Some may complain it's slightly verbose, but few if any will claim it's not clear.

Re: Clear is better than clever [pdf]

#190
post #163

I'm fairly sure that we as a community can come up with an objective measure for the phrase "clever code". Something like: Exploits implicit and/or hidden details to correctly function. However, I've never seen a convincing and objective description of clear code. Sometimes people mean that clear code is verbose code. Sometimes people mean that clear code is uniformly formatted code. [For best results we should manua…

All IMO:

> However, I've never seen a convincing and objective description of clear code.

I take "clever code" to mean "code that looks pretty on surface the but requires the reader to dig in in order to understand the intent".

The problem with clever code is that it is misleading. It lacks empathy. You assume the next programmer will understand it at first glance just because it looks cool or pretty, but they'll struggle to parse it.

---

> Sometimes people mean that clear code is verbose code.

I also consider some verbose code to be "clever code" too. Most of the time it is just people using hammers where screwdrivers would be more appropriate:

- Unnecessary structures - eg: classes that could be functions taking 2x or 3x more space

- Unnecessary usage of polymorphism - eg: inheritance chain that could be a simple if

- Excessive indirection - eg: layered code where most of the time the layers don't do anything

- Plain wrong abstractions - eg: using query builders to build queries that would be smaller and more readable in SQL

- Fear of making classes too big - eg: instead of adding a method to a class, making a second class that knows too much about the innards of the first one

- Procedural code disguised as OOP - eg: breaking up a method into multiple private ones, but ending up having a lot of instance variables that were local variables before. When everything could be a single function.

---

> The best we have is cyclomatic complexity, but there's some reason to believe that line count may be a better indicator (which can't be good). And cyclomatic complexity completely misses the effect of mutable or immutable state, the presence of bad APIs, poor variable naming, etc.

Great observations. I agree 100%.

Since you mentioned it, I find cyclomatic complexity a bit too easy to game. I always wanted to have a metric that prevented people doing that, and took multiple methods into account.

When you break a method in three or four without adding REAL abstractions (a.k.a. "things you don't have to follow with the debugger to understand"), you're not making it easier to read, in fact you're making it harder because the reader has to jump around your code.

Of course, it's harder for machines to know the difference between good and bad abstractions. But I think we should take that into account in code reviews and such.

Post reply on HN