Skip to content(if available)orjump to list(if available)

Clean Code vs. A Philosophy Of Software Design

ilitirit

It still blows my mind how dogmatic some people can be about things like this. I don't understand why anyone takes these things as gospel.

Who else has had to deal with idiots who froth at the mouth when you exceed an 80 line character margin?

And it's not just programming styles, patterns and idioms. It's arguably even worse when it comes to tech stacks and solution architecture.

It's super-frustrating when I'm dealing with people in a professional setting and they're quick to point out something they read in a book, or even worse - a blog - with very little else to add.

This was especially bad during the NoSQL and Microservice hype. Still somewhat feeling it with PAAS/SAAS and containerization. We have so many really really basic things running as Function Apps or lambdas, or simple transformations running in ADF or Talend that add zero value and only add to the support and maintenance overhead.

Always keep in mind that sometimes the only difference between yourself and the person writing the book/blog/article is that they actually wrote it. And that their opinions were written down don't make them fact. Apply your own mind and experience.

trevor-e

I cringe thinking about PR comments I left early in my career.

"akshually this should try to follow more SOLID principles"

But, coming from a formal engineering background, I thought this is what it meant to be a professional software engineer. Little did I know these "principles" were just the musings of a consultant lol. Turns out most folks have good intentions and want a standardized way to write code, but for some reason it always results in code that looks like the Enterprise FizzBuzz meme repo.

sunrunner

For some reason in software there seems to be an incredibly large space for non-evidence based thinking and belief systems.

I wonder if that's because in a lot of cases (depending on the domain) the space of possible valid/working solutions is near infinite, and if you don't have hard requirements that are backed up by measurements you're free to concieve of any valid system structure and justify it as 'better' without that ever being something that can be observed and measured.

Aurornis

> For some reason in software there seems to be an incredibly large space for non-evidence based thinking and belief systems.

The secondary problem is that book authors have become extremely good at inventing pseudo-evidence to support their claims. It most commonly takes the form of “I talked to X companies with Y total number of employees over Z years and therefore I know what works best”.

If you cut out all of the grandstanding, it’s nothing more than “just trust me” but in a world of social proof it sounds like it’s undeniable.

mp05

The mark of a good engineer is knowing when this sort of handwaving is actually meaningful and helpful. Formality for its own sake is anti-pattern, but who am I telling?

Lyngbakr

    > It still blows my mind how dogmatic some people can be about things like this. I don't understand why anyone takes these things as gospel.
IMO, this is one of the key differences between the two books. CC has a vibe of hard and fast opinion-based rules that you must obey, whereas APoSD feels more like empirically-derived principles or guidelines.

pclmulqdq

APoSD is written by a highly respected computer scientist with a tremendous list of technical achievements and also a strong teaching history as a professor, while CC was written by someone whose resume is primarily related to writing about software, not writing software.

wangvnn

CC was written by a man who worked with a highly respected man .. let that sink :)

voidhorse

I think it stems from fundamental misunderstandings about what it is one is actually trying to do when writing code.

Coding is about building a computable model of some facet of existence, usually for some business. When it comes to model building, comprehension and communication are paramount. Performance and other considerations are also important but these are arguably accidental features of machines and, in an ideal world, would not actually affect our model.

Similarly, in an ideal world, we wouldn't even need programming languages. We'd be able to devise and explain computational systems in some kind of perfect abstract language and not need to worry about their realization as programs.

I think a lot of these blanket philosophies confuse people by not emphasizing the higher level aspects of the activity enough. Instead people get hung up on particular patterns in particular paradigms/languages and forget that the real goal is to build a system that is comprehensible to the community of maintainers that need to work with it.

gibibit

It seems that each software design/development system, ideology, and practice has a good reason it was created, and has certain inherent benefits. Each may solve (or at least help with) some common problem.

For instance, abstraction is good and short methods are good to some extent (who wants to read a 2000-line function?), but as John points out in the article, these can be taken too far, where they create new and perhaps worse problems.

It seems there's a pendulum that swings back and forth. We go from big up front design, to Extreme Programming, to a pervasive object-oriented design culture, back to other paradigms.

wglb

Yes, Uncle Bob is certainly capable of being pedantic. A friend of mine, a Smalltalk Consultant, partnered with him for a while. "With Uncle Bob, it's his way or the highway."

His clean code work is certainly pretty dogmatic. As I recall, he says that Java is not object oriented.

But if my memory serves me correctly, his book about C++ (Designing Object-Oriented C++ Applications Using the Booch Method) has some excellent parts. His description of the difference between a class and an instance is one of the better ones.

Then there is the famous sudouko puzzle incident, in which a student trying test-driven development can't get the solution. It is a very instructive incident which illustrates the TDD is unlikely to help you solve problems that are beyond incremental changes. Peter Norvig's solution makes that very clear. Uncle Bob does not seem to realize that.

> Who else has had to deal with idiots who froth at the mouth when you exceed an 80 line character margin?

But I admit in my youth, I was pretty dogmatic about languages and development practices, so I've been that guy.

brianmcc

>> ... TDD is unlikely to help you solve problems that are beyond incremental changes.

Thank you for expressing this niggling problem with TDD. Personally I just cannot use it for "new stuff", I need to explore and create direct with "real" code for anything non-obvious.

arcanemachiner

I'm more of a DTT man myself: Develop, Then Test.

john_the_writer

I suppose it depends on how you use it.. If you view it like you would a bit of paper and a pencil then it works a treat.

I want this to do X when I ask it with Y param. If you write out your spec's before you sit down to code.

When a user enters a bad password... print this to screen

TDD works great for new stuff when viewed as paper replacement.

zamalek

> Java is not object oriented.

Java technically isn't OO in the strictest sense (Smalltalk, Ruby). It is OO in the modern sense (where modern >= 1980s, C++). Though I am not sure if this is what Bob is referring to - I don't have any respect for the man or his ideas, so my biased guess is his definition of OO is shared only between him and his fans.

fuzztester

>But if my memory serves me correctly, his book about C++ (Designing Object-Oriented C++ Applications Using the Booch Method) has some excellent parts.

If my memory serves me correctly, Grady Booch himself had a book with roughly the same title, except that his name would not be in the title, of course, but would be there as the author. I think I read a good amount of it long ago, and liked it.

Edit: I googled, the book is mentioned here under the section Booch method :

https://en.m.wikipedia.org/wiki/Grady_Booch

tqwhite

Bob's had a long life with too much success. He really believes in himself. But, I have to say that the other guy was aggressive and bad even though I am more inclined to agree with him. He willfully misrepresented Bob's ideas. I thought he presented more misguided certainty than Bob. No Bueno.

usefulcat

The "other guy" is John Ousterhout, author of the Tcl scripting language.

Although I can see why you might consider him more "aggressive", I personally think it matters much more that he was, in general, far more descriptive of his reasoning.

Merely having an opinion is the easy part; being able to clearly articulate the reason(s) why one has a particular opinion is far more important, especially in this kind of conversation, and I in that regard I repeatedly found UB lacking.

dsego

I didn't read it as aggressive, more like engaged in the discussion, he really wanted to get to the bottom of things and get his views and opinions challenged, but UB was a bit defensive and closed. Even though UB has strong opinions he wasn't as engaged in building a case for them, he just tried not to budge or maybe he conceded smaller points and then retreated to a less dogmatic stance.

unclebobmartin

Ah, yes. The famous Sudoku solver controversy.

In 2006 Ron Jeffries wrote four blogs about solving Sudoku in Ruby with TDD. His blogging effort ended before he completed the solution. I think he got interested in something else and left the whole thing hanging.

