Skip to content
TkDodo's blog

Road to Refactoring

โ€” principles, road to refactoring โ€” 2 min read

Road to Refactoring
Photo by Jesse Bowser
    No translations available.
  • Add translation

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. Doing a refactoring 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 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 - 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.


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

  • Create a branch (no direct merging to main)
  • Write proper commit messages
  • Get the required amount of reviews
  • Run the CI pipeline

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 ๐Ÿ”ฎ

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:

  • the bug is consistently reproducible.
  • the issue stays fixed in the future, for example, when you actually refactor that code.

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 twitter if you have any questions, or just leave a comment below. โฌ‡๏ธ

ยฉ 2024 by TkDodo's blog. All rights reserved.
Theme by LekoArts