6 ms·
This guy is so dogmatic about it it hurts. I would argue that clean code is a spectrum from how flexible vs how rigid you want your abstractions to be. If your
by vore 4y ago
This guy is so dogmatic about it it hurts. I would argue that clean code is a spectrum from how flexible vs how rigid you want your abstractions to be. If your abstractions are too flexible for good performance, dial them back when you see the issue. If your abstractions are too rigid for your software to be extendable, then introduce indirection.
We can all write code that glues a very fixed set of things end to end and squeeze every last CPU cycle of performance out of it, but as we all know, software requirements change, and things like polymorphism allow for much better composition of functionality.
- anonymoushn 4y agoIs there any flexbility tradeoff at all here?
- vore 4y agoThe shapes example is pretty contrived so I don't really have an opinion on it either way. But imagine you have something like a File interface and you have implementations of it e.g. DiskFile, NetworkFile, etc., and you anticipate other implementors. Why would you do anything other than have a polymorphic interface?
- anonymoushn 4y agoI don't know, I've never done anything where code needed to have runtime dispatch on the kind of file it has without also knowing anything about what kind of file it has.
- vore 4y agoHave you never used C++ iostreams? Or the Python file abstraction? Or Rust std::io::Read/std::io::Write? Or Node.js streams? Or DOM Web Streams? Or Ruby files? Or Haskell conduits? Or hell, fopencookie/funopen in C? The abstraction is super common and allows you to connect streams to each other without worrying about the underlying mechanism, which 99% of the time I don't really think you want to worry about unless you're sure it's a performance overhead. And that's great, because I surely don't want to write specializations by hand for all the different combinations of streams I need to use if I don't have to.
- anonymoushn 4y agoI've used open(2) and the io_uring openat :) I use generic readers and writers every day, but they don't have any runtime dispatch. The question was "Why would you do anything except vtables and runtime dispatch?" One answer is that my code that uses generic readers and writers and gets monomorphized gets to also be generic over async-ness.
- saidinesh5 4y agoI think the shapes example is more of a dig at various game engines where you end up with a long trees of inheritance (physicsbody -> usercontrollable physics body -> renderable user controllable physics body etc..) as opposed to the recent trend of using something like an Entity Component System. Also I think he isn't specifically against "clean code" but how the first tool used by various "clean code advocates" seems to be polymorphism via inheritance. I have seen this enough in lots of Java codebases and "Enterprise C++ codebases". "We need X." "Oh first I will create an Abstract Base class for X, then create X, so we can reuse X nicely elsewhere when we need it". It is still on the developers to understand that they may not even need it but for them it is "clean code".
- adamrezich 4y agoI wish he would've gone a bit more into why people reach for polymorphism instead of a struct with a variant enum: because it's how OOP is taught, and how it easily maps to human understanding of most problem spaces. like you're making an RPG, so you make an Item class, and then you make Armor and Weapon subclasses, and then you make Helmet/Chestplate/Leggings/Shoes and Sword/Polearm/Axe/Mace/Staff subclasses, etc. etc., just because it fits your mental model of the problem, when in actuality, you could've totally avoided all of that complexity, and the ensuing boilerplate, just by sticking it all in one struct and calling it a day. this is not always the solution, but more often than not it is.
- zelos 4y agoThat does depend on how the abstraction is defined, of course. I once worked on optimising a 2D Canvas C++ class which had a nice top level virtual interface so you could replace it with a different implementation. It was also crazily slow because it defined: virtual void setPixel(int x, int y, int color) = 0; and then implemented flood fill etc in terms of that.
- int_19h 4y agoThis isn't necessarily bad if all the other operations are also virtual. The idea is that you can quickly get something working (e.g. when porting) just by implementing setPixel(), and then gradually fill in other primitives with properly hardware-optimized versions. And you do need a virtual setPixel() in any case because some API client might need that one call.
- Arelius 4y agoI'm sorry, why do you need a virtual setPixel? You can still easily get something working just by implementing setPixel without virtual dispatch, the linker has no problem inlining that call at compile time. If some arbitrary API needs it to be virtual it's easy to implement the virtual call in just that specific case, instead of burdening your entire system with a virtual call that'll always be static in practice.
- int_19h 4y agoFor the same reason why you need a virtual drawLine etc. Because some code wants to render something, and doesn't want to care if it's rendering onto an on-screen surface, into a file etc. Your entire system will not be burdened with virtual calls in places where you use concrete implementations (so long as they're final/sealed, anyway). The overhead is only there if you try to use the abstraction generically, but why would you do that in the case where the virtual call "will always be static in practice"?
- mgaunard 4y agoAnything that yields data in chunks will not really be affected by runtime polymorphism much. It's only a problem when it's done per datum.
- alfalfasprout 4y agoRight, but the point is you can avoid polymorphism within a given implementation but still keep it at a higher level. Heck, even codebases used in trading can use polymorphism for very high level interfaces (or async tasks) but hot code paths don't use it.
- athanagor2 4y agoBut is it true that "clean code" makes the adaptation to changing requirements easier? I saw a few testimonies saying otherwise.
- vore 4y agoI think it really truly depends. I think it's always good to do the minimal viable thing first instead of being an architecture astronaut, but if you've been asked for three (random ballpark number) different implementations for the same requirement it might be time to start adding some indirection.
- mattgreenrocks 4y agoThe best idea in clean code is to stop coupling domain models to implementation details like databases/the web/etc. Once you grok that, then you're in a better position to work on eliminating unnecessary coupling within the model itself. There's lots of ways to do this poorly and well. There's no process for it. That's a feature. I feel like a lot of the flak clean code gets boils down to, "I followed it dogmatically and look what it made me do!" It didn't make you do anything; it's trying to teach you aesthetics, not a process. Internalize the aesthetics and you won't need a rigid process. Obviously when you do this you probably need more code than you'd normally write. That can be viewed as a maintenance burden in some situations, esp. when you don't have product market fit. Again, this shows that treating clean code like some process that always produces better code in every situation is extremely naive.
- deleted 4y ago[deleted]
- TheFattestNinja 4y agoCasey is a bit of a hardcore crusader on the topic, but I'd hardly call dogmatic someone who can provide you evidence and measurements backing their thesis. The tests he put together here are hardly something I'd call a straw-man argument, they seem like reasonable simplification of real-cases.
- st3fan 4y agoEvidence in a micro benchmark of a single page of code. The focus on performance here ignores the fact that most programs are large systems of many things that interact with each other. That is where good design and abstractions and “clean code” can really help. Like all things it is about finding a balance and applying the right techniques to the right parts of a larger system.
- TheFattestNinja 4y agoTrue, the example is simplistic, but that's just to make it fit within reasonable exposition. The author has in the past shown (elsewhere) how his techniques can actually make dramatic differences in more concrete examples (he rose to some Internet fame for building a performant shell that could actually handle larger outputs orders of magnitude better than most available alternatives). To me the interesting point is the reminder that there is an innate tension between going fast and being "clean" (i.e. maintanable/understandable). And once you are aware of it you can make your decisions in an informed way. Too often this tension is forgotten/ignored/dogmatically put to the back ("performance doesn't matter over cleanliness" and the likes). Mind you, I'm also of the camp that performance is very secondary to cleanliness in modern enterprises, but I appreciate a reminder of just how much we are sacrificing on this altar.
- cma 4y agoIt was Windows Terminal he wrote something faster than. Windows terminal was doing a whole GPU draw call per character (at 2x standard terminal height, 160x25, it was as many draw calls as a AAA game).
- jasmer 4y agoI don't think rich abstracions necessarily contribute to 'clean code' in fact often the opposite. Clean code means easy to read, maintain, not full of arbitrary things 'because performance'.
- hinkley 4y agoSpectrum or I believe more of a Venn Diagram. There was a moment in grade school where I was sat down and it was explained to me that you don't have to take a test in order. You can skip around if you want to, and I ran so far with that notion that at 25 I probably should have written a book on how to take tests, while I could still remember most of it. One of the few other "lightning bolt out of the blue" experiences I can recall was realizing that some code constructs are easier for both the human and the compiler to understand. You can by sympathetic to both instead of compromising. They both have a fundamental problem around how many concepts they can juggle at the exact same time. For any given commit message or PR you can adjust your reasoning for whichever stick the reviewer has up their butt about justifying code changes.
- Kukumber 4y ago0 responsibility developers you have Japan infrastructure, and you have Turkey infrastructure 6.1 quake in Japan = nothing destroyed 6.1 quake in Turkey = everything collapses The engineers in Turkey probably didn't value performance and efficiency It's the same for developers, you choose your camp wisely, otherwise people will complain at you if they can no longer bear your choice You act like innocent, but your code choice translate to a cost (higher server bill for your company, higher energy bill for your customers/users, time wasted for everyone, depleting rare materials at a faster rate, growing tech junk) Selfishness is high in the software industry We are lucky it's not the same for the HW industry, but it's getting hard for them to hide your incompetence, as more things now run on a battery, and the battery tech is kinda struggling Good thing is they get to sell more HW since the CPU is "becoming slower" lol So we now got smartwatches that one need to recharge every damn day
- ilyt 4y ago> The engineers in Turkey probably didn't value performance and efficiency Uh, no. It was corruption. They were standards, that worked, but people didn't do it, plain and simple.
- TeMPOraL 4y agoYeah, it's a bad and unnecessarily inflammatory (and arguably disrespectful) example. But the rest of the points GP makes are spot on. A "blame systems over individuals" version would be that the industry is externalizing bad performance onto users, damaging environment, causing frustration, wasting lives, and occasionally even actually killing people (shitty ER/hospital software comes to mind) - because there's no good feedback mechanism to force software companies to internalize those costs.
- Kukumber 4y ago> Yeah, it's a bad and unnecessarily inflammatory (and arguably disrespectful) example. But the rest of the points GP makes are spot on. I agree, i apologies for the mistake, i can no longer edit the post unfortunately
- overgard 4y agoI think part of the problem with supposed "clean" code is that it tends to be a matter of opinion. Is the polymorphic version cleaner than the switch statement version? I would argue the latter is actually easier to read. There's no real reason to think "clean" code is actually clean other than anecdotes and that someone wrote it in a book, but the performance is something that can be objectively measured.
- whstl 4y agoThe "Clean Code" that Casey is talking about is a book and a code philosophy that was explained in depth in talks and trainings and seminars, so I would disagree that it is a matter of opinion.
- alfalfasprout 4y agoYes... but I'm also seeing dogmatism from the opposing camps here in the comments section. The reality is that how flexible your interfaces and abstractions are and their design has to be a part of your original design considerations when building something. It's a bad move to just hand wave away performance concerns because you religiously adhere to some design patterns. It's also a bad move to drop down to using intrinsics for everything from the get-go and thinking you know better than the compiler when it's a codepath that isn't even computationally expensive or a bottleneck a priori.