How to Refactor a Codebase?

Yes, that thing you wanted to do a while ago

David Yu
Better Programming

--

Photo by Arnold Francisca on Unsplash

Today I will do what others won’t so tomorrow I can do what others can’t — Jerry Rice

Whether it’s trying to refactor your own code, or it’s refactoring other people’s code, a software developer will face code refactor at some point.

But why do we even have to refactor our code, and how do we do it?

What is a code refactor?

The process of restructuring existing computer code — changing the factoring — without changing its external behavior. Refactoring is intended to improve the design, structure, and/or implementation of the software (its non-functional attributes), while preserving its functionality. — Wikipedia

In short, change the code without breaking current functionalities.

Common Goals of Refactor

  • To make our code maintainable in the future
  • To increase the readability of code
  • To reduce complexity
  • To improve code’s performance
  • To help future features to be easier implemented

When Not to refactor

  • Adding comments/documents can make the code easier to understand
  • Code is not under active changes.
  • Code meets the current code standard
  • When proper specs exist, rewrite might make more sense
  • The current code has functional bugs
  • When a new feature is being added

This is not an exhaustive list. Every team will have different situations. Other constraints such as money and time will also be part of the conversation of to refactor or not refactor.

Have a plan

Once you and your team have decided to refactor. It’s important to have a loose plan to inform your team.

  1. Meet with a few developers who have worked on the code that you will refactor
  2. Come up with a step by step plan with the team
  3. Inform the other parts of the team about the plan

The reason why you would want to do it step by step is that it will be easier to review the code. And other developers can pull from your work quicker.

If you approach refactor like building a secret startup in a garage, you will face a lot of resistance when you try to merge the code. Not only your co-worker might need to rewrite some of their work, but it will also be harder to test and find what went wrong.

Have test in place

If you don’t have a test for the parts that you are about to refactor, you should write the test first.

It will save you time when you want to make sure that the refactoring doesn’t break anything.

Here are 13 tips for unit tests:

Here are some of my favorite tips more relevant for refactor:

  1. Test One Thing at a Time in Isolation
  2. Follow the AAA Rule: Arrange, Act, Assert
  3. Write Tests That Reveal a Bug, Then Fix It
  4. Name Your Tests Clearly and Don’t Be Afraid of Long Names
  5. Make Each Test Independent
  6. Run test often

Try to understand what the code is achieving

Documentation should exist but it doesn’t always exist. When it doesn’t exist and you don’t have the contact of the person who wrote the code, you are left to read to code and understand what’s going on.

Some tips for understanding what the code does:

  • Run the code
  • Run debugger with code
  • Find the entry point
  • Visualize the connections
  • Talk to the users
  • Talk to someone who worked on the code before

Recognize Pattern

Trying to stick to the DRY principle, “Don't repeat yourself”, it’s usually easier said than done. When the code base has passed through several developers’ hands, without a proper code standard and stylistic choice, repetition will occur at some point.

The tricky thing is how to spot the repetitive code and how to rewrite it dryer?

Retrace to source

Frontend:

  1. look at the UI to see what looks similar
  2. use “Inspect Element” in the console to locate some information that you could use to search for that code
  3. If they are actually two(or more) different pieces of code, turn them into a reusable component
  4. Whenever possible, separate presentational component from functional

Backend:

  1. Focus on one endpoint at a time
  2. Search for the endpoint keywords to find the entry code, e.g. “/user”
  3. If looking for a keyword doesn’t work, look for the entry point of the backend service. Then understand how the endpoint gets setup
  4. Find other service/function that the endpoint depends on
  5. Think about how to reduce the steps to get to the current response(output of the endpoint)

Using global search for keywords

If you’re lucky, there might be identical keywords being used at different places. And if you realize that the code does the same thing, then it will be a good place to replace it with reusable functions.

Visualize whenever possible

Draw out the code structure to see how everything is connected. The purpose of this is to discuss with your teammate to see if your understanding is correct. Also, having the diagram will help you to spot any design not is not ideal.

Some diagram tools:

  1. Free: draw.io
  2. Not so free: Lucidchart

Always keep in mind that diagram is supposed to help you and your team. If you find that you are spending too much time on the diagram, you’re probably adding too much detail or making the diagram look pretty.

Think input and output

This falls in line with the principles of “Single Responsibility” and “Separation of concerns”.

When looking at a function, if you know what is its purpose, it will be a lot easier to reduce the input or output.

Reduce variables

Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates

Although fewer lines of code don’t always mean better code, less code means the less cognitive load on the developer.

Less cognitive load means a developer can understand the codebase quicker and find bugs if there’s one.

A different approach for variable deduction:

  • Arrange → Understand → Rename → Remove Redundancy
  • Understand → Rename → Arrange → Remove Redundancy

Regardless which approach you go, make sure you have clear variable name and write some comments while trying to understand the code.

Reduce nested conditions

Similar to badly named variables, nested conditions can increase the cognitive load on developer as well.

Exit early

Think about how you can rewrite the condition where you can exit the function as early as possible. In this way, you can potentially avoid a lot of else condition.

Avoid Nested Ternary

I used to write nested ternary as well, because usually it requires less code. But after being on the receiving end of several crazy nested ternary, it can definitely slowly drive someone insane.

Comments, comments and comments

Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. — John Woods

Even if the person who is taking over your code isn’t a psychopath, you can still write comment to help that person not become a psychopath.

Comments are like a breath of fresh air after a person has been drowning in a sea of junk and dirt.

Even if you don’t care about the next person, think about your future self. Can you guarantee that you will always remember what you wrote?

Consistency and Formatting

Name patterns, design patterns, and code formatting should be consistent. At least from yourself, stick to one style.

For the team, you will need some documentation and communication. At the same time, you can take advantage of pre-hook and eslint to enforce some kind of code formatting.

Focus on one goal for one commit

Perfectionism can potentially hold you back on refactoring. Especially, the primary goal of refactoring is to improve the code structure.

It’s tempting to have one refactor that solves all the world’s problems, but the refactor might never be finished.

Besides having a plan to break it up into steps, each of the commits can be dedicated to one goal.

For example,

git commit -m 'renamed variables'

Or to be more specific

git commit -m 'renamed variables in profile page'

Depending on how large is the scope of your refactor, you can break it up by feature, component, service, endpoint, etc.

Obviously, this is based on my own experience from rolling around in code for years, I recognize that there are more people who are more experienced as software developers.

Want to Connect?Let’s connect on LinkedIn.

--

--

Full-stack developer based in Shanghai. I help people turning their ideas into reality.