Live data from Hacker News

Goodbye, Clean Code

overreacted.io

171–180 of 599 posts

Re: Goodbye, Clean Code

#171
post #131
post #84

The first thing I did when I read this was come to the HN comments and search for people complaining about DRY: https://news.ycombinator.com/item?id=22022599 https://news.ycombinator.com/item?id=22022842 https://news.ycombinator.com/item?id=22022896 https://news.ycombinator.com/item?id=22022836 The author of this piece was not engaging in a DRY activity even if he thought he was. He (perhaps unwittingly) admits to it…

Wasn't it the case that the requirements were the same at the time the code was written? TextBlock.resizeTopLeft(...) and Rectangle.resizeTopLeft(...) did the same thing for the same reason, not by coincidence. The issue was the possiblity (and eventually the reality) of future divergence.

I disagree.

If they had separate classes for TextBlock and Rectangle, then likely there are separate requirements involved. It may have been that at the time they needed separate classes the requirements for resizeTopLeft was identical, but they are still two separate requirements.

I suggest everyone take a minicourse or read a book on systems engineering - at least the part where they discuss requirements. The fact that two distinct entities have an identical requirement for something does not mean there is one requirement. It means there are two requirements which (for now) happen to be identical.

The key phrase is happen to be. You do not apply DRY to separate requirements even if they are the same/similar.

Look at it from a testing perspective[1], which is similar to how a systems engineer will look at it. If you would write a separate test for it, it is a distinct requirement. (Actually, the ordering should be the opposite: If it is a separate requirement, it should have a separate test).

[1] And not from a TDD perspective!

Re: Goodbye, Clean Code

#172
The author not only fails to explain why his refactored code is worse he doesn't even show all of his code. There is simply not enough information to know whether the refactoring is better or worse. In terms of less lines of code and code reuse, it is better in this sense. I suspect the author is pretty junior himself to imply the old way is better than his refactoring without a clear explanation as to why. I guess putting your baseless opinion on a fancy blog or medium makes you seem more legit than you really are.

I get his point, however. In the spirit of his argument there is an exact answer as to why one way of designing a program is better than another way despite more/less code re-use or more/less lines of code. I'm not even going to get into legibility here as that is just an opinion piece. Also I'm going to give a very concrete answer here. No design principles no design philosophy or any of that.

Let's say you have feature which we call "A" that can be created as a composition of several primitives. We call these primitives "a, b, c, d." Let's also say feature "A" can be constructed from a different set of primitives "b, f, g, a."

Note the overlap in primitives. The overall set of primitives are different but both ways of constructing feature "A" can share the same primitives if needed. The two sets in the example share primitives "a" and "b".

Now let's say we want the code to be flexible enough to construct feature "B" or feature "C" sometime in the future.

lets say feature "B" needs primitives "a, d, b" to construct.

lets say feature "C" needs primitives "b, f, a." to construct.

Which method of constructing Feature "A" is better knowing that you will need to construct Feature "B" in the future? what if it was Feature "C" for the future?

Obviously depending on "how" flexible you want your design to be you can choose one way to initially construct the program or another way. It's all an opinion and anticipation for the way you design your program in the future.

One strategy to remove opinion from your design is to try to incorporate the full union of primitives "a,b,c,d,f,g" Or find another completely different set of primitives (perhaps "h, i, j") that can be used to construct features "A", "B", and "C."

SO the concrete answer is "h, i, j" is the best set of primitives you can use to construct your program but if "h, i, j" aren't available then your choice of "a, d, b" or "b, f, a" or "a,b,c,d,f,g" both hinges on whether you need to construct feature "B" or feature "C" in the future.

The problem with this article is that he never got into the nature of program design/organization. Just vague reasoning and hand wavy examples.

Re: Goodbye, Clean Code

#173

The OP is using clean as a language figure. He attacks the idea of clean code but not as the hygiene of the writing but as in removing what he thought to be unnecessary repetitiveness only to later come to the conclusion that it was adequate yet somehow repetitive. Why repetition is the opposite of cleaneness in the first place? What he calls here clean code (and dirty code) is modelling that piece of software with o…

> Why repetition is the opposite of cleaneness in the first place?

It's an idiom after the book by Robert Martin, whose central theme sits around heavily refactoring code to remove repetition and in fact even non-repetitive code. The first few chapters hammer the concept that functions should be extremely short and aggressively refactored to compose them into subfunctions. It essentially coined the concept of "clean" code in these terms.

Re: Goodbye, Clean Code

#174
The only cost of duplication is when you're making changes to it. Let's say that the cost of typing, checking and finding the code to change is "t". Duplication makes it "n*t". Abstraction, in case of no special case, makes it "1t".

So if "n" is somehow big, it'll be costly to make changes and abstraction makes sense.

