Live data from Hacker News

Goodbye, Clean Code

overreacted.io

241–250 of 599 posts

Re: Goodbye, Clean Code

#241
post #131

Earlier quoted context omitted.

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.

The issue wasn't divergence. The issue was that there was no requirement that TextBlocks should behave like rectangles. It's very possible that DRY could apply even where divergence is expected. Suppose we had a requirement that "A user should not be able to resize a TextBlock if they are not allowed to read the text." At the outset, the read & resize permission logic might be identical & DRY is easy to apply. Nobody…

> The issue was that there was no requirement that TextBlocks should behave like rectangles.

Sure, though that’s not quite what the new code did.

Instead, it added a new abstraction (createBox), and then used that to incidentally assign the same behavior to TextBlocks and Rectangles. Your change could easily be accomplished by changing the last line to check the permissions and call createBox or something else, depending. I’d guess that createBox would also be useful for many other shapes, but if you wanted to do something totally different, like a star with handles at each point, you could just....not call createBox, and write a totally independent “let star()” function that does its own thing; it doesn’t seem like a base class constructor is forcing you to call createBox or anything.

So....this code doesn’t actually seem that bad to me. What am I missing?

Re: Goodbye, Clean Code

#242
post #232
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…

This is about ownership. I agree there's no need to be personally attached to your own code. I have no problem with someone taking ownership of my work (understand refactoring/abstracting the crap out of it for the sake of Engineering). But the one day that person leaves the team, you have no choice but deal with that person's mental model, as nice or convoluted it was while refactoring. I've had this experience in e…

Of course, we all have had that experience! So you have a choice... realize it is going to happen and learn to deal with it in a productive manner, or just continue to get upset about it. I know which choice I'm working towards...

Re: Goodbye, Clean Code

#243
post #130

Weird that there's no mention of writing tests. After all, they help in explaining the intent behind the given piece of code. With well written tests even the most "clever" implementations become at least reasonably understandable . Anyway, here's what I do to deal with this problem: 1. Write code (and tests). 2. Switch to some other task. 3. Forget about that previous piece. 4. Get back to it and judge if it's still…

Agreed. Tests often have the benefit of defining better interfaces, so the implementation details often matter less and code duplication often works itself out on its own.

Re: Goodbye, Clean Code

#244
I'm 52 many would consider my code a mess. Been a professional coder -> solution architect all my life, I work for me now with my own apps. With my own code I clean things up when I can, but sometimes it isn't worth it. I used to write clean code, spend time doing it but no more.

- Rewriting requires retest, introduces new bugs.

- If it ain't broke, don't fix it.

- Users don't care about clean code. They only care about the end product.

- Developers (less experienced?) obsess about this stuff. It is so expensive. Layers of coding management to deal with it all.

- If your code requires constant maintenance you are doing it wrong.

- Obeying the above rules means you can leave old software behind and generate new software if you want.

I am about to celebrate 10 years of my first software (an app) I wrote for myself. It is still in prod. It is fine, been earning $$ for years. I don't touch it unless I need to support new devices or support API updates. On the flip side, there isn't much code I wrote in my previous professional career that is still live - the reason is vendors only make a profit because they need to generate cashflow constantly, and they do it by breaking the above rules unnecessarily.

Do you work for a vendor? Think about the real reason you are breaking the above rules - the reason is because the extra time is billable. You are not breaking the above rules because it is the best way to write software. You are breaking the above rules because it generates the most profit for the company you work for.

Re: Goodbye, Clean Code

#245

Earlier quoted context omitted.

I think the catch all term for that is YAGNI.

YAGNI is about not adding functionality until it's needed. DRYing code isn't adding functionality.

DRYing existing code to handle exactly what is needed to support the current uses with no additional axes of variation isn't adding functionality (well, if it handles more than the existing values in the existing axes of variation, it's adding some but arguably de minimis functionality.)

Building code with support for currently-unused variation to support expected future similar-but-not-identical needs, that is, pre-emptively DRYing future code, is adding functionality.

Re: Goodbye, Clean Code

#246
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…

I disagree that people should NOT be attached to their code.

Having a sense of ownership for what you write can lead to higher quality systems where people are willing to stand up and fight for what they believe is higher quality.

BUT this importantly depends on the ability to compromise, admit being wrong, and change based on new information from the people who have a sense of ownership over their parts of the codebase. Like you said.

