134 Comments

Thanks for making this public - I fight these battles at work all the time, and it's nice to have a concise POV from someone that corroborates what I can show thru experimentation.

The pushback I imagine getting back from this is about maintainability, "Developer Experience" issues, etc, which I don't have the experience to refute. But at least I can measure improvement - proponents of those other issues kinda can't

Expand full comment
author

There is some kind of a weird thing where people think good code is not maintainable, but bad code is. It's like "if we make this code bad to begin with, then when we extend it with bad code, it will not get worse", which is true, I suppose :)

- Casey

Expand full comment

Yea, it's just hard to argue with people "who have seen some shit". As in, I'm 10 years into a programming career with some pretty cool stuff to show for it, but this argument is always the showstopper. On the other hand, it makes it very exciting to see things like this start to pop up.

Also makes me feel better about the looming threat of AI generated code - an AI that was trained on decades of "Clean Code". lol

Expand full comment

"Also makes me feel better about the looming threat of AI generated code - an AI that was trained on decades of "Clean Code". lol"

Yes, exactly! Thank you for mentioning it! I feel so alone when I try to talk about this with others...

Expand full comment
Feb 28, 2023·edited Feb 28, 2023

Based on my own use of ChatGPT, this is true except for when it is busy hallucinating invalid syntax which is at least 90% of the time for anything slightly nuanced/lacking tons of training examples like a hacker rank solution.

Expand full comment

Sir, the way you played down your experience in your original comment is a bit misleading. 10 years! Comon! You have got to have seen some shit!

If I had 10 years Ill fight with everyone

And yes, copilot is .... horrible

Expand full comment

I think that when you add code, you multiply the quality (say code quality can be between zero and one). So unless the code was a flat zero to begin with, it gets a lot worse even if it was terrible to begin with.

Expand full comment
Feb 28, 2023Liked by Casey Muratori

Casey is really nailing his theses to the door in this video.

Expand full comment
Feb 28, 2023·edited Feb 28, 2023Liked by Casey Muratori

Fantastic! It's really a relief to see someone make this case methodically and non-ideologically. (Your point about that switch statement is right on - sometimes just by laying out stuff in a sequential way makes it obvious where stuff can be cut down.)

i've sat in on conference talks where the guy up at the front took literally 5 lines of code in a single file and blew it up into like 50 lines across 10 files, and his justification for it was, verbatim, 'we want the next programmers who see our code to think we are smart'. It all felt very wrong, and yet i assumed he must know something i didn't - after all, he's presenting and i'm in the audience.

So, i dutifully worked for several months rebuilding a project following these clean code principles, and just found it created a lot of new spaghetti, duplication, and piles of boilerplate where everything had once been so direct and readable. Polymorphism especially - it *seems* like such a powerful idea, but ends up making the flow of information significantly harder to follow. Ended up abandoning the rebuild and simply refactoring my 'unclean' code in a last-minute marathon. Made me feel like a bad programmer, like i'm hiding some secret shame. There's such weird peer pressure around it now, seems to mount a little each year - with some people, it's like the code equivalent of saying you're a Trump voter or something.

Anyway, i'm subscribed now, gonna go back through the previous ones and watch through. i appreciate your pro presentation skills and clarity.

Expand full comment

Probably this goes outside the scope of the course (maybe better suited for an architecture course) but talking about "SOLID" principles and their downsides as you just did with "Clean" code could be great.

Expand full comment
Mar 1, 2023·edited Mar 1, 2023

There are a series of blog posts by David Bryant Copeland about SOLID is not solid as well as a book:

https://naildrivin5.com/blog/2019/11/11/solid-is-not-solid-rexamining-the-single-responsibility-principle.html

https://solid-is-not-solid.com/

Expand full comment

Have you already seen his video "Where Does Bad Code Come From?" on his Molly Rocket YouTube channel? He talked about SOLID there, but less from a performance and more of an architecture perspective.

Expand full comment

Nice! I just saw it.

