Yes, that thing you wanted to do a while ago
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?
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.
- 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
- 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.
Once you and your team have decided to refactor. It’s important to have a loose plan to inform your team.
- Meet with a few developers who have worked on the code that you will refactor
- Come up with a step by step plan with the team
- 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.
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:
- Test One Thing at a Time in Isolation
- Follow the AAA Rule: Arrange, Act, Assert
- Write Tests That Reveal a Bug, Then Fix It
- Name Your Tests Clearly and Don’t Be Afraid of Long Names
- Make Each Test Independent
- Run test often
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 the 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
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
- look at the UI to see what looks similar
- use “Inspect Element” in the console to locate some information that you could use to search for that code
- If they are actually two(or more) different pieces of code, turn them into a reusable component
- Whenever possible, separate presentational component from functional
- Focus on one endpoint at a time
- Search for the endpoint keywords to find the entry code, eg “/user”
- If looking for a keyword doesn’t work, look for the entry point of the backend service. Then understand how the endpoint gets setup
- Find other service/function that the endpoint depends on
- 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:
- Free: draw.io
- 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.
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.
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 a clear variable name and write some comments while trying to understand the code.
Similar to badly named variables, nested conditions can increase the cognitive load on developer as well.
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
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.
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 a 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?
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.
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.
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.