215 comments

[ 1.7 ms ] story [ 249 ms ] thread
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.
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.
Except having these discussions is really hard. It ends up being you against uncle bob and you are not gonna win that argument against a senior-er engineer.
Don't think so. If the senior engineer is worth his salt he should know that programming is always about finding a balance between several aspects of a given problem - some of them technical, some of them human. As long as the code does what it's supposed to do the rest is mostly up to personal taste - so yes, you can have an insightful discussion, but there's only to learn, nothing to "win" for all participants.
Yep, but in practice Seniority comes either from experience or time in the industry. So you'll undoubtedly meet seniors that won't try to find that balance.
> 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.

You should certainly be flexible around it, visit them every now and then to update. Also, we should enforce them once they are set.
Really don't like the enforcing. Okay, mixed feelings there.

Rules should help you along, perhaps set a framework for your thinking. But they never should limit you in achieving your goal. When establishing a rule you should also specify what you want to achieve by it, how you'll measure its effects and under which conditions it should be removed.

Rigid rule enforcement has a strong danger of shifting the priorities of the developers to the detriment of your business.

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.
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".

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.
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.

I had a similar experience. There was a developer in my group that adhered to Clean Code. Four line functions made everything verbose. When he left I had to take over all of his code.

The one part of Clean Code that saved my sanity was his choice of names and flow. That made it somewhat easier to navigate.

I have experiences with other code where it was so abstract, it took much longer to figure out.

I like to go by the rule: "write code for the average developer". I think finding that balance makes it easier to grok the code and maintain it.

Have you worked with 5000 line functions? Just trying to set breakpoints in them at meaningful points is a nightmare. Give me 1000 5-line functions any day - providing of course they have sensible names (and ideally don't cause unexpected side-effects etc., though when a function is 5-lines long, that's fairly easy to spot; in a 5000-line function, fuhgeddaboudit. My personal guideline is "it should fit on a screen" (at a window/font size you'd normally edit code at - it doesn't vary that much from dev to dev.)
> and ideally don't cause unexpected side-effects etc., though when a function is 5-lines long, that's fairly easy to spot

Not easy to spot side effects if the 5 line function calls 999 other 5 line functions, which you have to do if you replace 1 function with 1000 functions. When people start to arbitrarily break up tightly coupled implementations into tiny functions you will get a much worse mess than if they just kept it as big as it was when they developed it.

Small functions are great, but forcing people to write small functions is not great. They are two different things.

That simply overdoing it. 50 line functions are fine, 100 line functions can be useful. 5000 line functions should be an exception and 5 line functions can be useful if you can still give them a clear name and they end up being either re-used or are internal. If you start exporting 100's of functions you may want to re-evaluate the way you are interfacing your modules.
The question wasn't "what is the best way to write code" but "is 1000 5 line functions preferable to 1 5000 line function". Of course the best way is somewhere in between, but that wasn't what we were discussing.
I'd even say ANY 5000-line function can always be improved by breaking it up into 1000 5-line functions, but it would be rare that would be the ideal way to rewrite it either.
I agree but so much depends on context. This is like arguing a painting should not have too much blue paint. But what if you're painting an ocean?

In a programming context:

- If the business process you are trying to model clearly has 20 discrete steps is would be silly break them into 4 groups of 5 steps just to hit the ideal function size.

- Coming up with a good name for an abstraction in a moderately large program can be very tricky, but a poor name is hurting more than helping. Don't split it if you cannot think of a good name.

- If breaking out into a function requires passing in 10 variables then it may become harder to read than simply inlining it. You're probably not abstracting correctly in that case. Etcetera.

I've seen so many over-abstractions from junior programmers trying to be "clean" I wonder if these rules of thumb aren't hurting more than helping.

even worse try debugging it with async or gofuncs or threads... just not doable. You can't easily say "jump into that fork" and it just gets worse from there. Small functions and tons of async and then reflection and polymorphism and it's undebuggable.
If by "arbitrarily" you mean "at east 4/5 line boundary", perhaps, but in that case it's virtually impossible to give the smaller functions meaningful names. Personally I wouldn't tick a PR with functions more than about 30 lines generally, pretty much the only exception would be something like a simple switch statement that happens to need to deal with 50+ cases, and does so with a single function call for each case* (it may be preferable to do that using a data structure to map input cases to function pointers, but generally that makes code harder to debug too). Arguably that's "forcing" other devs to stick to small functions. I've never seen a 1000+ line function that has anything like any decent sort of unit test coverage, for a start.

*) message handlers in raw Win32-apps come to mind

I have, at two different companies, in fact, and 1000-line chubbers at a couple of others. The 5000 line functions weren't great, but they weren't that bad because the code was pretty straightforward, doing one thing after another top to bottom.

Over time they all got gradually broken down into smaller functions/methods.

(Disclaimer: I was not the original author of any of these.)

Yes I have, and some even approaching 10kl. It's actually easier when you only have one "dimension" to worry about, and can simply scroll up and down instead of having to jump around between functions, or worse, files.

That said, a lot of these huge functions were basically for implementing defined step-by-step processes, and comments delineated the sections as well as loop and condition ends.

It's not better unless the smaller ones do a poor job of breaking up functionality. One class owning, say, all the code related to making database calls is much better than a bunch of database code strewn throughout unrelated logic.
I think I have realised after ruining a previous codebase with overeager abstraction. Most code should read like fiction, not a reference book.

That is they should read top to bottom with minimal jumping around and information hiding.

(Of course not in all cases, YMMV, etc)

Hyperbole aside what can actually be said doesn't make great bumper sticker advice: functions shouldn't be too long because that's hard to understand, but they shouldn't be too short either because that's also hard to understand. I think a decent rule of thumb is if it doesn't fit on your screen it might be too long.
I have. It was mostly a big switch statement, and individual bodies could be read in sequence. It was easy to work with and you could read it by scrolling, instead of jumping around a file.
Agree, but picking those 1000 sensible names is usually also very hard, and when people get it wrong and use bad names, sometimes it's worse than nothing.
The first thing I do when refactoring spaghetti messes is to remove all useless and counter-productive functions and abstractions, so I can actually get one single, huge function containing all the process.

Only from there it becomes possible to have the broad view, reorganize and (re-)factorize the code again properly.

So if I had to choose between both, I'd take the 5000 lines function any time, since it makes my job easier.

