Personally, my biggest gripe comes from experiencing people trying to introduce it to a team. Inevitably someone tries to enthusiastically implement some part of it and suddenly are writing 1 line functions everywhere. I think this is the type of thing Casey is also implicitly talking about when he's railing against the rules being brought up.
Also, something most people over look is the OOP example in his article is 100% vanilla OOP. There is nothing “clean code” about it. So he’s really railing against OOP and not clean code.
My article shows that the impact of the overhead of OOP, but really the impact of the overhead of _anything_, can range from huge to basically zero, depending on the time scales involved. So my main point is you should be very mindful of the time scales involved.
But if you want to rule out OOP for non-performance reasons I think that’s fine. All styles have pros and cons, and situations can vary widely. Like for instance the existing style of the project and the skills and preferences of the people involved.
But if the overhead of OOP, as shown both by you and by Casey in his original video, makes up a significant chunk of your computation time it will inevitably lead to bad performance. Once you are mindful of the timescale and realize that OOP is the culprit, your only choice to go faster is to get rid of it.
DOD is generally going to have less in the way of user-defined classes/types. This can sometimes make it harder to convey the intent of the code, but in term it means there's a lot less to do if there's a big change in the problem domain – e.g. you don't have to refactor a whole class hierarchy.
Jonathan Blow has mentioned some ideas in his language Jai that would let you write code that can easily switch between OOP and DOD ideas, e.g. Array of Structs vs. Struct of Arrays, and I've seen that in some languages like Julia as well. I'm hoping it becomes more common place so that there's less of a tradeoff to make.
That is, let you toggle from DOD to OOP in your IDE. The way you can vary the font in your IDE today. Very non-trivial but in the long term could happen.
For most of these the limits are in the 5 - 9 range but for lines in a function/method the limit is 50 lines.
I agree with this. One line functions are okay sometimes but 50 line functions can be fine as well if clear and well written.
A lot depends on the cyclomatic complexity: how many loops and branches, how drastically the indentation varies basically.
So I disagree with how strenuously Uncle Bob harps on short methods, although paradoxically I do agree many methods in the wild are too long. Most of them grew to be too long because it’s easier to add a few lines to an existing method than to refactor things —- particularly if a code review is required.
While "programming" moves away from both of those: 1) Higher cyclomatic complexity is allowed. 2) Functions frequently call into other functions.
For instance, I like bash only for scripting. The moment it starts to resemble programming, I tend to switch to Python. However, some people are fine doing a fair amount of "programming" in bash, people who know it really well.
Something I'm happy about is if I can create an object to implement some isolated part of the domain in a way that's obviously correct and consistent within itself. For example, currently I'm working on a bit of a backup analyzer to identify different problems, estimate sizes and such. One object in there is the PGBackrestState and it has a whole bunch of one-line methods like total_size_on_disk, full_backup_count, average_incemental_size. Most of these are just one list comprehension. That's nice, readable, correct and stable code - and it makes other code more readable.
But some functions in that code base are an implementation of pretty much a flow chart of grabbing information, figuring out the state of the system and eventually dropping into more specific analyzers. This can be a whole bunch of imperative code in a function. And I've tried, but chopping this into smaller functions makes the overall idea and control flow much harder to understand. Sometimes a rough screen length of logic can be easier to understand as well.
Although a significant factor is if you chopped it up, would there be multiple callers for the new smaller methods? Because that’s probably better than adding flags to your large method to make it behave differently for different callers, increasing its cyclomatic complexity.
No.
However thats not the case. The author has distilled lots of experience into something reasonably readable.
e.g. a user is enabled if they have paid... time passes... now a user is enabled if the team they belong to has paid. now you need to move the logic to another struct / class and the data to another table.
now this is where "payables" and things come in and you start going the path of clean code with 1000 classes.
instead, the best way to do this is immutable data streams and event listeners.
after over a decade of programming, i feel for most software with a customer focus (not low level, embedded etc), immutable data streams where data flows through and listeners do things to themselves is the best way.
Principles arent universal across technologies
Hell, principles arent even universal across software kind (e.g goto arent used in C# web dev, but std lib? yes)
I could not read it.
Please split it into five lines.
This is actually an improvement!
Like, this is a conversation Uncle Bob had with Casey Muratori on GitHub, I came away feeling like Uncle Bob is more interested in playing rhetorical judo than anything else: https://github.com/unclebob/cmuratori-discussion/blob/main/c...
It's a story of two worlds. Casey lives in a world where nanoseconds almost always count. He says OOP is horrible for performance, but he really means it's horrible for performance in that world. But Uncle Bob's book is about a different world: business Java programming from 10+ years ago. There, the overhead of OOP is almost always negligible. One of my main points in the article was just to be aware of the world you are in.
Performance aside, is Uncle Bob's advice good or bad? I think it's a mixed bag. His four main books total 1,552 pages. Some of his advice is rooted in the Java programming world from the 1990s and 2000s, but I think other advice (like "use good names") is valid today in many languages. Sorting through all those pages of advice would require a much longer article; I didn't try to do that; I just tried to address Casey's performance claim, since I think he's totally wrong (in many cases) even while being totally right in other cases.
When he wrote Clean Code, Uncle Bob created a monster. Tons of people hold that book up as the Gospel of how to write not just OOP Java code, but all code.
Now is Uncle Bob really to blame for what other people do with the ideas in his book? Absolutely yes. All throughout he writes in a super authoritative tone, snarks at people who disagree with him, and asserts that his 40 years of software engineering experience mean his opinions must be right and that he doesn’t have to explain why.
The reaction to his writing is a product of the way he wrote it, and the reaction sucks. In workplace after workplace I’ve had to push back against people trying to sully nice readable code by blindly and stubbornly applying his principles.
It’s an endless struggle, one that would be helped if, like Merlin Mann (creator of Inbox Zero) he looked at what was happening and disavowed his creation and spent years working to correct the public’s misconceptions. But Uncle Bob doesn’t want to do that, so here we are.
My article and video are responses to Casey's mega-popular video "Clean Code Leads To Horrible Performance," which has 873k views. In it, he falsely claims that clean code always leads to horrible performance when, in fact, it only sometimes does. In my article/video, I explain when OOP/clean code does and doesn't lead to horrible performance.
My goal was to debunk Casey's claim since he was factually wrong and continues to mislead people about this to this day. I partially agree with your point that Uncle Bob's advice is wrong for other reasons. His main four books total 1,500 pages. Some of the advice is good, some is dated, opinionated, or just wrong. Untangling all that would require a much longer and very different article, which wasn't the one I was writing.
And to save you a lot of time: Most of the pages Uncle Bob has written don’t matter. Clean Code was his only book that has significant sway over the software community at large, the other books didn’t have nearly as much influence and don’t really matter to the discussion.
Like, it doesn’t matter if Uncle Bob makes a good point in the middle of a forest when no one is there to hear it. The thing that matters is that he wrote a bad book that made the software engineering community worse.
However, I am just curious, since you seem to have a very rational opinion on this topic, what do you think about Domain-Driven Design?
It's not technically CA, but from what I have read, the two can commonly be found together.
My understanding that performance issues with DDD are also not a guarantee, but performance is often the first thing sacrificed in order to maintain "domain purity."
In other words, if you were to develop a size > medium project, what would you use as an architecture?
I only ask because I have been attempting to implement DDD after attempting CA, and I am not convinced the juice is worth the squeeze for either one of the patterns. I am not saying that either design pattern cannot work, but at the same time, I also wouldn't say that a Rube-Goldberg Machine cannot work either.
Casey: Normalized data has an extreme overhead of recalculating things, it's horrible!
Reality: You look at the situation and decide when you need cached values to get acceptable performance. (And I consider most denormalized items in the database to be a form of cached value.)
Like "small functions." I say, okay, okay, stop writing these 2000 lines of spaghetti, I'm a hundred percent on board.
Then when you get to the concrete example, it's decomposing this 15 line function.
Good idea. Calibration is waaaay off.
I personally found it to be one of the most needlessly dogmatic things I've read.
In that section of the book, he takes a Java port of Donald Knuth's PrintPrimes program, and refactors it. But in doing so, he actually breaks it. He moves state out of local variables and parameters and makes them all static fields, and then refactors the code into long-winded methods that specifically perform side effects or operate on those fields (names like isNotMultipleOfAnyPreviousFactor(), isLeastRelevantMultipleOfNextLargerPrimeFactor()). But the simple act of moving state that would otherwise be part of the stack frame into static fields means he has changed the behaviour of the code - it is no longer thread safe! calling it from different threads will have undefined results because all threads will be operating on the static fields. He demonstrably made the code worse!.
It invalidated the entire book for me at that point. Here's "Uncle Bob" trying to pretend to be some aged, skilled, craftsman hand guiding us young, ignorant whippersnappers into being proper craftsman and not only can't he properly refactor a simple prime number sieve as a demonstration, but he's so blind to awful code that he doesn't even see it in his awful example enough that it gets published in the same book that complained about programmers who don't have "code-sense". Mistakes and "errata" are one thing, but when he makes such a big noise about "do it right the first time" and then has an example where he refactors and literally breaks something, that's another.
The code was simply God awful. Nearly every method was tagged ad “throws Exception”, no comments, and the famous endless sea of classes with only a few lines of code per method.
The code itself ran with constant exceptions filling up the logs. This was going back over a decade or more when I looked at it.
You can see his lineage of trying to sell consulting services and books all the way back to the comp.object Usenet groups back in the 90s.
Sadly he still commands big fees to speak at various conferences and companies, I was very disappointed when he spoke at Bloomberg many years ago when I worked there.
I hate when I get comments on a PR talking about some subjective piece of code suggesting an alternative that they think is “cleaner”. How clean a piece of code is shouldn’t be a part of consideration while reviewing any code. Instead, like I said before, the only thing that really matters is if it works and if it’s readable. I wonder how many junior engineers have been bogged down with pointless PR comments over the years because of this idea that code can somehow be “neat” or “clean”.
Uncle bobs book didn’t age well, but neither will the dogmatic stances on immutability and side effects.
They mean literally any change at all. If your function is named addOne(&x), then they consider the result of x being one greater to be a “side effect”, whereas I suspect actually sane people such as yourself would not see that as a side effect.
This type of definition fuckery is one reason (among many), that I’ve come to fully disrespect functional programming communities.
One thing that also needs to be understood is that side effects (to your definition) are not explicitly bad things. Side effects ignoring boundaries, ignoring APIs, holding references, etc are probably mostly bad though.
In fact, in gaming, providing hooks to empower side effects is often explicitly good for granting designers mechanic freedom.
In embedded and gaming state changes in response to input is the point of everything.
"Side effect" doesn't have any useful meaning if it doesn't have teeth.
Nothing is a side effect if it's what the programmer intended to do.
I was trying to use a function. You had to create an instance of it to use it, but I was using one method that by appearances should have been static. You feed it an input, it gives you an output, there's nothing to remember. I was calling it from multiple threads--occasional corruption. There must have been something being stored into the object but I have no idea what or why.
// Not a side-effect (intentional)
if(++i++) {
}
One could almost view Clean Code as a response to Java Swing - a potential case study of bad API design. I personally greatly appreciate the new focus Clean Code put on API design that was lacking beforehand.
You can’t replace experience. There’s theory, then there’s practice. Ideas are worthless until tested in reality.
Things like strictly requiring 4 or less lines per function leads to huge a mess that is very hard to untangle and work with, or even understand for newcomers.
I ask because I can see his ideas being great to him if he is never around for damage control because what feedback does he truly have that cannot be fought back with some 'Get out jail free' card like "The company deviated from my plan after I left!"
Since clean code was written, the two decades since, a number of values are more salient and some novel. Ideas like Single-abstract-method, functional programming, immutable state.
Hence, the damage that was being fixed is a swap of convoluted, extremely stateful APIs- to cleaner looking ones, that were more usable, less stateful. (Albeit, Still damaged by modern standards)
Things have come along even further since.
The ideas in clean code are many, have a context, and were certainly not the final step for programming - which is effectively brand new to humanity. For example, I've been learning to sew recently. Everything I have been learning, except the actual sewing machine, has been known for a couple hundred to couple ten thousand years already. Zero is new. Contrasted to programming, where the first programs are still in living memory - it's a crazy different and new field of knowledge, it is YOUNG.
Knowing the context of what was before 2000 imo is critical to contextualize the many ideas in clean code.
This is false. I tried to look this up a few weeks ago, there is no number. The book says (close paraphrase) "functions should be short, and then they should be even shorter than that". It also says, "a function should be only so long that it expresses a single cohesive idea"
There is lot of nuance and wiggle room there, and those are not absolutist statements. Reasonable people can disagree whether small talk length functions are good or not, but the characterization of an absolute line limit is false.
> Every function in this program was just two, or three, or four lines long. Each was transparently obvious. Each told a story. And each led you to the next in a compelling order. That’s how short your functions should be!
But I'm not sure I even believe that, because the worst thing is dogmatism. Uncle Bob convinced young developers to be dogmatic.
I've dealt with code that was a mess with no adherence to rules. I've dealt with teams where everything need to be perfect. The first group shipped products. The second groups had neat looking code bases but had much more trouble shipping. Sometimes it was impossible to get in simple lines of code because it didn't match "the rules".
That's his only option.
I am not trying to speak ill of the man, but I am quite dubious of his advice.
I truly believe everyone has something to offer. However, I would feel more inclined to follow Uncle Bob's advice if he actually had the portfolio to back it up.
If Linus Torvalds were to give advice on how to build an operating system's kernel, I feel as though I should listen. Not only because Torvalds had/has good ideas, but because he also has demonstrable experience and work to back up his claims.
Uncle Bob has a lot of words, but projects speak louder.
I'm pretty sure Linus' advice is 'don't be r***d'. In recent years he's learned a bit and now also would say 'don't be a c*t, probably, most of the time'. :P
Yeah nah. At the rate that programs are getting worse, there’s simply no way that this is remotely true now, especially with cloud costs growing quite rapidly.
Premature optimization is the root of all evil. (Donald Knuth)