Casey Muratori is basically describing Data Oriented Programming. Perhaps the most famous talk about it was from CPPCON 2014 [1]. I come from an embedded background (microcontrollers and bare metal) so I'm pretty sympathetic to his argument. If you work in it long enough, it feels natural and "clean". Uncle bob's Clean Code probably feels natural to Java devs.
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.
I think DOD is great and Casey’s optimized version is great. But his claim that OOP inevitably leads to horrible performance is factually wrong. And he’s repeats it over and over even after the viral video. My main goal with the article/video was to debunk this false claim of his.
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.
> My article shows that the impact of the overhead of OOP ... So my main point is you should be very mindful of the time scales 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.
My work has adopted DOD for roughly the last 3 years. Compared to OOP/FP[0], I would say DOD is generally more performant, more work to set up initially, harder to adapt to small changes, and easier to adapt to big changes.
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.
I’m not at all familiar with Julia but that sounds interesting. I’ve been thinking recently that with AI we might eventually decouple the style of the source code from the style of the executable code.
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.
There no problem with them, and they can often be useful. But there is a problem if all the code is only functions of one or two lines, or when people move things to functions when it just doesn't make much sense, "because it's Best Practice™".
Pylint warns if you have “more than X” for a lot things like local variables, number of arguments, number of member variables, etc.
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.
Yup. I would like to keep things on the screen if feasible, but it's really always cyclomatic complexity that matters. A linear flow of lines, I don't care about until I can't see it all at once but you're rarely writing a linear flow of lines.
I always felt that true "scripting" with a "scripting language" was two things:
1) Most of your functions were linear: low cyclomatic complexity.
2) Functions rarely called into other functions.
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.
I mentioned in another comment pylint’s 50 line limit for methods. So I agree - a well written 50 line method is sometimes better than the same code chopped up.
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.
I like this article. At the start I feared it was going to be one of those polemics written by someone who's never had to read other people's code, or worry about speed, efficiency or third party users.
However thats not the case. The author has distilled lots of experience into something reasonably readable.
The other cool thing about an OOP approach to this problem is that you can hide your optimized LUT behind the OOP if you're careful about the design of it. This lets you have your cake and eat it too. Your users who might be more comfortable with an OOP approach get a way to declare the kinds of shapes they're interested in and the number of shapes they want to work with. Then you can take responsibility for building a LUT that meets their needs and give them the supporting optimizations they need to be successful.
the problem with code is that the abstraction changes and data needs to be migrated:
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.
I fully agree, but this requires a certain level of experience to organize code in a way that makes it easy to navigate and modify. It's quite easy to end up with with "rube goldberg" code where you don't know what listeners are being triggered by what streams. Or to end up with a ton of edge cases where things aren't handled properly when issues occur.
Yea agree. I feel one of the best books ive read that makes this concrete is Grokking Simplicity by Eric Normand. It’s less about functional programming and more about functional thinking. Lots of concrete great advice in that book.
Amazing book. My favourite parts are the framing of fighting complexity as the main goal of design (strategic vs tactical thinking) and the idea of "deep modules".
I think a programmer can set badly any good pattern. We have onion pattern in our code and I get sick every time I enter it. All's great but it implements a graph of state changes navigating which resembles debugging a regexp, also all the ctrl+click navigation is broken because there's one general method for each message root type
Personally, I'm a fan of when he has argued with academics on Twitter about things that he is actually ill-equipped to discuss, but he refuses to budge because their (factually correct) statements don't align with his limited worldview. Really made me want to get his books for sure.
So like, Clean Code really does say you should write four line functions, and does not put caveats on that statement, Uncle Bob even gives a lengthy terrible-looking example of a class written with 4-line functions and holds it up as the ideal. His views as expressed in that book are extreme and uncompromising, the “everything in moderation, there are many ways to write clean code” stuff is a rhetorical act he does when people call him out. Then when they’re gone and he thinks he can get away with it he starts speaking in absolute statements again.
As the OP, I was not arguing that Uncle Bob's advice is good; I was arguing that performance is not the reason to reject his advice... unless you are counting nanoseconds, in which case performance is a great reason to reject his advice.
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.
But performance isn’t the reason to reject his advice: common sense and good taste are more than enough of a reason.
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.
> But performance isn’t the reason to reject his advice: common sense and good taste are more than enough of a reason.
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.
I get that Casey’s video is popular (though I wouldn’t call a video with less than a couple million views in it’s first year mega-popular) but I also feel like it’s a bit of a straw man. People don’t dislike Clean Code because the examples aren’t performant, they dislike it because it very stridently and wrongly argues for ways of writing code that are difficult to read, reason about and work with.
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.
I am sorry to be tangental, as I am not the GP of this comment thread.
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.)
Yes, there are heuristics given. Except, until clean code, it was not common to even see "use good names" as a principle. In the bad times, pre 2000, a lot of code was just focused on getting it right. The: then make it clean, then fast (in that order)" was not a thing.
In those bad times, the idea of spending time on naming, that naming was even important - was not a widespread thing. The idea that naming all the way down to variables was important - was popularized by Clean Code. Which is to say, the idea to emphasize and care about naming was not widespread at the time, let alone how to do it.
In fact, if Uncle Bob really doesn't want 4-line functions, and wants moderation, he should write a better book. Whatever he claims to mean, everyone sure seems to think that's what his book says.
I personally found it to be one of the most needlessly dogmatic things I've read.
For me, I bought "Clean Code" because it was, at the time at least, quite highly recommended. After being rather confused for a while, I finally stopped reading right around page 141 when I realized that the reason his examples looked awful were because they were.
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.
I started reading the code Martin wrote with his son for the “FitNesse” acceptance tracking framework.
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.
Interestingly enough, John Ousterhout was a recently on Book Overflow podcast and he says he uses this very refactoring but on reverse to teach good software design. So it’s good for something!
I even hate that the title got popularized. Code isn’t “clean” or “dirty”. It works or it doesn’t work. That’s the most important aspect. Another very important aspect is that It’s readable or unreadable. Readable code may be very long winded, but I would much rather have that than somebody’s idea of a “clean” concise very abstract piece of logic.
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”.
I mean. It’s not like people pushing “side effects” (read: literally any change at all) aren’t also being overtly dogmatic, and writing utterly terrible code.
Uncle bobs book didn’t age well, but neither will the dogmatic stances on immutability and side effects.
In this case, By "side effects" I mean non-obvious changes to the static state, Where a seemingly simple function will both rely on static fields to evaluate it's result as well as make changes to various other fields that will subsequently change the result of that function going forward. I don't think one needs a dogmatic adherence to immutability and avoiding side effects to find that to be undesirable.
I agree. Just letting you know that, most likely, the people you are learning about “side effects” from don’t actually mean “side effect” in the colloquial sense of “state changes beyond the expectation”.
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.
My belief is the people you are talking about think of programs as batch operations where you take data transform it and then pass it to someone else's API calls. In that case state is sort of evil.
In embedded and gaming state changes in response to input is the point of everything.
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.
Avoiding side effects as much as possible is not a new idea, though; it's been around since at least the 80s. You can just ask people who have worked on Haskell code written in the past 25 years to determine for yourself if the idea aged well or poorly. Opinions will probably be mixed, though, not clearly one way or the other.
I don't understand how this relates to my comment. I'm saying that avoiding side effects isn't a new idea, so it already has aged, either well or poorly.
I don’t really care about the opinions of Haskell coders, as they are probably the only programmers on earth that have more terrible dogmatic ideals than uncle Bob does.
If you're open to challenging your preconceptions (dare I say dogma?) about Haskell programmers then I'm willing to share my opinions on good software development and how Haskell helps, based on over a decade of professional experience with Haskell. Free tidbit: I find effect tracking to be extremely beneficial to software engineering in the large.
Maybe, but that was not the question. I think that perspective is lacking context as well. Programming has generally gotten better over the last 50 years. It is not a dichotomy of just good code vs bad. Most code is bad, has been bad, and before that was even worse.
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.
Yeah, I get the impression that he just sells his image as some "guru" of software engineering, and that he doesn't actually deal with or write much real life code.
I think the aspirations of the agile manifesto are great, but basically everyone involved in it was an enterprise software methodology consultant. I forget who the exception was; someone will remind me.
Oh I think there are plenty experiences out there practicing various strategies of clean code in the wild which he can observe without creating them himself
You really have to experience it yourself to see how the strategies you come up with affect debugging and changing the code when fixing bugs or adapting to new requirements.
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.
Isn’t that the same case for Robert Martin? I’m not going to be the one defending Uncle Bob, but most of the people out there giving advice about “clean code/architecture” come from the consulting world without any work that can be analysed.
I am rather ignorant of the world of consulting. If someone like Uncle Bob consults a company and the company implement his ideas with his guidance, then is a consultant like Uncle Bob typical gone by the time maintenance rolls around?
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!"
Clean code had a place and context, it is almost two decades old. The idea of writing usable API's at the time was somewhat novel. Most developers were busy trying to get the code to work at all, that the API was an afterthought. EG: Methods with 3 string variables followed by 4 boolean was not unheard of.
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.
> So like, Clean Code really does say you should write four line functions, and does not put caveats on that statement
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.
I suspect that number comes from this quote a little further down
> 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!
Thanks for pulling the quote. I agree. It is a distinction of "should be" and must be". FWIW: Uncle Bob clarified this exact topic in a recent youtube interview with Primeagen (about 5 months ago). The gist is if the length to have a single cohesive function is 50 lines, then so be it. It was clarified that clean code does not give a hard and fast limit (but does give a very weighty recommendation to go for small functions based on a number of heuristics).
Perhaps the Uncle Bob principles do not create the best code, or most clear code, but more dogmatic adherence does make it harder to create the code I see on a daily basis that is so much worse and much more unmaintainable.
I suppose if the options are "Uncle Bob's Rules" and the straw man of "No Rules" that could be true. That's like if you gave yourself the options of "one hand behind your back" or "two hands behind your back" in a fight when we already knew how to fight with "zero hands behind your back".
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".
> I came away feeling like Uncle Bob is more interested in playing rhetorical judo than anything else
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.
> If Linus Torvalds were to give advice on how to build an operating system's kernel, I feel as though I should listen.
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
>It is economically better for most organizations to conserve programmer cycles than computer cycles.
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.
Casey Muratori is basically describing Data Oriented Programming. Perhaps the most famous talk about it was from CPPCON 2014 [1]. I come from an embedded background (microcontrollers and bare metal) so I'm pretty sympathetic to his argument. If you work in it long enough, it feels natural and "clean". Uncle bob's Clean Code probably feels natural to Java devs.
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.
[1] https://www.youtube.com/watch?v=rX0ItVEVjHc
I think DOD is great and Casey’s optimized version is great. But his claim that OOP inevitably leads to horrible performance is factually wrong. And he’s repeats it over and over even after the viral video. My main goal with the article/video was to debunk this false claim of his.
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.
> My article shows that the impact of the overhead of OOP ... So my main point is you should be very mindful of the time scales 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.
I don't quite follow how you proved it wrong. All the examples seemed to be alternatives to OOP?
My work has adopted DOD for roughly the last 3 years. Compared to OOP/FP[0], I would say DOD is generally more performant, more work to set up initially, harder to adapt to small changes, and easier to adapt to big changes.
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.
I’m not at all familiar with Julia but that sounds interesting. I’ve been thinking recently that with AI we might eventually decouple the style of the source code from the style of the executable code.
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.
Can be summarized as automated refactoring between different coding styles.
In addition to Mike Acton's talk, there is also the "DOD Book": https://www.dataorienteddesign.com/dodbook/
Don't see a problem with one line functions per se, especially if the name of the function helps understanding the code
There no problem with them, and they can often be useful. But there is a problem if all the code is only functions of one or two lines, or when people move things to functions when it just doesn't make much sense, "because it's Best Practice™".
Pylint warns if you have “more than X” for a lot things like local variables, number of arguments, number of member variables, etc.
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.
Yup. I would like to keep things on the screen if feasible, but it's really always cyclomatic complexity that matters. A linear flow of lines, I don't care about until I can't see it all at once but you're rarely writing a linear flow of lines.
I always felt that true "scripting" with a "scripting language" was two things: 1) Most of your functions were linear: low cyclomatic complexity. 2) Functions rarely called into other functions.
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.
Nah, short and single-line functions can be good.
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.
I mentioned in another comment pylint’s 50 line limit for methods. So I agree - a well written 50 line method is sometimes better than the same code chopped up.
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.
> Uncle bob's Clean Code probably feels natural to Java devs.
No.
I like this article. At the start I feared it was going to be one of those polemics written by someone who's never had to read other people's code, or worry about speed, efficiency or third party users.
However thats not the case. The author has distilled lots of experience into something reasonably readable.
The other cool thing about an OOP approach to this problem is that you can hide your optimized LUT behind the OOP if you're careful about the design of it. This lets you have your cake and eat it too. Your users who might be more comfortable with an OOP approach get a way to declare the kinds of shapes they're interested in and the number of shapes they want to work with. Then you can take responsibility for building a LUT that meets their needs and give them the supporting optimizations they need to be successful.
This is a great point and very true.
the problem with code is that the abstraction changes and data needs to be migrated:
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.
I fully agree, but this requires a certain level of experience to organize code in a way that makes it easy to navigate and modify. It's quite easy to end up with with "rube goldberg" code where you don't know what listeners are being triggered by what streams. Or to end up with a ton of edge cases where things aren't handled properly when issues occur.
Yea agree. I feel one of the best books ive read that makes this concrete is Grokking Simplicity by Eric Normand. It’s less about functional programming and more about functional thinking. Lots of concrete great advice in that book.
Same conclusion here. May I ask about your stack/domain?
If anyone wants to read the antithesis of Clean Code, check out "A philosophy of software design"
Amazing book. My favourite parts are the framing of fighting complexity as the main goal of design (strategic vs tactical thinking) and the idea of "deep modules".
I simply love this book!
I think a programmer can set badly any good pattern. We have onion pattern in our code and I get sick every time I enter it. All's great but it implements a graph of state changes navigating which resembles debugging a regexp, also all the ctrl+click navigation is broken because there's one general method for each message root type
The most important thing when evaluating things you need to know is: context matters
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)
The context in this case being that Robert "Uncle Bob" Martin is a charlatan and an impostor.
Personally, I'm a fan of when he has argued with academics on Twitter about things that he is actually ill-equipped to discuss, but he refuses to budge because their (factually correct) statements don't align with his limited worldview. Really made me want to get his books for sure.
this comment says more about you than him :)
Too many words in a line.
I could not read it.
Please split it into five lines.
This is actually an improvement!
And across teams, business.
So like, Clean Code really does say you should write four line functions, and does not put caveats on that statement, Uncle Bob even gives a lengthy terrible-looking example of a class written with 4-line functions and holds it up as the ideal. His views as expressed in that book are extreme and uncompromising, the “everything in moderation, there are many ways to write clean code” stuff is a rhetorical act he does when people call him out. Then when they’re gone and he thinks he can get away with it he starts speaking in absolute statements again.
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...
As the OP, I was not arguing that Uncle Bob's advice is good; I was arguing that performance is not the reason to reject his advice... unless you are counting nanoseconds, in which case performance is a great reason to reject his advice.
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.
But performance isn’t the reason to reject his advice: common sense and good taste are more than enough of a reason.
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.
> But performance isn’t the reason to reject his advice: common sense and good taste are more than enough of a reason.
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.
I get that Casey’s video is popular (though I wouldn’t call a video with less than a couple million views in it’s first year mega-popular) but I also feel like it’s a bit of a straw man. People don’t dislike Clean Code because the examples aren’t performant, they dislike it because it very stridently and wrongly argues for ways of writing code that are difficult to read, reason about and work with.
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.
I am sorry to be tangental, as I am not the GP of this comment thread.
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.
Uncle Bob: Thou shalt not denormalize your data!
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.)
I’ll add that “use good names” is good advice. But “use the names Robert Martin prefers” may or may not be good advice.
Yes, there are heuristics given. Except, until clean code, it was not common to even see "use good names" as a principle. In the bad times, pre 2000, a lot of code was just focused on getting it right. The: then make it clean, then fast (in that order)" was not a thing. In those bad times, the idea of spending time on naming, that naming was even important - was not a widespread thing. The idea that naming all the way down to variables was important - was popularized by Clean Code. Which is to say, the idea to emphasize and care about naming was not widespread at the time, let alone how to do it.
Uncle Bob sounds good verbally.
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.
In fact, if Uncle Bob really doesn't want 4-line functions, and wants moderation, he should write a better book. Whatever he claims to mean, everyone sure seems to think that's what his book says.
I personally found it to be one of the most needlessly dogmatic things I've read.
For me, I bought "Clean Code" because it was, at the time at least, quite highly recommended. After being rather confused for a while, I finally stopped reading right around page 141 when I realized that the reason his examples looked awful were because they were.
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.
I started reading the code Martin wrote with his son for the “FitNesse” acceptance tracking framework.
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.
Interestingly enough, John Ousterhout was a recently on Book Overflow podcast and he says he uses this very refactoring but on reverse to teach good software design. So it’s good for something!
In my opinion the only good part of that book is the title.
I even hate that the title got popularized. Code isn’t “clean” or “dirty”. It works or it doesn’t work. That’s the most important aspect. Another very important aspect is that It’s readable or unreadable. Readable code may be very long winded, but I would much rather have that than somebody’s idea of a “clean” concise very abstract piece of logic.
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”.
I always took it more as readable code.
I mean. It’s not like people pushing “side effects” (read: literally any change at all) aren’t also being overtly dogmatic, and writing utterly terrible code.
Uncle bobs book didn’t age well, but neither will the dogmatic stances on immutability and side effects.
In this case, By "side effects" I mean non-obvious changes to the static state, Where a seemingly simple function will both rely on static fields to evaluate it's result as well as make changes to various other fields that will subsequently change the result of that function going forward. I don't think one needs a dogmatic adherence to immutability and avoiding side effects to find that to be undesirable.
I agree. Just letting you know that, most likely, the people you are learning about “side effects” from don’t actually mean “side effect” in the colloquial sense of “state changes beyond the expectation”.
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.
My belief is the people you are talking about think of programs as batch operations where you take data transform it and then pass it to someone else's API calls. In that case state is sort of evil.
In embedded and gaming state changes in response to input is the point of everything.
I'm one such person!
"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.
What's especially important is non-obvious state.
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.
Avoiding side effects as much as possible is not a new idea, though; it's been around since at least the 80s. You can just ask people who have worked on Haskell code written in the past 25 years to determine for yourself if the idea aged well or poorly. Opinions will probably be mixed, though, not clearly one way or the other.
The definition of "side effect" has to mean something, otherwise it defaults to the opinion of whoever wrote the code.
I don't understand how this relates to my comment. I'm saying that avoiding side effects isn't a new idea, so it already has aged, either well or poorly.
I don’t really care about the opinions of Haskell coders, as they are probably the only programmers on earth that have more terrible dogmatic ideals than uncle Bob does.
If you're open to challenging your preconceptions (dare I say dogma?) about Haskell programmers then I'm willing to share my opinions on good software development and how Haskell helps, based on over a decade of professional experience with Haskell. Free tidbit: I find effect tracking to be extremely beneficial to software engineering in the large.
Has uncle Bob actually produced any useful software? Are there any examples of his work out there which can be analysed?
He’s got a GitHub. This seems to be the most notable thing: https://github.com/unclebob/fitnessedotorg
Seems like this only includes the compiled class files and no actual source?
You’re right. The source is at https://github.com/unclebob/fitnesse
[flagged]
His career IIRC starts in the 70s. After 50 years, the answer is assuredly yes to the first question.
It is absolutely possible to code terribly for 50 years and get away with it. Experience is no proof of anything.
Maybe, but that was not the question. I think that perspective is lacking context as well. Programming has generally gotten better over the last 50 years. It is not a dichotomy of just good code vs bad. Most code is bad, has been bad, and before that was even worse.
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.
Yeah, I get the impression that he just sells his image as some "guru" of software engineering, and that he doesn't actually deal with or write much real life code.
I think the aspirations of the agile manifesto are great, but basically everyone involved in it was an enterprise software methodology consultant. I forget who the exception was; someone will remind me.
People can give good insight about things despite even not creating these things, see e.g. physicists who study atoms
It’s more like someone who gives talks about smashing atoms to study subatomic particles without ever having been to a particle accelerator facility.
You can’t replace experience. There’s theory, then there’s practice. Ideas are worthless until tested in reality.
Oh I think there are plenty experiences out there practicing various strategies of clean code in the wild which he can observe without creating them himself
You really have to experience it yourself to see how the strategies you come up with affect debugging and changing the code when fixing bugs or adapting to new requirements.
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.
Isn’t that the same case for Robert Martin? I’m not going to be the one defending Uncle Bob, but most of the people out there giving advice about “clean code/architecture” come from the consulting world without any work that can be analysed.
Those are the same person
I always confuse them. I meant Martin Fowler.
I am rather ignorant of the world of consulting. If someone like Uncle Bob consults a company and the company implement his ideas with his guidance, then is a consultant like Uncle Bob typical gone by the time maintenance rolls around?
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!"
Either that or the company hires yet another consultant to rewrite everything because it can't be maintained.
Clean code had a place and context, it is almost two decades old. The idea of writing usable API's at the time was somewhat novel. Most developers were busy trying to get the code to work at all, that the API was an afterthought. EG: Methods with 3 string variables followed by 4 boolean was not unheard of.
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.
[dead]
> So like, Clean Code really does say you should write four line functions, and does not put caveats on that statement
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.
I suspect that number comes from this quote a little further down
> 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!
Thanks for pulling the quote. I agree. It is a distinction of "should be" and must be". FWIW: Uncle Bob clarified this exact topic in a recent youtube interview with Primeagen (about 5 months ago). The gist is if the length to have a single cohesive function is 50 lines, then so be it. It was clarified that clean code does not give a hard and fast limit (but does give a very weighty recommendation to go for small functions based on a number of heuristics).
Perhaps the Uncle Bob principles do not create the best code, or most clear code, but more dogmatic adherence does make it harder to create the code I see on a daily basis that is so much worse and much more unmaintainable.
I suppose if the options are "Uncle Bob's Rules" and the straw man of "No Rules" that could be true. That's like if you gave yourself the options of "one hand behind your back" or "two hands behind your back" in a fight when we already knew how to fight with "zero hands behind your back".
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".
> I came away feeling like Uncle Bob is more interested in playing rhetorical judo than anything else
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.
> If Linus Torvalds were to give advice on how to build an operating system's kernel, I feel as though I should listen.
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
You know what? That's still good advice. I think many should take it to heart.
>It is economically better for most organizations to conserve programmer cycles than computer cycles.
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.
He's the son of a pastor, he's definitely interested in rhetorical judo.
tldr;
Premature optimization is the root of all evil. (Donald Knuth)