Make it run, (try to) make it right. Of React and refactoring

I started to work on Tweries in October of 2019. Tweries is an app that breaks down a long paragraph into several tweets of the appropriate length.

Tweries landing page

In this post, I would like to share my journey from the “make it run” build phase towards the goal of “make it right”. And in particular some interesting, in my opinion, refactoring strategies that I’ve tried so far.

The front end is written in React and deployed using GitHub pages. It deals with authentication using Auth0 and interacts with the Twitter API through a Node.js Express app hosted in Heroku.

Overall the React app is pretty simple. It has been bootstrapped using create-react-app. No additional state management library added, just (my favorite) useReducer to track the updates in the global state.

On paper, it’s a pretty straight forward design.

The problem is that I ended up with the main component that is more than 300 lines of code long and very hard to test.

Why did I end up in this situation? I think for 2 reasons:

  1. There has been a lot of back and forth between me and my users (or clients?) in terms of what to build and prioritize.
  2. I just wanted to build stuff without spending too much time on design and testing (I know, shame on me).

I’ve decided at this point to take some time and completely refactor the application. The goal, (at least I hope) is to make it more modular, testable, and, most importantly, easy to change while adding new functionalities.

The strategy I am adopting is to move parts out of the main component into separate modules and then test them in isolation.

So the technique is pretty simple:

  1. you move some code somewhere else (like for like)
  2. you make sure the application still compiles
  3. you add some tests (some decent code coverage is advised here)
  4. then you refactor and see how the tests behave

Incrementally migrate a legacy system by gradually replacing specific pieces of functionality with new [applications and services].

The last part is interesting as it continuously highlights that you should have indeed spent more time on designing your application with a test-first approach rather than adding tests after.

Then you continue with steps 1–4 for as many parts as you want until you have a very linear and logical structure for your main container component.

Next, you need to add tests to that component as well.

You will then realize how easy it is to test that in isolation or that there are still opportunities to further refactor, at which point you go back to the 1–4 flow.

I am still playing with the final result, but I’ve already released to production 2 intermediate steps and so far I didn’t get any complaints. Wish me luck!

It seems that the strategy is somehow working, so I’ll continue with it while improving the modularity and testability of my app.

268 changes: 43 additions & 225 deletions

What do you think?