That same year Peter Norvig wrote a Sudoku solver in Python using a constraint based approach. You can see their respective documents here. https://ronjeffries.com/categories/sudoku/… https://norvig.com/sudoku.html

The anti-TDD lobby, at the time, hailed the two documents as proof that TDD didn't work. Ha Ha, Nya Nya Boo Boo.

I was aware of this silliness, but never bothered to study it. I had better things to do. Until March of 2020. Then I thought I'd use Sudoku as a case study for Episode 62 in http://cleancoders.com.

I had not read either of the previous documents and decided to maintain that ignorance while writing the solver in Clojure using TDD. It turned out to be a rather trivial problem to solve. You can see my solution in http://github.com/unclebob/sudoku

I don't know why Ron stopped blogging his Sudoku solver in 2006; but he picked it up again in 2024 and has written much more about it.

The trick I used to solve Sudoku with TDD was to consider the degenerate cases. Sudoku is usually a 3x3x3x3 grid. Let's call this a rank-3 problem. I started with a rank 1 problem which is trivial to solve. Then I moved on to a rank 2 problem which was relatively simple to solve; but was also very close to a general solution. After that I could solve rank N problems.

The TDD strategy of starting with the most degenerate case (rank 1) and then gradually adding complexity may not have been well known in 2006. TDD was pretty new back then. If you explore Ron's first four blogs you can see that he briefly considered rank 2 but opted to go straight into the rank 3 case. The sheer number of variables for each test (81) may have played a role in his loss of interest. In my case (rank 2) I had far fewer variables to deal with.

switchbak

Wasn’t that Ron Jeffries who failed to solve that?

I think that says more about the person at the keyboard and their lack of familiarity with the solution space than anything about TDD per-se. You still need insight and design with TDD, blind incrementalism was never a good idea.

Crenshaw2

I agree.

I have used TDD professionally in several development teams. It's useful in the right team. TDD works well when you are not too dogmatic about it. As with everything, you need people in the team that are experienced enough to know when and where. I think the same is true for any tool, coding standard, best practice or what have you. You have to know when to deviate.

I've also held entry courses at university level teaching introductory programming. I believe that TDD can be a good tool teaching programming. Students tend to sit down and write a complete program and then start debugging. TDD teaches them to write small bits at a time and test as they go.

wglb

What UB's description of how to do TDD does not suggest that there are problems that require a different level of thinking and TDD as he describes, does not account for that.

hliyan

To restate something I've said here last month:

I'm fond of saying that anything that doesn't survive the compilation process is not design but code organization. Design would be: which data structures to use (list, map, array etc.), which data to keep in memory, which data to load/save and when, which algorithms to use, how to handle concurrency etc. Keeping the code organized is useful and is a part of basic hygiene, but it's far from the defining characteristic of the craft.

tikhonj

I disagree entirely. Design is fundamentally a human-oriented discipline, and humans work almost exclusively with code before it is compiled. A strong shared mental model for whatever we're doing is as much a part of software development as any code that runs on a computer.

Programming languages can (should!) be amazing tools for thought rather than just tools for making computers do things; using these tools to figure out what we're doing is a critical part of effective development. The highest-leverage software engineering work I've seen has involved figuring out better ways of thinking about things: developing better tools and abstractions. Tools and abstractions compound since they fundamentally impact everything built on top of them. A good high-level design is the difference between a team that can add some specific capability in a day, a team that would take six months and a team that would say it cannot be done.

mattmanser

I agree, I recently keep having the thought that by far the hard part of programming is code organisation. Whether that's where the files go, or how you've wrapped up commonly used idioms like validation or data access in easy to use abstractions.

It's so easy to get it spectacularly wrong and end up in a mess.

And it's seems so deceptively pointless at the start. It's easy to throw together a greenfield project and get something working, but make a complete mess of organisation. But then it becomes so expensive so quickly to then make changes to that code.

I've joined quite a few projects after 1/2 years of someone else making the project. And so often it's such an imposing mass of code that basically does sod all. Bad architects who don't understand why they're even using the patterns they are or mid/junior-level coders making projects is basically a recipe for the project just grinding to a halt just when it looks like you're getting near the end.

It's when 1,000s of lines are easily refactored to 100s that you start thinking, how can these people honestly believe they have the ability to lead a project? They are so clearly completely out of their depth it's depressing.

We seem, as an industry, to have a complete inability for management to distinguish genuine senior developers from people who will never be.

leidenfrost

My take is that the book also works as a source of authority for aspiring SSR and SR devs.

Comments about code style are usually subjective, and, they can be easily dismissed as a personal preference, or, in the case of a Jr dev, as a lack of skill.

Until they bring up "The Uncle Bob book". Now, suddenly, a subjective opinion from a Jr dev looks like an educated advice sourced from solid knowledge. And other people now have a reason to listen up.

All of this is totally fabricated, of course. But it's like the concept of money. It's valid only because other people accept it as valid.

tempodox

What are “SSR and SR devs”?

dkarl

> Keeping the code organized is useful and is a part of basic hygiene, but it's far from the defining characteristic of the craft.

I'm with you, but I don't think it makes sense to elevate one absolutely over the other as the "defining characteristic." Either one can tank the development of a piece of software and prevent it from coming into being in a useful way.

Arguments about which aspects of software are more important than others usually arise between people who have personally suffered through different ways that projects can fail. Any aspect of software development will feel like the "defining characteristic" if it threatens to kill your project.

geodel

> Any aspect of software development will feel like the "defining characteristic" if it threatens to kill your project.

That does not make sense to me. There can be thousand things that can kill project. One has to consider what are the odds for them.

analog31

Granted, while I program a lot, I'm not employed as a programmer per se. My impression is that programming is easy and fun, but software develoment is hard and laborious. Things like hygiene are among the differences between the two.

jolt42

100%. Dealing with legacy issues is much more laborious and also complicates hygiene.

yuliyp

Systems need to be able to handle all kinds of stresses placed on them during their useful life. The runtime bytecode/machine code/config is what deals with the actual running of the system. The code is what deals with the engineers making future modifications to it. The monitoring system deals with being able to allow operators to ensure the system stays up. All of these affect the reliability and performance of the deployed system during its lifetime. All of them are a part of the design of the system.

Pxtl

> code organization

It's also the code documentation.

Having documentation that is legible is good, right? And so a reviewer is reasonable to say "this is hard to read" since it's failing at its primary purpose.

abirch

Remember when most people give presentations about a technology, they’re in the honeymoon stage

crabbone

Professionals in other industries don't "just" write books. In a sense that usually the field has several acclaimed authors and they put some solid work into ensuring their books make sense. While there are disagreements in other fields, or some nonsense conventions, the conventional wisdom is usually at least good enough to make you a good professional.

In programming it's the Wild West. Many claims are made based on nothing at all. It's very rare to see any kind of sensible research when it comes to the science part of CS. But following rules makes life easier. Even if rules are bad. That's kind of why conservatism exists as a political idea.

WillAdams

This is a big part of why I've been drawn to Literate Programming --- the author can't hide their dogmatism behind text --- they have to show the code:

https://www.goodreads.com/review/list/21394355-william-adams...

ebcode

> It's very rare to see any kind of sensible research when it comes to the science part of CS.

Have you discovered the work of Victor Basili?

nicce

> Always keep in mind that sometimes the only difference between yourself and the person writing the book/blog/article is that they actually wrote it. And that their opinions were written down don't make them fact. Apply your own mind and experience.

But that difference is actually huge. I think you are downplaying the value of the writing process. Assuming that writer is acting in good faith and truly tries to provide the best possible information.

