top | item 34843128

It's probably time to stop recommending Clean Code (2020)

202 points| flykespice | 3 years ago |qntm.org

210 comments

order
[+] dietrichepp|3 years ago|reply
My experience is that I run into a lot of relatively junior programmers who are concerned about clean code. Is my code clean? How do I organize my code? How do I make it clean? Should we clean up this code?

I almost never want to use the word “clean” when I’m talking about code.

These days, when someone asks me to review code, and they start talking about “clean” code, I shift the discussion to two points—code should be correct and easy to understand. I think “correct and easy to understand” is a much more useful rubric than “clean”. Obviously it’s still subjective. Code that is easy to understand for you may be hard for me to understand. Likewise, code that is correct for your use cases may be incorrect for my use cases. But it’s much easier to come to an agreement about “correct and easy to understand”, or at least communicate issues with the code using that basis.

Like, “you shouldn’t use boolean flags as parameters, you should use enums” becomes “I can‘t understand the meaning of true/false at the call site, so let’s use an enum instead”. This gives us a very clear, articulable basis for how we talk about code quality.

Of course, “correct and easy to understand” is not the be-all and end-all for describing good code. It’s just a nice substitute for the horribly vague, terribly subjective “clean”.

[+] vaughan|3 years ago|reply
I think the problem with talking about understandable code is that we only talk about textual representation.

We should break out of the ide and visualize code more. I think then we would see the tangled mess we are creating. And then if we started talking about code that was cleanly visualized, we would truly have understandable code.

We need two-way sync for visualizations of all data structures and data flows. And then we need to see the actual data values inline as it flows through the system as we read the code.

Wallaby.js is a great leader in this space. I found it too hard to setup and too slow - but I’m convinced this applied to the entire stack is the future.

We draw diagrams on the whiteboard to explain every system but they are completely absent when we actually code.

[+] thaumasiotes|3 years ago|reply
> code should be correct and easy to understand. I think “correct and easy to understand” is a much more useful rubric than “clean”. Obviously it’s still subjective. Code that is easy to understand for you may be hard for me to understand.

There's a deeper problem here that has driven a lot of disputes over the years.

"Ease of understanding" is not a stable property of code. If you have two separate pieces of code that are easy to understand, and the only thing you do with them is combine them, your resulting one piece of code may also be easy to understand.

Or it might be next to impossible to understand.

[+] zepolen|3 years ago|reply
> Like, “you shouldn’t use boolean flags as parameters, you should use enums” becomes “I can‘t understand the meaning of true/false at the call site, so let’s use an enum instead”.

That's not the reason to use enums instead of parameters for functions, rarely will you not be able to understand what the bool arg means in:

  create_user(..., is_admin: bool)
If you don't then you need a better IDE.

The reason to use enums and for that matter structs is that as requirements evolve, inevitably you will need to extend the code base and at some point `is_admin` alone won't cut it, you'll need an is_moderator etc.

This leads to two things, one is function signature pollution and unnecessary extra validation code, eg. what happens if both is_admin and is_moderator are set?

What about the functions relying on the old signature? Perhaps you make separate versions of the functions, eg. create_admin_user and create_moderator_user and keep the old one with the flag, then you can keep backward compatibility...but at the cost of an api that makes people go insane when using it.

More likely you'd then go to an enum which leads backward incompatible changes, and likely a refactor, which in the event of a library can break dependent code and in all cases is a lot more work than just using an enum in the first place.

Try to write code for the future reader and writer of the codebase. An enum will allow readers to immediately understand that there is a [normal, admin] distinction and adding a moderator would be as simple as adding a [normal, moderator, admin] in there.

Then, assuming you've written your code in a proper manner, your IDE will tell you all the places where a moderator needs to be accounted for in the code base.

[+] ebiester|3 years ago|reply
What is easy to understand code?

What if you have three people in the same code base who write "easy to understand" code for them, but inconsistent with each other?

Even if those three all understand each other while writing it, this will not continue as the team changes and people have to onboard.

As such, it isn't sufficient to have a linting standard: great codebases should have a consistent mental model. "Clean" code, within the context of Bob Martin "Clean", is one consistent mental model. I don't like all of it, but it gives one coherent top-to-bottom model.

