5 ms·
"Easier to write, easier to read" is the part that's wrong with that advice. It absolutely is, on toy problems like the one described in the article. It very
by kyralis 4y ago
"Easier to write, easier to read" is the part that's wrong with that advice.
It absolutely is, on toy problems like the one described in the article.
It very frequently is not when embedded in much larger domains as part of large projects maintained over years by teams.
- jrumbut 4y agoAnd when you combine inadequate abstractions with programmers who aren't the kind of geniuses brought in to optimize game engines you get very difficult to fix performance problems. One of the nice things about some of the clean code concepts he uses is that (as he shows) you can tactically step back from them in key, performance critical areas and reap these wins. If you stay too low level you get lots of tangled spaghetti code with major performance problems and no obvious way forward besides "make it better."
- dsego 4y agoIt could in fact sometimes be easier to refactor low level code, than to dig yourself out of bad leaky abstractions. https://caseymuratori.com/blog_0015 https://caseymuratori.com/blog_0015
- whstl 4y agoI find it a bit disingenuous to call what Casey Muratori is doing "staying in the low level". Using procedures/functions is not exactly "low level". Using switch is not low level. Lookup tables are something you have to do in high level code all the time. Sure he could have used much better variable naming (CTable?) and probably documentation, but code-wise there's nothing that screams low level there.
- jrumbut 4y agoI'm not sure how it's disingenuous, I sincerely believe what I said and I'm not trying to fool anyone. I would consider most of his replacements lower level than typical the clean code practices he critiques (especially the ones like iterators that he mentions but avoids in order to steel man the clean code side a little bit), not the lowest level possible. They take into account how the machine actually works and avoid additional indirection which is why they perform better.
- ablob 4y agoHis code does translate relatively straightforward into haskell. Do you think haskell is a low-level language, too? Take Listing 27, getAreaUnion for example: f32 const CTable[Shape_Count] = {1.0f, 1.0f, 0.5f, Pi32}; f32 GetAreaUnion(shape_union Shape) { f32 Result = CTable[Shape.Type]*Shape.Width*Shape.Height; return Result; } Is represented quite straightforwardly: {-# LANGUAGE OverloadedRecordDot #-} data Shape = Square { width :: Float, height :: Float } | Rectangle { width :: Float, height :: Float } | Triangle { width :: Float, height :: Float } | Circle { width :: Float, height :: Float } cTable :: Shape -> Float cTable shape = case shape of -- The "lookup table" or "array" Square {} -> 1 Rectangle {} -> 1 Triangle {} -> 0.5 Circle {} -> pi getAreaUnion :: Shape -> Float getAreaUnion shape = cTable(shape) * shape.width * shape.height Although it is typically easier to abstract in a "high level" language, abstraction does not require it. This whole debate is rooted on false assumptions and the need to take a side, imo. Casey has a point, it is just ignored in a typical hand-wavery fashion. "The toy example doesn't scale" is a poor argument, especially when what we can observe is slow software. The stuff proposed in the post is not rocket science, it is a very straightforward implementation of tagged unions. Instead of fetching a vtable and jumping to a value there, he proposes to branch on the tag. This is essentially dynamic dispatch on a known set of types. Additionally, he shows that this can result in speedups greater than a factor of 1. Any program that wants low latency or high throughput can profit from this observation. This way of programming is by no means the one to rule them all. It has different advantages and drawbacks; none of which have anything to do with the percieved intelligence of the programmer or later consumers, for that matter. An objective disadvantage of this style is, that the program can't interface with code, that hasn't been written yet, as a caller. Another disadvantage is that the size of the tagged union is defined by its largest "subclass". In the end, what he has shown is that speed is often a compromise made unnecessarily. This doesn't really have to do with clean code anymore, as I can see how a compiler could implement what he is angry about with virtual functions in every situation where his style is applicable. Casey has had a similar thing about the windows-terminal and somewhere in his videos a different, yet arguably worse, problem comes to mind: a lot of libraries do not care about performance enough. If you write a program and care, you may run into the problem that the library you use is your bottleneck. If this library is hard to replace (imagine needing a rocket-scientist), then you are done for. In that specific case it was DirectWrite and some other Windows-API that were slow. So if, for one reason or another, the windows team was required to use both, they'd have a hard limit on how fast they could go, just due to that. There is no "being smart" or "requiring a genious" involved in the forced/strongly recommended library here.
- whstl 4y agoAs a counterpoint: "Clean Code", at least the variant from the book, is very frequently also extremely difficult to write or to read in larger codebases too. The claim that "Clean Code" scales better or allows for more maintainable software hasn't been proven by anyone, and everyone with enough experience has worked with several counter examples. The problem of code maintainability is not solved by this coding philosophy.
- singron 4y agoSometimes people forget that "Clean Code" is a book, and it's not exactly stellar. This is a pretty thorough tear-down: https://qntm.org/clean https://qntm.org/clean I'm totally onboard with prioritizing readability over performance for most code, but the style in the book has a lot more tradeoffs than it discloses, and you often don't really appreciate that until you are trying to debug if/how A transitively calls Z in a 10 million line codebase. It's really hard to have a constructive conversation about this though since it's so subjective, any example is too trivial, and any real system is too large.
- deleted 4y ago[deleted]
- DeathArrow 4y agoDo you have any example of an open source project written in the simple style suggested by Casey Muratory that is very difficult to be maintained and you think would benefit from using "clean code", "SOLID", design patterns and abstraction on top of abstractions? If you can't point to an actual example, I don't think you have a solid case.
- LorenPechtel 4y agoThis. Clean code isn't about writing stuff like this. For what he's talking about the overhead of polymorphism is a major part of the total cost and his case is simple enough that there's little value. However, the bigger your task gets the more value there is to polymorphism and in general the smaller the percent of total time goes to the polymorphism overhead. And note that his attack is only on polymorphism, not the other aspects of clean code. I strongly suspect the compiler optimizes away much of the clean stuff I do but I have never checked. I also find profiling easier on cleaner code, it makes it very obvious where the time sink must be and thus what warrants expending effort to improve. Profiling almost always shows the vast majority of time going into the unavoidable (say, disk reads) and a small number of other routines. Spend your optimization effort on the spots that need it because 99+% of your code doesn't run often enough for it to matter.