262 comments

[ 1.6 ms ] story [ 234 ms ] thread
My guess is that Fred isn't going to be giddy about being taken off the main codebase.

The idea that he might be good at the plumbing doesn't mean he wants to do it.

There's also the danger that you end up with endless churn in your plumbing, testing, etc, tools instead.

I get the gist of the article, but sometimes you have to face the hard reality that there's a bad match between a team's needs and a team member's desires.

Yeah this lines up pretty well with my experience. Usually when I see someone endlessly refactoring something it's because they have an idea they're really into.

You end up with everything having dependency injection or one service that has been churned on 15 times. I haven't seen it yet where it's general plumbing fixes.

I am that guy and every day is a struggle to manage that inclination!

I really appreciate the author’s attempt here to get into the psyche (developer experience vs user experience) and to find a constructive resolution.

I’m not sure I agree with the conclusion though because I think often the issue is a matter of lacking experience (which takes time) and discipline (which takes practice). Though I’m sure many cases are just as the author described.

IMO being able to cut through work that you don't love is a tremendous skill, especially for software engineers. Many of the most successful engineers I've worked with had a really valuable trait where they were 100% effective on projects that they loved while also 90% effective on ones that they hated. This really helped them stand out when the standard ratio was closer to 100% / 20%.
For me the difference is often that I'm willing to put in what is essentially unpaid overtime for tasks I love, because I enjoy it and want to see that route succeed (a bit of selection bias here).

Its not that I'm less productive writing your CRUD application with set of 6 different popular obscure libraries of the week, it's just that when I reach a point where I've spent 40-45 hours doing that, I'm going to report back that it needs more time, because it does. I'm less likely to report back something I love needs more time, because I'm already giving it more time.

So often that efficiency difference you see is me consuming tech debt or cost in the form of personal time. It has little to do with the fact I struggle because I think the work is silly or meaningless and more to do with the fact that I'm not giving 110% if I feel it's meaningless, but you will get 100%. Not slacking on the boring tasks, just giving additional effort on the interesting tasks.

Fred was employed to do a job. Fred isn't doing it. Fred needs to learn the ropes; everyone would love to be in a position to work on whatever they want at the expense of everyone else. Fred could raise tickets to allow him time to do the refactoring but he sneakily does it as part of other work which puts his colleagues in a position where they have to accept his work because it's late.

Don't be Fred. Freds get fired.

Fred's employer needs labor. Fred is doing labor which the employer doesn't know it should value. The employer needs to learn about the cost of maintaining code over time; everyone would love to be in a position to produce features and customer value all day and never have to maintain what they've built. The employer could structure their teams and sprints so that refactoring and other maintenance tasks are built-in, but instead let it become invisible work which cuts into labor.

Don't be Fred's employer. Set up teams which build tools, maintain developer infrastructure, work across the company to improve code quality standards, and ease the burden of deploying services at scale.

I've been at three different employers on the equivalent of a tools team. It's a pleasure to have fellow developers as my customers, and I understand that my role is integral to not just success, but comfort and peace of mind.

It's not really about the employer, it's Fred. Fred is treating his assigned work as though it was titled 'Implement Feature X (and also anything else you'd like to do)'. That's just bad discipline. There's a time and a place for refactoring. If Feature X cannot actually be done without refactoring then mark that ticket as blocked, and start work on a new ticket that will unblock it.
Fred’s possibly a “DevOps Engineer” or maybe a “Tech Lead”. It’s conceivable he understands the software delivery game better than the “just ship it” brigade who don’t really care beyond what the jira ticket says.

I wonder if Fred accurately estimates his work to include all the quality, longevity and cost reduction work he does but just gets shouted down as pessimistic during their iteration planning?

Worst case Fred spends 2 years doing that chases off all of the customers as he is not adding features and the whole division is laid off. At least they had a nice severance package....

In my exp re-factor happens when you add a new feature and it does not fit into the existing framework. Sort of a arch version of the rule of 3.

That doesn't seem like a fair characterization. The distinction between "refactoring" and "implementing a feature" is not always very clear. If a new feature violates some assumption of a dependent API, do you hack around it or refactor the dependent API to take change the underlying assumption. The refactor may not be strictly necessary to get the feature to work, but an experience developer may understand that the hack, quick solution will cause other more serious issues later and choose to do more work up front to head it off. Should that be a new ticket or a separate task? Maybe. We have to make judgements calls all the time on stuff like that and I don't think it is really possible to micromanage development of a complex application that way.
In my experience, Freds are often a holdover from an earlier time in the companies' life when autonomy was not only desirable but required. Some years pass, a few managers get hired between the CTO and Fred and sooner or later the manager needs Fred to fall in line because he makes the manager look bad.