But when you start writing, you start noticing that this idea might not be that good after all. Maybe I need to read more about this? Did you note everything? This idea conflicts with this other topic than I just wrote? What is correct? And the list goes on. When you structure all your thoughts as written text, it is easier to detect all conflicting ideas and mistakes. Not all writers are that good, but you should understand what I mean.

__mharrison__

Writing is an excellent way to determine your opinions. There's a large gap between ideas and those formed when writing said ideas.

Copenjin

You just need to work on one project built by someone that implemented Uncle Bob recommendations blindly when the books came out to know how much they are worth. There were some low hanging fruits to pick at the time regarding trying to be better at software engineering and he generated some text about them.

Full of terrible advices, he never wrote anything significant (in scope and notoriety) during his time as a software engineer like many other prominent authors at the beginning of the agile era. The success is only the result of a wave of junior devs searching for some sort of guidance, something that there is a never-ending need for.

Horrible recommendations that produced a lot of code that is a pain to work on with the abundant amount of indirection it has. Really painful guys.

wiether

> The success is only the result of a wave of junior devs searching for some sort of guidance, something that there is a never-ending need for.

The issue is that, some never grown out of it. I interviewed with companies where they give the book to any new intern/junior. Then, during the hiring process, they don't even ask if you read it, they straight up ask questions about your knowledge of it. Like "What does Uncle Bob says about X in his book Clean Code?". And they constantly refers to it. Some people go as far as quoting it in PR.

The worst part being that once they leave their company, since they don't know anything else, they'll apply the same stuff elsewhere & convert their new company to it.

jbreckmckye

(English tip: advice isn't a countable noun, so you don't pluralise it)

I agree entirely. My encounters with Uncle Bob were as a junior developer receiving advice [no "s"] from other junior developers.

And yes, I too find it suspicious how many mavens of the "Agile era" never really managed to ship anything.

disgruntledphd2

It's important to note that Kent Beck is not one of those people, as he shipped the first unit testing library, as well as a bunch of ones in other languages later.

Like, I personally prefer the bare assert style of testing (like pytest), but the junit style is basically everywhere now.

machine_ghost

Kent Beck is just as bad as Uncle Bob! He drank his own proverbial Kool-Aid and went all in on the crazy XP programming fad he started (... which contains brilliance like requiring pair programming for every line of code written).

Look, both authors are very smart people who have great insights into development that we can all learn from ... but both also have the failing of being way too in love with their own ideas.

It blinds them to the flaws in those ideas, and makes it so when you read their work you have to be skeptical and evaluate each individual idea on their own.

Copenjin

Thanks for the correction, my opinion on the lack of credentials is that in the early days you just didn't need them to become popular, so little content that no one checked. I bit like it's happening nowadays with the anime profile pic twitter accounts acting like they invented AI, with zero code or achievements being shown.

Scubabear68

It is very instructional to read the source code to FitNesse framework.

https://github.com/unclebob/fitnesse

You can see how all his ideas come together into a ball of hundreds of almost empty classes, and gems such as "catch Throwable".

snapdaddy

I was completely ready to agree with you, but the code that I browsed actually looked really good.

https://github.com/unclebob/fitnesse/blob/master/src/fitness...

Yup, OK, got it. The FitnessContext looks a bit rough, but no big deal.

https://github.com/unclebob/fitnesse/blob/master/src/fitness...

That's completely readable, I get it.

https://github.com/unclebob/fitnesse/blob/master/src/fitness...

Again, looks fine.

The main issue with all of these files is that there is quite a bit of boilerplate code, but that's Java's fault, not the code's fault.

I'm sorry, but I disagree. Looking for the most substantial pieces of code that I could find, and they look really good. I'm sure there are some little utils or something that look strange out of context, but I would be thrilled if I were called to work on legacy code and it was this nice.

__loam

A Philosophy of Software Design on the other hand is concise, excellent, and based on decades of teaching experience.

mdaniel

If I win the lottery, I don't want a building named after me I want to donate a copy of that book to every university computer science[1] student and make it required reading

1: I'm aware it's a software engineering book, but since there are very few B.S. Software Engineering programs out there, You Know What I Mean ™

kragen

More importantly, experience writing great software.

k__

The world isn't binary

mbonnet

what's your point?

dgb23

Clean code, design patterns etc. were also picked up by teachers, professors and course instructors.

I think these paradigms and patterns often operate on the wrong layer of abstraction, while mostly ignoring the things that matter the most, like efficiency, error handling and debugging.

But getting good at these things requires a lot more blood, sweat and tears, so there's no easily teachable recipe for that.

bluGill

Clean Code is trying to operate at a layer far more important than efficiency: code maintenance. In the vast majority of cases computers are fast enough that you don't need to worry about efficiency. (part of this is any modern language provides all the common algorithms that are already highly optimized and easier to use than the writing them by hand and so the common places where you would want to worry are already efficient).

Of course error handling and debugging are part of maintenance. However there is a lot more than those two that need to be considered as well.

There is reason to hate Clean Code, but the worst adherents to the rules are still producing far better code than some of the impossible stuff that happened before. "Goto considered harmful" is one of the early steps in fixing all the bad things programmers used to do (and some still do), but you can follow the "rules" of goto considered harmful and still produce really bad code so we need more.

dgb23

From personal experience, the most time consuming maintenance issues arise because of the following things:

- third party dependencies, compatibility issues, breaking changes

- code that is bloated with abstractions and indirection

- performance issues, especially when worked around via caching etc.

- bad error handling

- inconsistent data

Simpler code that can be followed and stepped through in a straight forward manner avoids 3/5 of these from the get go. Patterns and abstractions that emerge over time are sometimes beneficial. Legacy code or third party code that overuses abstractions is really more of a hindrance and significantly slows down how fast I can understand, own and fix things.

sudobash1

There is an important case for comments that neither of them touched on. Sometimes you are dealing with bugs or counterintuitive processes beyond your control.

For example, I am writing some driver software for a USB device right now. It is so easy to get the device into a bad state, even when staying within the documented protocol. Every time I implement a workaround, or figure out exactly how the device expects a message to appear, I put in a comment to document it. Otherwise, when (inevitably) the code needs to have features added, or refactoring, I will completely forget why I wrote it that way.

The prime number example is a self-contained, deterministic algorithm. While I did find it far easier to parse with comments, I could still spend the time to understand it without them. In my USB device driver, no amount of review without comments would tell another person why I wrote the sequence of commands a certain way, or what timings are important.

The only way around that would be with stupid method names like `requestSerialNumberButDontCallThisAfterSettingDisplayData` or `sendDisplayDataButDontCallTwiceWithin100Ms`.

zbentley

> The only way around that would be with stupid method names

Yep. Method names make terrible comments. No spaces, hard to visually parse, and that's before acronyms and ambiguity enter the conversation.

As the person who often writes borderline-essay-length comment blocks explaining particularly spooky behaviors or things to keep in mind when dealing with a piece of counterintuitive/sensitive/scary code, my reason for mega-commenting is even simpler: all the stuff I put in comments should absolutely instead live in adjacent documentation (or ADRs, troubleshooting logs, runbooks, etc). When I put it in those places, people do not read it, and then they do the wrong things with the code. When I put it in comments, they read it, as evidenced by the rate of "that bug caused by updating the scary code in the wrong way happened again"-type events dropping to zero. It's easier to fix comment blocks than it is to fix engineers.

colddevil

Upvote for mentioning ADRs! ;)

I really like the approach and as other comments already mentioned, this is a nice way to capture the "why" of specific decisions that go beyond the "how" in the code.

Here is a good starting point for people not familiar with ADRs: - https://cognitect.com/blog/2011/11/15/documenting-architectu... - https://adr.github.io/