Obviously, both of your extremes are totally unmaintainable and it doesn’t matter which somebody prefers of the two.

What I think matters in the general case is the likely mutation rate of the code, and the familiarity of its design. Both of these are speculative, subjective factors, so right-sizing a function is basically a gut feeling. It can prove wrong in hindsight and others can disagree with your choice from the get-go. So it goes.

But a library function that won’t need much maintenance and implements a well-known pattern, style, or algorithm can safely be much longer than something that’s likely to see a lot of change (especially by other people) or that’s very unusual.

> Obviously, both of your extremes are totally unmaintainable and it doesn’t matter which somebody prefers of the two.

But we work with 1000 five lines functions all the time in high level languages.

All the stuff done on array/list/hash and the likes (map,fold/inject/reduce,sum,select,reject..) are five lines functions.

Why would you not want that for your own types?

If they’re operations used in a hundred places, then they should be whatever size they need to be.

Of course there are a lot of 5-line functions in that set, and also some 500-lines functions and also many functions that recurse or stack very deeply, and also some very shallow ones. They’re polished stones.

But function length isn’t a useful goal in itself and if it’s made to be, you’re deprioritizing the many other factors that matter.

I'd rather work with complex data than with complex code. Just structure the data as best as you can and then use functions that are simple to operate on that data. This will help keep code complexity down and makes it much, much easier to refactor because usually you'll be operating on a small subset of the data anyway.
You can have 1000 5-lines functions in all languages, not only java. Typescript FE projects can be as hard as java ones to read. It seems to me that there's a lot of prejudice against java, maybe because it has been used by many low skilled devs developing poor quality codebases.
It seems to me that there's a lot of prejudice against java, maybe because it has been used by many low skilled devs developing poor quality codebases.

I don't think that's the reason. The cultures of Java, C#, and now TS/JS definitely seem to worship abstraction far too much, and as a result you end up with what's classically known as "enterprise" code. I've seen the work of "low skilled devs" in other languages like PHP, VB, etc. and that leans far on the under-abstraction side; you're far more likely to find lots of copy-pasting and kiloline-long functions (if they even bother to write more than one), yet that code tends to be easier to work with in my experience because it's largely linear and not nested.

After some years I recently had to work on a very old PHP project with linear and untested code. Full of bugs, unmanaged corner cases, unnecessary code duplication, magic numbers...we didn't even try to understand the business logic or reverse it.

I still think it's just low skilled devs vs professional ones. You can make similar mistakes with many, many languages, it seems to me that Java went out of fashion, considered old.

As a counterpoint I'm working on an ex-PHP4 codebase, that's been "developed" (in the loosest possible meaning) by assorted people over 15 years. It's full of security vulnerabilities and the worst code I've seen in my career.

I'm also working on a modern Symfony PHP codebase using popular 3rd party Symfony bundles. I've used Symfony for a decade but find the compiler passes and levels of abstractions (particularly when debugging 3rd party bundles) slow me down and make the code less understandable. Yesterday I spent half a day tracking down a bug which was the wrong setting in the config, but was obscured by too many layers of indirection.

I'd take the terrible but understandable codebase any day as I know I can debug and improve it more easily.

I upgraded a quite old java app from java 6 and spring 3 to java 8 and spring 4.30 in 4 hours without changing a single line of code.

I guess we both may have good anecdotal examples :)

> The cultures of Java, C#, and now TS/JS definitely seem to worship abstraction far too much

Unfortunately true. C# has been around long enough that some of the more 'mature' developers I've worked with still treat the book as gospel. Trying to find the actual code that does actual work can be tricky. That's started to change in recent years fortunately - especially with newer developers joining thanks to the increasing popularity of Core.

As to the discussion: my basic rule of thumb is that single responsibility should be applied to every level of the stack, whether that be a single microservice, a single controller, a single function, or even a single property. Provided you follow that, many of the discussions vanish.

For instance, how big should my functions be? Big enough to do one thing. Any bigger and they are too big. Any smaller and they are too small. Regardless of lines of code.

So it narrows down to a simple decision - in the context of the code I'm writing, what is the one responsibility of this function? Following that practice it rarely ends up as a one-liner as it is hard (not impossible!) to encapsulate a complete piece of work in one line. It also rarely ends up more than a screenful as that usually means it's doing more than one thing.

The important point is to switch the question away from "How big should it be?" and instead consider "What is the sole thing it is responsible for?" [1]. Then write code accordingly. After all, we know from the bad old days of being paid per line of code that 'lines of code' is a useless metric.

---

[1] I'm aware that defining the responsibilities too tightly leads to fragmented code and a multitude of functions/abstractions. I'm also aware that defining them too loosely leads to huge functions and monolithic code flows. I'm not offering an answer, just saying we need to rephrase the question.

Ye flat functions are way underrated. It is so hard to keep track of the call stack when reading code for me.

Deeply nested code is read only, unless the problem is nicely modelled as recursive in some way.

And what kind of problem is nicely modelled as recursive?

It’s when your data items have children, which can have children, which can have children...

Clean code does emphasize that the structure of the class file and layout of functions should strive to allow for the entire class file to be read in a single pass from top to bottom.

Though, I'd agree that is quite different when someone puts in a linter that requires 5 line methods and scatters the flow of logic.

IMHO, "imperative shell with functional core" (which implies consistent levels of abstractions) is a follow up the book could really use.

To another extent, I think small and controversial rules of thumb like "keep functions short" can easily be taken out of context. A refactoring I often see that can be done when methods are poorly broken up is class variables can be converted into local variables once some of the methods are inlined.

The point of the small function is so that a developer can keep track of fewer moving parts in their head while reading. If the shattering into functions causes the class to have 30 class variables in order to support that, then it's very much a case of following the wrong heuristic. IIRC, clean code holds a highest priority on the developer only needing to keep track of 3-5 things at a given time (and pretty much everything else is in service of that goal)

> 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.

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”.
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...

> 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.

> A compiler should do this for you.

Point of inlining is to make it explicit that it is an ad hoc implementation for that location, so you don't have to be afraid of adding more stuff in it just to solve a local problem.