Eventually the "make a ticket for everything" scrum crowd wins out and it's now Fred's fault for refactoring without a ticket. Fred long ago learned that refactoring tickets always mysteriously sink to the bottom of the backlog, so he doesn't make them anymore.

TBH, the Freds I encountered are not really in any danger of getting fired. Because they were the only ones touching all parts of the codebase with their refactorings, they were pretty much walking bus factors.

"Fred long ago learned that refactoring tickets always mysteriously sink to the bottom of the backlog, so he doesn't make them anymore."

Exactly this. Maybe Fred understands that if he creates a ticket to do a required refactor then it will never get prioritized and when things blow up in production 2 months from now it's his phone that's going to ring in the middle of the night.

Fred knows the codebase so well he has a list of functionality he wants to rewrite. He patiently waits until a ticket comes up in the backlog that touches that code.
I make tickets for my refactorings. Solving tech debt is also important.

You're spot on, though. I'm currently in a position where I refactor a lot. This is in a team that started with just me cobbling together a prototype, later with two other freelancers. We'd regularly completely reorganise our entire stack, because we were still figuring stuff out. Now it's a team with 7 developers, an actual PO, a separate scrum master and a designer, and I still love to refactor stuff while everybody else is building features. Another freelancer that's still left is our infra guru and he keeps tinkering with that instead of building features, but it's definitely helping the team.

> I make tickets for my refactorings. Solving tech debt is also important.

You're right - it's important. But, guess what, most business people have no idea what refactoring means and no amount of explaining seems to get that through. They think it means developers get the equivalent of a paid holiday because of some preconceived notion they had at some other company they worked at.

Then explain to them why it's important. That we don't want the code to turn into an unmaintainable mess. That maintainance costs dwarf initial development costs. That this is not some sort of hobby project, but a professional, production-quality project where you can't afford to let code quality drop.
I've tried many times. It usually comes down to this: "I don't get it. We've managed for so long to do things quickly - why is it now that we can't go fast?"

Unless they wrote code in the last few years - I don't see people ever relating. It's just some abstract problem to them and you never get enough time to thoroughly explain how it all works.

Unfortunately, we're not like a factory. You can't make the retooling argument in the same way and see blatantly obvious results. It's more nebulous.

If someone out there figures it out - I'm sure we'd never have these discussions. They'd say, "Oh, why don't you just use the bradlys argument? He wrote it up on HN some years back and this refactoring discussion has been solved ever since."

> "I don't get it. We've managed for so long to do things quickly - why is it now that we can't go fast?"

"You can go fast when you're rapidly prototyping something that doesn't have to go to production, doesn't have to support thousands of users, is allowed to fall over at any time, and doesn't need to be maintained. You can also go fast when you're working on a well-designed system that is designed to support what you're trying to do. But the moment you start making it do something it wasn't originally designed to do, you slow down, and you risk turning the code into a mess that will slow you down even further in the future. If you want to go fast, let us refactor it now, so it will support the things we want to use it for."

And if they don't get that, maybe they shouldn't be in a position to make decisions about this sort of thing. Though I guess they won't respond well to you telling them that.

Many managers should indeed not be in the position to make decisions about these sort of things. Yet they are.

As you mention, they don't respond well to telling them that. Their own managers also don't like the implication that they made a mistake in whom to promote. Since everyone wants to protect their own career, nobody will ever step down or revert such a decision, with predictable results for overall product quality.

Fred understands that it's unsustainable to always deliver features. Fred has been doing this for years. Fred knows the ropes. Fred has morals and stopped producing garbage code because he knows how painful it is for the whole team. Fred intelligently does it as part of the requested workflow because he knows it'll be heavily scrutinized by non-technical staff. Fred knows that deadlines are made up and don't mean anything, and should probably find a new job because he's not valued within his organization.

Be Fred. Get fired.

Based on that strawman story, all other developers despise Fred. So maybe his refactorings are not that great? And if they are great, where is tech lead or other developers in all that that should protect Fred?
Wooshed over you I see...Fred will get a better job and everyone at the company understands Fred because they are all Fred at heart but they like where they live and they can't afford to spend the time job hunting or moving cities with another kid on the way. Fred doesn't have kids, Fred has projects and ideas and the only people who don't understand why Fred is doing what he's doing are managers who can't understand why they are paying him for a week with no features even though there is a very very good chance he has cut down future development time considerably.