The analogy that he made around clean code by applying SOLID principles being similar to blessing the food before eating remind me the "Introduction to git" on handmade hero and its "git bless" command :P

Expand full comment

can you tell me which episode that is? I havent seen it.

Expand full comment

The tittle of the episode is: "Handmade Hero Day 523 - Introduction to Git"

Expand full comment

Holy shit this is gold. I had no idea Casey made this kinda video. Hilarious

Expand full comment

I was confused at first and sad he was advocate for git. But after 5-10 minutes I got it. I'm certain if I show it to one of my colleague at my company, they will just don't notice it.

Expand full comment
Feb 28, 2023Liked by Casey Muratori

It's nice to hear confirmation of programming practices that I've also found problematic.

For example, I have a note in our company handbook to disregard linters' warnings about "cyclomatic complexity", because resolving the warning typically involves extracting a "small" function that does "one thing" - but after this extraction the code is harder to read, harder to refactor (like when the area-calculation logic is in multiple files in the video's "clean" code), etc.

We outsource a lot, and my code reviews after handoff involve undoing a lot of these practices. This typically results in less code that is easier to read and maintain. I'm not sure what these practices are providing, except maybe to prevent inexperienced programmers from writing something really incomprehensible and buggy (but they should be given more coaching and supervision in that case, instead of given "clean code" principles to follow).

Expand full comment

I totally agree that breaking things into functions just to make the functions small without actually understanding what the code is supposed to do is a very bad practice. I remember once doing something similar that led to a bug in the program I was writing. Debugging the problem took me a while until I found out that, while trying to prematurely break things into different functions, I ended up splitting some code that needed to be in the same function for things to work properly.

I also don't find that practice really "clean". Over-fragmentation of the code into smaller and smaller functions for the sake of it tends to me it a lot harder for me to read the code, because I constantly have to break my flow to understand what the code is trying to do.

Expand full comment

One thing that gets me is the staggering selfishness of the "developer experience" people. Not only are their pronouncements usually totally untested, not only do they constantly cite some Big Other (who would certainly know if these practices didn't work!), but the one that hurts me the most is "my time is worth more than the computer's time" or flippant remarks about engineering salaries. Even if "clean code" does make development faster (unproven!), to think that a couple hundred bucks of engineering salary is worth actual human lifetimes of loading screens (which are easy to reach at even modest user counts) is just beyond the pale to me.

Expand full comment

I feel like you want to do a running slap

Expand full comment

Always found it amusing that using dynamic dispatch, and being in the dark about how things work is 'clean.' :)

Great stuff, thank you.

Expand full comment
Feb 28, 2023·edited Feb 28, 2023

Playing the devil's advocate here

I don't think clean code was ever about performance, so was this comparison unfair? I'm not saying the performance comparison shouldn't be made. But maybe the appropriate comparison would be by readability and maintainability. To "beat" clean code, one should show performance-aware programming is more readable and maintainable, right? Even in large code bases. Give a slightly more complicated performance-aware program to some programmers. How fast and well can they understand and add features to given program? Do you have these measurements?

Of course, the more the programmers are trained on clean code, the harder it will be for them, I'm aware. You're probably suggesting a whole revision of the programming education "ecosystem". Starting from "data transformations" instead of "objects and what they do" in the early stages when teaching programming. The latter seems so natural and intuitive (or maybe I just forgot the struggles when I learned OOP). That's quite a effort.

On another note: another way clean code is practiced is by following "don't write code for the computer, write code for other programmers". It's almost saying "programmers don't know or don't care about what the computer is actually doing". That seems to be the general mindset which causes the 1000x slower software.

Expand full comment
Feb 28, 2023·edited Feb 28, 2023

The idea of creating a video around "code that is performant and easier to understand and add features" would definitely be a great video, and I would definitely watch it, but given the name of this course, I imagine it would probably be outside the scope to be included here.

I do think that Casey could succeed at creating such a video, but with this video he makes a simpler point:

