top | item 29203295

It's probably time to stop recommending Clean Code

120 points| azth | 4 years ago |qntm.org | reply

88 comments

order
[+] usefulcat|4 years ago|reply
> Martin asserts that an ideal function is two to four lines of code long

This is the kind of assertion that's going to need a very compelling, realistic example in order to avoid being seen as anything other than some sort of purely theoretical fantasy.

[+] hesdeadjim|4 years ago|reply
I’ll take a 100 line commented function over one that delegates to a dozen other methods that aren’t used anywhere else. The more functions you add to the mix, the less clear the logic becomes and the easier it gets to break things.

Abstractions have their place, but the evolution of my 15 year career has led me to use them as a last resort when I find myself needing to copy code more than twice.

[+] Waterluvian|4 years ago|reply
It seems fundamentally nonsensical. It’s like saying a sentence is ideally 7-14 words long.

A sentence should be as long as required to concisely get across one idea.

“A function should be as long as required to concisely do one thing” might be a better sentiment. But even then it will have exceptions.

[+] SonicScrub|4 years ago|reply
It's worth noting that the book opens by explaining that none of the rules within it's pages should be treated as iron-clad law. Rather they should be treated as guidelines. They should be followed more often than not, but a skilled designer/developer should know when it's appropriate to break them. Keeping functions short is usually good advice, as it helps prevent developers from breaking more important rules such as "one function should do one and only one thing". When writing a function that's becoming too long, memory of this advice should raise a flag in a developer's mind to reconsider restructuring their code. Reconsider. Not act as a robot and restructure to hit some arbitrarily limit. The book is very clear on this.

It's been a while since I read Clean Code, but I seem to recall the author giving numerous examples of bad code where it was obvious the developer attempted to condense their code into as few lines as possible at the expense of clarity. This immediately proceeded the advice to keep functions short. The author does this as a warning to not follow this advice blindly. Commenters on this thread are cherry-picking advice from the book and ridiculing it by presenting edge cases where the advice would be bad. To which I imagine the author facepalming and saying "yes, that was the entire point".

[+] treeman79|4 years ago|reply
Multi hundred to thousands of lines of code in a single function that are pure spaghetti code used to be the norm in my early career.

I was certainly guilty of it at times.

The clean code movement has drastically improved the quality of most projects I’ve seen.

I see plenty of garbage but nothing like 20 years ago.

[+] ugjka|4 years ago|reply
I've seen a lot of Go code where things are chopped or abstracted on purpose just to make it look nice on "paper" but it is actually really hard to follow what the code does
[+] danielrhodes|4 years ago|reply
…until somebody makes it a lint rule, and you are tasked with making the examples. :-)
[+] Swizec|4 years ago|reply
The easiest way to write unreadable code is by splitting your functionality across 20 clean small functions.
[+] svnpenn|4 years ago|reply
That advice is absolute garage. That would be like writing a book, but you can only have four sentences per page. Maybe fine for a kids book, but any real author trying this would be laughed at, rightfully.
[+] ajuc|4 years ago|reply
My mine problem with Clean Code is Uncle Bob technique for refactoring big functions into classes.

When you have a long pure function that has several local variables and changes them in several places - you can't trivially split it into smaller pure functions (because you would need to return several values from each of them).

You could return Pair<X,Y> or similar, but that's clumsy in Java. Sometimes these values form a single consistent type - then it's simple - make a class out of them and return that. But often they don't, they are just conceptually independent parts of the algorithm. For me that just means the function is cohesive, either change the algorithm or keep away.

Uncle Bob's advise is to make a new class out of that function, change the local variables into private fields and split the code into methods of that class with side effects modifying these variables all over the place.

This results in exactly the kind of code this article complains about, and I agree it's awful. We managed to change code that was explicit about what it does, with no side-effects and no easy way to break it accidentally - into a mess of small interlocking surprises. Whole classes of programming errors that were basically impossible previously are now easy to make.

Can this state even happen when I'm in this function? Should I check for it? Can I call this function after I called that other function? Can I change this variable in this function after requirements changed? The answer after the refactoring is "Dunno - solve this intricate puzzle to find out."

[+] chrisseaton|4 years ago|reply
The problem with almost all of Martin's advice, and the advice of all those other full-time conference speakers who apparently never actually write any code, is that it's entirely unsupported. It's random opinions thrown out there.