[+] treeman79|3 years ago|reply
Been around awhile. Reached the point of. I really don’t care what style bosses use. Just be consistent.

Can’t stand being asked multiple times to change something depending on what order people review code.

I’ve learned to love auto linting for the sole reason that it instantly shuts down any discussion on styling.

[+] cush|3 years ago|reply
It can definitely be confusing to hear "clean" when communicating in a professional setting.

That said, in the book he covers correctness and understandability in nearly every paragraph. It's all the book is about really. It's called "clean" because it's concise and a catchy book title.

[+] nathias|3 years ago|reply
> Obviously it’s still subjective. Code that is easy to understand for you may be hard for me to understand.

there is nothing obvious about this, I think it's very objective what unreadable means (mostly it's complicated without a reason)

[+] ZephyrBlu|3 years ago|reply
I like Carmack's approach to functions the best:

"If a function is only called from a single place, consider inlining it.

If a function is called from multiple places, see if it is possible to arrange for the work to be done in a single place, perhaps with flags, and inline that.

If there are multiple versions of a function, consider making a single function with more, possibly defaulted, parameters.

If the work is close to purely functional, with few references to global state, try to make it completely functional.

Try to use const on both parameters and functions when the function really must be used in multiple places.

Minimize control flow complexity and “area under ifs”, favoring consistent execution paths and times over “optimally” avoiding unnecessary work."

http://number-none.com/blow/blog/programming/2014/09/26/carm...

[+] KerrAvon|3 years ago|reply
Yeah, but read the very top of that post for his updated thoughts. The problem with manually inlining large functions is that they make code impossible to follow or reason about, which over time means that they turn into spaghetti code because people get lazy about reusing pieces from random other places in the same function.

His later addendum is important not to omit, because this is really the key to more reliable code:

> The real enemy addressed by inlining is unexpected dependency and mutation of state, which functional programming solves more directly and completely. However, if you are going to make a lot of state changes, having them all happen inline does have advantages; you should be made constantly aware of the full horror of what you are doing. When it gets to be too much to take, figure out how to factor blocks out into pure functions (and don.t let them slide back into impurity!).

[+] jacquesm|3 years ago|reply
> If a function is only called from a single place, consider inlining it.

A compiler should do this for you.

> If a function is called from multiple places, see if it is possible to arrange for the work to be done in a single place, perhaps with flags, and inline that.

This makes no sense to me.

> If there are multiple versions of a function, consider making a single function with more, possibly defaulted, parameters.

This can be useful, but only if it does not mess up code readability unless you are trying to squeeze your code into a cache.

> If the work is close to purely functional, with few references to global state, try to make it completely functional.

This makes very good sense, but it can be harder than it seems. One of the easiest ways (depending on how far down the call stack you are) you may just be able to pass the individual parts of that global state in as parameters.

> Try to use const on both parameters and functions when the function really must be used in multiple places.

Not all languages support this, but where they do this is good practice. In general: limiting scope and mutability is always an advantage.

> Minimize control flow complexity and “area under ifs”, favoring consistent execution paths and times over “optimally” avoiding unnecessary work.

Is also good advice.

In general you want to avoid nesting your ifs too deeply because at some point you lose track of what the local context is that got you there. Then it is usually better to break out a function and name it well so that that context is clear again. In general, naming things well is hard.

[+] siva7|3 years ago|reply
That would lead to a Big Ball of Mud if given to an inexperienced or reckless developer as i've witnessed many times in my career. The problem with such best practices/patterns/thought pieces is that they stem from highly experienced developers but given to the inexperienced they are like a hand grenade in the hands of an ape.
[+] bambax|3 years ago|reply
> If a function is only called from a single place, consider inlining it.

The main keyword is consider. Sometimes it's a good idea, and sometimes (often) it isn't.