Be Fred, quit your crappy job until you find one that can use your talents instead of calling you dead weight for exercising a grain of innovation in an otherwise destitute business grind.

Refactoring is what you do to existing, rough production code as a prelude to developing new features on top of it, so that the new code is built on a solid foundation and the whole lot doesn’t degenerate into a big ball of mud.

Therefore, if refactoring is all that Fred does, he’s not adding value, he’s an OCD hack and/or make-work bluffer who’s masturbating the code for his own gratification. Sack his worthless disruptive ass and move forward already.

If Fred was spending his days fingerpainting on the walls, you may have a point. I don't like the idea of being a technical leader and frowning upon people doing productive, deep, technical work because it doesn't check a dozen boxes of being on the approved list of things to be working on.

The art of management is to take that creative energy, skill, and passion and direct it towards generating value for the organization. Top tier teams happen in part because management flips this bit from zero to one for each team member, through leadership, coaching, decision making, and providing 'air cover.' You go to war with the army you have, and the best leaders turn people who would fail under average leaders into superstars.

If this is the person's only flaw, and they're otherwise a great team member, good person, and capable engineer, if they are ultimately fired, that is management's fault, not Fred's.

Blaming employees for breakdowns in process and communication and firing them is one way to run a team. This article proposes an alternative to that.
... Unless they're really good at it.

I hate to admit it, but I'm a Fred. Extrinsic motivation doesn't work well for me.

Yeah, I need to make some money I guess, but I'll make some pretty hefty compromises re: pay and benefits if it means working with an interesting stack or on an interesting problem because intrinsically motivated work is far higher quality for me than extrinsically motivated work.

A common pattern in my career is that my work stands out in some way that causes me to get pulled out of the main grind--and put on some fun special project.

As long as the value you're providing by being a Fred is greater than the value of the job you were hired to do, I find that they'll find ways to keep you around.

I do feel like a Jerk sometimes because I end up getting what feels like a promotion for doing my job poorly, while those doing it well have to stay and keep doing it, but when I buckle down and keep my nose to the grindstone I end up writing shitty code and letting my team down.

If Fred is otherwise hard working, there's probably a place for him nearby.

I'm a Fred. Not fired, getting good raises every year. Gets huge support from the team because they see I'm working for them.

Don't be in a job that fires Fred. That job is a ticking bomb.

Your definition of Fred is someone who's not communicate the problems and tries to fix them during the time he needs to do planned work. I was a real Fred and did exactly that. My work was really valued but I always had the impression that no one really saw my intentions and the importance of this extra work. In the end I burned because of this. Today I'm still a Fred but I learned that transparency is the key to successful communication. If something is odd and needs refactoring that doesn't fit in my current ticket I raise a new ticket with exactly the problem I found (instead leaving a dead Todo in the code). Then I discuss this finding in the next planning and we budget to do it if it's required to be done. Most of the time it helps tremendously to share thongs with my colleagues and welcome up with an even better solutions than I came up in the first place. That's a Fred you want to have in your team.
Fred should learn how to squeeze in his refactorings into other, related tickets. And to learn how to argue about why an user story is more points worth, without explicitly saying the R word.
A big risk here is that it's very easy for refactoring you make a codebase worse.

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.

Yup, instead of DRY I'm a big fan of WET, Write Everything Twice.

Third time? Maybe consider a refactor at that point.

I learned this from the smartest dev manager I've ever had in the second or third week at the job, it really tore down a lot of the notions I'd formed as a self taught developer.

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.