Where is the data or evidence for his ideas?

Nowhere.

Even a few that call themselves 'scientists' still don't offer any evidence. I don't know why we tolerate it. Why doesn't someone shout out 'where's your data' at their talks?

[+] gregjor|4 years ago|reply
90% of the "good" advice from Uncle Bob is unattributed restatements from Kernighan, Plauger, Ritchie, Yourdon, Myers. Big revelations like "DRY" and "SOLID" are buzzwordy rehashes from The Elements of Programming Style (1974) or Myers' books on composite software design.

I've wondered what experience and successes "Uncle Bob" has on his resume that makes him an authority. Whenever I read his articles I get the feeling he's a pompous fraud.

[+] skybrian|4 years ago|reply
Because it's not practical. There are few people in a position to do research on entire software teams.

Also, even when it's possible to research something, external validity (whether it applies in another situation) is often a matter of opinion anyway.

We still need ways to share what we've learned from experience. But I think case studies, telling stories about what we learned in particular situations, are about the best we can do.

[+] zz865|4 years ago|reply
The problem I have with Clean Code and other design philosophies is there are so test driven. TDD adherants will tell you that lots of unit tests will make the code clearer, but I'm not convinced. All the hoops people have to jump through with dependency injection, test code overhead I'm not convinced any more that all this clean code is better than the stuff we wrote 20 years ago that was just tested at the final product level.
[+] dktoao|4 years ago|reply
My own experience is that companies that have some sort of formal automated testing have MUCH higher quality code. A testable codebase has to be modular, people spend less time chasing bugs and can refactor more confidently. That being said there are some over-the-top testing philosophies that are more actively harmful than helpful.
[+] handsaway|4 years ago|reply
In my experience, TDD adherents tend to work in languages with no type systems (JS, Ruby, Python) or languages with types of insufficient expressivity to enforce invariants in the code without significant effort (Java). Unit tests are ultimately failings of your type system, a sufficiently advanced type system with dependent types would make them entirely redundant IMO. Integration tests are still useful as a compiler can hardly be expected to check the validity of cross-application-boundary concerns (unless?).

That said, I think TDD is a cargo cult. I think a better approach is to determine on a case-by-case basis if a test is useful for whatever you're working on and developing a sense for that is part of becoming a better software developer. Things like coverage metrics completely obliterate this nuance and lead to some of the most obvious, ridiculous tests I've ever seen.

[+] spywaregorilla|4 years ago|reply
Yes, I hate the mentality that unit tests are the holy grail. I work in data science. A lot of data scientists are very mediocre programmers. The solution was for software engineers to assert that data scientists at our org should work more like them. Test coverage is pushed as a critical metric. But it's really a terrible fit for what we're doing. Data science QA needs to track the state of the data at different steps in the pipeline. Unit tests, imo, are better suited to uses cases that deal with user input where you can concretely cover a good spectrum of expected states.

Not all unit tests are bad of course, but many in my context, imo, are unhelpful.

[+] barbazoo|4 years ago|reply
I've grown to rely much more heavily on integration level testing for that exact reason. One still has to mock certain dependencies such as 3rd party APIs but it's far more reusable to figure out how to do that imo than figuring out how to mock every single class you're interacting with.
[+] mike_d|4 years ago|reply
I write concise procedural PHP with 0% test coverage for all my personal projects. They handle easily 2-3x the traffic of $dayjob and don't fall over on a regular basis.

We need to go back to writing code like we are on resource constrained systems, which forced you to be explicit in what you did and think about why you were doing it.

[+] cjfd|4 years ago|reply
Perhaps when you just release something once for once customer and never look at the code again. If you actually want to maintain something and release it multiple times this is very untrue. Let us say that a feature takes twice as long to implement the TDD way. So without tests it takes N and with tests it takes 2N. Sounds like a clear win for omitting the tests, right? No, very wrong.... How do you even know any of your previous features still work? Testing? Hmm.... not automatically if I get the gist of what you are saying... Manually? O dear.... Hmm.... You do realize that it is very easy for code to have unintented consequences, right? So, ideally, with every release any feature could be broken. You have to test them all N.... manually... If there are m releases this would be m N. If m is some percentage of N. E.g, on average you release every 10 features, we see that development time suddenly becomes of the order of N^2. Ow.... the TDD development time is still order N..... Could it perhaps be that this statement is actually incredibly short sighted....