rswail

> Yep. Method names make terrible comments. No spaces, hard to visually parse, and that's before acronyms and ambiguity enter the conversation.

Which is why snake_case or kebab-case (if the language allows it) is much better than PascalCase or camelCase.

Even worse when camelCase enters into JSON because people want to automate the serde but are too lazy to make the actual interface (the JSON Schema) easy to read and debug.

slotrans

> I will completely forget why I wrote it that way.

This is the main reason for comments. The code can never tell you "why".

Code is inherently about "what" and "how". The "why" must be expressed in prose.

Cthulhu_

And the described use case - USB stuff with very specific exception - makes a strong case for literate programming, that is, more prose than code.

lompad

Does everything have to be pushed into a structure-prescribing set of rules?

Can't we just say "comments are useful here" without trying to make it into a case for $methodology?

ninetyninenine

Why not put the prose in the name of the function?

SAI_Peregrinus

Function names are limited. E.g. can't provide a circuit diagram of what you're controlling in a function name. But you can do that in a comment (either with ASCII art or an image link).

bch

> For example, I am writing some driver software for a USB device right now. It is so easy to get the device into a bad state, even when staying within the documented protocol. Every time I implement a workaround, or figure out exactly how the device expects a message to appear, I put in a comment to document it. Otherwise, when (inevitably) the code needs to have features added, or refactoring, I will completely forget why I wrote it that way.

