I know this seems pejorative on some level, but I'm trying to paint a contrast here between sensibilities. I think it is worth considering that one person's urgent and long-needed reduction in technical debt is another person's over-engineered. It often comes down to whether the person passing judgement has the awareness of how everything fits together and thus can benefit from a structural tune-up. It could also depend on how many systems and code bases they have had to maintain.
I mean, on balance, we have a really bad track record as an industry of building unmaintainable code bases. Most developers are working on the things they find enjoyable with less regard for making it work well for other developers. I think that the author's remedy of splitting these responsibilities out into separate roles is an indicator of the overall problem. It shouldn't have to be framed in that way to make it palatable to the average developer.
As a developer, you should always want to make the code you work on work well for others, and that includes refactoring on occasion. If you find yourself in a posture where you are always "innovating the new fun stuff" while others are seemingly dragging you down with making it actually manageable, it's time to slow down enough to realize what this means to your team.
By not taking care of your own code so that your other developers find it manageable, you've turned them into your cleanup crew. You need to thank them for being "That coworker" who you find "gold plater, or unproductive, or slow", and perhaps stop building prototypes and calling them done.
Just some food for thought.
The code melts and changes for awhile... and then congeals into unchanging rock again.
One project I worked on had almost a half dozen "lava flows" in the JavaScript alone... I had a hard time convincing folks we could rewrite it using ASTs because all the rewrites scared folks away from any further rewrites!
People learn by doing. And after a few such messes, the dev will learn how to do smaller refactors.
I chip away at it in my down time but it's no longer prioritized.
I find in this line of work, you will always seek to find ways to balance business/market needs vs. being a paragon of programming.
In my current project, there are piece of code that I've wanted to remove for months now. I was working on refactoring it out completely, but there's one place that still uses it, and that wasn't so easy to fix as all the other places where it had become unnecessary. It still needs to be done, but there hasn't been any time. Or urgency, really. But it's still there, taking up space in my head.
Plenty of it is also on managers who simply aren’t competent to manage, of which there is a tragic abundance. If that really bothers you, move up to management and start doing the job better or go find a new job. Otherwise, not your circus, not your monkeys.† Just make sure to keep your papertrail.
--
† Whereas the liabilities for screwing with production code outside your scope of work def should be. Though again, that assumes a management that actually understands what’s going on in its own shop, and isn’t just running around with its hair on fire 24/7.
Even big corporations are open to refactoring when you put it in the context of data integrity and consistency (foreign keys), because data inconsistency causes tickets which cause churn which is a number the CEO reports to the board.
That’s very different to just dicking with the company codebase for one’s personal amusement. That’s the bloke with the dozen rusted automobile shells sitting on bricks in his front yard, while he’s in his shed “busy working” on number thirteen. He’s not productive, he’s just playing with himself. And making the whole place look like trash while he’s at it.
Third time? Maybe consider a refactor at that point.
We were looking at a pull I'd created and he asked 'are these things really the same or is it just a coincidence because the features aren't mature enough yet?'. And yes I'd conflated two things with entirely different intents by over-abstracting.
But I do think it falls apart when applied at a more macro level.
The kind of duplication you want to avoid is knowledge duplication. If two pieces of code literally do the same thing but for different reasons/purposes, it may not be a good idea to deduplicate them.
The result made it hard to understand the code, and hard to graft in new functionality. So over time I refactored it to duplicate the code, but each string of code is a lot easier to understand.
I know that eventually I can move all the identical code out of there to its own service, and replace the 8 services with just 8 simple configuration lists, and that's definitely where we're going at some point in the future, but for now, the 8 times duplication is actually fine. People know these services and what they do, and I know they're conceptually all instances of the same thing, it's just not quite represented that way in the code yet. We'll get there.
It's possible a few still have some exceptions, so that needs to be figured out first, but due to the way the code is put together, there's no rush; people are only changing the parts they're meant to change anyway.
Rather you should ask yourself if you want these two functions to be coupled? So that by changing one you are forced to change both. This is very desirable in some cases, but when you don't want components bolted together just copy and paste.
I'd guess that most devs don't c/p two lines, unless they're particularly slow typers. Or you're one of those fancy vim people that jumps all over the joint with keystrokes instead of having to move to the mouse to select text.
Also, the reason I basically never c/p is because I do it manually. I have a habit of using old code as a reference while I write new code, and the two pieces of code somehow end up being the same. The difference is, I've vetted every single line of what I've written, even if it's a duplicate, so I know it's relevant to the new context I'm working in. If you c/p, there's no requirement for you to mentally check that the new code is all relevent, and correct, for the new context.
Copying the code - 2 seconds. Copy the unit tests? Another 2 seconds? Refactoring the copy pasted thing that is now in 25 different git repos? Sad face
I think knuth was the first time I heard of reeditable.
For example I had to recently write some code to center a form shown as a dialog based on the overlying window. Because we use a MDI. I had to take into account local to screenspace coordinates.
Once it was a usercontrol embedded in a form, once just a form and once the top window, etc. All of those needed just tiny adjustments, while rolling it into one function would be MUCH more cumbersome.
That sounds like a board fresh-out-of-school dev. An experienced "refactorist", who knows the risks such as pissing off the other team members who find their memory of the program expired, in addition to pissing off managers with extra latency, isn't going to waste precious political capital on dubious refactors.
Make more your senior devs start the refactor, or better yet, they start them, and junior devs have to finish them. Grueling but useful training exercise.
> I've seen projects take literally months longer because the team hit caught up in "refactoring" code written by previous engineers - not because it needed it, but because the new engineers preferred to write their own code than dig in and understand what was written before them.
I've seen that too.
But a good refactor can also be the antithesis of NIH syndrome: a good refactor means someone took the time to understand the old code enough to safely change it. A slap-on "write only" patch conversely may indicate someone didn't bother to understand what was already there.
It's important to distinguish between refactors that "kneed" the code, mostly moving existing bits around, and partial rewrites that replace one portion of the code with all-new stuff. The former is a lot more trust worthy.
That seems to happen when refactorings are infrequent and random, vs. a well-groomed codebase. Of course the well-groomed codebase is a mythical creature.
The problem I see is that if refactoring isn't part of the culture, what tends to be built is a tower of cards that never gets the benefit of hindsight because with each subsequent change, the understanding of interactions decreases, and the fear of breaking something increases, leaving to very narrow cludgy changes that become impossible to maintain.
The added impetus of external schedules just exacerbates the issue until the whole codebase falls over, unless people come along who take on that risk and attempt live brain surgery.
which would take nearly as long yet have more uncertainty.
everytime i see this, which is all the time, it's because engineers are not trained nor managed properly. with everyone doing "agile", and the wrong incentives in place, engineers write code that only lives until the next sprint.
you can't just blame the engineers. you need both sides of the whole to make it work. management has to place importance on quality, and engineers have to learn to write code for someone ELSE to read. (usually themselves, 6-12 months later).
the common, pervasive flaw i always see is the curse of knowledge. engineers write code that they understand, at the time they write it. when i review, i want to see code (rather, comments, for the most part) that don't expect specific and deep internal codebase knowledge. a one-line 10s comment that seems obvious at the time (due to the curse of knowledge), can save 10m later. when poring through the code 6 months later, each of those 10m chunks adds up, very very quickly.
In this particular case, it takes like 15 seconds to split those places ... even if you dont remember that they were ever split.
The point is that any engineer who is always refactoring or never refactoring is doing it wrong. All cases require judgement of the specific situation and balancing short-term goals with long-term vision.
once you dig in and understand what was written before, that's the logical time to refactor. so the next guy doesn't have to spend the time
In that case, let Fred focus on improving the unit tests.
Someone spots the same functionality in two places, applies DRY, then two weeks later a new requirement means they need to work slightly differently from each other and that change has to be unwound again.
I've seen plenty of helpful refactors that broke code in subtle ways (if your tests aren't robust enough this can happen really easily).
I've seen projects take literally months longer because the team hit caught up in "refactoring" code written by previous engineers - not because it needed it, but because the new engineers preferred to write their own code than dig in and understand what was written before them.