Live data from Hacker News

“Clean” code, horrible performance

computerenhance.com

101–110 of 932 posts

Re: “Clean” code, horrible performance

#101

Most of those Clean code rules are BS. 1. Prefer polymorphism to “if/else” and “switch” - if anything, that makes code less readable, as it hides the dispatch targets. Switch/if is much more direct and explicit. And traditional OOP polymorphism like in C++ or Java makes the code extensible in one particular dimension (types) at the expense of making it non-extensible in another dimension (operations), so there is no…

In his small example he already added unforeseen couplings that could get out of hands if it was a big codebase. If you follow the principle "switch statements over [X]", try to add a new shape down the line and see how quickly you run into problems. In the clean code version, your compiler will remind you to implement calculateArea, calculateNumberOfVertices, calculateWhatever, and so on and so forth. With his versi…

> With his version you have to add a new `case` to every switch statement and hope you didn't miss one with a default case, because the compiler won't catch it.

The compiler not catching it is a limitation of the language he uses, not the limitation of the general concept of switch / pattern matching. Scala, Haskell, Rust do catch those.

> If you follow the principle "switch statements over [X]", try to add a new shape down the line and see how quickly you run into problems.

And who said you'd ever need to add a new shape? Maybe you will need to add a new operation? Try to add a new operation `calculateWhatever` and see how many places of the code you need to chnage instead of just adding one new function with a switch.

Often you really don't know in which direction the code will evolve. Most often you can't guess the coming change, so don't make the code more complex now in order to make it simpler in the future (which may never come).

Re: “Clean” code, horrible performance

#102

This is the first video I’ve seen by him. I’m by no means a fan of clean code. But I think he’s making a fool of himself here. Picking out 1 code example from the book doesn’t proof that much on its own. This stuff is so language, os, hardware and compiler specific anyway. The iPhone comparisons are extremely cringe. Real application do so much more then this contrived example. Something that feels fast isn’t the sam…

[dead]

Re: “Clean” code, horrible performance

#105

Earlier quoted context omitted.

In his small example he already added unforeseen couplings that could get out of hands if it was a big codebase. If you follow the principle "switch statements over [X]", try to add a new shape down the line and see how quickly you run into problems. In the clean code version, your compiler will remind you to implement calculateArea, calculateNumberOfVertices, calculateWhatever, and so on and so forth. With his versi…

> With his version you have to add a new `case` to every switch statement and hope you didn't miss one with a default case, because the compiler won't catch it. The compiler not catching it is a limitation of the language he uses, not the limitation of the general concept of switch / pattern matching. Scala, Haskell, Rust do catch those. > If you follow the principle "switch statements over [X]", try to add a new sha…

>The compiler not catching it is a limitation of the language he uses, not the limitation of the general concept of switch / pattern matching. Scala, Haskell, Rust do catch those.

You're thinking of defaultless switch statements. Rust and Typescript catch those issues as long as you don't add a default. I'd already thought of it when I typed it

>And who said you'd ever need to add a new shape?

ô_o

>Maybe you will need to add a new operation? Try to add a new operation `calculateWhatever` and see how many places of the code you need to chnage instead of just adding one new function with a switch.

You do realize that your "one function with a switch" does have to cover every case exactly like the clean code version, it's not magic you're just fitting it all into one switch/case, and you'll probably end up extracting those case: blocks into separate functions anyway.

And on top of that you're not safe from a colleague adding a useless "default" case at the end ouf ot paranoia and making your compiler not catch a future problem when a new shape gets added.

Re: “Clean” code, horrible performance

#106

I take issue with the idea that maintainable code is about "making programmers' lives easier", rather than "making code that is correct and is easy to keep correct as it evolves". Correctness matters for the user - indeed, sometimes it is a matter of life and death.

> I take issue with the idea that maintainable code is about "making programmers' lives easier"

He's talking about "clean code", not maintainable code. The claim that "clean code" is more maintainable is an unproven assertion. Whenever I interact with a "clean code" codebase, it is worse in every way compared to the corresponding "non-clean" version, including in terms of correctness.

Re: “Clean” code, horrible performance

#107
post #88

The example of using shape area seems like a poor choice. First off, the number of problems where having an analytical measure of shape area is important is pretty small by itself. Second, if you do need to calculate area of arbitrary shapes, then limiting yourself to formulas of the type `width * height * constant` is just not going to cut it. And this is where the entire optimization exercise eventually leads: to b…

To summarize: if you make the problem complex enough, then the specific performance methods used in the article don’t work. But hey, why not take your complex example? If you apply a polymorphic approach to Bézier curves and polygons, then you still get a 1.5x slowdown compared to a switch statement. If you have any commonality between your implementations of area for them, it’s harder to find, which could be worth 2x or more. If there is a common algorithm for calculating all polygons and curves, wasting work for simple shapes, but vastly improving cache and predictor performance, then you could be leaving another 5x on the table. Orienting the code around operations instead of types is still a win for performance with similar cognitive load compared to type hierarchies.

