back arrow back

Refactoring Code with Queer Eye's Fab Five

Cover Image for Refactoring Code with Queer Eye's Fab Five
This is a post that I wrote during the pandemic but never posted because life happened and I got distracted...
Small squiggle to seperate between each post

Let me begin by explaining what the show "Queer Eye" is all about.

It's a reality makeover show with five queer show hosts. In each episode, the hosts (also called the Fab Five) give one protagonist an all-around makeover. The makeover is devided into five areas, each led by one of the Fab Five:

  1. Tan France (fashion designer) does the fashion makeover
  2. Jonathan Van Ness (grooming expert) does the hair and beauty makeover
  3. Bobby Berk (interior designer) does the home makeover
  4. Antoni Porowski (chef) teaches healthy cooking
  5. Karamo Brown (culture and life coach) helps the protagonist with their self-image and relationshioships with others.

During each of the five makeovers (or lessons), we get to know the protagonist a little bit better through their interactions with the Fab Five and gain more empathy with them and their struggles. The show always ends in a big reveal, with before-and-after comparisons, lots of happy-tears and group hugs. As cheesy and one-format-to-rule-them-all as the show can be, I enjoy the fun and adorable personalities of the the five hosts and how they stir up the lives of the strangers they makeover in a very lovely and wholesome way.

So there I was, stuck in the first lockdown of this pandemic, refactoring a Rails service during the day and watching the "Queer Eye" in the evening, when it suddenly struck me—those makeovers have a lot in common with the code refactorings I've been doing! Much like the five hosts, I followed a rather strict plan with my refactorings and went from one aspect of the code to the next... and area by area, I made the code more and more fabulous! ✨ But to be entirely honest, I also just wanted to write a fun tech post and use lots of GIFs with the Fab Five... So here we go!

Fab 5

🎯 Start with a goal and then a plan

It's very easy to get lost in code refactorings, especially when the code is so messy that everything is somehow connected to everything else. You think you'll just quickly move this group of methods into its own class, but lo and behold, you find yourself taking apart the underlying data models, migrating databases and rethinking API endpoints...

Here are some rules to help you decide whether you should start with a refactoring and to keep you from going down a rabbit hole:

  • Refactor for the sake of delivering values. Think about the things you want to improve with your refactoring and why, this will also help to define a clear goal and tell you when and where to stop.
  • Once you have a goal, you can plan the steps of your refactoring. Choose small steps and go from one aspect to the next. The book Refactoring by Martin Fowler provides a great catalog of step-by-step refactoring strategies. It will allow you to stop the refactoring at any point, and you would have improved something. And it will save you from accidentally burning everything down with a huge PR that none of your colleagues had the time to fully understand and therefor just waved through.
  • Wrong abstraction is more difficult to change than duplicated code. So refactor code when you start to repeat it more than 3 times or when you are planning to do some bigger conceptual changes, and therefore having the shared logic consolidated first will make the change easier.
  • Whenever you touch any code, refactor some small things around the code you're touching. Those tiny refactorings will accumulate and improve things over time.

Karamo

🥗 Have a Healthy Test Coverage

Before I make any changes in the functional part of the code, I always check the test coverage first. If I see that the code is tested poorly, I would start the refactoring by writing some tests.

  • Having those tests gives me more confidence when I refactore the functional part of the code.
  • Tests also acted as documentations, so looking into existing specs will give me better understanding of what the code is supposed to do.
  • Even if the project has decent test-coverage, I would still start with refactoring some of the tests first, so when I start to move the functional code around, it will be much easier to move those tests along with them.

There are lots of libraries and tools that can help you check how healthy your test coverage is. Most of them can be intergrated as part your CI to automatically ensure that the quality of your test coverage doesn't fall below a certain threshold and turn writing tests into a good habit.

Antoni

✂️ Get Rid of the Stale Stuff

There are rarely good reasons for keeping unused code around during refactorings, they will just clutter up our mental load, make the refactoring take longer, and create higher chance of introducing bugs. So, if I see that the code I'm planning to refactor contains lots of unused functionalities, I try to get rid of them first.

