Skip to content
TkDodo's blog

Road to Refactoring

Jan 22, 2022 — Principles, Road to Refactoring
Road to Refactoring
Photo by Jesse Bowser

Over the years, I’ve come to work on many medium to large scale code bases. Most of these have organically grown over time, some of them being full of Lava Layers (opens in a new window). Doing a refactoring (opens in a new window) in those code bases is often not trivial. Incidental complexity is high, test coverage is low. There are more features than you can count.

Also, where do you start? There are so many things you’d like to tackle and do differently, but everything you touch has the potential to introduce regressions.

In this series, I’m trying to list some of the things that I’ve done to make refactorings a success rather than a disaster. This is by no means an exhaustive lists, and is heavily biased by my personal experience. Further, it likely doesn’t apply to your side-project or early start-up, so as usual, your mileage may vary. That being said, here we go with the first tip: 🚀

Don’t mix refactorings with hotfixes

You get a bug report, highest prio, customer is escalating, account management is permanently asking: “what is the ETA on this, what can I tell the customer?”

You look at the code and analyze the issue. Maybe it’s in an area of the code base that hasn’t been touched for a while, or maybe you’ve not looked at it in a longer time.

Likely, you won’t like what you see. Software patterns, especially in the frontend world, can evolve rapidly. Even if you start with something new, chances are you would do it differently in a couple of months.

Maybe you see a React Class Component that fetches in componentDidMount. Wtf, we’ve moved to react-query (opens in a new window) half a year ago, what is this? Or maybe there are some global styles or deprecated components being used. Oh, and this dependency could really need an update…

Scout’s principle (opens in a new window) - time to clean up this mess…

Don’t. Just don’t.

There’s a time and a place for everything, but this is not the time for a refactoring. You don’t want to prolong the actual fix. As an engineer, you are a problem solver, and your only goal here should be to fix the actual problem. Also, you might introduce another regression, and code reviews will take longer if you add unrelated changes.

Quality

That doesn’t mean we should compromise on quality. Even in those situations, we still:

And make sure that all other quality gates that we have set up still pass. We surely want a fix as fast as possible, but not at all costs.

A failing test case

This is the flow I usually take when getting a bug report:

Avatar for TkDodo
Dominik 🔮
@TkDodo

This is the best flow:

🐞 find a bug

🕵️‍♂️ investigate it

⇆ reproduce it

🧪 write a failing test for it

💻 fix it

🟢 see the green test

🚀 ship it

- Nov 22, 2021

Writing a failing test case before you start fixing the issue is something I can really recommend, as it will ensure that:

This presumes that you have a somewhat easy way to add a test case for the bug. If you’re in the unfortunate situation that you’d have to introduce a testing framework first to actually write a test - go back to the beginning of this article. 😉

The right time

So when is the right time to refactor the horrible thing we’ve found? I’ll try to answer this in part 2 - so stay tuned 📻


That’s it for today. Feel free to reach out to me on bluesky (opens in a new window) if you have any questions, or just leave a comment below. ⬇️

Like the monospace font in the code blocks?

Check out monolisa.dev

Bytes - the JavaScript Newsletter that doesn't suck