I believe in general there is a case for this (your case sounds like a perfect candidate). The implementation of Dtrace is another example[0] full of good description, including ASCII diagrams (aside: a case for knowing a bit of Emacs (though I'm sure vim has diagramming too, which I would know if I pulled myself out of nvi long enough to find out)).

[0] https://github.com/opendtrace/opendtrace/blob/master/lib/lib...

mbo

While I am not a Uncle Bob-style "no comments"er I do love a ridiculous method name. I pay very close attention to that method and the context in which it is called because, well, it must be doing something very weird to deserve a name length like that.

hakunin

That’s exactly why you should save that length only for a method that’s indeed doing something weird. If every method is long, the codebase turns into noise. (IOW I agree)

ninetyninenine

This doesn’t happen in reality. Your program does so many things that practically speaking short names work for a lot of functions in the program. It’s like English. There are big words and there are small words and usually to communicate a combination of big and small words are used.

Nobody practically communicates with big words. A long function name only pops up when needed.

Pxtl

I used to work this way, but I found that every non-trivial method involves edge-cases and workarounds documenting them the method name destroyed readability.

tikhonj

There are a few Haskell functions with names like reallyUnsafePtrEquality# or accursedUnutterablePerformIO, and you know something interesting is going on :P

Mawr

Sounds like you should instead be making these invalid states unrepresentable by encoding them in types and/or adding assertions. Especially if you're exposing them as interfaces, as your example function names would imply.

kragen

They're invalid states inside the USB device, not inside the driver code. So nothing you do to the driver code can make them unrepresentable. The best you can do is avoid frobbing the device in the problematic ways.

marcosdumay

The GP is making those invalid states unreachable by writing a device driver.

ninetyninenine

I don’t see anything wrong with those names. A bit hard to parse but the name moves with the function call while a comment does not.

It’s annoying to look at but when you actually read the function you know what it does. A more elegantly named function is less annoying to read but less informative and doesn’t provide critical information.

The name just looks ugly. But it’s like people have this ocd need to make things elegant when elegance is actually detrimental to the user. Can you actually give a legitimate reason why a method name like that is stupid other then its “hard to parse”. Like another user said… use snake case if you want to make it easier.

null

[deleted]

klysm

Encoding temporal dependencies (or exclusions) between methods is hard. You can get partially there by using something like a typestate pattern (common in rust).

marcusbuffett

I strongly recommend "A Philosophy of Software Design". It basically boils down to measuring the quality of an abstraction by the ratio of the complexity it contains vs the complexity of the interface. Or at least, that's the rule of thumb I came away with, and it's incredible how far that heuristic takes you. I'm constantly thinking about my software design in these terms now, and it's hugely helpful.

I didn't feel like my code became better or easier to maintain, after reading other programming advice books, including "Clean Code".

A distant second recommendation is Programming Pearls, which had some gems in it.

narnarpapadaddy

Implicitly, IIRC, the optimal ratio is 5-20:1. Your interface must cover 5-20 cases for it have value. Any fewer, the additional abstraction is unneeded complexity. Any more, and your abstraction is likely too broad to be useful/understandable. The example he gives specifically was considering the number of subclasses in a hierarchy.

It’s like a secret unlock code for domain modeling. Or deciding how long functions should be (5-20 lines, with exceptions).

I agree, hugely usual principle.

abhis3798

This is a good rule of thumb, but what would be a good response to have interfaces because, "what if a new scenario comes up in the future"?

Copenjin

The scenario NEVER comes up in the future as it was originally expected. You'll end up having to remove and refactor a lot of code. Abstractions are useful only used sparingly and when they don't account for handling something that doesn't even exist yet.

narnarpapadaddy

When doing the initial design start in the middle of the complexity to abstraction budget. If you have 100 “units of complexity” (lines of code, conditions, states, classes, use cases, whatever) try to find 10 subdivisions of 10 units each. Rarely, you’ll have a one-off. Sometimes, you’ll end up with more than 20 in a group. Mostly, you should have 5-20 groups of 5-20 units.

If you start there, you have room for your abstraction to bend before it becomes too brittle and you need to refactor.

Almost never is an interface worth it for 1 implementation, sometimes for 3, often for 5-20, sometimes for >20.

The trick is recognizing both a “unit of complexity” and how many “units” a given abstraction covers. And, of course, different units might be in tension and you have to make a judgement call. It’s not a silver bullet. Just a useful (for me at least) framing for thinking about how to manage complexity.

kragen

If you own the code base, refactor. It's true that, if you're offering a stable interface to users whose code you can't edit, you need to plan carefully for backward compatibility.

lmm

"We'll extract interfaces as and when we need them - and when we know what the requirements are we'll be more able to design interfaces that fit them. Extracting them now is premature, unless we really don't have any other feature work to be doing?"

kragen

Maybe some examples would clarify your intent, because all the candidate interpretations I can think of are absurd.

The sin() function in the C standard library covers 2⁶⁴ cases, because it takes one argument which is, on most platforms, 64 bits. Are you suggesting that it should be separated into 2⁶⁰ separate functions?

If you're saying you should pass in boolean and enum parameters to tell a subroutine or class which of your 5–20 use cases the caller needs? I couldn't disagree more. Make them separate subroutines or classes.

If you have 5–20 lines of code in a subroutine, but no conditionals or possibly-zero-iteration loops, those lines of code are all the same case. The subroutine doesn't run some of them in some cases and others in other cases.

tremon

That function covers 2⁶⁴ inputs, not cases. It handles only one case: converting an angular value to (half of) a cartesian coordinate.

narnarpapadaddy

Think of it more like a “complexity distribution.”

Rarely, a function with a single line or an interface with a single element or a class hierarchy with a single parent and child is useful. Mostly, that abstraction is overhead.

Often, a function with 5-20 lines or an interface 5-20 members or a class hierarchy with 5-20 children is a useful abstraction. That’s the sweet spot between too broad (function “doStuff”) and too narrow (function “callMomOnTheLandLine”).

Sometimes, any of the above with the >20:1 complexity ratio are useful.

It’s not a hard and fast rule. If your complexity ratio falls outside that range, think twice about your abstraction.

jacobsenscott

I was around before the clean code movement, and like all software movements, it was a reaction to real problems in the software industry. Massive procedural functions with deeply nested conditionals, no structure, global variables, no testing at all. That was all the norm.

Clean Code pushed things in a better direction, but it over-corrected. In many ways APOSD (published in 2018) is a correction against the excesses of Clean Code (published in 2008).

Will people swing too far back, to giant methods, deeply nested conditionals, etc? I don't know. But probably.

regularfry

I believe that there is a genuine physiological effect that makes it a good idea to have the area of code that you need to think about fit entirely on one screen, without scrolling. There is probably an upper limit to the screen height where that limit is useful: I would believe a 100-line function to be above it and a 24-line function to be safely below it, but I wouldn't want to hazard a guess in the middle.

It's all to do with how your brain processes what it's seeing, and the planning processes involved in getting to the next bit of information it needs. If that information is off-screen, then the mechanisms for stashing the current state and planning to move your hands in whatever way necessary to bring it onscreen will kick in, and that's a sort of disfluency.

Similarly with tokens too far from whatever you're currently focused on. There's likely to be a region (or possibly a number of tokens) around your current focal point within which your brain can accurately task your eyes to scan, and outside that, there's a seeking disfluency.

I think this is why you get weird edge cases like k and j, where they pride themselves on having All The Code in one 80x24 buffer, and it actually works for them despite breaking all the rules about code legibility.

tremon

The term you're looking for is cognitive load. It's a qualitative term used to represent the amount of information a person has to keep in working memory while working on a task.

regularfry

Cognitive load is part of it, but it's not the only part. If you want to scroll the page, you have to engage physical movement. That's an inefficiency in itself.

chillpenguin

I agree. I once attempted this on a javascript project (a personal project, not at work), after reading about APL/J/K people and their philosophy. My constraint was: I should never have to scroll. I also aimed to have as few files as possible.

The result was surprisingly pleasant, and it changed how I feel about this sort of thing. I think the Clean Code approach makes a lot of sense when you are working on a big project that contains lots of code that other people wrote, and you rely on IDE features like jumping to definition, etc. But if you can write code that fits on one screen without scrolling, something special happens. It's like all the negative aspects of terse code suddenly vanish and you get something way simpler and overall easier to work with and understand. But you really have to work to get it to that point. A middle ground (terse code but still spread out over lots of files, lots of scrolling) would be the worst of both worlds.

matsemann

I think learning extremes can be useful, just don't take any one paradigm as gospel. Practicing Clean Code forces you to think in a special way, and when you've tried it, you start to get a feeling for where you should draw the line. Doing CC makes you a better programmer, but you have to figure out yourself where the tradeoffs are.

Other examples are TDD. Forcing myself to write tests for everything for a period has made all my code since better, even though I don't practice TDD now.

lyu07282

I feel the same way, the benefit of testing is that it forces you to write code that can be tested, which tends to make code better just in general.

k__

If you don't nest that much, big functions aren't so bad.

You can just scroll down and see what happens in a linear fashion.

pclmulqdq

This is the Linux kernel approach, and is a big part of why the kernel uses 8-space tabs. It's generally very effective for understanding what is happening. I'm happy with a 200-line straight-line-with-error-handling function, while a monstrosity of 10 20-line functions that all do if-else is quite a bit harder to read. The latter is "clean code."

abirch

As Ben Franklin wrote "the best physician that knows the worthlessness of the most medicines."

overgard

I don't know, I think the kind of person that comments on hacker news is not the average sort of programmer. I mentioned Clean Code to a more experienced colleague today and he had no idea what I was talking about, and when I mentioned the ideas to him he laughed at them. So I don't think there's some sort of pendulum swinging the other way and people are going to start writing massive functions. You'll probably just see small subcultures come up with some new idea that's obnoxious.

There have been some pendulum swinging around things like monoliths/microservices, but even then the amount of people that those things effected is actually much less than the larger community of programmers as a whole.

donatj

I have worked with a couple of people over the years who instead of breaking functions out when something would say make sense to be reused or made some sort of logical sense as a unit, instead seemingly just bundle lines whose only real relationship was that they happened to be near each other when they decided to "refactor".

Having read Clean Code back in college as it was assigned reading, it was absolutely the vibe I got from Uncle Bob generally. See any number of lines at the same indentation level, select them, extract method, name it vaguely for some part of what it does, repeat.

I honestly think that it comes from this type of school of thought that a function should be X lines rather than a function achieving a function. Thinking about this now, it's sort of the difference between "subroutines" and "functions".

Working on their code, I thank god for modern IDEs ability to inline. I often go through and restructure the code just to understand the full scope of what it's doing, before restoring what I can of the original to make my changes as minimal as possible.

zelos

The warning sign I see when methods are split too much is that the method boundaries start to get messy: methods take too many arguments, or state is saved into confusingly named class members, or you end up returning some struct containing a grab bag of unrelated values.

donatj

All of this all the time

kraftman

It's been a long time since I've read the book but I took it to be less 'cut the function at X lines' and more 'long functions tend to be doing too many things at a time'. I think if you're able to give a good name to some sub section of a function, it's a good sign that it can be extracted out. At that point, you shouldnt need to look at the functions implementation unless its the specific function that you want to modify, because its name and arguments should be enough to know what it does and that you don't need to touch it.

Are we talking about the same thing and you'd still find that hard to understand?

ctrlp

I've enjoyed both books but Uncle Bob is something you grow out of. He was a bit of a cult figure at the time. Trying to actually follow the guidelines in Clean Code taught me a lot about "over-decomposition" and, ultimately, how not to write code. It reminds me it's possible to take aesthetics so far the results become ugly. Fussing over a proliferation of small functions that do only one thing is a kind of madness. Each individual function eventually does zero things. You are left sifting through the ashes of your program wondering "Where did I go wrong?"

On the meta level, these exchanges, while mildly interesting, have the vibe of debating how many angels can dance on the head of a pin. I'm reminded of the old saying: "Writing about music is like dancing about architecture." If you want to write good code, read good code. Develop a taste that makes sense to you. I don't think I'll ever read a book about code composition again.

Willingham

> If you want to write good code, read good code.

As a junior in the field working at a small company, I often rely on this community for guidance, and this seems the most sound advice on this thread.

acmj

You need to know what is good code. Opinions may vary a lot between programmers, even senior ones. The Clean Code cult would tell you to find good code there but that is the most poisonous programming book I have read.

namuol

Forget about the code itself and focus on the results.

What I mean by that: Good code is code that has proven itself by surviving quietly in a long-living project that has changed a lot over many cycles of new engineers (experienced or otherwise) being onboarded. The less you hear people complain about it but the more you find people using or relying on it in some way, the better the code. If people are loud about how much they like it, it’s either new, or it’s something they’ve convinced themselves to like but know in their hearts is bad. It’s the stuff that just works that’s good - it’s so good people don’t even notice it.

ctrlp

there are lots of very robust programs in various languages to learn from. It would be hard to know in isolation but by contrast it is easier to learn what good code looks like. Some code will flow and be easy to read. Other code will be obtuse. Start with simpler projects that don't involve a lot of low-level calls. Work up to more complex implementations. There was never a better time to read code than now with an LLM as a tutor. If you use one of the AI-integrated editors or a code packer you can provide a lot of context to the LLM. Ask the LLM for important modules and work your way through them. Ask it for alternative implementations of a function or translated into a different language. Set up an program in a running environment and walk through using a debugger. Look at the way the code is organized in files and modules. You will inevitably encounter cruft and "bad code". Sometimes there are good reasons for that too. If you prefer books, the Architectures of Open Source Applications (AOSA) books are interesting, but there really isn't a way to avoid pulling down a repo and reading the code. Soon, you'll develop your own taste for what makes sense to you and be able to think independently about the choices the developer made.

It is a bit sad but I think with the advent of LLMs some of the stylistic quirks of programmers past will become a bit anachronistic. Still, they will present opportunities for code archeology.

NortySpock

Just a point here, "good code" is sometimes subjective, and depends on understanding the context of what the code is doing. What you think is good code might be overly verbose to another person, or overly terse, or have poorly named variables, or not have sufficiently conservative guard clauses, or throw insufficiently-granular exceptions. What you think is confusing code might lack context for where it is in the stack and what problems it needs to solve at that layer of the stack.

You can also read critical reviews of someone else's work, compare them with the work in question, and see if the critic's punches land or if they look like misses.

https://qntm.org/clean

^ This, I thought, was a good takedown of Clean Code, highlighting some cases where Bob Martin made too many overly thin functions that lacked meat and made it hard for the reader to gain context for what the function was trying to do. [1]

I would also say, reading "the same code" in different programming languages might get you a feel for if you prefer code to be more verbose or more terse, more explicit or more implicit. e.g. https://rosettacode.org/wiki/Globally_replace_text_in_severa...

[1] sometimes derisively referred to as "lasagna code" or "baklava code" -- https://www.johndcook.com/blog/2009/07/27/baklav-code/

rswail

Agreed.

Some other heuristics:

* Every if statement is a chance of a bug because the code has two or more paths to follow. Keep the choice making at the business/requirements level of the code, not hidden inside lower level decomposition.

* A switch statement that is not exhaustive (ie covers all possible values) is a change of a bug, especially if there is no default case.

Modern languages with better type systems make the second point less relevant because they require exhaustive pattern matching.

tremon

Every if statement is a chance of a bug because the code has two or more paths to follow

This is known as the cyclomatic complexity of a program: https://en.wikipedia.org/wiki/Cyclomatic_complexity

A corollary to this is that it is also beneficial to converge separate paths as quickly as possible (e.g. using non-nullable types and default values) or converge them all to the same place (e.g. nonlocal exception handling).

mplanchard

> You are left sifting through the ashes of your program wondering "Where did I go wrong?"

Brilliantly phrased metaphor, thank you.

golol

>Each individual function eventually does zero things.

Lambda calculus, basically :)

kraftman

it has been years since I read the book, but I'm surprised that there's so much hatred for it here. From memory it seemed like fairly harmless things like give things good names, try to make the code readable, dont comment what the code does but why, use consistent formatting, avoid duplication.

Other than people going overboard with empty classes and inheritance Ive not really seen a problem of people breaking down functions too far.

Which parts are important to grow out of?

InvisibleUp

There’s a good overview of the faults of Clean Code at https://qntm.org/clean.

dsego

Please read it again, you might see it differently now.

overgard

Bob's comments on... commenting.. are so bizarre that I can't help but think that he just refuses to concede the point rather than admit he might have been wrong about it. Like, the paranoia around incorrect/stale comments is fairly absurd, I've been coding for 20 years across many code bases, and I can't even recall a time when I've been significantly mislead by a comment which caused a significant waste of time. However, the amount of time I've wasted on unclear code that has zero comments is absolutely staggering. However, what really sealed the weirdness to me was his argument that this was somehow a good comment:

                                                                    X
                                                        1111111111111111111111111
           1111122222333334444455555666667777788888999990000011111222223333344444
       35791357913579135791357913579135791357913579135791357913579135791357913579
       !!! !! !! !  !!  ! !! !  !  !!  ! !!  ! !  !   ! !! !! !
     3 |||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-||-
     5 |||||||||||-||||-||||-||||-||||-||||-||||-||||-||||-||||-||||-
     7 |||||||||||||||||||||||-||||||-||||||-||||||-||||||-||||||-||||||-
    11 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||-||||||||||-
    13 ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
    ...
    113||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
That's verbatim, I'm not unfairly clipping context away from it. Like, what the hell is that supposed to tell someone?! Wouldn't it just be easier to drop a link to the algorithm, or briefly explain the algorithm, or just give a name of the algorithm so someone can look it up? Instead he just talks about taking a bike ride to understand it and making a weird picture. He also has bizarre arguments that if something can't be expressed in a programming language, it's the fault of the programming language (what?!) and that code is more understandable than English. I really find it hard to believe that he thinks these are actually good arguments, I just get the impression he does not want to concede that he was wrong about this.

Terretta

> so bizarre ... what the hell is that supposed to tell someone?!

I liked this bizarre comment. It was like seeing a physical geometry proof for trig. Or like thinking about primes while riding a bike for an hour.

In 10 - 15 seconds this comment offered a flash of appliable intuition into primes I'd not appreciated before.

Granted, the bulk of that was first gathering that the top 3 rows of digits were a series turned sideways (printing them rotated would have made that instant). Joys of plaintext.

But then the pattern popped, and the code, including the optimization, made sense, but now from the "grok", with MTOWTDI.

Neither their commentary nor their function names and comments, caused the grok. I could "accept" the assertions, but to me neither naming nor comments were intuitively self-evident the way the diagram was.

Both of them commented on having to dwell on what the code was doing to consider refactoring. Once this flash happens, one no longer needs reference code at all, it's just another property of primes.

nvlled

> I'm not unfairly clipping context away from it

Yes you are, you didn't attach the surrounding code where this comment was found. That comment would make a lot of more sense even just with the function name.

badmintonbaseba

The code is there to explain the comment.

overgard

If you need the code to understand the comment, the comment is a failure. I guess what I meant is there wasn't any additional meat to the _comment_, you had to read the code to know what the comment even meant

nvlled

That's the point of comments right? Comments are not supposed to be stand alone, they exist to provide additional information that the code cannot express fully, but no more than necessary that it becomes a noise. It's all about balance.

> If you need the code to understand the comment, the comment is a failure.

Nope, not all comments are for the public API. Some are very context-specific and can only be understood in that context. Fully providing all the excessive details in the comments that it becomes context-independent is pointless and time-wasting.

seabombs

Lol, this reminded me of those engravings we put on spacecraft. Like, if we had to communicate the algorithm to an alien civilization then sure, this might be the best way to do it!

https://en.wikipedia.org/wiki/Pioneer_plaque

lynguist

The juxtaposition of how the original comment starts, then the appearance of the verbatim “good comment”, then the spacecraft engravings made me laugh tears.

Especially the buildup from how bizarre the understanding of UB of comments is to actually seeing one “in the wild”.

wglb

I'm pretty much in agreement with you on this, however I'm always aware of the possibility of comments being bugs. If code gets moved around, there is the very real possibility that the comment is now attached to the wrong method or line of code.

I now comment on method or function basis, describing what the method does. The how should be evident in the body itself.

He doesn't seem to often concede to being wrong.

kristiandupont

The how should be, but the why might not be. I think comments should explain choices, especially ones that I am likely to question when returning to the code later.

unclebobmartin

Actually, I presented that figure as an example of just how difficult that problem was to understand, and how (on a bike ride) I was finally able to visualize what was going on. The ascii image was presented a bit tongue in cheek.

However, if you study that image, you might come to the same insight that I (on my bike ride) came to; and that the english comments never helped me with.

cjfd

Once you notice that the primes are in the top rows, it becomes a pretty good comment.

xeyownt

Just learned that 9, 15, 21, ... are primes. Excellent comment ^^.

froh

well, ackchyually, 'technically' the primes _are_ in there in that top row

just not only primes ^^

imjonse

I am biased ( a former coworker was an Uncle Bob fan, and was bent on doing everything by the book, with layers of abstraction, patterns, hexagonal architecture, lots of unit tests, no cutting corners, even as we did not know what exactly we want to build and needed an MVP ASAP) but I'll just say this: Ousterhout wrote TCL - widely considered one of the best C codebases - besides being a professor at Standford and having other software achievements under their belt, while Robert Martin is more like a software technology evangelist. The former good at actual deliverables the latter good at selling.

Also Ousterhout's book on design is very easy to read and I guess I liked it because I mostly just nodded in approval while reading and there were very few things that made me stop.

j_san

Biased against the approach of your former coworker and thus the "Clean Code" way? I assume it did not work out well, because you needed to move fast to build an MVP before trying to do it right?

imjonse

yes, biased against knowing 'the best way do write software' and applying it regardless of what the current requirements and constraints are. And arguing for their position by sending people links to Uncle Bob videos for 'enlightenment'.

mangodrunk

Following Clean Code is not the right way to develop software in any stage of the project. It is a few opinions of someone who has not actually written any code of substance. In addition to Clean Code being a bad approach, it can also be a very slow process.

begueradj

Let's not forget that Uncle Bob, by the time of writing "Clean Code" had 4 decades coding experience.

WillAdams

Do not make the mistake of the craftsman who claims to have 20 years of experience, but in truth only has 1 year of experience repeated 20 times.

kragen

My middle school English teacher had 4 decades of experience writing. What she wrote was lesson plans. That doesn't make her Stephen King.

mbonnet

Kenneth Copeland has been a pastor for 50 years and his theology and pastoral practice is still terrible. Years of experience is not a useful metric when you could instead look at results.

intelVISA

Software is results driven, there's no value in simply warming a seat for X YOE, talking about code instead of actually executing.

throw16180339

What has he shipped, though?

wglb

Are there large code bases that he has written that we know anything about?

seanwilson

I find the lack of discussion of type systems really surprising in these sorts of discussions and books. Effective use of type systems is a killer factor for me for creating clean, safe, readable and maintainable software designs.

When used correctly, strong static type checking make certain kinds of bugs impossible, spare you from writing many kinds of tedious tests that often get in the way of refactoring, serve as documentation, and make refactoring/maintenance an order of magnitude faster and safer. Even when a type checker isn't available, avoiding dynamic behaviour is very often the safer way to go so learning how to think in that way is still beneficial.

Most of these minor topics like how big a function should be, what to name your variables, or even if you write tests before/after coding... it's like trying to come up with general rules on how to write essays, creating graphic designs, or how to cook. "It depends" on the context and juggling different priorities each time. It's the kind of thing you only learn properly through practice (https://en.wikipedia.org/wiki/Tacit_knowledge), so there's only so much to gain in reading about it or discussing it after you've defined the no-brainer things to always do and always avoid.

jghn

Because the pendulum of typing hadn't swung back to static being in vogue when the Philosophy of Software Design came out. At the time you had mostly the Scala & Haskell people standing in a corner screaming until they (well, we as I was one of them) were blue in the face about reducing "certain types of bugs", and making impossible states impossible.

Since then, everyone and their brother is on the static typing train. And from that lens you're right. It seems like an omission. Give it another 10 years and people will probably think the opposite.

null

[deleted]

WillAdams

That was exactly the approach taken by Prof. Ousterhout in setting up the class which lead to this book --- rather than just having students turn in working code for a grade, the code is reviewed with the student and the student then works to make it better --- in turn, the 2nd edition of the book was informed by the experience of teaching the class and the author actually changed his position based on the experience gained.

seanwilson

Why is that convincing though? Students aren't experienced coders, aren't working in large teams, and student assignments aren't like long-term large commercial projects.

If you mean the additions here https://web.stanford.edu/~ouster/cgi-bin/book.php, I read these and it still sounds like general rules of thumb you'll only really learn and understand by practicing a lot e.g. "In my experience, the sweet spot is to implement new modules in a somewhat general-purpose fashion" "Having good taste is an important part of being a good software designer".

WillAdams

It's better credentials and experiential basis than most other programming books.

Moreover, it is the students' inexperience which give this text credence --- since it results in their making errors and poor architectural/design choices it affords the chance of correction.

I think it is remarkable that the author switched from "modules should be specialized" to "modules should be generalized" (rough paraphrasing, mailed my copy to Brazil and waiting to buy a replacement).

If you know of other books which you merit recommendation and which have a similar or better context for their authorship and exposition, I would be glad to hear of them.

null

[deleted]

pclmulqdq

Type systems and type-based coding patterns are very hip right now, but they weren't 6 years ago. That is partly because the type systems in the main languages in use 6 years ago were hack jobs (to put it politely).

I do expect the pendulum to swing against type systems at some point soon for the same reasons it swung against OOP: Too much heavy lifting done by something that's hidden from the programmer, encouraging people to be "too clever," etc. Like OOP, algebraic types are a tool that have to be used well, and the current users are people who really like type systems and do use them well. It's only a matter of time before the tool gets into the hands of the average programmer, and then we will see how terribly a great type system can hurt you.

getnormality

Uncle Bob's insistence that functions should be 2-4 lines long is baffling to me. I don't understand how he can be taken seriously. Is there a single application in the entire world with substantial functionality that conforms to this rule?

PlunderBunny

I've seen this in what I call 'lasagna code' - multiple thin layers that seem to do nothing (or almost nothing) but each one is an implementation of some abstruse interface that exists in the mind of the original developer.

Eventually, your code has to do something. Get that thing in one place where you can look at it in its whole.

CyberDildonics

John Carmack would disagree with Uncle Bob and John Carmack actually programs.

My own experience is that with an IDE that can collapse a new scope in the middle of a function, you can make large functions that accomplish a lot and are very clear by writing a comment and starting a new scope.

If something is going to be called multiple times a new function makes sense, but this idea that anything that can eventually return a single value needs to be it's own function is a giant pain that creates more problems than it solves.

Just makeing a new scope in the middle of the functions lets you use all the variables in the outer scope, do transformations without introducing new variables and ultimately "return" a new variable to the outer scope.

I've never understood why polluting namespaces with dozens or hundreds of names (most of which may not be very descriptive since naming a hundred small things is already going to be confusing) is seen as a good idea. You look at a list and you have no idea what is important and what was being shoved in there to satisfy some do nothing public speaker's arbitrary rules.

layer8

The problem with collapsing is that you need to know a priori which sub-scopes are independent and hence collapsible and which aren’t. Meaning, you have to analyze the unfamiliar code first in order to know which sub-scopes you might want to collapse. And, given an already-collapsed sub-scope, due to it being collapsed you can’t see if it reads or mutates some local variable. The benefit of extracted functions is that you know that they are independent of the implementation details of the caller (i.e. can’t possibly depend on or modify local variables of the caller other the ones passed as arguments).

Too many arguments can become a problem. Nested functions can help here, because they allow you to move their implementation “out of the way” while still having access to shared variables. And sometimes a collection of parameters can sensibly become its own class.

IDE affordances are fine, but I’m opposed to requiring reliance on them for reading and understanding code, as opposed to writing.

CyberDildonics

you need to know a priori which sub-scopes are independent and hence collapsible and which aren’t.

What does independent mean? I would just collapse them all because they were meant to be collapsed in the first place.

you have to analyze the unfamiliar code first in order to know which sub-scopes you might want to collapse

Unfamiliar? I wasn't refactoring and collapsed them all.

The benefit of extracted functions is that you know that they are independent of the implementation details of the caller (i.e. can’t possibly depend on or modify local variables of the caller other the ones passed as arguments).

That's true to an extent, but this is more of a way to make monolithic functions simple, which then makes the program simpler over all because you can avoid lots of tiny little functions. What you can end up with is programs that do non trivial things but don't have tons of functions confusing the issue.

Pragmatically this isn't really a problem. The whole "it isn't exactly a 1:1 replacement" isn't the point. You can still put in comments and const references if you really want to.

IDE affordances are fine, but I’m opposed to requiring reliance on them for reading and understanding code, as opposed to writing.

Why would it be required? The alternative is that you still have these commented sections with their own scope but they aren't collapsed. You can always work on it without the IDE and when you go back to the IDE it still works.

The reality of it is that you can see a broad overview of a function then see details one section at a time and you don't even have to go skipping around to other parts of the file or other files to do it.

WalterBright

Too often I see functions that are shells that reshuffle the arguments and pass them to another function, which also reshuffles the arguments and forwards them to another, and on and on. One was 11 layers deep.

js8

I have seen such code too - with just S,K and I combinators. It wasn't readable.

Copenjin

And a lot of people doesn't understand how dangerous shuffling parameters is, especially in languages that do not have named parameters...

Pxtl

Powershell is an awful language but it has made me fall in love with named parameters. Name all the things.

mrkeen

If you have a 2-4 line function and you spot this, you can trivially remove it.

ninetyninenine

Yes. This works but only if the functions are pure and using pure function composition.

Uncle bob doesn’t mention this.

   createspecialString(y) =
       Capitalizefirstletter .
       MakealllowerCase .
       AddNumberSuffix .
       removeLetterA .
       removeLetterB .
       ConcatwithWord(x)

   CapitalizeFirstLetter(a) = a[0].upper() + a[1:]
   MakeAllLowercase(a) = map(a, (t) => t.lower())
   Addnumbersuffix(a) a + 3.toString()
   RemoveLetterA(t) = filter(t, (s) => s.lower() == “a”)
   RemoveLetterB(t) = filter(t, (s) => s.lower() == “b”)
   ConcatenateWithWord(x) = (y) => y + x

   
There see? It’s mostly doable in pure functional composition where the dot represents function composition. I program like this all the time. No way anyone can pull this off while mutating state and instantiating objects.

   F . P = (x) => F(P(x))
Forgive some inconsistent formatting and naming im typing this on my phone.

People who complain about this style tend to be unfamiliar with it. If you had knowledge about procedural coding styles and a function composition approach like this then usually this style is easier as the high level function literally reads like English. You don’t need to even look at the definitions you already know what this complicated string formatting function does.

No comments needed. And neither author tells you about this super modular approach. They don’t mention the critical thing in that this style requires functions to be pure.

Thus to get most of your code following this extremely modular and readable approach… much of your code must be minimizing IO and state changes and segregating it away as much as possible.

The Haskell type system, the IO monad is pushing programmers in this direction.

Again neither author talks about this.

mplanchard

Based on his blog, Martin has been getting into Clojure in recent years. I was kind of hoping that the experience with a functional lisp would shift some of opinions that he previously stood by in Clean Code, but based on this discussion, it doesn't seem like it.

LudwigNagasena

`createspecialString` is seven lines long though.

ninetyninenine

Then split it. All pure functions are easily decomposed into the most primitive units so even the most dogmatic ass hole can't talk shit.

   createSpecialString = createFormattedString . createNewString

   createFormattedString = 
       Capitalizefirstletter .
       MakealllowerCase .
       AddNumberSuffix .


   createNewString(y) = 
       removeLetterA .
       removeLetterB .
       ConcatwithWord(x)
Or put it all on one line.

      createspecialString(y) = Capitalizefirstletter . MakealllowerCase . AddNumberSuffix . removeLetterA . removeLetterB . ConcatwithWord(x)
The amount of lines becomes off topic once you get into this style. It's a completely orthoganol concept as it's completely irrelevant to readability and modularity.

Lines doesn't makes sense for pure non imperative functions. Lines ONLY make sense for imperative functions because each line represents an instruction.

Scubabear68

There are. A lot of Java code bases look like this.

It is all as bad as you imagine. Functionality is spread out all of the place so it is very difficult to reason about how it all hangs together.

MobiusHorizons

I once fully spelunked such a Java call stack to convert some code to golang. It was amazing, there were like 5 layers of indirection over some code that actually did what I want, but I had to fully trace it keeping arguments from the full call stack in mind to figure this out, because several of the layers of indirection had the potential of doing substantially more work with much more complex dependency graphs. I ended up with a single go file with two functions (one that reproduced the actually interesting Java code and one that called it the way it would have been called across all the layers of indirection. It was less than 100 lines and _much_ easier to understand.

nyarlathotep_

It's always fun stepping through 412 stack frames that are all 2-line long methods to figure out where the thing you're interested in actually happened.

monksy

Add in Go and C++ code bases to that as well.

lmm

Yes, I've worked on a couple of codebases like that. It's glorious, you break everything down little by little and every step makes sense and can be tested individually. Best jobs I've had.

MrJohz

But are those steps actually doing anything that can be tested? My experience with these sorts of codebases was always that most of the functions aren't doing much other than calling other functions, and therefore testing those functions ends up either with testing exactly the same behaviour in several places, or mocking so heavily as to make the test pointless.

Or worse, I've seen people break functions apart in such a way that you now need to maintain some sort of class-level state between the function calls in order to get the correct behaviour. This is almost impossible to meaningfully test because of the complex possible states and orders between those states - you might correctly test individual cases, but you'll never cover all possible behaviours with that sort of system.

lmm

> testing exactly the same behaviour in several places

I think that's actually fine. Particularly if you're doing a testing pyramid style approach where you do a lot of tests of some piece of low level logic and then a few tests of the higher level piece that makes use of that lower level logic, I don't see any problem with the higher level test covering a codepath that's also used in the lower level test. If anything I find it makes it easier to understand and debug failures - you know that the behaviour of the lower level piece hasn't changed because otherwise the lower level test would have failed, so the bug can only be in the higher level component itself.

CyberDildonics

These large compound statements look nice if they are perfect, but when you make giant expressions without intermediate variables, it is much more difficult to test.

When you have small expressions that have incremental results stored in variables, you can see the result in a debugger so you can see each stage.

dionian

Like with a lot of his approach, its great for teaching people better coding skills in an educational setting, but doesn't make as much sense in the real world.

wglb

It is a bit weird.

However, a friend of mine was a professional Smalltalk programmer. He claims that his median line count of methods, over his 17 year career, was 4.

It is harder to do in other languages--it seems that C would be on the order of 10.

Clearly it is a rule that can lead to complexity of too many methods, compromising whatever gain smaller methods give you.

igouy

> median line count of methods

Auto-generate getters and setters for every instance variable and that will drag the average down. (Maybe a lot of those getters and setters should not have existed.)

wglb

Not part of smalltalk.

nickm12

This was a fun read. I read APoSD for the first time a couple of months ago and found myself nodding enthusiastically as I read. I have a few quibbles, of course, but overall it matches my experience in how to write software that is correct, maintainable, extensible, and understandable.

I've never read CC, but I've read some of the take downs[1]. I was worried that the take downs were attacking a strawman, but no, Uncle Bob believes this stuff, including that comments are evil and you just need to read all the code and keep it in your head.

Even if that were true, the code I write is better for having written the comments, especially interface comments, because the writing helps my thinking. Moreover, it helps my code reviewers—without written interfaces. If all you have is the code and not a description of what the code is supposed to do, how can you know if it is correct? I think most code reviewers are verifying the code against what they infer the interface to be. It helps us both to just be explicit.

[1]: https://qntm.org/clean