Easily solved with a comment and that frees you up from using it in more than one place if you ever feel like it. Inlining is a very low level implementation detail, I think making that explicit is something that you only want to do if you are breaking out the function for clarity and to be able to name it when it only lives in a single location. A bit like an assembly macro.
Not sure what a comment would solve. The point is that if you are making an ugly implementation for some reason, then inlining that is the right choice, breaking it out into a function that can only safely be called in that exact spot is by far the worst technical debt you can create. If you have a pile of shit then don't put it on a fan and spread it all over the room, isolate it so you know where it is and other parts can ignore it easily.
A compiler should do this for you.

Unless it outputs source code, it can't.

From what I got the context he more or less implied a compiler due to the language used (C++). I know that's now how C++ started but nowadays I think you can assume that when people discuss ways to program C++ it is by using a compiler rather than a code generator.
It looks to me like there's some confusion here; Carmack is talking about inlining for purposes of source code management; compilers inline for purposes of performance optimiziation. The two seem to be getting conflated in this thread.

So GP points out that, no, unless your compiler is outputting source code (therefore changing what you see as a programmer), having it inline the function does not accomplish the goal at all.

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!).

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.
That’s true of every methodology used by inexperienced or reckless developers. There is no shortcut to quality or experience. But Carmack’s approach has the distinction of ultimately delivering results by orienting one towards the objective necessities of the problem at hand rather than, for instance, the never-ending spiral of arbitrary and inefficient abstractions that OO ideology commonly produces.
It is like claiming you can teach someone how to read by having them watch and emulate already proficient readers. Hrm...
This feels like an alternative way of thinking about DRY. Make the running progra@ not have to repeat itself.
> 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.

Man, I really disagree with those first three. Branches should be avoided whenever possible, not leaned into. Especially within abstractions.
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.

Slightly offtopic but I have summarized my own 20 years of clean coding experience into a system:

https://www.fabianzeindl.com/posts/the-codequality-pyramid

I like that post of yours.

If there's one thing I slightly disagree with, it's that I would put Code Performance below Test Performance, but that's because my personal experience ([1]) has been that code performance affects test performance more than anything else.

But still, this is an excellent model, and I'm bookmarking it. Thanks!

[1]: https://gavinhoward.com/2019/08/why-perfect-software-is-near...

I think that in terms of "what to strive for", the pyramid makes sense in a generalist way.

And, code performance is, as you say, part of test performance. If the tests take too long, perhaps the code needs to be improved. OTOH, I have got a few test suites that run for 30 minutes or more, just because the search space is so big. But that's code that only needs to be tested once.

> And, code performance is, as you say, part of test performance. If the tests take too long, perhaps the code needs to be improved.

Yes. I mentioned this is in the last section. When test-performance is alright, typically you don't need to optimize code anymore.

For me, optimizations in the "code performance" category are for example trading off a generalist API for a more complicated tweaked version that reaches into details and enables caching. The kinds of optimizations game developers do where they make the code less general and pretty, but more performant.

> OTOH, I have got a few test suites that run for 30 minutes or more, just because the search space is so big. But that's code that only needs to be tested once.

What are these suites testing?

> What are these suites testing?

One is testing all permutations of actions on some observability thingy: does everyone get the updates they requested in the correct order, basically, but suppose you have 3 "clients" that can connect and disconnect at arbitrary moments, and we're testing a single insert-like operation on the initial empty data, there are already dozens of combinations.

Another one is some operation on two arbitrary JSON objects and an inverse, and I wanted to make sure that it works for things that look like normal looking objects, small and large, and for "random" objects. Fuzzing with hierarchical data. This code is in JS because it has to run in the browser, so it is not super fast.

Interesting, thank you.

> This code is in JS because it has to run in the browser, so it is not super fast.

Try profiling heap allocations in Chrome Debugger. In my experience especially array-allocation can slow down JS-code a lot.

Thank you, let me know if you have suggestion and how whether you can apply it to your work.
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)

It's probably time to stop recommending "It's probably time to stop recommending Clean Code"
Reposts are fine on HN after a year or so, if the article is good for HN*. If the article is bad for HN, best not to post it in the first place. I don't know about this article.

* https://news.ycombinator.com/newsfaq.html

Quite interesting that it's always been the same article
This stuff still pops up in the wild so the article is a useful corrective. But one wonders how that sample code ever passed without comment.
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.

Which SOLID principles have you found issues with, either theoretically or in practice? They still seem like broadly sensible principles to me.
For example the Open Closed principle is often times used to justify a plethora of inheritance chains rather than having a more generic class that can handle cases based on some parameter. Specially when all a subclass does is modify some class attribute or change some implementation by one line. Obviously some times it's good to apply the principle, but not EVERY time it fits.
I've recommended it before (I think there's even a blog post of mine written in direct response to this article floating around somewhere): A Philosophy of Software Design is really good.

It isn't so concerned with all of the details about what exact decisions you should make ("how many lines should my function have?" etc), but rather the high-level design choices that go into those decisions. A lot of the book is spent on the philosophy of interfaces, which is the author's way of describing the boundaries between a piece of code and its caller.

It's written by John Ousterhout, of Tcl fame (among many other things) based on his own experience, but also his attempts to teach software engineering to students, and the techniques that worked well for those students in practical situations, so I think it's fairly well grounded in actual advice.

I was also thinking about this book when I read that article. It's really an excellent book and it's condensed and because it's written by an expert in teaching it's easy to grasp.

It's also recommended in this article:

"Update, 2020-12-19 After suggestions from comments below, I read A Philosophy of Software Design (2018) by John Ousterhout and found it to be a much more positive experience. I would be happy to recommend it over Clean Code".

Haha, I read the article immediately after this comment and saw that comment, I think it wasn't there last time I read it (so presumably before the end of 2020!). I agree that it's probably not as good for complete beginners, but it's really good for anyone with a bit of experience looking to move from "I'm just writing code" to "I'm writing actual software".
I'm still amused by claims that the book is dogmatic when these paragraphs are in the opening chapter:

> Consider this book a description of the Object Mentor School of Clean Code. The techniques and teachings within are the way that we practice our art. We are willing to claim that if you follow these teachings, you will enjoy the benefits that we have enjoyed, and you will learn to write code that is clean and professional. But don’t make the mistake of thinking that we are somehow “right” in any absolute sense. There are other schools and other masters that have just as much claim to professionalism as we. It would behoove you to learn from them as well.