Even if an engineer thought that this "clean code" method created more understandable, easy to edit code (Which again, is a big claim worth testing), could you justify extinguishing 15+ years of hard won hardware performance gains to get that benefit?

Because that is essentially the choice that is made every day, but it's made without any attempts to justify it.

Expand full comment

You may want to check out a video by Shawn Mcgrath. Its not a study with metrics but he shows one case where the clean code is just impossible to understand. He is also eating samosas and drinking scotch... its an absolute gem of a rant. I copied the video like at the timestamp where it starts: https://youtu.be/q4nUK0EBzmI?t=11512

There is also another one here showing comparison of how these principles just produce code that is just not good for reading: https://www.youtube.com/watch?v=IRTfhkiAqPw

Expand full comment

I agree with Seth's reply to this but also feel like adding that I am not sure anyone can really claim to 'have these measurements' when it comes to concepts like readability and maintainability: They're nebulous to pin down, and it's hard to design (and afford) an experiment that captures the kind of scale that you probably actually care about -- i.e., not the effectiveness of a novice on day 1 of the job, but some longer-term success. (And is there even a real definition of whether a code-base has 'true' clean code, given that these are often just subjective rules of thumb?) I wouldn't think of this kind of software engineering self-help advice as if it were a science.

Expand full comment

( I see another comment pointed to Casey's "Where Does Bad Code Come From?" talk, which also comments on the problem of measuring these things: He is more optimistic that we could measure them, but points out that the clean code advocates have not even really tried to do so: https://youtu.be/7YpFGkG-u1w?t=1531 )

Expand full comment

I agree with this. I feel like no one can honestly argue that "clean code" presented here is performant. It's objectively slower as Casey shows here.

I think what "clean code" is maybe attempting to do is to provide guidelines for how to manage software complexity, but it fails to do it because it's applied blindly at too low a level. When you start drawing boundaries everywhere you actually make it more complex when you zoom out.

The actual problem of managing software complexity, where and how to define your boundary lines, breaking up your system into smaller pieces that are easier to understand, etc. seems to me too hard to solve with just some simple ruleset like "clean code".

The programmers I've met who are good at this seem to have gotten there all by writing and reading lots and lots of code, for lots and lots of different kinds of problems. The sheer volume of work they've gone through gives them some sort of instinct for making good architectural decisions. And even then they still make some wrong decisions the first time solving a new problem!

Expand full comment

Great talk, thank you. Just a little disappointed to not have any information on how far away the sun is in this lecture, seems like a very important point in a programming lesson :).

Expand full comment
Feb 28, 2023·edited Feb 28, 2023

This comes at the right time, because i have actualy a use-case that exactly shows that writing best-practices software hurts performance and even memory a lot!

We have hundreds of signals containing 10k up to 500k samples and i always need the minimum and maximum value of the actual data. But there is a caveat, not each sample are valid, because of this there is an array with the same length of data that stores a flag that indicates if the sample is valid or not. It is not sorted, so we always have to check the index for every value.

The team writing the library that computes the min/max values used a thread-based for-loop that computes the min/max for each valid value. Also it is guarded behind at least 4 levels of indirection before the math is actually executed. It is painfully slow and it uses a ton of heap allocated memory. Those codes where written using solid and clean-code principles -.-

After analyzing the code, i have written a much faster solution using only vector instructions (SIMD) and stack allocated SIMD vectors, that does the same thing and use conditional selects to only compute valid values. It is at least 10x times faster than the other code. After unrolling the loop 4x times, its twice at fast now.

I didn´t changed the underlying crappy data structure, because it was written by another company.

Also the language is pure C# / .NET 6.0, so i can´t write it in C unfortunatly :-(

Expand full comment

I think one thing the lecture should have shed some more light on, or even dedicated an entire video about is the fact that these "clean" code rules actually and fundamentally goes in the opposite direction of what the CPU want you to do to run more efficiently. Virtual functions, array of pointers to objects, separation of concerns, each object should know how to do the work on his own in isolation instead of doing the work on multiple objects together, etc. all these ideas fundamentally go against the features that the CPU has to improve performance. These ideas will confuse out-of-order execution, pollute the cache following pointers, and make it difficult to load data into SIMD registers.