[+] jakelazaroff|3 years ago|reply
Man, I really disagree with those first three. Branches should be avoided whenever possible, not leaned into. Especially within abstractions.
[+] c3534l|3 years ago|reply
This feels like an alternative way of thinking about DRY. Make the running progra@ not have to repeat itself.
[+] userbinator|3 years ago|reply
A common theme not only in software but other industries: Beware of people selling you advice. They are the ones who will breed dogmatic illogical cargo-cults of people whose only rebuttal when questioned is some variant of "because someone who sold me this book that claims it'll make my code better said so", and that can't be a good thing in general.

but we assume that Martin doesn't literally mean that every function in our entire application must be four lines long or less.

Unfortunately I have worked (and fortunately, briefly!) with codebases like that --- not surprisingly, in Java. In addition to the complexity of whatever the code is doing, smashing things into tiny pieces also adds its own complexity. The fact that a short function is "easily understandable" on its own, which is what a lot of proponents of this dogma argue, is useless for understanding the functioning of the whole. The latter is far more important when debugging.

Function length is ultimately meaningless. I'd rather have a 5000-line function that reads top-to-bottom, than 1000 5-line functions if it means I don't have to jump around several-dozen-level-deep callstacks and try to keep track of 30+ character long identifiers to understand how the thing works.

[+] ornornor|3 years ago|reply
From my decade writing software professionally and my current job search, I really question the actual demand for clean code.

That’s unfortunate because it’s my specialty and what gives me job satisfaction.

I love fixing things. I actually enjoy working on a crappy codebase that has made the company money but is now too hard to maintain/extend and needs cleaning. Adding tests, refactoring, extracting functionality to discrete functions, figuring out what the black box actually does, etc. This is what I’ve specialized in.

However, it seems to me that companies don’t actually value that. They all say they do of course, while actually being afraid of doing this because it takes more time and money. Even if they’re mature enough that survival isn’t an immediate concern anymore, there is time to clean things up, and the mess is actually slowing them down through downtime, bugs, and not being able to ship relatively trivial features/updates in less than weeks.

I also suspect that not focusing on clean code is a strategy many managers have because they can show velocity to their higher ups and get promoted before it all blows up and they’re held responsible.

So what do you all think? Is clean code really actually valuable in the eyes of organizations or will they always take the quicker and dirtier option given the choice? Will I ever find work selling code cleaning (even to companies that ask for it) or should I “rebrand” on fast and cheap code at the expense of quality?

[+] Groxx|3 years ago|reply
Yeah. This fits with my reading of it as well. Periodic chunks of "yep, makes sense" scattered through a really disturbing miasma of questionable stuff and incredibly tightly bound methods sharing gigantic balls of state that must be called in an order, but with nothing that hints at or enforces that order. It adds up to a really horrific result pretty frequently.

I'm not sure how it got its status at the beginning, but I think it retains it through sheer scale - it's a gigantic book for what it actually contains. It batters you with poorly connected ideas over and over and over until you can't tell right from wrong, and in the end you're just agreeing with whatever because doing otherwise gets you nowhere and nothing but pain as you try to read more. It's like a cult brainwashing ritual.

[+] DanielVZ|3 years ago|reply
Clean Code was one of the first books I read as a History student trying to become a self taught developer. From my point of view, coming from the rigor of historiography, the book was inconsistent and dogmatic. Still I took it as a replacement to talking with an experienced engineer, because that’s how it felt and most of the principles were fine when not taken to the extreme.

But now in my career I’ve seen awfully unnecessarily complex code in the name of the SOLID principles and others like DRY. Other times completely misunderstanding what the principles stand for and applying them in hand wavy ways.

This is a personal stretch, but even the marketing behind the principles is a bit of a red flag for me (Clean Code, SOLID, UNCLE BOB????). To me it sounds like someone made a career out of this.

I wish we had a better book to recommend to newcomers.

[+] brundolf|3 years ago|reply
"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.

...What just happened? What was flicking the switch for? Was tapping the empty mug part of the procedure? Where did the coffee come from?

That's what this code is like."

This is such a good metaphor

[+] Octokiddie|3 years ago|reply
> Martin states, in this very chapter, that it makes sense to break a function down into smaller functions "if you can extract another function from it with a name that is not merely a restatement of its implementation". But then he gives us:

[function that restates its implementation]