> Indeed, many of the recommendations in this book are controversial. You will probably not agree with all of them. You might violently disagree with some of them. That’s fine. We can’t claim final authority. On the other hand, the recommendations in this book are things that we have thought long and hard about. We have learned them through decades of experience and repeated trial and error. So whether you agree or disagree, it would be a shame if you did not see, and respect, our point of view.

Generally dogmatic people don't say, in short, "We could be wrong, we don't think we are, but go see what other people have to say, too.".

The claim is not that the book is dogmatic, but that its worshippers (for lack of a better word) are. The infamous Design Patterns book, despite having a similar "disclaimer", has had a similar effect.
(comment deleted)
Quoting the person I responded to, who wrote:

> the book was inconsistent and dogmatic.

How do you square that with:

> The claim is not that the book is dogmatic

You are claiming they didn't write what they wrote.

It's dogmatic in the sense that it often pursues some point (such as very short functions) to the level of a dogma, without much regard for the benefit accrued.
And no evidence. Other programming books cite academic or case studies. Martin's book is essentially a long appeal to authority to himself. If he'd written like, Quake or something I might buy it, but he hasn't.
(comment deleted)
If this verbiage is only in the introduction, it will not carry much weight. People, and especially IT people like rules, that you can indiscriminately apply.
If people claim to read a book and skip chapter 1, did they really read the book? I'd say no, they only read part of it. It's not the authors' fault that people skip a chapter that generally provides the context for reading the rest of the book. That's on them. Don't be foolish, learn the context of what you're reading before you read it. That is what should be encouraged.
My point is that people are better at remembering and applying specific rules. And it is my perception that people involved in software development like explicit rules. This is partly due to the exponential growth (and thus low level of experience) and also because it feels more objective.

“Code should be readable” is a higher level goal that is inevitably subjective, so it is harder to enforce than for instance, DRY.

This is kind of like a recipe book opening with a disclaimer saying "Some of these recipes might make you ill."
Immediately followed by many, many pages recommending that you add asbestos for flavoring, or lead for color.

A disclaimer doesn't change whether something is done or not. And it's a rather small fig leaf compared to the rest of the book.

I didn't remember that in the introduction. But if that was the case the book should've included more counterexamples and a less imperative language.
Why? How strong would their [0] arguments be? Is it worth increasing the size of what was already (had to look this up) a 464 page text to include weak arguments about other people's views? Or, you know, people could actually read the giant disclaimer (it was more than those two paragraphs, I just copied them here) and then take it at face value, go learn other ideas about programming. Practice your own judgement.

No book, no philosophy, no technique, no programming language, no ChatGPT can ever remove the need for the programmer to think for themselves and determine their own approaches to programming and which approaches are appropriate for which programs.

[0] I keep using they and their, there were multiple authors for the book. Several chapters were written by people other than Martin.

> Why? How strong would their [0] arguments be?

I imagine the strength of their arguments would be approximately related to the strength of said arguments merits / demerits. You don't have to present an unyielding argument if your goal is to educate your reader and help them to reach an informed position.

He's saying he expected more, coming from a different background with a culture of rigor.

There are literally hundreds of better books for newcomers. As just one, I would mention Programming Pearls by Jon Bentley. I would say it communicates a much more healthy approach to the craft.
I always recommend "The Pragmatic Programmer" (Hunt, Thomas) and "Code Complete" (McConnell).
Honestly anytime I run into someone spouting something "Uncle Bob" has tried to propagate in our industry while trying to sell books and his "knowledge" I find that it is safe to assume they are probably a perpetual junior level developer.
Good points, interesting context coming from a history student and now with some experience, I'd like to read more on your thoughts if you have a blog.
I personally like this rule of thumb, if a function is hard to test, it's time to simplify some code.
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.

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).

If OP expects a book, every coding book, will give advices to be followed literally, that's a problem. After 14y I know there's no recipe valid for everyone and every scenario and that's what I always says to new junior devs I have to train: it always depends. Are you prototyping? Are you developing critical infrastructure code, or a website? Restricted resources industrial appliance code?

I read that book when I was young, I followed some advices, some I didn't, then I made mine ideas.

Of course reading others books is also very helpful to contextualise Clean Code.

What I really, really recommend is NOT to read The Clean Coder book by same Rob Martin, it's one of the few I have been unable to even finish, it has been unbearable to me.

Haven't read but it's definitely time to start demanding people write clean code. My house often gets messy, and so does my code. And then I clean my house and refactor my code. I try to steer away from refactoring for the sake of making my code look sexy, which it does, but putting time on the calendar for an almost guaranteed need to shuffle things around when you get a chance feels good and makes your future self happier.

A lot of people have no idea how to write clean code and those parts of codebases and those people should be avoided as much as possible. I'm not trying to hate, but wow some devs have no shame.

The issue is that the meaning of "clean code" here isn't code that's clean and legible.

It's code written in the typically over engineered OOP-heavy Java style. Think lots of "patterns", massive amounts of inheritance, interfaces and factories everywhere.

I made the decision of abandoning any clean-code-ish practices within my teams long ago.

I still demand the code to be structured sensibly, be clear, well named and documented.

"clean" means more formal and specific?? That's often the opposite of clean. But ok understood. I should have read the article.

Let's not add LOC if it's not helpful. I finally picked up Typescript to strengthen my Javascript code, and I'm a huge fan now, but a lot of the added features aren't always helpful whether because of project size or complexity. And the typeahead -> add code features on IDEs add a lot of bloat like yeah interfaces on _everything_.

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?

Of course the people signing the checks don't care about that directly, but it's something you can do as you add functionality.
That’s my understanding as well, quality for the sake of it isn’t appealing.

But it is known that a quality foundation enables everything in the business, reduces waste, churn, and increases profits.

Yet when it comes down to actually doing it, nobody cares. Fast and cheap always wins over quality.

That’s not necessarily a problem overall, but it is for me because I derive no satisfaction from shipping low quality code and the constant firefighting that results.

This is a struggle because it’s the vast majority of the demand for software, which makes me want to quit the industry altogether even though I love programming and improving “legacy” codebase. But there seem to be no actual demand for this skill despite the appearances.

Think of it this way. Imagine you hire an electrician to do some task. If while he's there he cleans up some stuff around where he was working, that's welcome. If he starts rewiring your whole house for no particular reason, even though everything was working fine, and billing you by the hour, you might not be pleased.
Absolutely but this isn’t the situation I’m talking about here.