However do not abstract similar code for different behavior early (as in OP's case), but abstract similar behavior early (ex: file system, db access)

Re: Goodbye, Clean Code

#176
post #13

> A healthy engineering team is constantly building trust. Rewriting your teammate’s code without a discussion is a huge blow to your ability to effectively collaborate on a codebase together. I'm against the idea that people should be attached to "their" code (that is: the code they wrote). Now I also understand that humans that humans, but the priority should be to make them evolve toward more detachment from their…

If someone spends a week or two writing a patch and you come in and rewrite it in an evening, that, in and of itself, is telling me something: You think your teammate is a worse coder than you, given you were able to solve it with "cleaner" code. You assumed that your solution was better, without talking to the person who authored it to see if they did things that way for a reason. This could have been solved with a…

> If someone spends a week or two writing a patch and you come in and rewrite it in an evening, that, in and of itself, is telling me...

Or, what it might be telling you is the second person wouldn't have been able to clean it up in an evening without the first person having already spent a week or two on it. The second person was building on the first. Just because it was (hypothetically) less lines of code doesn't mean it could have been written without starting with the more lines of code and refactoring; it often takes more time/steps to get to fewer lines of code.

But don't get me wrong, I'm in favor of colleagues discussing code, not just changing each other's code without discussing it.

Re: Goodbye, Clean Code

#177

This should rightfully be on the front-page of HN. I've gone through something almost the same as Dan Abramov here, and I feel it's difficult to impossible to explain this and you have to experience it yourself. The tricky bit is where to draw the line, which I've learned only after pouring thousands of lines of code and I understand is a bit different for everybody. The rule of three[1] was a very useful rule of thu…

The sign of a junior is someone who refers to hand wavy design principles rather than getting to the heart of the problem.

This is the real issue which the author completely glosses over:

https://news.ycombinator.com/item?id=22023568

Re: Goodbye, Clean Code

#178
post #173

The OP is using clean as a language figure. He attacks the idea of clean code but not as the hygiene of the writing but as in removing what he thought to be unnecessary repetitiveness only to later come to the conclusion that it was adequate yet somehow repetitive. Why repetition is the opposite of cleaneness in the first place? What he calls here clean code (and dirty code) is modelling that piece of software with o…

> Why repetition is the opposite of cleaneness in the first place? It's an idiom after the book by Robert Martin, whose central theme sits around heavily refactoring code to remove repetition and in fact even non-repetitive code. The first few chapters hammer the concept that functions should be extremely short and aggressively refactored to compose them into subfunctions. It essentially coined the concept of "clean"…

Good clarification! Thanks! I didn't read that one, although, I generally like Rober Martin, I really don't like when language figures go too far.

If pushed one bit too far it becomes propaganda in a culture war instead of a healthy intellectual honest discussion. What we know as flamewars is an example of that.

Re: Goodbye, Clean Code

#179
post #138

Earlier quoted context omitted.

The downside I see with go's error handling is that you can forget to check. With rust, if the function being called returns Result, you have to deal with the error (even if dealing with it just means propagating it out). Missing error handling is such a common source of bugs that go really turns me off here.

> you can forget to check Linters can help with this.

Specifically for this issue, linters also have many false positives. Some Go libraries trying to encourage a fluent style will accept an error for some logic also return it, so you can `return x.HandleError(err)` - but if you don't want to return it, you obviously don't care it returns what you just passed it. (I personally consider fluent methods a bad idiom in Go, but I also don't get to write all the Go code in the world or even in my project.)

There are also a lot of functions that return errors because Go's type system demands that if the interface returns two values `T, error`, every implementation must also - it won't auto-create a nil value for the second result. That's reasonable if you are committed to errors just being normal values. But such a restriction would not be necessary if the interface could be declared with a sum type - promotion of a `T` to a `Either` or `Just T` or so on would be fine for all types, not just error handling . Lots of infallible Writer implementations like bytes.Buffer and hash.Hasher suffer from this, and linters can't be aware of all such cases.

Re: Goodbye, Clean Code

#180

If there's something that I have learned about refactoring code that is repetitive into "cleaner" shorter code, is that the refactored version looks better but it's way harder to understand. When other people try to look at the "cleaner" version they have to spend more time trying to understand it and mentally untangle the abstraction. I like syntactically shortcode as long as it's clear. I also understand that somet…

It can be harder to understand; but, even worse, it can also be harder to change.

Which is a bit ironic, because if you asked one of us to explain why we "DRY", we'd probably say something about it making the code easier to change, because a change only needs to happen in one place.

The problem is that whenever you make abstractions, you necessarily limit your axes of flexibility, abstractions always have certain sorts of uses in mind, and built-in implicit limits. Abstractions make certain explicit things easier to change, while making all sorts of implicit things you haven't even thought of yet -- but which might come up later -- harder to change.

Still, obviously of course sometimes abstractions and DRY are the right thing to do. The trick is knowing which is which, which you get better at with experience at software in general as well as the specific domain -- and I'm not sure it can be systematized or formalized, I think it's still a craft.

Post reply on HN