Because of that, the more someone apply these ideas the worse it is for the CPU today or even in the future.

Expand full comment
author

I agree with that, yes. Older programming styles tend to be more similar to what CPUs do, newer styles seem completely divorced from any idea that a CPU is involved.

- Casey

Expand full comment

Thanks for making this video! I do notice one thing that we're keeping the same memory size for all the shapes by using a union. This avoids the pointer indirection, but also wastes memory. In this case it probably doesn't matter much. But what if we have polymorhpic types of objects that have drastically different memory size. Isn't it a waste to use the maximum amount of memory to hold all types of objects?

Expand full comment
author

This is very rare, but when you have this case there is a very simple solution: keep more than one array. This is also the solution when the processing differs too dramatically.

In fact, you would often prefer to keep each type in a separate list, as it is both more space efficient and more efficient to process. But most programming languages make it onerous to write code that way, so it tends to be done less frequently than one might wish.

- Casey

Expand full comment

I think that section alone has the potential to change the industry. Most companies are "data-driven" these days, except for their programming practices. You've just done a data-driven presentation on why the industry's practices are harmful to the software being built. Obviously, a mountain of excuses would then follow, but it's a great seed for great change.

Expand full comment
Mar 1, 2023·edited Mar 1, 2023

Thinking on this more, the one thing that bugs me is how circles fit into that superset struct. That seems like a genuine harm to readability and usability.

If you wrote this somewhere in some large program to process shapes, and I needed, say, a circle, I would see that there's an enum for circle, but then I'd need a "width" and a "height" and circles don't have those - and I'd also need to know to make them both the same. This really DOES seem like one of those cases where it hurt readability and opened up a pretty clear path for bugs (e.g., thinking "width" and "height" are the diameter of a circle, not knowing you need to put identical values for both for circles). Is there some other piece I'm missing? Would you, in practice, abstract over this with constructors for the different structs that have more conventional names?

Expand full comment
author

It depends. In this particular case I would just change the name to ellipse, and support them. But in a real world case I might do something fancier like a real discrimnated union, or separate arrays, or any of a variety od other techniques.

- Casey

Expand full comment
Mar 2, 2023·edited Mar 2, 2023

I've seen people do a discriminated union thing like this, giving a name to each thing it can be (substack killed the formatting, not sure how to fix).

struct Shape {

ShapeType type;

union {

struct {

float radius;

} circle;

struct {

float width;

float height;

} rectangle;

struct {

float base;

float height;

} triangle;

};

};

Expand full comment

Ah okay, you're talking about the "Uncle Bob" Clean Code book. I see it now, "G23: Prefer Polymorphism to If/Else or Switch/Case". I tend to disagree with Bob's advice on other topics online so I never bought his Clean Code book. In any case, I think you've proven pretty easily that it's not advice that anyone should follow.

Expand full comment

Do you have any ideas on how to retain extensibility for other people with this kind of design? This stuff is great if everyone has full control over the codebase, but if that's not the case, it hard codes a lot of functionality into functions that only someone with full source access can modify. Take Minecraft for example. While not exactly a pinnacle of optimization, the fact that it is written in Java means that it is very easy to extend the functionality of a whole lot without modifying the base code due to all the objects and its "everything is a class" model. Extending classes provides a very easy way for the mods to inject arbitrary code where they need it to provide any sort of custom functionality in a way that can be automatically compatible with every other mod, and since everything is a class, everything automatically has this property. Say what you want about Java, but the benefits to extensibility for mods are undeniable. I don't think Minecraft mods would be anywhere near as good as they are now had Minecraft been written in performance aware C.

If you have any tips about how to do that sort of extensibility in a performance friendly way, I'd love to know.

Expand full comment