Then there is the code quality issue. The stuff that was written 20 years ago was somewhat hard to refactor because noone dared because who knows what would break. So basically every code base would degrade into a mess and become harder and harder to maintain over time. A code base that is maintained with automated tests can be improved as time goes on with relatively low risk.

[+] jjtheblunt|4 years ago|reply
mocking and dependency injection (what a stilted phrase!) seem to have be championed by "Uncle Bob" (definitely not my uncle).

perhaps the pertinent take-away is "be sure you test thoroughly" rather than "do things my way and you will mess your code up egregiously, likely increasing the cognitive load to work with it beyond sensibility, and maybe test thoroughly as a side-effect thereof".

[+] skybrian|4 years ago|reply
Whether and how to use mocks is a different decision than how much to test. There are people who write a lot of tests and also dislike mocks.
[+] nowherebeen|4 years ago|reply
I kind of agree on some level. The amount of work required doesn't necessarily justify the benefits. Especially for non-critical features.
[+] mkl95|4 years ago|reply
With enough time and effort, anyone can write code that adheres to some coding guideline. Meanwhile a disproportionate amount of developers struggle to provide practical, concise documentation of their code. I don't want to dive into Mr Clean's legacy code to find out how to call the stuff needed to develop some feature because he's too smart to write it down somewhere. I'd rather have a more informal codebase where the developers have taken the time to explain what each relevant thing does and the quirks of each non trivial function.
[+] lcuff|4 years ago|reply
Sandy Metz, a Ruby lecturer, also advocates 5 line functions. I'm keen to have functions limited to 24 lines, because as someone with significant vision challenges who uses large fonts, that's as many lines as I can get on a screen, and I want to see the whole function at once.

But I also think it's worth asking "what language are we in"? When I'm writing in C, I'm going to be maxing out that 24 lines, because it's sufficiently low level that seems appropriate. With powerful languages like Python and Ruby, you can accomplish so much with 3 lines of code, maxing at 5 doesn't seem terrible. With Java (yuck, not a fan), one of the problems is there's so much boilerplate for every function that it just seems like the screen is filled with non-informative blah-blah-blah.

[+] zmmmmm|4 years ago|reply
Most of the discussion is about the rather secondary topic of function length, but the actual article is much more indepth. I love the dissection of the render method (which is in fact, only 4 lines long):

So... imagine that someone enters a kitchen, because they want to show you how to make a cup of coffee. As you watch carefully, they flick a switch on the wall. The switch looks like a light switch, but none of the lights in the kitchen turn on or off. Next, they open a cabinet and take down a mug, set it on the worktop, and then tap it twice with a teaspoon. They wait for thirty seconds, and finally they reach behind the refrigerator, where you can't see, and pull out a different mug, this one full of fresh coffee.

Worth a read for the instructive value of the critique - whether you agree with either point of view, much is to be learned in the "space" between different viewpoints.

[+] 0des|4 years ago|reply
I love succinct analogies like this. Anybody could understand that.
[+] marginalia_nu|4 years ago|reply
In the context it was written, it was pretty good advice. Maybe a bit too far on some points, but it was a solid step in a good direction. This caused the book to be praised. But we tend to elevate praised books well beyond what's reasonable, dogmatically follow their advice even when they cause problems, and recommend them even though they're responses to a discussion that nobody has been having for 15 years. Same with Gang of Four and a bunch of other books.
[+] blippage|4 years ago|reply
"provided that we aren't too dogmatic about how we define 'one thing'"

And herein lies the problem. If I define a function that calls two other functions, does the caller do one thing, or is it two? The main() function ultimately does everything. Does that mean that main() is the worst function in an application?

I studied programming in the 80's, and I seem to recall that evidence suggested that large functions were not necessarily more unreadable nor unmaintainable than small functions.

So in the end, are we are forced back into a circular argument: good programs are written by good programmers, and bad programs are written by bad programmers. How do we know that they're good programmers? Because they write good code!

Parenthetically, I've always been suspicious of his "Uncle" moniker.

"Functions should have no side effects." So, Haskell it is, then. No printf for you, monads is where it's at.

