“Clean” Code, Horrible Performance
"Clean" Code, Horrible Performance (2023)
In this video from the Performance-Aware Programming series, Casey Muratori demonstrates how following “clean code” guidelines—like preferring polymorphism and hiding internals—can lead to significant performance costs. Using the example code from clean code literature, he shows that a polymorphic shape hierarchy runs at ~35 cycles per shape, while a simple switch-based approach runs at ~24 cycles, a 1.5x speedup. He argues that violating these rules can yield even greater gains, and that the clean code emphasis on abstraction often obscures optimization opportunities.
So by violating the first rule of clean code — which is one of its central tenants — we are able to drop from 35 cycles per shape to 24 cycles per shape, implying that code following that rule is 1.5x slower than code that doesn’t.
- Aurornis
I consider Clean Code to be in the category of books/styles that is helpful for early developers who need some structure, but harmful to late-stage developers who adopt it as dogma.
On a long enough career path, eventually you will run into one Clean Code zealot who carries an air of superiority and nit picks every PR over things like a function having more than an arbitrary number of lines in it instead of reviewing the actual code. This is the point where most people come to hate Clean Code.
- taybin
Yes, a toy problem only needs a simple implementation. This is a straw man. And I don't even like Robert Martin's Clean Code, but the author is not addressing where this style actually provides benefits. When you're updating 23 if-statements because you had to add support for some new business workflow, you'll wish you had a conceptual entity that encapsulated the operations on the type of workflows so you just had to implement them in one place.
- jayd16
Ok now add a Path shape that has to calculate the area of a polygon with arbitrary complexity.
Consider how the workload is now dominated by the core task of actually calculating the area, reducing the impact of struct usage.
Consider the diffs required to make this change.
It's not like Clean Code should be taken as gospel but this micro-benchmark is not a realistic example of what CC is trying to solve.
- aw1621107
Related:
HN post for original article on 2023-02-28 (https://news.ycombinator.com/item?id=34966137), 739 points, 914 comments
Discussion between Casey (author of this article) and Uncle Bob (author of _Clean Code_, whose programming patterns Casey is critiquing), posted on HN on 2023-03-11 (https://news.ycombinator.com/item?id=35105528), 223 points, 213 comments
"Horrible Code, Clean Performance", a "homage" to Casey's original article, posted on HN on 2023-04-19 (https://news.ycombinator.com/item?id=35596069), 121 points, 114 comments
- jeffnash
It seems like the main takeaway is that many textbook OO paradigms aren't the most optimized representations of the code. In this case, the cost is dynamic dispatch and pointer-chasing. This is a function of the Shape abstraction, but not the abstraction itself.
But the argument is you're trading some of that performance optimization for maintainability. None of this is exactly news. And while I'm here ranting: I never understood why shapes are the canonical OOP example. Shapes are a closed set of types (yes I'm sure GPT-324 invented a new one) with an open set of operations. There's always going to be one more thing you need to do with those shapes, but you'll never be adding new shapes down the road unless you are still in Kindergarten. OOP is useful for the exact opposite case, where there is a relatively fixed set of operations and you routinely introduce a new subtype that needs to perform all or most of those operations.
I've noticed that most courses that introduce the concept of OOP do so in a way that (perhaps unintentionally) emphasizes the false notion that everything should have an 'x-is-a-y' taxonomy before actually asking the question if that is appropriate. Putting the Cart extends Vehicle before the Horse extends Animal.
- bellgrove
I think the author comes from a very specific perspective; RAD tools, as I understand it, generally has only one, or a very few, software engineers per product. The way I would write code on a personal project is very different than the way I’d write code in an environment with changing team members, interns, guest commits, etc.
Also, In real-time simulations (ie games) often then way you write code can be the bottleneck. In web services the bottlenecks are more often network calls, database model, etc.
- scelerat
How much of the performance differences come down to language or compiler choice in these examples?
Would I see the same kinds of performance gains or losses avoiding or using certain patterns in Go or Rust or Java? Are they the same examples as in C++?
What about dynamic languages like ruby or python or javascript?
- flossly
I'd say Clean Code is teaching many bad-practices. Too many to be recommended.