Hacker News new | ask | show | jobs
by bc_programming 697 days ago
For me, I bought "Clean Code" because it was, at the time at least, quite highly recommended. After being rather confused for a while, I finally stopped reading right around page 141 when I realized that the reason his examples looked awful were because they were.

In that section of the book, he takes a Java port of Donald Knuth's PrintPrimes program, and refactors it. But in doing so, he actually breaks it. He moves state out of local variables and parameters and makes them all static fields, and then refactors the code into long-winded methods that specifically perform side effects or operate on those fields (names like isNotMultipleOfAnyPreviousFactor(), isLeastRelevantMultipleOfNextLargerPrimeFactor()). But the simple act of moving state that would otherwise be part of the stack frame into static fields means he has changed the behaviour of the code - it is no longer thread safe! calling it from different threads will have undefined results because all threads will be operating on the static fields. He demonstrably made the code worse!.

It invalidated the entire book for me at that point. Here's "Uncle Bob" trying to pretend to be some aged, skilled, craftsman hand guiding us young, ignorant whippersnappers into being proper craftsman and not only can't he properly refactor a simple prime number sieve as a demonstration, but he's so blind to awful code that he doesn't even see it in his awful example enough that it gets published in the same book that complained about programmers who don't have "code-sense". Mistakes and "errata" are one thing, but when he makes such a big noise about "do it right the first time" and then has an example where he refactors and literally breaks something, that's another.

4 comments

I started reading the code Martin wrote with his son for the “FitNesse” acceptance tracking framework.

The code was simply God awful. Nearly every method was tagged ad “throws Exception”, no comments, and the famous endless sea of classes with only a few lines of code per method.

The code itself ran with constant exceptions filling up the logs. This was going back over a decade or more when I looked at it.

You can see his lineage of trying to sell consulting services and books all the way back to the comp.object Usenet groups back in the 90s.

Sadly he still commands big fees to speak at various conferences and companies, I was very disappointed when he spoke at Bloomberg many years ago when I worked there.

Interestingly enough, John Ousterhout was a recently on Book Overflow podcast and he says he uses this very refactoring but on reverse to teach good software design. So it’s good for something!
In my opinion the only good part of that book is the title.
I even hate that the title got popularized. Code isn’t “clean” or “dirty”. It works or it doesn’t work. That’s the most important aspect. Another very important aspect is that It’s readable or unreadable. Readable code may be very long winded, but I would much rather have that than somebody’s idea of a “clean” concise very abstract piece of logic.

I hate when I get comments on a PR talking about some subjective piece of code suggesting an alternative that they think is “cleaner”. How clean a piece of code is shouldn’t be a part of consideration while reviewing any code. Instead, like I said before, the only thing that really matters is if it works and if it’s readable. I wonder how many junior engineers have been bogged down with pointless PR comments over the years because of this idea that code can somehow be “neat” or “clean”.

I always took it more as readable code.
I mean. It’s not like people pushing “side effects” (read: literally any change at all) aren’t also being overtly dogmatic, and writing utterly terrible code.

Uncle bobs book didn’t age well, but neither will the dogmatic stances on immutability and side effects.

In this case, By "side effects" I mean non-obvious changes to the static state, Where a seemingly simple function will both rely on static fields to evaluate it's result as well as make changes to various other fields that will subsequently change the result of that function going forward. I don't think one needs a dogmatic adherence to immutability and avoiding side effects to find that to be undesirable.
I agree. Just letting you know that, most likely, the people you are learning about “side effects” from don’t actually mean “side effect” in the colloquial sense of “state changes beyond the expectation”.

They mean literally any change at all. If your function is named addOne(&x), then they consider the result of x being one greater to be a “side effect”, whereas I suspect actually sane people such as yourself would not see that as a side effect.

This type of definition fuckery is one reason (among many), that I’ve come to fully disrespect functional programming communities.

One thing that also needs to be understood is that side effects (to your definition) are not explicitly bad things. Side effects ignoring boundaries, ignoring APIs, holding references, etc are probably mostly bad though.

In fact, in gaming, providing hooks to empower side effects is often explicitly good for granting designers mechanic freedom.

My belief is the people you are talking about think of programs as batch operations where you take data transform it and then pass it to someone else's API calls. In that case state is sort of evil.

In embedded and gaming state changes in response to input is the point of everything.

I'm one such person!

"Side effect" doesn't have any useful meaning if it doesn't have teeth.

Nothing is a side effect if it's what the programmer intended to do.

What's especially important is non-obvious state.

I was trying to use a function. You had to create an instance of it to use it, but I was using one method that by appearances should have been static. You feed it an input, it gives you an output, there's nothing to remember. I was calling it from multiple threads--occasional corruption. There must have been something being stored into the object but I have no idea what or why.

Avoiding side effects as much as possible is not a new idea, though; it's been around since at least the 80s. You can just ask people who have worked on Haskell code written in the past 25 years to determine for yourself if the idea aged well or poorly. Opinions will probably be mixed, though, not clearly one way or the other.
The definition of "side effect" has to mean something, otherwise it defaults to the opinion of whoever wrote the code.

  // Not a side-effect (intentional)
  if(++i++) {
  }
I don't understand how this relates to my comment. I'm saying that avoiding side effects isn't a new idea, so it already has aged, either well or poorly.
I don’t really care about the opinions of Haskell coders, as they are probably the only programmers on earth that have more terrible dogmatic ideals than uncle Bob does.
If you're open to challenging your preconceptions (dare I say dogma?) about Haskell programmers then I'm willing to share my opinions on good software development and how Haskell helps, based on over a decade of professional experience with Haskell. Free tidbit: I find effect tracking to be extremely beneficial to software engineering in the large.