and

[function that restates its implementation]

The article's main beef is that Clean Code sets standards that it itself does not meet. The author makes that point through the above examples and many others. The sheer number of examples suggests that this is a regular pattern in the book.

The reason seems simple enough: it's really easy to give general advice about writing software. But it's quite another thing to put that advice into practice as the thing you're working on turns to goo, which is exactly what appears to have happened with Martin's model project, FitNesse.

[+] initplus|3 years ago|reply
Even outside the context of refactoring/extending an existing codebase the code examples seem bad, look at his example of a prime generator from the bottom of the article. Think it’s more a case of “your own sh*t doesn’t smell”.
[+] dang|3 years ago|reply
Related:

It's probably time to stop recommending Clean Code - https://news.ycombinator.com/item?id=29203295 - Nov 2021 (88 comments)

It's probably time to stop recommending Clean Code (2020) - https://news.ycombinator.com/item?id=27276706 - May 2021 (658 comments)

It's probably time to stop recommending Clean Code - https://news.ycombinator.com/item?id=23671022 - June 2020 (8 comments)

[+] andirk|3 years ago|reply
It's probably time to stop recommending "It's probably time to stop recommending Clean Code"
[+] ravagat|3 years ago|reply
Quite interesting that it's always been the same article
[+] DotaFan|3 years ago|reply
The amount of money I've spared to a companies by first learning the importance of Clean Code before I've tackled projects are immeasurable. Sure, Uncle Bob might be to opinionated and you might not like him, and some coding practices you might not agree with, and are indeed unnecessary today, but rules are important, and this is what we're missing today. Ground set rules, so everyone is on the same page.
[+] phendrenad2|3 years ago|reply
What's important is that people have the shared language of Clean Code (or some other repository of best practices, could be integrated into your company styleguide, for instance). I.E. If Uncle Bob said "Don't do X" then we can have a conversation in that context if, in this scenario, doing X is acceptable. Without that logical reasoning, people will debate the very existence of "not doing X" as a best practice.
[+] leftbit|3 years ago|reply
> but rules are important, and this is what we're missing today.

Programming rules are important - they make you think before you break them. Just don't turn rules into dogma - otherwise your devs will be more concerned with following the rules than solving the actual business problems. And you don't want that.

[+] admo1|3 years ago|reply
Am I the only one who thinks it's a great book and that it should still be among the top books to be recommended ? Maybe not the first one but easily in the top 10.

Each time I see the same discussion on HN about this book, I'm always wondering why I see so many people going in either direction : not following the advice of the book at all, or following it too religiously.

For me, the philosophy of the books was always about "having a set of guidance on how to improve your code, IF YOU NEED IT", and never about a strict set of rules that were mandatory to follow in order to have "clean code".

The fact that the book gives strong statements about what clean code is, such as "a function should be only one line" was always an exaggeration for me, albeit a useful one to actually makes the reader think more deeply about its function length, but never about forcing you to split your 1000 lines functions into 100 functions of 10 lines, if you don't need it.

[+] leftbit|3 years ago|reply
Exactly that - the subtitle really says it all: "A Handbook of Agile Software Craftsmanship"

A craftsman is an expert in his field who applies his knowledge and techniques judiciously, not religiously or automatically.

[+] ravagat|3 years ago|reply
You're not alone, its a great book and one of the better ones to be recommended but shouldn't be the only one. Unfortunately a lot of folks do exactly that.
[+] YouWhy|3 years ago|reply
Towards the end of my tenure in a team, a new boss instituted mandatory viewing sessions of Martin's training videos; I grew somewhat averse of them.

While the heart of Martin's teachings are about expressivity and semantics, the praxis is much more about recipes than about what matters to me as a professional, which is:

1. Well-definedness of the computation being done (in a mathematical sense of the word),

2. Engineering, that is establishing of a connection between human narratives about what the SW should do with the program's structure.

Overall, it seemed to me that Martin is first and foremost trying to sell to sell a certain paradigm of competency to corporate cookie-cutter-culture organizations.

My issues with this paradigm of competency are that:

1. Doing it properly requires a massive investment, which does not make business sense.