Re: Goodbye, Clean Code

#247
In a nutshell this captures my argument against functional programming, design patterns, and this whole code fetish cargo cult.

I care about what the code does, not what it looks like. If two different things compile to the same hardware instructions, then it makes no difference to me. Repeat yourself if you want. Make a bunch of indirections via factories and generics if you want. Use a bunch of ugly branching if you want.

It's all so anachronistic. We have self-driving cars, VR, drones, neural nets, machines that can write code themselves, post-quantum cryptography--and you're lecturing me about the $(No One Cares) Pattern from the hottest bestseller of 1994?

Re: Goodbye, Clean Code

#248

I’ve usually heard this phenomenon called “incidental duplication,” and it’s something I find myself teaching junior engineers about quite often. There are a lot of situations where 3-5 lines of many methods follow basically the same pattern, and it can be aggravating to look at. “Don’t repeat yourself!” Right? So you try to extract that boilerplate into a method, and it’s fine until the very next change. Then you ne…

That can be boiled down to the “Rule of 3”. My CTO often asks me to implement a feature to do X and make it “generic enough to handle future use cases”. My answer is always the same - either give me at least three use cases now or I am going to make it work with this one use case. If we have another client that needs the feature in the future then we will revisit it. Of course, there are some features that we know in…

From (I think) an old Joshua Bloch talk on API design, paraphrased:

* If you generalise based on one example, you will get a flexible API that can handle only that example. * If you generalise based on two examples, you will get a flexible API that can switch between those two examples. * If you generalise based on three examples, you have a chance of abstracting over the common essence.

Re: Goodbye, Clean Code

#249

Earlier quoted context omitted.

One of the areas where I really like "incidental duplication" is in tests. Tests can sometimes be very repetitive and identical, and it's tempting to want to refactor it in some clever way. That's almost never good. On top of the reasons laid out in parent comment, tests also function as unofficial documentation. I like having everything explicit in there, it makes them easier to read and understand.

Tests rarely have bugs, I find, so generally dry isn’t critical. Also, dry is for security (see below)

> Tests rarely have bugs, I find, so generally dry isn’t critical

DRY is important for tests, but the areas where it is have probably (if you aren't writing a testing framework or using a language that doesn't have one that covers your use case) largely covered by your testing framework already.

Re: Goodbye, Clean Code

#250
This may not come across well, because it is related to very large CAD models. Think rockets, cars, ships, airplanes, type large.

In the assembly paradigm, each component has 6 degrees of freedom. On average, a good human can mentally process a handful of components. Beyond that, it gets very hard to balance all the degrees of freedom.

Solvers also do not scale well once things reach the hundreds, sometimes low thousand component level. Linear compute performance bound. Parallelization is hard and a focus of current development.

Very large assemblies present problems similar to the ones seen in large software.

The dominant way of building big models, assemblies is to compartmentalize sub systems, build to common interface points, and only constrain important component groups.

Resolving all the degrees of freedom requires making big investments in time and compute power that only pay off when things change.

Almost never makes sense.

The balance is making those investments where change is known or predicted with high confidence.

Otherwise, it is easier to defer this task and work with common assumptions, until such time as a more complete model is warranted and indicated.

It is always a hard balance to find because the allure of "handling the future changes" now is seen as a savings when the truth is almost always a loss.

Those efforts become debt in a few ways:

Big investment in change ready model sees changes that lie outside the scope of already implemented model dynamics. (Big refactoring needed) 2x if not more work required.

Model actually defined incorrectly. Creates problems that would not otherwise exist. Hard to find these due to massive compute and human time spent simulating and debugging.

Model overly compartmentalized, due to compute limits and too many assembly model constraints. Makes reasonable changes difficult. Refactoring is needed.

Inability to operate with full or large fractions of model, due to inability to compute it reasonably.

Finally, where the large model is made across various CAD systems, designing in place, just putting things relative to common assembly mating points actually is the most efficient way!

When a dynamic assembly model is needed for analysis or other simulation related task, or changes, making it right then, even sometimes throwing it away when the work is done can make the best sense, with only the final result kept for the future.

None of this is intuitive at first. Everyone will tend to over model, until they hit one of the pain points, have to over correct, and work through it all.

Post reply on HN