Refactoring - an ode to code
What is refactoring? Should you redo your whole project? How can you proceed?
This article will give you an overall explanation of refactoring in software development.
We will explore the theory and then present some examples from real life.
But, as I always say, no explanation is complete without clarifying the benefits and challenges first.
Let's get to it, shall we?
What is it?
Refactoring refers to independent techniques and steps to improve code that has already been written.
To make it short, refactoring is the art of increasing the maintainability of a piece of code.
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.
— C.A.R. Hoare
But don't get me wrong, having to refactor your own codebase is not bad. It doesn't make you a poor-skilled developer.
Actually, during your career you will always be refactoring code (yours or others).
The Technical debt is a complete part of the development process, as nicely stated in this article from Marc Friederich & Gilles Doge.
On a standard 101-key keyboard, there are 100 keys that increase technical debt, and only one key that reduces it.
— Josh Wulf
During a refactoring process, you might be doing a lot of changes, adding new Classes, renaming, reorganising. But remember, at the end of your journey you won't ever be altering the observable behaviour of the code.
First, refactoring is not debugging. Your code needs to be running in the first place.
Of course, during a refactoring process you may find bugs. The moment that you start dealing with them, you've stopped refactoring and started bug fixing, which is another mindset.
Second, refactoring is not about performance.
I was speaking with a young developer the other day who was trying to convince me that refactoring a codebase makes it obviously faster.
It is a very common misconception that bad code automatically leads to performance leaks. The performance of your code is not the goal of a refactoring process - nor even what you should expect out of it.
Finally, refactoring is not adding features.
When you are refactoring a part of your code, you should not change its behaviour, otherwise you are no longer refactoring.
Should you do it?
You did not fix bugs, you did not make it faster, you did not add any new feature.
So, you may ask me "why should I refactor then?".
Well, the answer is quite simple. Well-structured code will make it so much easier to add new features and new capabilities in the future. And, it will make it easier to start analysing performance or overall improving the application.
By improving the code quality, you will also improve the overall team efficiency, certainly save a fair amount of money and make your developers happy to work on that codebase - or at least I hope so.
It's called technical debt, because it's like taking out a loan. You can accomplish more today than you normally could, but you end up paying a higher cost later.
If you face tedious difficulty getting approval on spending time for refactoring - because the benefits aren't immediately apparent - remember that you're not refactoring for your end-user, you're refactoring to get the future roadblocks out of your way.
How do we do it ?
Bored of reading theory? Alright - me too - let's dig into some real-life examples.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
— Martin Golding
One more thing, if I could give you one piece of advice, and share insight on one thing it would be this one simple sentence: "Never refactor a production code that does not have unit tests".
The main reason is maybe pretty obvious, but let me say it out loud again: without unit tests, you will end up with broken functionalities that are nearly impossible to fix because you won't be able to figure out what's happening anymore.
So please, if you have to refactor something, first make sure it is covered by tests.
Conditional Guard Clauses (return early)
To keep readability in functions and methods, it's wise to return early if simple conditions apply that can be checked at the beginning of a method. Then, when it's hard to determine the normal flow of code execution, try to isolate checks.
It's better to return early, keeping indentation and brain power needed to follow the code low.
Isolate all special checks and edge cases into separate clauses and place them before the main checks. Ideally, you should have a flat list of conditionals, one after the other.
Assignments to Parameters
Sometimes, you will need to alter a given parameter value inside your own method body.
If this parameter is passed via reference, then after the parameter value is changed inside the method, this value is passed to the argument that called this method.
Very often, this occurs accidentally and leads to unfortunate effects. Even if parameters are usually passed by value (and not by reference), this coding quirk may be weird to those who are unaccustomed to it.
Use a local variable instead of altering the parameter.
Replace Array with Object
Arrays are an excellent tool for storing data and collections of a single type.
But if you use an array like post office boxes, storing the username in box 1 and the user's address in box 14, you will someday be very unhappy that you did that way.
You have an array that contains various types of data.
Replace the array with an object that will have separate keys for each element.
Array, you will be able to apply properties assertions, I would suggest everybody to take a look at the
ArrayAccess implementations which is great to replace object-like
Replace Magic Number with Symbolic Constant
Magic numbers or magic values are encountered in the source code but have no obvious meaning. This "anti-pattern" makes it harder to understand the program and thus refactor the code.
Often, a simple Find & Replace won't work on those values, as the same data may be used for different purposes in different places, meaning that you will have to verify each line that uses this value.
Replace this number with a constant that has a human-readable name explaining the meaning of the number.
Consolidate Conditional Expression
When your code contains a lot of alternating operators that perform identical actions, it is not clear why the operators are split up.
The main purpose of consolidation is to extract the conditionals to separate methods for more clarity.
Consolidate all these conditionals in a single expression.
Use the language capabilities
Often, developers forget some awesome capabilities of the programming language they use.
Many of these features can save you a lot of effort. Take a look at the next examples and notice how it can be easy to achieve the same result by using the type
Could be changed to a simpler method with the same capabilities
Prevent usage of temporary variable when necessary. For example, when you have a temporary variable that is assigned the result of a simple expression and nothing more.
Replace the references to the variable with the expression itself.
What else ?
I would like to end with a few more quick tips on better coding:
- Use a new array form
[ ]instead of the old
===operator instead of
==unless it is important to not check for the
- Use prefix
is/haswith functions that return boolean ex:
- Organise class methods with public methods at the top
- Always apply the single responsibility concept to your classes
For the most curious of you, here are some sources of additional information that inspired the creation of this article.
Sourcemaking (23 August, 2019). Refactoring
See on https://sourcemaking.com/refactoring
Refactoring Guru (23 August, 2019). Refactoring Guru
See on https://refactoring.guru/refactoring
Mohamed Aladdin (19 August, 2018). Refactor Your PHP legacy Code (real projects examples)
See on https://medium.com/hackernoon/refactor-your-php...
Josh Wulf (15 April, 2018). There is no good coding, only good refactoring
See on https://medium.com/@sitapati/there-is-no-good-...
Fionna Chan (11 October, 2018). Code Refactoring — How to Write Better Code
See on https://medium.com/@fionnachan/code-ref...
Thomas Deniffel (20 April, 2019). Brutal Refactoring)
See on https://medium.com/@tdeniffel/brutal-refactorin...
Samuel Roze (21 August, 2018). Refactoring the right way: regional, not global
See on https://medium.com/@sroze/refactoring-the...
Omar El Gabry (02 October, 2017). Refactoring — The Hygienic Habit
See on https://hackernoon.com/refactori...
Shawn McGrath (22 August, 2016). The Exceptional Beauty of Doom 3's Source Code
See on https://kotaku.com/the-except...
The copyright for CommitStrip is held by CommitStrip and is licensed to CommitStrip.