To reuse your analogy, I’m talking about a restaurant who has a mess of an installation, several panels and breakers, not all up to code, and the electricity frequently shuts off taking hours to figure what happened and how to restore it. It’s also a mild fire hazard and there have been a few close calls that could have burned the place down.

The son in law, a hobbyist electrician, did the installation and extended it for free while the business was starting up and growing. He didn’t do it right but it works and got the restaurant so far.

Now the restaurant is very popular but the constant blackouts are driving people away and hurting profits. The mild fire hazard and not following code irks the authorities and insurance companies and they’re threatening closure. It needs an extra stove to keep up with customer demand but no one can figure out how to hook one up to the existing rats nest, let alone do it safely.

The owner knows this is a problem and is looking for a proper electrician to fix the mess.

In real life, I’d expect the business owner to agree with the electrician on a plan so that either the retaurant stays open (maybe at a lower capacity) so that the tidying up can happen gradually, or close down for renovations for a while so the whole thing can be overhauled quicker.

But what happens in our industry and in my experience is that instead the restuarant owner asks the electrician to tighten a screw or two here and there so that this power socket isn’t as wobbly anymore but then demands even more additions to the janky system and demands they be done as fast and cheap as possible as this is now the top and only priority.

Nothing really improves but the owner (at least for a while) can hide it from the authorities and insurance company because he has a quote and a specialist working on the system. Then the owner can blame the electrician because “he wasn’t able to balance fixing up the installation while extending it, so we’ll fire him and look for someone actually good this time”. Rinse and repeat ad nauseam.

Well, to put it another way, "clean code" can be the means of achieving an end, but if you tell them you're replacing existing code with clean code that sounds like a waste of time. You should be able to describe some concrete goal you are working towards and they should leave it to you to worry about how you do it.
> They all say they do of course, while actually being afraid of doing this because it takes more time and money.

That is my experience too with most places - they say that they support clean, well tested and maintainable code and then turn around and ask things to be delivered in unreasonable time resulting in quick and dirty code.

> 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.

Indeed - furthermore, because the code lacks quality, it requires more effort to manage and (intentionally or not) helps them to demand headcount which requires more managers which results in them building hierarchies, moving up and having their fiefdom. It is a smart approach too in a perverse sense: how will you get to a position managing 100 people if you only delivered with 5 people due to your efficiency? You'll be labeled as "lacks experience managing large orgs".

So… how do you deal with this on a personal level? How do you retain any shred of sanity or enjoyment from your work?

I can’t stand self inflicted toil and the stress from constant firefighting which is invariably the result of these short term policies. It makes me want to quit programming altogether and work in something totally unrelated even though I love programming. Just not the way employers want it.

You stand up and talk about it publicly.
Who listens, though? Look at seminal work like “the mythical man month”… while most people “in the trenches” agree that nine women can’t make a baby in one month, project managers and executives still very much think it’s possible 40 years later.
You can explain it to them, but you can't understand it for them..
The issue is, managers want these things, like a nice code base, but are unwilling to pay for it. Or more correctly you have to pay most of the cost of that Hodge podge of hacks. So while they can off load that cost to you, why would they make an effort to improve the situation?
So what do we do? Working this way drives me crazy, depressed, and unhappy; I don’t want to do it anymore.

I don’t want to be part of the problem either by becoming one such manager so that others will have to pay the cost of my benefit.

What’s left? Opt out, live on shoestring budget for the rest of your life, and hope it works out?

There are thousands of software engineering jobs out there, how many have you tried? A handful? Increase your sample size.
One option is to work for a company where an important part of the product they make is code itself, e.g. an SDK. Another is to switch to a programming language that tends to attract quality-minded people, e.g Rust, Go, Haskell.
One is very different from the others.

The one whose designer said “ They’re not capable of understanding a brilliant language“

Ha, well I’d agree with you on that, but even so I’ve seen Go mostly adopted by people who are capable and care.
You switch jobs. Of course, there are managers everywhere and of course generally they have similar motivations. However, every once in a while you get one who was a developer-at-heart once (or still is) and does give some amount of shit even when they are looking for their usual goals. If that person doesn't happen to be evil, you stick with that team until things change (maybe the manager moves on). The good thing is that when good managers move, they tend to take the good and trusted ones with them to the new place. Assuming they saw your efforts and value your skills, you can move with them. Alternatively, if they are not the type to do so/you can't move with them, switch again. Either way, side effect is that your pay goes up too due to the switching - just don't do it too frequently.
What you described reflects that corporate structure and might be industry wide. They look at code as only valuable in the monetary return it makes. They might even consider it a cost center. That will always be a less than pleasant place to work in an organization.

So find places and maybe start at industries that consider their code an asset or profit center.

It’s ultimately just a valuation perspective, but understand to a business everything has a market context or business case. It’s can be infuriating, but better to know the system than blindly suffer through it.

I think as professionals many of us like to take pride in our work. A natural way to do that is for code to be "clean". The problem, as I see it, is that this is often a subjective metric and what we take pride in doesn't always align with what delivers tangible value to an organization (a point you alluded to).

Clean code can bring tangible value, but big rewrites taken on for that goal can often not. I personally try to be more pragmatic--what is valuable depends a lot on context and the point in life of a particular project+team+organization.

Sometimes the most valuable thing is to ship a messy code that works to get the ball rolling or prove viability of an approach. Other times it's to make sure what you deploy is bullet proof to avoid liability or expensive downtime. The value of clean code will be reflected by your team and organization's needs at that point in time.

> what is valuable depends a lot on context and the point in life of a particular project+team+organization.

For sure, and I only target companies or teams that are mature, struggle to move forward because code quality grinds things down to a halt, and that express the desire for improvement there.

However, there are very few companies that 1. Even have the awareness to realize this is where they’re at, and 2. Actually mean it. I’ve been passed over many times in favour of someone else who ships code quickly and cheaply by lowering quality. Every place I’ve ever worked at, quality is the least concern in relation to the others.

Now for a startup that has limited runway and has to find product market fit for example then it’s totally different. Focus rightly is on shipping stuff at the expense of quality because survival is on the line. In a few years, once this company has made it, they’ll look for someone to clean their codebase up.

