I don't want a teammate like this. I want to work in a team where we all do this sometimes. Fortunately, I have that team. I think if I hired people and then determined how I could use them best I've already failed vs. if I see where I could use help the most and then hired the guy who can do that the best.
Every engineer I know who I respected for this behaviour also delivered boatloads of functionality independent from "improving dev productivity 25%" which kinda makes sense since if you go around refactoring everything you kinda know the whole thing. Also, how do you even know if you've improved dev productivity 25% if you haven't even improved your own functionality-delivering at any rate (which you'd only know if you attempted to deliver functionality)?
In fact, every engineer I know who I respected to deliver boatloads of functionality also had this behaviour.
All of the guys who thought they were this guy but didn't deliver boatloads of functionality were just some wheel spinners who weren't really useful. I'll probably never work with them again if I do my hiring right.
The problem with "we all do this sometimes" is that no team ever gets to 75% of their backlog. And no manager is able to justify spending the extra few days to automate something correctly or "get it right" because there's always pressing product work to do. I've found it to be much more practical that the team lean into each other's strengths than to fool themselves into thinking you'll ever see "infrastructure automation and fix flaky tests" as the mantra for a few sprints.
I guess a well oiled team could rotate positions around but then you tend to lose a coherent vision of what "right" is in any given domain because you don't have domain experts anymore since everyone has to be an omni-tool.
> And no manager is able to justify spending the extra few days to automate something correctly or "get it right" because there's always pressing product work to do.
Product people aren't reviewing your code and infrastructure. The manager is responsible for the speed and quality of their team's delivery, so if they have the team devote some time to polishing their tools, they're just using their professional technical expertise to keep things running smoothly for the product folks.
They do need to be good at it - not overseeing a bunch of feel-good but not-actually-that-useful rewriting and cleanup - but that's a question of competence more than responsibility.
It's a very similar thing with rotating people. A manager is going to get into trouble sooner or later if they only ever have the same people work on the same systems. It's the short-term optimization, but not the long-term one. And that doesn't mean nobody can be an expert in an area, but it's bad if only one person is able to work in that area.
A company that wouldn't let a technical manager make the call on that - who works on what, and how much to attention to pay to their tools - may as well have the devs report directly to the product manager. The manager has to stand up for that and not just be steamrolled.
> And no manager is able to justify spending the extra few days to automate something correctly or "get it right" because there's always pressing product work to do.
As-if the business side of things has any idea of how long something will take. Refactoring or doing some infrastructure work can just be tagged together with some other bits.
For the business side of things it's more important to know when things will be ready and knowing that timeline.
Further, a manager can explain that e.g. "the car needs maintenance". Business isn't against such things, but if the manager cannot switch from IT-speak to business language then it might seems that the business side won't get it.. but maybe it was the explanation that was lacking.
Especially in sprints I noticed that it's understandable to everyone that not everything is finished in one sprint. Though if it happens to often, maybe just promise less.
I think if every team had a person like this, things would end up much cleaner. We all add tech debt and have unfinished business, all the time. We always want the time to come back later to clean things up, but priorities never give us the chance. Having someone to do that would be useful to me. Of course, the ideal for me is that we fix the root of the problem, and avoid agreeing to schedules that don't allow us to build things maintainably.
The other thing I worry about is other team members getting sloppy, because "don't worry, Fred will come clean that up later". For many developers this won't be a problem, but for some it will be, especially when there's time pressure (and there's always time pressure).
I like planning some of these things as parts of projects.
To do X feature, you need to clean up tech debt Y.
Tying it to projects means we can intentionally distribute these to different team members.
... It also means that if cleaning up the tech debt is hard, you're project gets delayed for something where you could have found a way around fixing the tech debt.
My own productivity has been horrible lately, so maybe that should be taken with a grain of salt:P
That approach assumes to know the relation of X and Y in advance, meaning you have a curated, up-to-date list of your tech debt or even better: a map of techdebt-to-feature.
However, working with legacy, you often find techdebt or inadequate architecture/infrastructure mid project...
> Also, how do you even know if you've improved dev productivity 25% if you haven't even improved your own functionality-delivering at any rate
It really depends on what kind of improvements you are talking about but if someone measurably decreases the time it takes me to land a PR by improving CI/CD systems then its really easy to say that such improvements massively effect developers in a positive way. Also opened ticket to landed code can be looked at more broadly and be measured and optimized, as can downtime.
I think there is a fitting English saying that kind of sounds like "eat your own pie" or something like that. I can't find it!
I agree with your general sentiment.
But there are situations and teams where programmers don't tend to have an optimal individual skill-distribution so to speak. Surely there is an opportunity to complement each other to a certain degree.
Glad to hear it. I try to be as transparent as possible with my expectations so if we're ever in a situation where we might end up working together, you'll know so you can dodge me. Early compatibility checks are great, imho.
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)
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.
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.
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.
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!
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!
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.
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....
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.
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.
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.
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.
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.
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.
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 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.
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.
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 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.
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 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.
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.
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.
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
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.
> 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.
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.
> 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
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.
This post talks about a team role that has been called the "librarian". Some of the software engineering literature proposed that a team of three can be most productive as a pair supported by a librarian that writes useful library functions.
I have been in teams where we carried out projects, and each project's codebase re-invented a lot of functionality (e.g. converting HTML to plain text, tokenising an English sentence). Of course I proposed a librarian role, which as the article contends is suitable for particular personalities.
In a group of, say, ten or more people there is a room for that kind of specialisation (librarian, sysadmin, statistician, ...).
Of course there should probably be a toolsmith at the team level, but also at the org or even company level to avoid the re-inventing problem you mention.
Because it implies that Fred is not just doing refactoring, optimization and automation work but also writes new code that others use/interact with (hopefully with sufficient documentation).
Also because I'm often this kind of Fred, sometimes because it is expected of me, sometimes because I just have a natural urge to do so. However with the caveat that I also complete features and projects as is the nature of my work.
In the past I was _too_ Fred-like, but with experience comes pragmatism I guess. KISS and YAGNI are the mantras that keep Fredism at bay.
A simple yet effective technique to fight Fredism when inappropriate is to just write things down in plain $Language and keep going with your feature/ticket/task. It also helps in another way: Sometimes the first instinct is not the right one so you avoid premature optimization etc. by sleeping over it, bouncing it off your collaborators/team and so on.
We have someone on our team who maintains and develops a repository that is basically ran as a OSS project that directly services our main product.
It’s wonderful. With guaranteed longevity and having internal devs as the customer, things like on boarding are a snap. The documentation is comprehensive and there’s a slack channel monitored 24:7 for questions and onboarding.
when i worked on a team of 10 people this is what i did (and, fortunately, got a lot of appreciation for it). i extracted a common framework from a bunch of separate subprojects, refactored the subprojects to use them, and then spent my time writing useful common functionality while the rest of the team worked on user-facing features. now that i am in a team of 3 there is a lot more of "everyone does a bit of everything", though fortunately all three of us are enthusiastic about keeping the code clean, testable and well-factored.
I do the same, any kind of minimally unexpected code gets its explanation as a comment if the code can't be clear about the why. I like to leave a comment with what is happening, the background for it and any kind of gotchas I'm aware at the time.
This has saved countless man-hours for my teams, including myself when refactoring part of the codebase a year or so later, having a nicely written rationale behind some weird-looking code gives me not only peace of mind but a lot of trust in the rest of the team.
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.
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.
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.
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.
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?
My god, this describes me to a T, and every one of those bullet points sounds like a fantastic way to spend time working. It was only when I caused a production issue due to a refactor that I gave up on trying to making a behemoth codebase better for everyone who has to work in it, and that’s when work got sad and boring.
We can’t be afraid to change code. If your code change caused a production issue, what’s stopping someone else from causing a similar issue even if they aren’t refactoring?
I have worked on teams where people don’t know what the code does and the test coverage is spotty. It’s a minefield and only a matter of time before something breaks. It sounds like your refactoring work was a much needed step in the right direction.
There needs to be sufficient testing and monitoring in place to catch problems earlier than production, so that people are not afraid to change code.
Would you be willing to share more details about the specific problem you ran into?
Yep, me too! Please come to Google. This is the only place that I have ever worked that values code quality and testability, rather than just saying it values these and then letting everything fly to meet some arbitrary deadlines. There are downsides to working here as well though. Happy to chat! gkrimer@geeeeeemail.com
I think the thing a lot of devs don't know is that there's a limit to refactoring. At some point, you're done.
It's like a mountain. The team is dropped half-way up the mountain. The dev that refactors all of the time continues to head up the mountain. It can be slow work. It's a tough climb. Most of the time, devs on the team don't know any better, so they just do the work in front of them, slowly sliding down the mountain. Eventually, they look at the mountain and say "There's no way anybody could conquer that! We're lucky just to get our work done without too many bugs!"
Meanwhile, the refactoring dev reaches the top and slides down the other side. That dev starts looking for bigger mountains. The rest of the team he left behind sink into despair and cynicism.
Eventually, when organizations grow large enough, most everybody lives at the bottom of the first mountain. Devs who leave and try to summit are ridiculed at best, shunned at worst. They're hated if they only go halfway and then fail.
I am of the opinion that most big-organization developers don't actually know how to code. Sure, they know how to type in programs. Hopefully they know enough to test what they're writing. But a real understanding of what programming is? No. Until you've been over a few mountains you don't know what you don't know. Sadly these are many times the folks that get promoted.
But to extend it, consider this. The refactoring dev can’t help but climb every single mountain they see. The product focused dev decides instead they want to climb Everest.
You drop them both in a mountain range in the US. The refactoring dev starts climbing. The product focused dev decides to hop a plane and fly directly to Everest.
Taking a step back from the analogy, not everything is worth refactoring. How much will that code change over time? And what’s the opportunity cost of doing the refactor?
This article is based on a very commonly used but flawed premise that all product-driven work actually delivers value to the business and that tech-driven work doesn't.
This is only really true in the real world for a subset of brand new startups, where none of the low hanging fruit product ideas have been built yet but the "idea" for the business is very good. Tech-driven work doesn't pay off here because of scale. It's not worth working on perf when your total server bills is in the hundreds or thousands of $. It's not worth working on dev exp when you have < 10 developers. You don't need to refactor yet as it's greenfield code anyway and it's easy enough to glob stuff on to support business ideas ... for now.
If you are working on a sufficiently mature product, there is a fairly high chance that the product-driven feature you are about to spend months on will fail to gain any traction and be totally scrapped. Anyone who has 5+ years experience has probably experienced this multiple times, this happens all the time even at "good companies".
This is because all the obvious low-hanging fruit ideas were already built years ago (which is why the business even has any money to employ YOU as a software engineer).
In more established companies you are less likely to deliver value to users but more likely to deliver value to the business by executing on "tech-driven" initiatives. This is because of scale:
- If you work on perf, cutting 10% of server costs is a big deal when you run thousands of them vs. tens of them
- If you work on developer efficiency, improving that for hundreds or thousands of developers is a big deal
- If you refactor to support future changes, preventing forced "big bang" rewrites of large established systems you will save the business millions of $ in salary
A very respected developer in my previous company once told me: "make the change easy, then make the easy change". I guess everybody has already heard it because it's kind of a famous quote but it has really changed the way I develop since then. Every times there is something I need to implement and I feel like the codebase "resists" it, I think about this quote. I then take the necessary time to make this change easy first.
i was once working on some game code that had developed organically, so there was a weird mix of various sprite operations being done in either the world or the sprite's coordinate system. someone wanted to add sprite transformations like mirroring (basically the ability for a sprite to turn into its reflection sprite along one of its axes) and scaling, but there were several places where there would need to be checks for e.g. "if sprite.x_mirrored" and "if sprite.y_mirrored" when dealing with other transformations like off-centre rotations, and no one was eager to sit and go through them all.
so i took some time and consolidated all the sprite geometry code into a bounding box object, and then had every sprite own a bounding box, and have the rest of the code interact with the bounding box where possible. that suddenly made a bunch of other things easier to do, because they came down to "how will this affect the bounding box" and they could be done in a single place.
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.
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.
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?
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.
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.
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?
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.
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.
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.
Fred was a human performing a craft and applied science with ingenuity, experience, and some autonomy. Then people tried to beat that out of him because they belonged to a hierarchical organization based around various kinds of punishments.
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.
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.
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.
You need to put Fred on stuff that other developers have some buy in for. If you have zero automation, putting Fred on that is a bad idea. If you have zero tests, don't put Fred on that either.
Because the minute Fred leaves, all that work will rot then be discarded. It's better to have Fred work on something that will be maintained beyond his employment.
Right, if the root cause is a culture issue (which results in not having adequate testing), then having Fred write the missing tests will not solve it.
Most code needs ongoing maintenance - if folks aren't writing tests to begin with, then they probably aren't going to update them as functionality changes over time if Fred isn't around. And automation might be great for the current situation, but eventually need some improvements that nobody is willing to do. Eventually the tool seems less useful and people stop using it.
Requirement changes, I change the code, test fails, I remove the test, Fred isn't there to speak for the test.
Automation fails, I turn it off and run the thing by hand. Fred isn't there to turn the automation back on, or yell at me for running it by hand instead of fixing the automation.
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.
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.
I am on a small team that factors/refactors heavily. Some thing I have noticed about the practice over the almost three years since I started this particular job:
1. Adding small features is extremely predictable, in terms of delivery time estimates we give to management.
2. Our bug count is very low, and when we solve a bug it is often small, pervasive, and resolved in exactly one piece of code.
3. The more we factor, the better we get at it, and the easier it is to add new factors rather than new globs that need factoring.
I don't see the promise of "agile" work out every time but factored code delivers like this every time for me.
Ha - I’m this guy at work except I’m a couple of times more productive than everyone else because I know the codebases back to front and our team is several times more productive than other teams because the code is neat and tidy after continuous refactoring for years. We added 2 engineers to the team and they were both shipping features after a week of training because the code is neat and tidy. Having close to 100% test coverage makes refactoring easier, although sometimes new bugs do happen. ;)
Refactoring always requires a deeeep muscle-memory of the codebase, getting to that point at a job is a great feeling. Sometimes (always?) making things pretty pays off, too! :)
It depends on how the code is structured. Our apps use a micro kernel / plugin architecture of loosely coupled modules so most of the time it is sufficient to know only a few modules to be able to refactor. Fundamental changes require a lot more knowledge - this is true - but they are not common.
In my experience so far it is the structuring for maintainability that really shows the skill of the programmer - writing the logic itself is the easy part.
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.
If the Fred is providing value to other developers, why are other team members despising him to the point of calling him names often?What this shows is a strawman situation that is meant to make the point, but is actually showing dysfunctional team.
It does not show enough details for us to know whether Freds refactorings are needed or making codebase worst. It is sort of Rorschach test where we are supposed to pick a side and then fight among ourselves about following two questions:
Question 1: "is the refactoring you never seen on the codebase you have no idea about by a guy that is despised by his team" good or bad?
Question 2: "A guys is disliked by his team. Is he right and team is wrong or other way round?"
The only correct answer is "who the hell knows but damm start managing that damm team".
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.
I've been on a team like this before with heavily separated feature and refactor pods and it doesn't really work.
The feature pods outpace the refactor pods. Feature pods can use a bad pattern 50 times in the time it takes refactor pods to fix 10 of them. Not every fix can be scripted. Some bad patterns restrict the ability for the codebase to stay up to date on dependencies, etc. Lots of reasons not to accumulate this stuff faster than you can strip it off.
It also impacted team cohesion, as refactor pods started to resent the poor quality of feature pods' deliverables. Since refactorers are peers, it got weird when they exerted refactor pressure on feature devs who got wedged between their peers and their managers.
Ultimately I think this article is a little too black and white. The "just do the ticket" dev is as undesirable a teammate as the "refactors to excess" dev. It's better if devs are a little of both and the team culture helps everyone keep a healthy balance.
A story about me! I mean, I'm not Fred, but I am. I always try to leave the code a little bit nicer than I found it, and that means if I find warts on code that I need to change, I'm going to fix those warts.
I love the project I'm currently on, and that's probably because for the first time since I've gotten good at this, I actually get the freedom to do it.
Would it be better to just build those features quick and dirty? Maybe. Certainly for the first few weeks or months. And then the code starts turning into a mess, and somebody needs to clean it up, and apparently that's what I love.
The alternative is usually that if Fred isn't allowed do these improvements, they never get done, then you end up with 5 year out of date dependencies, a useless test suite, and a mountain of technical debt.
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.
> 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.
At a startup, there's a good chance Fred's time would be better spent knocking out feature work (you only have so much runway, after all). But at a mature tech company, assuming he's good at his job, Fred is likely providing a ton of value by
- Keeping his coworkers sane (barely functional codebases that are difficult to work with have a way of killing morale)
- Having an intimate knowledge of the code base that can inform future feature work
- Paying down tech debt that's generally uninteresting to more feature-oriented coworkers
Having a whole team of Freds is probably not a good idea, but having one in a senior engineer or tech lead role is really useful.
Refactoring is the design phase in an agile process.
The old way, inspired by bridge building, I guess, was to first thing out a great design for your software, and then implement it.
This turns out to not work because, unlike bridges, software is always changing and needs new features.
So in agile, you just write the simplest thing that can possibly work to solve your next task. Then, when things work, you refactor the code to have a good design.
Designing something after it's built is a mind bending concept, but it's made possible by what we make being just text in files, not set in stone.
I’ve suffered from never-refactored codebases. And made people suffer from my refactoring.
My company once decided to use dependency injection, everywhere, everywhere, and for anything. The code became a needlessly orchestrated nightmare.
Now any refactor to make my code solve a generic rather than specific problem — and I’m making the codebase bigger in code and number of files — gives me pause for thought.
The more abstract something is, the further away from specifics it is, and sooner or later specifics change, and those changes break your happy abstraction.
I think it's important to make the difference between generic and intentionally designed.
Just using an "abstraction" doesn't mean that your code is abstract, it may just not be a tangle of functions that were stringed together on the fly.
At least for me, there's a clear point where I've coded something and find myself getting overwhelmed when making changes or trying to bridge the final gap to completion. That's when I step back and try to make sense of it and refactor.
If you follow the advice of this article your refactoring wizard becomes the operations silo. Code quality on the team goes down overall because operations silo Guy can't review all CR's himself and if he tries to the rest of the teams velocity suffers. Don't worry operations silo Guy will clean it up later anyway which doesn't happen because more code is written by the other developers than operations silo Guy can refactor. Other developers on the team will put off refactoring and automation work because that's now operations silo Guy's domain. They grow rusty in those skills. You also lose the ability to include operations silo Guy in swarming on features as he hasn't been keeping up with the product's development because he's always refactoring some old integ test or run script.
Yup, I’ve found that even when pretty much every engineer on the team knows that velocity is slow because necessary refactors have not been done, refactoring is often not considered real work and real progress. It’s the kind of thing you’re supposed to do in your spare time, off the roadmap. Also, people who find refactoring difficult or unpleasant will try to get you to see things from their point of view! Or they and their teammates have tried so many times to get the green light from leadership to pay down technical debt that they have given up.
It’s underappreciated that developers have different strengths and interests and could be doing different kinds of work rather than being treated as interchangeable workers, even at a relatively small start-up, in my experience.
I think the biggest value of refactoring is reducing code size, which is directly correlated with bug count / severity (there are tons of studies here). Ultimately you're providing a huge value to your clients. If your code stinks on the inside, it's gonna take a huge effort to make it smell nice on the outside.
If you stop repeating yourself, you may make less mistakes because there are simply less chances of it. Good abstractions help.
They also speed up your development speed, accelerating you towards your goal instead of coasting.
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.
I draw a parallel between refactoring and code ownership. Once I've worked in an area of code for a while (several sprints) then I probably know enough to try refactoring. Longer time working on it == more ability to refactor.
The idea of what is correct (to you and to others) and what needs refactoring changes over time, even the product changes. Sometimes it can be beneficial to wait a while and see if you still feel the same way after a few weeks.
If your team is 5 people, and each one makes the company 4% better every year, then after a year the company is 3.3x better, and after 5 it's 403x better. If you team is 5 people, but one of them makes other engineers 4% better each year, and the rest focus on the company, then after a year the company is 3.2x larger and after 5 it's 392x better. Wasn't worth it. At six engineers the balance slightly shifts.
Group 1 year 5 years Gain
5 product 3.3 403.4
1 tools, 4 product 3.2 392.8 -2.6%
6 product 3.5 492.7
1 tools, 5 product 3.3 501.0 +1.7%
Now, obviously the team probably won't stay 5 engineers for 5 years, but my point is: like everything else, this is a cost-benefit analysis. For small teams, it turns out it's not worth it to have happy Fred on your team, and you should let him go work on internal tools for a few dozen engineers where he can have massive impact there.
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.
Codebases follow evolutionary paths. If the code isn't going to be needed for long, there's no point in refactoring. If it does have a long lifespan, then every change, every addition, has the potential to increase complexity and make it harder to adapt. That's ok for a while, but in the longer term requires a counterbalancing pressure in order to be sustainable.
Whether that pressure is provided by regular developers, or partly or wholly by dedicated people, depends on the interests and skills of the people on hand. Carving off a Fred is one successful strategy. So is having core developers actively maintaining the integrity of the overall system as they make changes.
You have to pick, or stumble upon, the right strategy for your situation and staff.
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!
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.
Having someone responsible for the dev environment and tools sounds great if your team and organization has scaled enough to allow this.
Having one person be the point person for refactoring, tech debt and code review on the other hand seems like a bad idea - these should be every developer's responsibilities. This sounds like a pretty silly team overall - all the team members write quick, messy code and then one person is on "cleanup crew" fixing bugs and improving design?
Talk about a way to brew up some tension and misalignment on a team. "Hey George... I know you are about to open a 2nd pull request to add additional features to your work, but I just refactored your whole initial implementation, and now there are 30 merge conflicts..."
We always talk about the bus rule at work - if someone got hit by a bus tonight, would the team be able to operate tomorrow? If we had "refactor guy" on our team though, I probably would have to resist not giving them a gentle nudge forward every time we wait to cross the street together.
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.
I have a Fred on my team. He recently took a few weeks to refactor a large part of the codebase which consisted of about 200 file changes. The problem with this approach is that he never discussed his changes with the team, leaving the discussion until code review time. Some refactoring doesn't quite need a discussion, but large changes should.
It should go without saying that I think refactoring is super valuable. The way to go about it is to:
1. Recognize what in the codebase you see an opportunity to refactor
2. Discuss with the team (more experienced developers can have some useful suggestions)
3. Write a separate ticket for the changes (you can prioritize this after your required work)
Obviously, #3 won't work if your group doesn't value refactoring. In that case, you should bring up the conversation. You'll probably find many/most of your coworkers will be in agreement.
I welcome a coworker refactoring a section of the codebase to make it easier to change in the future. I do not like it when a coworker, new to the project, assumes that a certain way is incorrect. Like, introducing docker and additional tooling when no one asked for it. I especially do not like it when abstractions are unnecessarily forced.
Another perspective, if I am busy building out a feature and I open my source control to several huge PRs containing trivial changes, I groan. Now, not only do I have to respond to these PRs in a timely manner, I have to ensure that the changes are part of a codebase's design roadmap.
Large refactorings should be discussed, agreed upon and expected. Breaking a several thousand line module into a folder containing separate modules is nice and a welcome endeavor. I just don't want to see it as part of a feature PR. Discuss it and create a separate PR.
Also, just because you refactor, doesn't even your refactor is good. You can refactor in a worse off way as well. Your refactor may make the codebase nicer now, but could prevent easy changes later. Your refactor may go against best practices. I have seen this from developers that are on the Dunning–Kruger spectrum. Once again, refactors should be discussed and agreed upon.
As a side note, this seems like a great interview question to determine cultural fit. I do not want to work with a "fred" that doesn't communicate concerns and intentions.
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.
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 often refactor code as a way to procrastinate on other tasks I am not sure about how to approach them. Sometimes it's productive and results in better code but sometimes it's just shuffling code around without much improvement.
I like Fred, he cares about doing stuff the right way rather than get work done, with Fred our Operating-system's would be much better/faster and easier to maintain.
Refactoring and tooling work make sense if that work is oriented towards increasing productivity and maintainability, using contrasted personal experience, and skill.
Do you envision being done at some point? Are you getting a actual productivity boost with your meta-work? Are you solving problems proven to be actual problems from previous experiences?
Honestly good and secure code is only achieved through multiple refactoring. I hate people who just jump from features to features and have no intention of maintaining and on improving a piece of code once it's written. This is how you get tech debt.
If refactoring didn’t introduce regressions, then that would be great to have someone constantly refactoring to improve the codebase. In my experience though, it often makes more work for the rest of the team, sometimes for years afterwards.
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.
My advice short: Fire Fred.
Long: Depends on your process. Generally you should have refactoring tasks. So a dev detected bad architecture or some issue in the ticket. Then do the stuff and make a refactoring task. Or change the ticket. This way Fred would have enough of work that he loves. Sometimes he should do also stuff which he hates - its a job.
I said fire because it reminds me of a coworker who was very lazy. He complained about code (yes it was messy) but he was extremly slow in delivering. Simple tasks took 5x the time they should take and the refactoring wasn't that good. I mean 2x the time might be ok if you improve stuff. But no way a 5x delay.
Especially in a large project, if there are some really messy things, you need to talk with other devs to decide on architecture and so on. It's no way a one man show.
I'm Michelangelo - but that by definition makes me a loner.
I love staring at my code and contemplate on it's beauty just like the result of the code working.
I'm perfect at one-man projects.
I hate processes, standups, scrums and all that team playing bullshit.
I love customers and bosses who are trusting and freedom giving.
I love beers, steaks, good food and team gatherings - and i love people whom i am "working" with - but everyone knows i am working on something that is NOT "let get this shit done quickly and push this out of the door by next friday". That's not me.
Although I'm the one my boss comes to with "i don't know how but can you do something about it tomorrow"?
I'm good and super sharp focusing and delivering on my own.
If i need help - I'll ask.
I'm all for skunkworks.
I debug my code myself, using techniques i polished myself over the years and I'll end up with lightbulb that will last 100 yrs. Not the one that cost $3 and requires full replacement after 3.5 weeks of light usage.
I try not to buy stuff made in China. I love stuff made in Europe or Japan.
So, don't push guys like me into your "processes" and "change managements" wasting pipeline bullshit.
I won't fit.
I speak at conferences. I do evil harmless things. I violate countless stupid compliance rules. I take risks no one knows about.
I don't follow rules, and pretty much skip reading them when i can.
I do my best to deliver masterpieces. One piece at a time.
I think the post was saying that different folks have different skills and we should give people the space to do what they enjoy. I agree with it, and somewhat fit the archetype of "Fred" in the post.
That said, I could only nod along with the first half of your comment - the rest started to get a bit condescending.
I mean, you do you - if this working style works for you, that's great! But I think you can still work on your craft and be a perfectionist while appreciating how others want to work as well.
This emergent phenomenon is pretty much hard-coded in humans. Vide: the entire human history. Confer: multiple social experiments of building "the perfect society".
Each animal can be said to posses a survival strategy; ours resides not in talons, or rapid flight, or keen perception, or camouflage, but in cooperation. Even the hierarchical structure of our social organization aids in this.
It is a humble observation, even if it was difficult for me to appreciate for such a long time.
Sure. But if your are actually as flippant as this response indicates then why make it. Certainly why make it on the internet whose only goal is to provide more efficient societal connections.
Engineering beauty is when form follows function. A hundred year bulb that will only be used for 5 minutes is ugly and imperfect
One that lasts 3.5 weeks is still ugly, but one that last 20m +/- 15 is beautiful, and one that lasts 10min +5/-5 is a masterpiece. An engineer still wants a safety factor, after all
It goes both ways: I hate shipping ugly code, which makes me a slow coder. On the other hand I love spending weeks refactoring pieces of code which will have no repercussions on the end user but will make life easier to the rest of the team.
I've also been accumulating home-made scripts for debugging and such for the last 10 years. The fact that I made them makes them more powerful.
I don't think I've ever seen this combination of "very much cares about code quality" and "slow coder". Most of the clean code fanatics I've seen (and I include myself), are also very quick to deliver features - whereas developers who simply try to complete the feature in the shortest possible time, usually end up taking longer, because their quickly written code is buggy, then hard to read, and so hard to debug and fix.
It's not usually a choice between, leaving the code as it is (and just adding a bit of functionality), or refactoring to make it better. It's usually a choice between making it worse, or making it better. So the developers who aren't Fred, are making things worse. I try to make refactoring and keeping the code clean part of every team members' job description. It's often hard to get the business to schedule time for this sort of thing, so it should just get built into the time for each task. As for automating tasks which could be automated - seriously, if you have developers who are not doing this, then they are the problem, not Fred.
I don't think I've ever seen this combination of "very much cares about code quality" and "slow coder".
I humbly submit that you have been exceptionally lucky in that case, unless you're quite new to the industry. We've had problems with dogmatic developers and architecture astronauts for as long as software development has been a thing.
In the 1990s, they were the ones trying to shoehorn as many design patterns as possible into every OO project. There was little evidence that doing so made the code better in any material way, but that didn't matter, because Design Patterns Are Good Things.
In the 2000s, they were the ones adding lots of artificial complexity to otherwise reasonable designs to enable their preferred test automation tool to hook in everywhere. There was little evidence that enforcing these rules universally actually improved quality or saved time, but Everything Must Be Unit Tested.
In the 2010s, they were the ones refactoring anything with more than five lines in a function or more than one level of nesting. There was little evidence that this improved any important code metrics, and even some evidence that it actually made things worse, but the longer or deeper version violated someone's arbitrary list of Code Smells That Are Bad so it had to be fixed.
A recurring theme here is taking the essence of a reasonable idea that might be useful in some circumstances but then applying it with an absurd degree of rigidity and scope without regard for the cost-benefit trade-offs being made. XP advocates even made this the centrepiece of their philosophy, though XP was born out of a project that was hardly a great success...
I’ll upvote, not downvote since this is worthy of discussion.
First observation... This stance only works if you’re so talented that you can succeed without outside help, and you have a boss that respects your value and can take ownership of building bridges for you. Those bosses are rare.
Observation two... Building bridges and helping others makes it easier, not harder, to get things done.
Observation three... Some people who take this stance aren’t worth it. Many folks take this stance to purposefully close their ears to ideas that could be useful.
Observation four... The person referenced in the article is someone who can be a huge net positive in building developer tools.
I think the GP is describing an artistic/hobbyist view of software development. If you're doing it purely for your own enjoyment, with no external requirements to constrain you, you can take all the time you want and follow whatever process you want and try to achieve whatever you consider to be perfection.
This contrasts sharply with professional software development, where you are building software for a purpose and often within a specified budget and timescales. In this case, you can't be selfish any more, because the whole reason for what you're doing is to satisfy the need of someone else. Processes and tools that you can happily ignore on your fun projects become relevant. Being unwilling or unable to work effectively with other developers or with people who have different roles on the project team makes you almost worthless. Taking risks that no-one else knows about becomes a danger to the whole project. Your goal isn't to achieve perfection, it's to deliver software that meets the requirements, on time, on budget, to an acceptable standard.
This is me as well. It really hit home when I was talking with a friend about not joining a company that's just trying to get stuff out the door quickly. She said "you're an artisan, you just want to practice your art! And that's hard to do at some places."
I do really like that analogy, of artisanship or craftsmanship. I understand the business need to move quickly, but over time I've found myself more intrinsically motivated by building what I think of as well-designed systems. That, to me, is art. That's what I get excited about. I'd also like to think that some of the extra time I spend upfront is saved down the road when things don't break. I think great engineering cultures respect that balance.
> over time I've found myself more intrinsically motivated by building what I think of as well-designed systems. That, to me, is art. That's what I get excited about.
Me too.
But art without constraints is boring. What gets me off is building the most perfect well designed system possible within the constraint of shipping things that solve real problems for real people and the business.
I tend to refractor too much but that kind-of makes me terrible at one-man projects, since I know enough about my code to see all the mistakes and all the possibilities, working with others, even if I do sometimes begrudge it, forces me to take a bigger picture look at things and say this is good enough.
Probably the ideal thing for me is to do a project where I am coordinating with others but they are working independently, like microservices for instance where you can look at the back end and say this looks like crap let me tear it up without blocking anyone else's progress.
So I'm surprised by the assertion that that sort of code perfectionism is good for one-man projects, but to each their own.
I mean pivot this a little bit and you could easily make this about some hipster coffee snob, some PC gaming supremacist, or whatever else society has arbitrarily decided is elitist snobbery over a maestro executing art.
The worst part about manifestos like this is when they enable people who think they're this good when really they are subject to the Dunning-Kruger effect.
There is growth in fully understanding that perfect is enemy of good enough. Perfect systems not deployed have no value and don't create wage paying revenue. As in all things, balance is important. Anything is good enough is the opposite problem.
>I am a Linux guy and I hate it when co-workers are too stupid for utf8 encoding.
You sound like a jerk. Team-mates not understand something is not an excuse to call them stupid. If you truly love something then usually you'd want to share that love with the uninitiated.
> Speaking from experience; once it’s been shared ad nauseam, and they still don’t get it, what would be the alternative conclusion?
Your not sharing it in a way that's understandable?
People learn and think about things in different ways. An explanation for something that's clear to you might be gibberish to me. Continuing to explain it to me in a way you understand doesn't really help me.
> Your not sharing it in a way that's understandable?
In my experience, people develop a better understanding of a particular concept if they take the time to figure it out for themselves rather than relying on someone to explain it to them.
The person might not be able to figure it out on their own. That doesn't mean they're dumb; they just missed a particular intuitive leap that was easier for you.
The person might dive into it and learn it incorrectly. They might find something on the internet that seems to make sense, but isn't a good way of doing it. Then you have to clean up the mess their imperfect understanding caused, plus help them unlearn the wrong thing.
I think the best bit is somewhere in between. When learning something new, everyone should do some of their own research and self-teaching. But having someone knowledgeable to also teach can be essential, and if someone like that is available, I think it's wise for a self-learner to at least check their understanding with that person, early on.
You sound like the guy at my work who pushed a 'perfect' change on a Friday afternoon and backed up my queue of tickets for the next 2 weeks. I had to purge several days of automated processes, take a few days to manually process it all after fixing issues induced from the change, then took another week to catch up on what was on hold.
Good for you, but change management saves my time more often than not.
I'm with you (in a slightly less over the top style.) Bootlickers like the author of this blog are why everything is riddled with functionality and security bugs.
Personally when people move into the "dev tools" role they often seem to lose sight of the forest for the trees. They'll argue for an idealized workflow - everything has to have 80% test coverage, everything has to have tracing, everyone's editor needs an exact set of plugins. They start out fixing papercuts they've personally experienced and end up building tooling to solve problems nobody has.
Every engineer I know who I respected for this behaviour also delivered boatloads of functionality independent from "improving dev productivity 25%" which kinda makes sense since if you go around refactoring everything you kinda know the whole thing. Also, how do you even know if you've improved dev productivity 25% if you haven't even improved your own functionality-delivering at any rate (which you'd only know if you attempted to deliver functionality)?
In fact, every engineer I know who I respected to deliver boatloads of functionality also had this behaviour.
All of the guys who thought they were this guy but didn't deliver boatloads of functionality were just some wheel spinners who weren't really useful. I'll probably never work with them again if I do my hiring right.