You’re right that once you’ve done the 15x performance gain that Casey demonstrates, the code is pretty brittle and prone to maintenance problems if the requirements change a lot. But I think we can have our cake and eat it too by maintaining our code with the simple path available to fall back to if we get a complicated new requirement. Need to add in complex cases that weren’t thought of before? Add them to new switch cases that are slower, and then keep looking for more performant ways of calculating things of need be.

Re: “Clean” code, horrible performance

#108
post #64

Earlier quoted context omitted.

If you optimize for readability performance would suffer. If you optimize for performance readability will suffer. Casey prizes performance over everything else.

In this particular case I find the code optimized for speed (the one using switch) to be also more readable and simpler than the code using virtual dispatch. The problem with virtual calls in a big project is that there is no good way of knowing what is the target of the call, without some additional tooling like IDE. But in case of a switch/if, it is pretty obvious what the cases are.

Sure, but what happens, once you want to start supporting other shapes other than basics? Because clean code assumes code will be changed/maintained.

Then you get people writing their own horrible hacks.

Both clean code and performance oriented design have their extremes.

Clean Code has Spring with Proxy/Method/Factory monster... and hyper performance has the extreme in the story of Mel (i.e. read-and-weep only code).

Re: “Clean” code, horrible performance

#109
Malicious compliance for C++ programmers. This is the person who thinks they're clever for breaking stuff because "You didn't say not to". Managing them out of your team is likely to be the biggest productivity boost you can achieve.

In the process of "improving" the performance of their arbitrary benchmark they make the system into an unmaintainable mess. They can persuade themselves it's still fine because this is only a toy example, but notice how e.g. squares grow a distinct height and width early in this work which could get out of sync even though that's not what a "square" is? What's that for? It made it easier to write their messy "more performant" code.

But they're not done, when they "imagine" that somehow the program now needs to add exactly a feature which they can implement easily with their spaghetti, they present it as "giving the benefit of the doubt" to call two virtual functions via multiple indirection but in fact they've made performance substantially worse compared to the single case that clean code would actually insist on here.

There are two options here, one is this person hasn't the faintest idea what they're doing, don't let them anywhere near anything performance sensitive, or - perhaps worse - they know exactly what they're doing and they intentionally made this worse, in which case that advice should be even stronger.

Since we're talking about clean code here, a more useful example would be what happens if I add two more shapes, let's say "Lozenge w/ adjustable curve radius" and "Hollow Box" ? Alas, the tables are now completely useless, so the "performant" code needs to be substantially rewritten, but the original Clean style suits such a change just fine, demonstrating why this style exists.

Most of us work in an environment where surprising - even astonishing - customer requirements are often discovered during development and maintenance. All those "Myths programmers believe about..." lists are going to hit you sooner or later. As a result it's very difficult to design software in a way that can accommodate new information rather than needing a rewrite, and yet since developing software is so expensive that's a necessary goal. Clean coding reduces the chance that when you say "Customer said this is exactly what they want, except they need a Lozenge" the engineers start weeping because they've never imagined the shape might be a lozenge and so they hard coded this "it's just a table" philosophy and now much of the software must be rewritten.

Ultimately, rather than "Write code in this style I like, I promise it will go fast" which is what you see here, and from numerous other practitioners in this space, focus more on data structures and algorithms. You can throw away a lot more than a factor of twenty performance from having code that ends up N^3 when it only needed to be N log N or that ends up cache thrashing when it needn't.

One good thing in this video: They do at least measure. Measure three times, mark twice, cut only once. The engineering effort to actually make the cut is considerable, don't waste that effort by guessing what needs changing, measure.

Re: “Clean” code, horrible performance

#110
post #26
post #11

The problem with the contemporary "clean code" concept is that the narrative that performance and efficiency don't matter has been pushed down the throat of all programmers. Re-usability, OOP concepts or pure functional style, design patterns, TDD or XP methodologies are the only things that matter... And if you use them you will write "clean code". Even worse, the more concepts and abstractions you apply to your cod…

If you have more than one person working on a codebase; clean code matters a lot. A code base that can't be understood and maintained by the whole team, will degrade quickly.

> If you have more than one person working on a codebase; clean code matters a lot.

You can have fast and clean code, just not the Uncle Bob style of "clean code". Uncle Bob hijacked the meaning of cleanliness. It doesn't mean that code written like that is actually clean, in fact it's usually the opposite: Uncle Bob's clean code is NOT clean.

Post reply on HN