My broader question though is whether I should keep branding myself as a “code cleaner”. Despite a (minority) of companies advertising positions for that skill, my experience has been that the vast majority simply don’t care. And those that do, don’t actually care all that much. It’s a struggle though because I get no satisfaction from shipping low quality code and eventually end up quitting out of boredom or frustration. I’m now at a point where I’m considering doing something entirely different from programming because I can’t derive any job satisfaction from writing low quality code and the resulting constant firefighting that results from it.

Any organisation with messy code doesn’t care about clean code - sure, they may regret it, they may wish it would magically go away, but if they cared then they wouldn’t have allowed all the dirty code to pile up. The only organisation in which your work will be valued is one in which the code is already clean.
Interesting insight.

I’ve never come across such an organization though. There are a handful that I think are in this spot where quality has mattered since day one but they’re so popular that I don’t stand a chance to even get noticed when applying in a sea of thousands of applicants. They also don’t hire very often.

And they’re very hard to find because what do you even search for? The ones I know, I found empirically through word of mouth, reputation, or just plain randomness by stumbling upon.

> 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.

I am thankful that people like you exist, but after having taken on that role out of necessity myself, I've mostly been left disliking the experience.

Working withing under-documented and under-tested "legacy" codebases, especially the kind where the developers got clever with design patterns, both putting them in when they make sense as well as when they didn't. Those codebases are hard to navigate and hard to refactor (outside of fully automatic renaming/extracting interfaces etc.) and even harder to change - in many cases because you're not even aware of the design assumptions or aspects of the architecture that were made by someone who is now long gone.

For example, I worked on a system where users could submit corrections to data and those could either be accepted or removed. There was a CorrectionFormService, that also was related to CorrectionEntryService, but both of those were abstract and had corresponding CorrectionFooFormService and CorrectionFooEntryService instances, as well as an additional CorrectionFooService. In the database, there also were foos and foos_corrections tables, the latter of which was related to correction_forms, sometimes with additional related tables. The problem was that the logic didn't actually fit such neat structure, so depending on whether you're working with Foo, Bar or Baz, more and more of the methods had to be overriden, as well as new ones added, none of which was actually documented. In most cases you were supposed to have the original_id column point at the foos (or whatever) table id that had the actual data, except when for some reason original_id was the same as id and instead you had something like object_id store that information. And on top of that, there were database views which were used for querying the data, where there were additional rules for the id, original_id and object_id column values, with about 10 prior Jira/Redmine issues related to how this data should work. Not only that, but there were also requirements for accepting some of the data recursively (since parent_id was needed for some of the data structures), but not in all of the cases, only when some other enum column had a particular value in a related table.

Long story short, the more you looked into it, the more details spilled out, to the point where you could not keep a full mental picture of it in mind. Some of the implementation was okay, some of it was ridden with iteration overhead and accidental complexity. Contrast getting to write new code and getting things done in hours, versus spending days if not weeks (across multiple developers, actually) working to get things done within this pre-existing setup. Maintenance work will typically take longer than developing new features and, in my personal experience, will be more mentally draining.

I find that you can’t always fix everything. But the biggest hurdle is actually getting buy in (even if there is already buy in and that’s what you were specifically hired to do)

Rewriting is tricky. It’s sometimes necessary (outdated and unmaintained language, obscure tech you can’t find people for, or sheer tech bankruptcy) but it can also not solve anything. As you implement the new version, you rediscover all the arcane business rules, edge cases, and sheer craziness that you didn’t know about and the rewrite ends up either lacking these or becomes The Mess v2 because you had to bolt all this weirdness on after the fact making the new codebase suck in a different way but overall as problematic as the one it replaced.

It will also take much longer than estimated for the same reasons.

I’m not super keen on rewrites in most cases.

> As you implement the new version, you rediscover all the arcane business rules, edge cases, and sheer craziness that you didn’t know about and the rewrite ends up either lacking these or becomes The Mess v2 because you had to bolt all this weirdness on after the fact making the new codebase suck in a different way but overall as problematic as the one it replaced.

Sometimes I wish we could just look at an implementation that's meant to serve a business process and go: "Listen, we gave it a shot, but the technology and developers we have available cannot provide a satisfactory implementation for this, without making the entire thing a liability. Can we simplify the domain instead?"

I've actually tried this in the past (in a more mild form) and it has actually worked - sometimes getting a paragraph or two of changes approved in a 50 page spec can save you weeks of work ahead of time, which you'd otherwise spend because someone didn't realize which parts of the spec are technically unfeasible.

Sometimes it's actually easy to reason about, such as when you're doing a rewrite from AngularJS or something else that's deprecated to something more modern, and can suggest a few simplifications to "prioritize quicker iteration, shipping core features quickly and simplified UX for less friction", which might mean simpler components.

Or you might find yourself in an inflexible domain, having to replicate "the old thing" close to 1:1 which will take way more time than either writing or maintaining it would. There, rewrites don't make that much sense. At that point, if you need more modern elements (runtimes, libraries, frameworks), I'm not even sure what you could feasibly do, aside from setting up a new service and rewriting paths for particular new API endpoints to the less rotten codebase, but that has challenges of its own and can get out of hand.

> Can we simplify the domain instead?"

I can see this working in more mature and healthier orgs, but they’re a minority. As cynical as it sounds, most orgs are dysfunctional meat grinders where you’d get attacked for not having the skills to pull it off, in my experience.

Nobody brings this up for that reason and the madness continues unabated, ensuring the project becomes a slog over the medium/long term with the expectation that most will have moved on before it gets there.

This is also how we run our societies, leaving problems for later so that the next guy/president/generation has to deal with it at the point where it’s impossible to ignore anymore. Instead of much earlier when it requires much less effort and is reversible.

I think you can easily roll an analogy with having a plan documented on 5 napkins, bits sprinkled over 127 emails, a few doodles on a white board, 93 photos on your phone, some files in some folder and 20% in your head.

You could start execution right away! Every bit of information is readily available, sure, the numbers in those emails might change in the process but you can just document those changes on additional napkins.

But there is a stage 2 to the plan that needs to happen in 3 to 6 years. By then there will be tens of thousands of relevant emails, hundreds of napkins, you cant find the original photos and you have so much related information in your head that that important thought is buried much like the email... I'm sure I have a folder someplace on this computer.... where is the chat log?