Wow, you just blew my mind. "Don't Repeat Yourself" could just be premature optimization and overfitting on a tiny data set.
Absolutely. This has been discussed here quite a bit (e.g. https://news.ycombinator.com/item?id=120614530). In real life it's often visible when you have methods that take one or more flags to tell them how to behave. Fixing involves duplicating the method - one copy for calls where the flag is true, one for where it is false. Then simplifying, then repeating. The code that falls out at the end is often dramatically simpler than what was there before. It makes me wonder about some of these programming aphorisms - they only seem to work if you are already experienced enough to know to take them with a pinch of salt.
DRY is really valuable for noobs. Like, defining magic numbers twice in adjacent functions is really common behavior for beginners. Likewise, writing the same exact logic several times in different parts of the code without encapsulating into a function. Stuff that would be obvious for even junior devs.

But I do think it falls apart when applied at a more macro level.

The book "Pragmatic Programmer" talks about this in chapter 2, topic 9.

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.

Yeah I think DRY has been bastardized to mean dont repeat code when it actually means encode business logic in one place.
I mind duplicated code a lot less than duplicated data. If two things have to match, but they're using two different configurations, then there's going to be a mismatch some day. I'd rather create the second config out of the first one. If front- and backend need to know something, have the front-end get its config from the backend, instead of hard-coding it in two places.
I've got the bunch of services that all do the same thing on a different data model. Originally they were all different and completely custom. Over time, I've managed to get nearly all of the code practically identical across 8 services. The same code is duplicated 8 times, but I prefer that over when they were all different, because then they were all doing basically the same thing in 8 different ways.

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.

Very early versions of the my compiler's code generator were set up to minimize code size, for the simple reason that 640K wasn't enough. This meant merging disparate sequences of code, often with goto's.

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’ve inherited codebases like this, they almost always have subtle bugs because people changed something but missed the other place the logic needed to be modified. My thought is that functionally cohesive blocks of code should almost always be extracted to a function and named: if this causes a bad abstraction, you can always either duplicate the function and rename or inline the function and re-abstract.
I've had the opposite experience: bad abstractions generally cause more pain than duplicated code. Generally this is because bad abstractions tend to leak their bad-ness into the code around them.
I think that's called technical debt.
Thanks for adding that acronym to my vocabulary. I use this strategy for a long time but needed more words to explain it.
Oftentimes it is better to make code that is easily copy pasted/edited later than to make reusable code IMO.

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.

I prefer Write Everything Thrice.
If I recall correctly this is also the advice PG gives in On Lisp when deciding whether or not it is time to write a new function.
People think I'm a charlatan when I advocate copy and pasting code around, but really it just takes a few seconds so I don't think time concerns are a good argument against it.

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.

This is fine until the copy/paste paradigm escapes the sensible boundary.

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 the problem with copy pasting is that it implies you're copying a chunk big enough to be worth taking the shortcut. If you happen to have two of the same two-liners, then there's a good chance they shouldn't be abstracted. But if they're five-liners that probability goes way down.

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.

just be sure to stop before it just becomes "We Enjoy Typing"
I have seen code based that were following this principle, but nobody realised they were writing the same thing for the 5th time.
Well obviously don't refactor in a vaccum, part of an engineer's skillset is anticipating future changes and being flexible to them. You can still get DRY with slightly different functionality, most people with some OOP background should know how. Most of the points you bring up have nothing to do with refactoring and everything to do with other failed processes.
I'm not saying refactoring is bad. I'm saying that in my experience engineers who "never stop refactoring" (the topic of this post) frequently do it badly and cause more harm than they actual long-term code improvements.
I have had the opposite experience which is more in line with the author of the article. Most coworkers I've had that refactor a lot were the best technical coders on the team and we're usually refactoring other people's short sightedness.
If you do a lot of refactoring you get better at it. You learn to write code that is easily refactorable, so that it doesn't hurt to refactor it again later.
Maybe it's just me but in my career (15 years at this point as a professional software engineer) by FAR the biggest problem is teams not doing enough refactoring. When new features come along that violate basic assumptions of the initial codebase, instead of refactoring out those initial assumptions you hack around them. It's almost always faster to do that then do a proper refactor for a particular feature but over time the codebase devolves into a tangle of spaghetti and incomprehensible chains of if/then/else where variable names have no semantic connection to how they are being used anymore. Sure, you can have the opposite problem where you refactor too eagerly and waste time but all the incentives in a corporate setting work against erring on the side of too much refactoring.
Similar experience and totally agree. How often do people go out of their way to improve a severely limiting code base? Not enough.
Often it's because they are being pressured by leadership not to. Leadership's bonuses and promotions are dependent on developers delivering value NOW.
Some of that is on developers, famous for overpromising and under-delivering and generally not managing expectations. There is a real skill to effectively explaining a job’s requirements to non-developers, and it’s something I think a lot of us suck hard at.

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.

Doesn't leadership hold a lot of stock?
Most productive refactoring carries forward from a more effectively designed data model. There can be value in pushing code around but often a lot of that can be subjective, which this thread has discussed.

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.

Mostly agree but I think the distinction is not always clear between data model changes and changes to application-tier logic. In most cases you can do a particular refactor either way and you have to figure out what is the best way to achieve it. Just pushing code around is usually not tremendously valuable but it's also not particularly costly.
Don't forget halfway refactoring. The worst case I've ever had was a project with 3 completely different and incompatible ways of accomplishing everything. Different packaging schemes, different DAO designs, different UI framework/libraries. The worst!
I think we can all agree that the dev who starts a big refactor and then drops it when it's half-done is a terrible human being
I don't agree. Big refactors are _hard_. They usually require levels of sustained focus at multiple layers of abstraction, and its really easy to get exhausted/burnt out or be forced to switch gears in the middle.

People learn by doing. And after a few such messes, the dev will learn how to do smaller refactors.

That's me. I want to finish my refactor but business requirements have me pumping out new epics.

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.

There's not always time to finish it properly, but there's often a good stopping point halfway. When there's a lot that needs refactoring, I start with one specific thing, and leave the rest for now, so the piece I've bitten off is still small enough that it can be wrapped up in a reasonable amount of time. And the rest will have to wait.

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.

And how does the team feel about it?
They also want to get rid of the old code, but they're not removing it. One of them also has a tendency to interesting structural solutions, though he tends to do it more by adding them than by rewriting what was already there, which has the advantage that it doesn't disrupt the old code that others are working on, and the corresponding disadvantage that the old code survives.
The technical term for that is "a Lava Flow".

The code melts and changes for awhile... and then congeals into unchanging rock again.

I'm using that term from now on. :)

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!

I've seen that applied to long running legacy projects. This is how they did things back in 2008. Here someone discovered an orm library. Now here, they started to try to introduce some microservice event driven architectural pattern that was all the rage in the blogs that year....
The best (well, worst, really) part is when it ends in a state where the parts are almost right, but not quite. The result is, quote one of my coworkers, "We ended up with the architecture that is so flexible that we can't change a single bloody thing in it! What the hell!" The whole is flexible, but this flexibility is pointless: you can make the parts behave differently, but there is only one behaviour that is actually useful; you can combine things together differently, but there are at most two combinations that do things in a useful way; etc.
Sounds like the effects of temporal coupling, or "settism and gettism", or other principles, which are violated. People are blinded nowaways too much by SOLID, and forget about the remaining 20 principles.
Yep, but there you are refactoring as a necessary prerequisite to achieving a clearly-defined end goal. That’s part of the development roadmap and should be planned and budgeted accordingly. When building a house, a professional builder knows to dig out and pour a solid foundation, so that by the time they get to tiling the roof the walls beneath it aren’t already sinking and tearing apart.

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.

Right, of course dicking around with the codebase for personal amusement is bad and a poor use of time. But I think refactoring doesn't necessarily have to be achieving a clearly defined end goal to be useful and appropriate. Primary because product development itself doesn't have a clearly defined end goal. It's an iterative process where the end goal can change as you discover new information. To follow on your example, if I'm building a two story house and pour a foundation to support that but then the foreman comes along and says "actually we need to build a ten story office building on the same footprint" then you absolutely should start over and pour a new foundation. If you have a well-defined spec to begin with and it doesn't change and you STILL need to do a bunch of refactoring along the way then you probably did something wrong. But in my experience that is almost never the case. Refactoring happens because the requirements change and assumptions you made are no longer valid.
I find the article quite presumptive in the sense that it presumes everyone has the same definition for "gold plated", etc. That, coupled with the With the remedy of "give the guy something else to do" makes me think that the writer is on the opposite of the spectrum which could be characterized as "throw stuff at the wall and see what sticks."

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.

This is what I've seen too. Higher pressure to get features out. Then the code gets worse and worse over time. Development gets slower and pressure gets more intense. Then customer gets pissed off and fault lands on the Devs.
> 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

In this particular case, it takes like 15 seconds to split those places ... even if you dont remember that they were ever split.

Let's take bad or short-sighted refactoring off the table and only consider good refactoring. Even in that case it's not obvious when refactoring is justifiable. If it's just one or two small features hacked onto a sub-optimal code base then probably not worth refactoring, but what if it's more? Well, you might want to actually build them first and then refactor once you have good test coverage and proven use cases. But wait, what if the old architecture is forcing a lot of incidental complexity that will be costly to unwind? In that case it may be better to pay the refactoring cost up front.

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.

Then that's a sign you should put more work in your test-suites.
> A big risk here is that it's very easy for refactoring you make a codebase worse.

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.

> than dig in and understand what was written before them

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.

> 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.

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.

> the new engineers preferred to write their own code than dig in and understand what was written before them.

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

The whole category of responsibilities listed (automated tests excepted) is what tech leads effectively do where I work... is this not normal?
IME, not really. And to be honest, I don't think it's a good idea to delegate these tasks to a single person on the team.
I wouldn't say it's delegated necessarily, but we consider a tech lead's goal to be unblocking the rest of the team, and if there's nothing specific to deal with at any point (hah!), dealing with these points is often the most obvious way of unblocking for the future.

Which isn't to say that the rest of the dev team can't do any of these things, but the tech lead is almost always the most experienced dev, and a lot of the DevOps type points aren't concrete ticket items, and a ticket which basically says "Explore [X] to see if it'll help" is (a) potentially too abstract for a very green dev and (b) the tech lead is probably best positioned to judge whether there'll be any meaningful help in practice.

By way of example, I might write a dev ticket along the lines of:

Background/Goal: With a larger userbase, we would like to add a moderator role. Users with this role would be able to hide posts, but not have any other elevated privileges.

Suggested Implementation:

* Add Moderator to the UserRole enum

* Split the post hide/deletion privilege into two separate privileges

* Associate the post hide privilege with Moderator and Admin roles

* Associate the post deletion privilege with only the Admin role

But a "improve the deploy process" ticket would probably look more like:

Background/Goal: Deploys currently take fifteen minutes on average, from start to finish. We would like to cut this down if feasible.

Potential Lines of Inquiry:

* Can we build things in parallel?

* Can we add more CPU/memory to our build environment?

* What would implementing blue/green deploys look like in terms of cost and benefit?

Comparing the two, you can see that the second is a lot more abstract--and maybe this is just me being bad at ticket writing!

The fear would be Fred runs out of useful refactoring and starts creating work for himself.

When you're a hammer everything looks like a nail syndrome?

I'm not a big fan of people just doing what the ticket states.

Quality doesn't start when a customer comes up with a simple fix/enhancement. Quality comes from people with experience who knows exactly what needs to be done even if it is not stated in a Ticket.

- Security - Maintainability - Usability - Clarity - Performance

You are the expert, NOT your PO. Your PO tells you what he/she needs, you tell them what you HAVE to do.

And personally, i have never heard anyone complaining that someone else did a better job on a ticket than the ticket stated.

I agree, the teams that cause the biggest messes are, in my experience, the ones that passively receive requirements from POs, without thinking about the conceptual integrity of their domain. However, complaints about stories taking “too long” sometimes are complaints that people are trying to do a better job on a story than specified.
100% Agree - I am the PO on my product. I was the original coder, I know this code better than any of the newer devs.

And yet they bring so many great ideas to the table that make the product far better than what I wrote up in a story. They make the code better, they ask great questions, they propose changes to designs and features... and we then end up with a much stronger product. The stakeholders love the results we are coming up with, and I'm thrilled with where the product is going, and the devs enjoy contributing at multiple levels and having some autonomy.

Is this not the norm?

I haven't been working for long, but the product manager at my job just tells us the features and the we're free to build it however we decide to.

Highly depends on the skill level and experience of your overall team.

There are plenty of people writing code in small companies who just never seen or heard it or never experienced the advantage of doing it good/better.

They get a task to do and thats what they gonna do.

There are also Teams in the wild where people get pushed to over a longer time of period because people gave up on them but you don't wanna be the bad guy firing them and there is still an it expert shortage and you might find something new for them to do, you know, people where you are wondering how they earn a paycheck.

And i have seen plenty of experts who just don't have the experience to see certain issues.

Good example are things like: Mandatory Code review (for shared ownership, for quality), taking metrics serious (yes messure what you do), proper CI/CD (no do not skip breaking unit tests...) etc.

I believe, one of my most critical skill for companies is making sure those things are in place or become good. It feels weird to be honest, that those things are so critical and still are not lived as you would assume, or at least i do.

I identify with the motivations outlined in this post. One additional thing I'll add is that refactoring allows me to learn and understand the existing code base. Being able to move and rewrite code reminds me of playing with Play-Doh. This practice lets me immerse myself into the code a bit more so I can grok it more easily. Half the time my refactoring is thrown out or shelved for ages so there's nothing in the project history to show for it other than my new-found knowledge.
There is pointless refactoring but Fred sounds like a senior developer cleaning up messes to me.
Ugh. I hate posts like this. Refactoring, speed improvements, documentation, testing, etc arent roles. They arent buckets. They arent seats you fill. Theyre part of every engineers job. The issue is we keep dividing issues into “product work” and “unproductive” work. If this person is fascinated with refactoring thats amazing. If theyre obsessed with speed, equally so. But there needs to be constraints on when to start optimizing and when to stop. The when to stop part is the one where people fail the most. Simultaneously, we should all be dedicating time on a regular basis to these types of activities. Like min 25% of our weeks for most companies. Instead we hide these improvements in features and complain when coworkers are “slow”.
I don't think the author is suggesting you go out and hire a "refactor person", a "new feature person", a "documentation person", etc. Rather, assigning the body of required work to each developer based on her interests.

If you have one new feature ticket and one refactoring ticket, don't give the new feature ticket to the developer known for refactoring too often.

> But there needs to be constraints on when to start optimizing and when to stop.

Now that is very true. But feels like a separate issue to me.

Actually I think assigning work specifically to people ends up solidifying responsibilities. Its like the person who doesnt mind writing tests. Guess who ends up writing nothing but tests? Guess how many tests the rest of the team writes?
Guess how much that person likes getting assigned to write tests after 6 months to years?
I often understand code based by refactoring things. And then I throw it all away. I use it as a way to play with the codebase and understand the rationale behind its design and organization.

Sometimes other people do that too, they just need some guidance that it’s okay to do that and then throw it away. The important artifact you’ve created is a deep understanding of the codebase.

Great read. Especially the point about developers vs users.

Programming langauges and IDE's have an effect too in my opinion. I've found myself and other team members refactoring much more when using OOP languages. Design is very subjective.

The leap from this:

> That coworker who never stops refactoring

to this:

> Fred is more interested in providing value to other developers than to users.

is not so clear to me, while the article assumes it to be self-evident.

I personally like refactoring even though I work on the codebase alone. I do it because refactoring allows me to ship features faster (in aggregate), and allows me to create a better overall experience for users. I care only about the end result for the users, and this necessitates that I keep the code clean and succinct.

I remember my first React project in 2015.

A co-worker initialized the project with CoffeeScript and Flummox, then switched to ES6 and then switched to Redux.

It was a few thousand lines of code, so it was often "Yes we can add that new feature when I did my refactoring" which often took weeks in which I couldn't do anything valuable.

Also, the bugs that grew of those re-writes followed us for months.

The greatest difficulty I run into as an indy developer is lack of internal documentation.

I often write something and don't look at it for a long time thereafter; In the intervening period, I forget some important aspect of how this code works. Or I inherit a piece of code from another developer, and I have no idea why it was implemented the way it was.

I would love it if developers wrote a line of comment or two per function, just laying out what's happening and why the code was written that way.

Our CTO cofounder was a "technical genius" asshole who did 3 entire rewrites of our stack before a single user touched the product. He would come back from a weekend and say "I stayed up and refactored everything to make it faster". Of course this left our other developer completely in the dark and it broke a lot of shit in the process.

We ultimately fired him and got another cofounder up to speed into that role. It was the most frustrating red flag that we should have caught earlier and put a stop to.

If you don't have test coverage, you can't refactor (or at least it's a terrible idea).

The first step in refactoring is writing a test (probably component level at first) such that you can make sure you don't break anything.

Seriously, this is all covered in Fowler's book on refactoring (which is incredibly worth a read).

This advice applies generally - leaning into the interests of your team acts as a multiplier. I haven't seen it done often, and sometimes it's explicitly frowned upon, but I often advocate for team interests to influence roadmap prioritization.

"But the customers' needs should prioritize the roadmap!"

Yes, they should, but it should be balanced. Better to have 10x the output, a happy team, and hit a broad surface area of customer value, than a slow, clock-punching team who comes into work being told what to work on by dictum based upon the most high customer demands. A sign you're doing this right is if there is some slight tension between engineers working on what they like, and what management feels is the important thing to be doing. You want that tension, and need to manage it from falling too far to one side or the other.

It often turns out if you have a balance here, you'll see innovation happen as a side effect. Team interests of a smart team often are somewhat far afield and lead to bursts of creativity, and can lead to new forms of thinking that lead to new features. Ensuring the team groks the customers' needs, and their demands, as well as giving them opportunities to pursue their passions, allows them to connect the two together when inspiration hits. Often times to innovate you need to do more than just listen to your customer, you also need someone with an orthogonal interest, knowledge, or talent stack to cross-connect things into something greater than the sum of its parts. Empowering the builders on your team to explore things is a good way to harvest some of this 'innovation space.'

I also think that customers often don’t know their needs, if that makes sense: the team can often see ways to streamline a product that never occur to the customer because the current behavior “anchors” their ideas (although, the reverse is often true too)
on a side note, i've witnessed a big difference between code that was properly architectured from the beginning, to code that evolved in a purely iterative fashion, driven by end-user feature tickets.

Architecture design is to me a different beast from simply refactoring, because it is also meant to anticipate future evolutions, in order to provide a solid technical layer upon which feature can be iterated.

Unfortunately i don't think a lot of teams knows when is a good time to seriously think about the time to (re)design / architecture a system. Nor what is truely meant by "architecture" ( hint: no, it's not just about picking the latest MVC variation of day).

I don't see why this perspective on someone doing things differently is so surprising in an era where we're pushing the advantages of diversity. Everyone comes at things from a different perspective and has different strengths.

I remember having a coworker ask me for advice on a storage array he was working on. He was banging on the UI, checking out all kinds of irrelevant paths, seemingly exploring all possible ways of accomplishing the task at hand, even the ones that were very unlikely to be correct. I was kind of frustrated that he was spending time exploring unimportant elements as I could clearly see it was one of just two or three options. I tried to brush aside some of the exploratory actions and he said "oh, I know that probably won't work, I just wanted to take a look.."

I didn't have the patience or attention span to wait for this ridiculously slow interface, so I wandered away after giving my best advice.

A couple months later, whose experience did we always rely on when trying to figure out a novel problem? That's when it clicked for me. I won't necessarily have more patience myself with the exploration steps, but I certainly appreciate those who do.

This blog posts describes the conflict as "providing value to other developers" vs "providing value to users", but that's false dichotomy. Refactoring isn't about providing value to other developers, it's about reducing the complexity and investment necessary to make future changes. That's valuable to the other developers, sure, but it's also valuable to the business, and also valuable to the users.

While there's certainly something to be said for assigning people responsibilities that work well with their personal strengths, that won't compensate for misunderstanding the purpose of refactors, how and when to apply them, and how to prevent the need for them in the first place. It's essentially hiding the problem rather than identifying the problem and introducing a relevant solution.

I am sympathetic to this viewpoint, but I realized how infeasible it is with this suggested use of Fred's obsessions: "quick and thorough code review". People like Fred will never be quick in their review, but boy, will they be thorough! Everything presented for review will be rejected for being fundamentally the wrong way to do the job, as explained in great detail.

Similarly, "refactoring and managing tech debt" will become "rewrite from scratch", and "automating anything that can possibly be automated" and "streamlining the deploy process" will be inordinately parameterized in order to cover every possible (and hypothetical) edge case.

Not all people like Fred will act as you describe. When I was at Google, Michael Chastain (I think he is/was a GDB maintainer) did lots of massive refactorings across the whole Google codebase, and was one of the hand full of people with ownership rights at the root of the source tree. He really was a major force multiplier.
I don't know Michael Chastain, but I know people who fit your description, and, unlike the generic Fred of the article, they are not slow to deliver useful software - quite the opposite, in fact.
Fred sounds like he may have ADHD. I'm Fred and I have ADHD. Now I'm not an expert so it's not necessarily true. ADHD comes in a spectrum of severity, and plenty of ADHD symptoms are just things normal people have trouble with too.

That said people with ADHD tend to have trouble with delayed gratification. Helping your coworkers has immediate benefits when they are grateful for your help, even though long-term it may make things worse. We also tend to have a lot of social anxiety and care a lot what other people think about us (rejection sensitive dysphoria). For these reasons, I believe people with ADHD have learned to be very helpful to those immediately around us, even to a fault.

Fred still has to get his work done though. Have some empathy for him because you don't know what he may be dealing with compared to you, but it's not an excuse.

I also have ADHD and my experience is similar. It's generally much more enjoyable to bash out a bunch of small bugfixes and refactors then to slog away at a feature for a week.

I've managed to strike a deal with my manager that I get to spend 25% of my time working on reducing tech debt and other smaller fixes, and the rest on primary product development.

It's also that the cognitive systems variant usually called ADHD is highly flexible, broadly scoped, continously introspective, sensitive towards inefficiency, and can intuitively pattern-match across different contexts. This means that the "ADHD" mind is just better at seeing what needs to be improved in processes and tooling.