"output arguments are to be avoided in favour of return values." So what about functions that affect multiple values? Are we supposed to use structs everywhere? What about error conditions? Are we supposed to use hybrid variables, which are often regarded as a source of problems?

No silver bullet.

[+] poulsbohemian|4 years ago|reply
I'd say it's long time past recommending "clean" anything. Every time I hear someone talk about "clean" writing, "clean" design, "clean" code, or anything else I want to vomit because it's meaningless noise. You challenge that same person to define clean and they spew out nonsense. Production of any creative work, whether it's architecture, code, writing, design, etc is hard and messy. Compromises are made. I can't help but think of Christoper Alexander, IE: the timeless way - yes, there is a something there, and it tends to be minimalistic, but is that "clean?" I just want to have conversations without pithy language.
[+] 46756e|4 years ago|reply
Clean code is specifically a book about how to write code well. This isn't criticizing writing clean code, but specifically what that book recommends programmers do.

I wouldn't say the mission of trying to create "clean" code is a bad one. Certainly there is terrible code to read and maintain, which implies there are good ways to write code.

[+] mnd999|4 years ago|reply
“Modern” is the same. It means basically fuck all.
[+] kafkaIncarnate|4 years ago|reply
This is why I never understood or liked NPM. Apparently it's the DRY methodology that I don't like, one thing per function.

This is one step closer to understanding a Bash coder. Just get things done, because you don't have the time to worry about how it was done. Maybe a bit of optimization but really optimizing some string or numeric loops that are split second over optimizing functions or operations that take longer periods of time are much more important. Readability and the ability to debug is also important.

Stop worrying about the Purity Of Essence of your code, you'll end up turning on your own kind while muttering something about the fluoridation of the code supply.

[+] mysterydip|4 years ago|reply
I used to only make a function if a block of code was used in more than one place. But now if performance isn't an issue (for example a block of code that isn't in a tight loop), I'll take a chunk that together does one thing and make it a self-descriptive function for readability purposes.

Instead of:

function foo() {

// this does abc

big block of code

// this does def

big block of code

...

etc

}

and having a function I have to scroll pages through, I now do this:

function foo() {

DoABC()

DoDEF()

}

[+] lido|4 years ago|reply
Yeah, what we really need is elements of programming style written in C, Python, or Go.
[+] pmcollins|4 years ago|reply
if i could distill clean code into one word it would be “small”. small functions, classes, files, changes, and iterations. i don’t see anything wrong with this but understand there are differing points of view. what i do think is important is that developers get placed on a team that is aligned with their taste. how you feel about uncle bob can be a revealing interview question.
[+] tpoacher|4 years ago|reply
The only real metric worth using here when discussing such issues is wtfsploc
[+] 0des|4 years ago|reply
Does your code work? Can your interested peers tell what's going on?

If yes, great, your code is fine. Clean code is a meme foisted upon us by bloggers and the equivalent of dev influencers.

Edit: Can we have one thread without the political topics overtaking the discussion? It's Friday, come on.

[+] mdoms|4 years ago|reply
Can your code be easily changed, or will a change in one place cause a domino effect all over your code base? Does a simple change in requirements require a change in one (or a few) places or is it a "shotgun surgery"? Is the difficulty of changing your codebase negatively affecting the viability of your business?

This type of attitude to what's "fine" is immature and in my opinion represents a clear and present danger to businesses. I have seen several companies brought to their knees because their codebases were so difficult to change.

And that's not even touching on issues like security, performance, reliability, etc.

Code quality matters.

[+] 0n34n7|4 years ago|reply
Goes a bit deeper. Can you replace a source of data with another in a few lines (besides the new implementation) and not break a whole chain of dependencies and associates unit tests? (apart from "does someone knows what's going on") - i.e. code at scale.
[+] danielrhodes|4 years ago|reply
My criteria is: how long does code last during product iteration and spec changes. If it’s immediately thrown away every time requirements change, the code probably ain’t that great. If it is relatively easy to evolve, something has gone well. However, if you can’t change the code because it’s such a complex web of interdependencies where you musta modify the requirements, the code is very bad.
[+] backoncemore|4 years ago|reply
Pleasantly surprised to see that this is a technical discussion instead of a critique against Bob's political views. I went into it fully expecting the latter.
[+] elzbardico|4 years ago|reply
Unfortunately I think that the motivation for that is the same.