You made promotion so stage 2 is left to the new hire. They had a wonderful resume but for some reason it takes him forever to progress. They wont argue it would have been nice if you wrote down what stage 2 involved years ago. In stead they ask for 20 more people to organize the data.

That makes sense. But how do you retain your sanity and job satisfaction if you’re not an early part of that first phase?
I suppose you could keep your eyes more on what you've done rather than what needs to be done?
I get the exact same joys from work. Taking an old but functional and profitable codebase and cleaning it up, bringing it up to conventions and standards, refactoring. At my current job, feature work is always a thing, but I make refactoring boards and get people on board with bringing in refactor tickets every sprint, etc.

So while not 100% of my job is refactoring, a large portion is. And sometimes I convince them it’s time to rewrite one entire section or another, so a couple of sprints at a time might be dedicated to just that.

I guess I’ve gotten lucky, probably a combination of that and being outspoken about what needs to be fixed.

> 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?

There's many sides to this.

Even Boeing doesn't value clean code (as shown by the 737 MAX disaster where 9$/h coders were hired in India). So you can imagine how much a local non-tech company values clean code.

There's also that a lot of these companies simply... couldn't recognize clean code even if they saw it! Since they aren't competitive with top players, the caliber of programmers they attract is nowhere near the level of engineers at real tech companies. Unless these companies "luck out" with an extremely high caliber hire, they won't really know there's a better way of doing things [0]. Think places where source control is still seen as too complicated or not needed.

[0] https://www.hanselman.com/blog/dark-matter-developers-the-un...

It would be prudent to include some data to substantiate your insinuation that some non white non European programmers did the Boeing MCAS code. The 9$ per hour wage maybe less than that of a burger flipper in the USA, but the cost of living is vastly different.Being an engineer , if you are ignoring this significant parameter then we definitely know one source of the issue .Maybe you are not an engineer but a fine arts major who “learned” coding by yourself.
https://news.ycombinator.com/item?id=20353342#20355864 . Hope this link works . This discussion was 2 years ago.

Still my word holds good . Boeing management didn’t give crap about engineers opinion be it 30$ per hour or 5$ per hour.

Boeing asserted that no "critical flight software" was outsourced, but it specifically didn't label the MCAS as "critical flight software" when describing it to the FAA.

What we know was outsourced is "flight test software" which sounds a lot like something that should have caught the 737 MAX failure...

I dont know - i think it still holds up. It just takes a little experience to learn to not be so literal with it.
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.

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”.

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.

If you say the book “covers correctness and understandability in nearly every paragraph” then I’m convinced we must be talking about different books.

For example, the book presents a rule for class names:

> Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager, Processor, Data, or Info in the name of a class. A class name should not be a verb.

What a proclamation! We could connect this to correctness and understandability. But the book does not. You could say that it is harder to understand a class named “SomethingManager” because “manager” is vague, and more specific words are usually preferred. Instead, the book presents a rule, without explanation or example. Kind of like the Strunk & White book on style, and if you like Strunk & White, then we disagree on at least two books.

That’s my general complaint about the book. Too many proclamations, too much doctrine, not enough explanation or foundation.

Let’s say my site has a download image feature that embeds per-user digital licenses into downloaded images at download time. This might take some processing so there is a queue and an abstraction representing the downloaded file. There are also several endpoints serving different types of image files.

What shall I call the unit of code orchestrating these download processes, if not ImageDownloadManager?

ImageDownloadPreparer?
ImageDownloadManager also communicates with the frontend about its state, so it’s not a completely preparatory process.
I think I would go with a class called Image, which provides a method called download().
ImageDownloadDispatcher, ImageDownloadScheduler, Image DownloadQueueManager... basically, the fix for "FooManager" names is to ask which aspect(s) of Foo the manager manages. Lifetime, storage, execution order (and according to which criteria), resource limits, uniqueness, exclusive access, efficient search, ...
All of those hint of functionality that is not present here. The code is just managing image downloads, not dispatching them, scheduling them, or even touching the queue (which is handled on a higher level).

ImageDownloadManager manages image downloads. Why use a name that makes less sense?

How about ImageDownloader?
There's whole pages of explanation about understandability in the Meaningful Names chapter preceding that distilled rule. Specifically it explains about making meaningful distinctions by avoiding vague words like Data or Manager or Info.
> and if you like Strunk & White, then we disagree on at least two books

I'd be happy to shred Clean Code, but keep your hands off my Strunk and White!

Strunk & White is really bad, try Style: Lessons in Clarity and Grace as an alternative.
> Strunk & White is really bad

I disagree. Also, tastes vary and it fits mine. Thanks for recommending Style, though - it's very good and I fully get that it may be preferred by other writers. It also serves a slightly different purpose to Strunk & White, so the two can co-exist on the same writer's bookshelf.

There's an entire chapter on naming things, with tons of examples. Why would a class be anything but a noun or noun phrase? It's not a hot take
I think you may have missed the context for why I brought it up in the first place.
I didn't. Ironically, you (or the juniors you're referring to in your original comment) missed the context in the book. You said he presents it as a rule, without explanation or example, but you happened to cherry pick the one paragraph from the 13 pages of Meaningful Names that didn't have an explanation or example.

You're right, the problem is vagueness, and that context is already set a few pages prior. It would be redundant to repeat the same reasoning.

Clarity! That's my goal for my code: https://m.youtube.com/watch?v=6sNmJtoKDCo
IMHO, that was an excellent talk, well presented and insightful.

I found your argument starting around 28:00 interesting. I’m not familiar with the specific tools you’re using and their idioms, so it’s possible that I’m missing some context here. As a general principle, I like to make it very clear where code is doing I/O and what is happening with any data involved. If this function receives data in one format, transforms that data into another format and finally sends that data to the database, then to me that seems like a useful separation of concerns and a clear representation of the overall behaviour. Maybe I wouldn’t choose the same name/structure as User.registration_changeset in your example, but the principle of hiding the implementation details of the data transformation seems helpful, for the same reason it’s helpful to see Repo.insert() instead of five lines of SQL at this level of the code.

Ah, the talker is not me, I'm just a fan of the concept
Ah, sorry, I misunderstood. In any case, it seems we probably agree! :-)
> 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.

> 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.

At the call site, unless it's a keyword argument, you don't know what the parameter is named.

IDEs help with this of course, but it's not just the "2 options becomes 3 options" situation that recommends against them

