Embrace the Refactor

Why there is nothing to fear when refactoring code, but fear of perfection itself.

Ryan Kelly
Equinox Media Tech
6 min readDec 1, 2020

--

What is a Refactor?

Implementing new features is exhilarating. But while implementation is a feeling we all enjoy, there is a darker side to this process. Once you complete a feature you are suddenly faced with requests for two more features. Like Hydra the features multiply. At face value, the team feels that it is in the driver’s seat of a Formula 1 car optimizing each lap and racing faster and faster. Sadly, underneath it all the code and systems are starting to deteriorate, even if the codebase isn’t being actively updated.

There are times when you need to take a step back from the whirlwind and address the code or architecture itself by way of refactoring. Refactoring involves improving the structure while preserving its behavior. Everything works the same way, it just does it in a way that is more efficient and more maintainable.

Image from: refactoring.guru

The Benefit of a Refactor

Refactoring, like sleep, acts as a regenerative step to the process, and provides the ability to break away from the vicious write/release cycle. What is breaking down can be addressed and repaired. Over time this process allows your systems to become easier to understand, easier to maintain, easier to scale/extend, and more.

Let’s use a simple example which adheres to the single responsibility principle. Let’s say you have a Lambda function where the handler function does two different things — handling the orchestration of the event and also the asynchronous execution of a query via AWS Athena. While for the MVP version of the code this can pass, it is better to break that into two functions each with just one purpose.

To extend on the example, let’s consider a scenario where, on a single team over time, multiple developers across multiple applications are handling their own Athena client connections. This means each developer has to know which encryption configuration to use which may have been dictated by their security team. If this is also hardcoded in every application then what happens when it needs to change?

Micro Refactor vs Macro Refactor

Do these terms really exist? If you Google them, you likely won’t find much. There’s a good chance I’m simply making these up. Despite this, there’s an important distinction to make here between refactoring efforts. Consider micro-refactoring as making small changes to improve small sections of the code such as logging enhancements, better failure handling, code modularization, etc. These changes can be done often and can be part of the standard Agile development process.

Now consider macro-refactoring as creating classes, making changes across classes, and even changing underlying architecture. Obviously this is harder to pull off successfully when you have a team working across various parts of the code base across separate projects. Regardless of the added complexity, they are vital for the maintainability of your services. Imagine debugging AWS Athena issues across five different implementations with varying styles of handling configurations and logging.

How would you know when to make a macro-refactor? It may be tempting to skip directly to a macro-refactor after being inspired by articles from Netflix, Spotify, and Airbnb. While their engineers are talented individuals there’s a good chance that their environment is vastly different than your own. Instead, digest the principles from those articles and use them as inspiration to build better software in your own environment. Ultimately, you’ll find that micro-refactors will fuel the fire for your macro-refactors and that it will be more applicable than the alternatives.

Ways to Embrace the Refactor

There are a number of tips below to help you embrace refactoring and get the most out of it. They are grouped by three themes: meet your basic needs, get feedback, and synthesize learnings. There is nothing fancy meant here by synthesize. It is simply meant as creating a more complex idea by combining feedback and learnings. The first two themes will help you tackle your micro-refactors but the third will help you tackle those macro-refactors.

Meeting Your Basic Needs

  • Hit the ground running: If you are introducing brand new features or a new product, shoot for MVP status. For everything else, release incrementally.
  • There is nothing wrong with perfectionism. Until it delays your release. Utilize an Agile workflow by setting that perfect idea as a north star and then breaking it down into sprint size endeavors.
  • It is okay to have some disarray at times, so aim to identify when to clean-as-you-go versus waiting-to-clean. Let’s say you were demolishing a part of your house over multiple days. At the end of every day it would make sense to sweep your floors but not Swiffer mop them. In terms of engineering, there are going to be instances where you will want to refactor before setting code live. If the code is working as intended, it would be better to launch the service, provided proper testing is in place, and refactor as a fast-follow. For related inspiration see the pareto principle.

Encouraging Feedback

  • Ask questions! With many projects running concurrently, it’s fairly easy to lose touch with consumers of the feature after it has been released. Naturally, team focus migrates towards the next feature, but you have to fight against the tendency to keep riding the waves of releases.
  • Leave time to revisit an application, function, or process. Use pair programming sessions with other team members to dive into these existing pieces of your engineering ecosystem.
  • Implement a cross-project architectural review council on your team where team members can present their solution to others and get constructive criticism. This helps break architects and engineers away from their work silo and encourages breadth in problem solving. Plus it’s important to remember that any team member can contribute improvements to your applications and services.

Synthesizing Learnings

  • Encapsulate the underlying idea of the feedback you’re given; whether it’s directional or immediately applicable. Write it down. As a note, beware of immediately applicable feedback as there’s the trap of implementing that feedback so quickly that you aren’t able to fully process the principles behind it.
  • Don’t only ask others for feedback on what you’ve built, also ask them how they have solved their own problems. Write them down. The goal here is to take yourself out of your known element.
  • Let yourself welcome ideas outside of your expertise. If you work in data engineering don’t avoid articles about software engineering. For example articles such as how a software engineering team solved API caching issues could help you rethink a micro-batching application you’ve built. Also write these down as well.
  • Revisit and review all of what you’ve written down. You’ll start to connect dots across ideas and develop more well-rounded solutions. Plus this will not only be useful towards what you’ve already solved but also towards what you haven’t even been asked to solve yet.

Conclusion

You may have the perfect vision in your mind of what your code/service will be. While that vision could be exactly what comes to fruition, what’s more likely to be true is that the goal will change over time. Since you cannot predict the future, let that inspire you to embrace a cycle of building and learning. Those learnings will help direct your refactors, which will be less costly than if you built everything you wanted and only then did a refactor. Then seek out macro-refactors from reflection across micro-refactors.

Lastly, don’t be afraid of refactoring. Of these two quotes below I’ve heard in my career, I find the latter is much more fear-inducing:

“We’re already thinking about refactoring before we’re live?”

“We can’t touch that service. It’s been running fine and the only person who knows how it works left a year ago.”

--

--