2. As is often the case, this paradigm makes it easy to feign competence without actually being competent, which is damaging to organizational trust.

[+] inimino|3 years ago|reply
I agree. Martin's approach is amenable to bureaucracies that want to increase (the perception of) organizational control over software projects. It is opposed to the Fred Brooks approach. It favors a lower standard of individual skill and relies on bureaucratic control of the process instead as the guarantor of good outcomes.

We shouldn't be surprised that it's popular, or that it's highly associated with Java (and certain other languages today).

[+] avg_dev|3 years ago|reply
Wow this is quite a takedown. For many years I was feeling like I let myself down by not reading the book Clean Code. I now feel like, by accident, I did exactly the right thing. That sample code he quoted is near unreadable to me. I also did enjoy A Philosophy of Software Design; the main thing I took away from it was to avoid unneeded complexity because you want to be able to “spend” your complexity budget for doing actual work.
[+] rgoulter|3 years ago|reply
I generally liked APoSD.

I liked its notion of symptoms of complex code.

I liked its framing of complexity as related to dependencies involved in calling some function. -- If a function has more parameters than it really needs (too many dependencies), or a function has fewer parameters than it actually needs, then it's more complicated to work with than it needs to be (its actual dependencies are obscure). -- I liked its emphasis on "interface" is "what you need to know to use the code", as opposed to implementation details.

I liked the suggestion of "writing documentation before the implementation" as a way of coming up with a clean interface; although I found it bizarre that this logic didn't carry across to "write some tests before the implementation".

[+] crucialfelix|3 years ago|reply
If you read the book, understand the principals and then disagree with a few of them, you will have learned more than if you never read it at all.
[+] dathinab|3 years ago|reply
the thing with that book, and many similar books is that if you take the recommendation verbatim they often 1) only apply to one language 2) get outdated really fast.

What matters is understanding the "general concept/ideas" and apply them "as appropriate".

Most important over obsessing about specific rules is nearly never a good idea, but to some degree that is what is needed to write a book like that to illustrate the idea. And this is also where Clean Code fails, it's often too specific in exactly how to do certain things. But that is also what made it so successful because it makes it accessible.

Today Clean Code and some other such books are still a grate source to compare your experience/knowledge against in a critical way to find additional insights.

Through if you want a book which you can blindly follow or you don't have the experience to judge code style recommendations its probably best to keep the hands of it.

[+] blt|3 years ago|reply
I get the impression that Uncle Bob Martin worked on CRUD-type projects with very few interesting design decisions to be made about what the software should do computationally. In these projects, the programmer's mind wanders. It latches onto the endless design decisions one can dream up about how the source code should be organized.

It's hard to imagine anyone going this far down the rabbit hole of strange code organization when they are facing serious challenges like real-time demands, high concurrency, reliability, complex algorithms, big data, embedded systems, etc.

[+] ornornor|3 years ago|reply
> CRUD-type projects with very few interesting design decisions to be made about what the software should do computationally.

Isn’t this by far the bulk of code businesses produce and need though? Very few businesses actually have the scale or product that requires more than a glorified CRUD with arcane business rules slapped on top.

[+] rhizome31|3 years ago|reply
I haven't read Clean Code but one book that helped me get better at coding was Refactoring by Martin Fowler. There's a second edition that uses JavaScript instead of Java. Anyone read it recently?
[+] moomoo11|3 years ago|reply
Eh. I like that people who actually understand the principles behind Clean Code can share a common understanding of how we are going to put the legos together.

Don’t have to spend too much time bikeshedding. Experienced enough to have some foresight and pragmatic enough to know where to draw the line.

[+] matsemann|3 years ago|reply
Meh, I don't get the hate for Clean Code, or other books like this.

Should you take everything it says as gospel? No. Did reading it in the beginning of my career make me a better developer? Yes.

Its the same as with every other practice. Do I practice TDD? No. Did trying it out for some time learn me things I still apply to code I write 10 years later? Yes.

[+] iainmerrick|3 years ago|reply
Don’t you think the example code quoted looks really bad? If the code is that bad, why should the advice be trusted?