That's not a problem with bool arguments, that's a problem with the language and tooling.
That can be resolved with enums.
If your language/tooling and naming is bad then no, enums won't resolve it either:

Is this a username or a state?

  create_user('ADMIN')
Same for, could be a variable for the admin username, can't be sure can you:

  create_user(ADMIN)
Even this:

  create_user(User.ADMIN)
If you tried to check in code with a 2-member enum I would try and stop you ("don't reinvent booleans"). Honestly this is totally fine early stage code, but I could try and mollify you with two functions, which feels more YAGNI. If we end up being wrong about that, we probably need more of a role/rights system than an enum will provide (I bet, for instance, you'll have a lot of business logic per-role that shouldn't just get stuffed in a single function).

This is what drives me nuts about seemingly simple rules like this: they let you feel like you can turn off your brain. You check all the boxes Clean Code tells you to and you feel like you're done. But you need to think systemically, and little simple maxims like "don't use bool flags" or "enums > bool flags" let engineers miss the forest for the trees over and over again.

> 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)

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.

Code is a kind of language. It is entirely possible to tell if person speaks clearly, easy to understand, or not. You may even train an ML model on Wikipedia section "Simple English".

The same applies to code. It is not 100% deterministic metric, but it is relatively easy to argue and have a consensus upon.

> it is relatively easy to argue and have a consensus upon

If you’re a reasonable person, yes. But when you’re not, all kinds of crazy patterns evolve.

E.g. a boss saying “I’m perfectly ok with functions that take more than 100 arguments.” (Compiler does not even support this.) and code being copy-pasted dozens of times being perfectly readable, but is a sink-hole when you refactor.

Clean Code (Martin’s book, and the movement in general) is my weapon when stubborn colleagues will not listen to reason. Sometimes they will listen to authority.

>It is entirely possible to tell if person speaks clearly, easy to understand, or not.

Just thinking out loud here, but isn't correctness(grammar and spelling) the only thing we all(mostly) agree on? For example, someone well versed with old English literature may consider Shakespeare easy to understand, but I certainly wouldn't. I think "speaks clearly, easy to understand" are just as subjective as our notions of clean/idiomatic/readable code.

Someone really well versed will have an understanding of how old English sounds for layperson. Also, we are discussing a situation when everyone is more or less fluent in a given [programming] language, and the probable culprit is just flexing their recently gained obscure expressions or constructing overcomplex one-liners.
Are you in Bangalore, Mississippi, or Edinburgh? All three can talk with those in the same accent and dialect easily but are going to labor when talking with each other.

What happens when half of your code uses functional patterns and the other half imperative/OO?

> I don't like all of it, but it gives one coherent top-to-bottom model.

Completely agree. Bob Martin “Clean” has some really good recommendations, and some subjective advices. Normally the dev team can use this mental model as baseline and agree on which of the subjective advice to follow.

I just had an issue related to this recently, one of the senior software engineer in my team was previously a university professor with no software engineering experience. Every code review is an endless discussion, as he don’t agree with most of Martin “clean” code. So his code not only has several bad smells, but it feels like a completely different dialect. It forced us to have really basic discussions about code practices, even when to use comments (the professor likes to comment almost every line of code).

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.

> 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

It's a very under-appreciated feature, but I know what you mean.

Alongside NCrunch, Wallaby is one of my most valuable purchases. For those not familiar with the feature being referred to, as a continuous test runner one of the great benefits is that the IDE plugins show the values of anything (variables etc) in your code, live. I don't mean some kind of hover-for-intellisense, but at the end of each line you see all the values for all the variables all the time.

So in real-time, as you code, any tests covering the code you're changing are being run and you can see holistically how everything you're working on is changing as you type.

Basic test runners run continuously on the command line. Better ones run in the background of your IDE, possibly adding coverage markers etc. Wallaby goes one step further and actually decorates your code with real values live in the IDE as you work.

It's very good (though, yes, tricky to set up and a little slow).

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.

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?
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.

It reminds me of the Elements of Style, which has for decades had a reputation as the book every English writer should read. In reality, it's a mix of obvious truisms ("omit needless words," gee, thanks, but how do I identify the needless ones?) and grammatical advice where the authors are so confused about the concepts that they constantly violate their own precepts. However, at least EB White was a talented author whose writing was beloved. Seems hard to say much complimentary about the sample code presented here.
(comment deleted)
I once stated a few of the author's criticisms in a forum, one of which was the 3 line function, only to get a reply from Martin himself, saying that he hadn't written that. Unfortunately for him, everybody reads it in the book, and his apostles repeat it, and it's part of his success.
(comment deleted)
I also found it to flip between "this is obvious advice" and "this is awful advice", and I think that's part of the issue. I've had multiple arguments with people where that natural motte-and-bailey led to "oh, you don't like Clean Code? so you think variables shouldn't have understandable names?"
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.

Don’t you think the example code quoted looks really bad? If the code is that bad, why should the advice be trusted?
Don't know if this is really a bad thing - had the same problems with the book, but the apparent contradictions made me really think about the stuff. So I'm quite prepared to consider his advice, I'd never turn it into gospel.
Is the advice to be trusted though, or merely considered?
I think _entirely_ ignoring the kinds of considerations it covers is such a typical failure case for inexperienced programmers (or non-career coders) that it's an excellent prod to expand the scope of what enters their awareness when writing code.

Ideally folks notice discrepancies between their experiences and the things it recommends and find their taste and judgement that way, though it doesn't always work out.

So like, maybe the fact that some of the advice is clearly dreadful if you try it could be a useful wedge against the kind of cult thinking so pervasive in software. I've seen considerably more grief from people treating his SOLID stuff like a bible that clean code. YMMV.

Just because developers lack common understanding on what, how to do Clean Coding, made me think NoCode is the future. Developers are just bad as coding. In general, human being lacks ability to clean code.
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.

> 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.

Actually it can get really complex really fast if the business rules happen to apply to migrations and operations on heavily relational data, especially with composite keys involving date ranges.
Then the advice in clean code and other similar books apply.

Parent comment implied the book isn’t relevant because it only applies to CRUD apps and I argue that they’re the vast majority of code produced so while the book isn’t relevant to 100% of codebases, it is useful advice in the majority of cases nevertheless.

Edge cases are gonna be edgecases and will require tailored advice.