I look out for endpoints that aren't being hit anymore, libraries from unsubscribed third-party services, entire sunsetted features, dependencies between microservices, or just methods and variables with no referenced usage. Get rid of them! But please don't just start deleting code wildly:

  • Make sure that the code you think is dead is really dead by asking around in your team/company and have a look at your logs or monitors.
  • Use linting tools to check for "dead code".
  • Make sure to only remove one aspect of the dead code with each PR, e.g. one class, one functionality, one small feature, one library or sometimes just one variable. Not only will your PR be approved much faster by your colleagues, but when you unexpectely break something with your deletion, it will be much easier to fix.
  • Make sure to also get rid of dead data from your database, scale down resources from your infrastructur, and delete related credentials from your company's shared password manager. All of this will very likely also save your company some money.

Jonathan

💅 Code Styles and 👗 Software Design

To refactor old and dusty code can sometimes be overwhelming. The most dauting experience I had was working with an approximately 10-years old monolith that had a lot of resemblance with Howl's moving castle.

I know that the opinions around whether to use software design patterns are divided, and I understand that sometimes people can be too fixiated on using certain patterns and end up writing very abstract and overengineered code. For me, having shared coding styles and agreed design patterns across a team is not about being dogmatic, but because I do believe that when the abstraction is used well, it can help us to understand the code faster and leave us with more mental space to focuse on other things.

The easierst way to guarantee shared coding styles across your team and repos is by using a code style checker or a linting tool, and make it part of your pre-commit hook and your CI.

And one key to ensure good software design is, IMHO, good engineering culture where developers communicate with each others constantly, e.g. directly, via a request for comments post or code reviews.

Creating and maintaining a good (engineering) culture is not a simple thing. You need an environment where people are not afraid to make mistakes; you need motivated people, who are good comminicators; you have to give people time to explore new ideas; And you have to create spaces or formats for people to share their learnings;

An easy format to setup for your engineering team is for example a tech book club. In our little tech book club at Blinkist, actually spent some time reading and talking about the book "Head First: Design Patterns". Reading the book didn't turn any of us into a design pattern disciples, but it provided us with shared names for some of the pattern we'd already been using and gave us some ideas about other ways to organise our code.

Tan

🏢 Refactoring Databases

I'm always a bit scared about database refactorings. It feels like moving around big furnitures or drilling holes into walls—the effort is high, and there's always a chance to hit a power supply line and kill the electicity for an entire building. (A friend of mine once drilled a hole into a water pipeline and floodered the flat underneath him.)

Something that I've learned to avoid when refactoring databases is to do everything at once. Instead, I try to take very small steps, and keep every step reversible. Although my step-by-step instruction below might not cover all sorts of database refactorings, it has been useful to me in most cases.

  1. Create your new migration that only adds things, deploy and run it to add your new column or table to your DB.
  2. Change your code, so it writes into the old and new column/table simultanously. Your code at this point is still reading from the old column/table. Deploy.
  3. Now run a script to migrate your exiting data into the new column/table.
  4. Change your code, so it starts to read from your new column/table. Your code at this point is still writing into both old and new column/table. Deploy.
  5. When you made sure that everything is working, change your code, so it stops writing into the old column/table. Deploy.
  6. Remove your old column or drop your old table. Done.
  7. Now get up, do some stretches and drink a glass of water.

At Blinkist, we've been using the strong_migrations gem in our projects to catch unsafe migration early. I can recommend reading strong_migration's README, it gives some very good advises regarding how migrate databases safely.

Bobby

tl;dr

  1. Look at which aspect of your code you want to refactor, know what value the refactoring will bring, have a goal and then a plan.
  2. Have a decent test-coverage before you start refactoring, those tests will give you a lot of confidents during the refactoring.
  3. Before introducing new designs and styles, discuss them with your peers and gain some shared agreements on how you all want your shared codebase to look like. Your codebase will reflect the culture of your engineering teams, and you don't want it end up looking like Howl's moving castle.
  4. Make changes in small (if possible revertable) steps. It will allow you to stop at any point and be happy with the things you've improved. Big before and after reveals are only fun on reality shows, if you try that in a PR, the tears you'll on your colleagues' faces won't be the tears of joy.
  5. Stop when you've reached your goal. Don't go down that rabbit hole... There is no light on the other side, only more smelly code.

Alright, the analogies between "Queer Eye" and code refactorings might be somewhat constructed, but I do believe that there are different aspects of code refactoring, and that doing them in a certain order and with a lot of discipline can be very helpful and keep you sane! There're of course more than the few aspects that I mentioned, especially when we get into the realms of refactoring entire architecture... but that's a topic for another post... maybe something like "Refactoring a monolith into micro-services with RuPaul's Drag Race Queens"?

Until then, stay healthy and hydrate!

Until then, stay healthy and hydrate!

Source for the GIFs: Giphy