20 July 2022
10+ PHP code refactoring tips for a meticulous developer
From figuring out the system structure, through static code analysis, to updating your libraries, and much more – there are a lot of ways to go about PHP code refactoring. However, the most important step is to create a viable refactoring process for your specific application. Read this extensive tutorial and get the best insights from a developer who refactors for a living.
In the course of my career as a PHP developer, I saw and worked on a lot of PHP apps in need of refactoring. They had all sorts of problems, but they all stemmed from a short-sighted approach to development. The first thing to do to refactor them is to change this approach.
But what to do next? After all, each app is different. While there is no one-size-fits-all solution, I’m going to provide you with the next best thing in the form of practical PHP refactoring tips based on my everyday experience.
If you have spent any meaningful time in the world of PHP development, you know that there are applications that almost scream: rewrite me!
Perhaps you’re here because you’ve got (a problem of) an app just like that. Or perhaps it was just curiosity? One way or the other, I’m going to share my experience in PHP code refactoring as 10 practical tips based on real-life projects I took part in.
What’s the problem with refactoring?
Why is it that sometimes it is so difficult for devs to figure out a viable way of refactoring their app?
The primary cause of refactoring problems is fragmentation or lack of proper project knowledge on the application’s business model and more.
When the project knowledge is poor while the app still needs to be maintained and developed further, its codebase can be expected to get more and more out of hand.
Of course, it’s much better when the knowledge is fragmented rather than nonexistent. In the former case, you can gather it and note it all down, using both words and software development diagrams. However, it is quite difficult to make sure that the knowledge fulfills all the requirements of present and future project stakeholders.
When the knowledge is simply not there, the situation gets even worse. Developers need to analyze the app using trial-and-error tactics. It’s messy, and it takes a lot of time.
Luckily, this extreme situation is rare in practice. Typically, we don’t start completely from scratch. After all, gathering business knowledge is a process that takes months or even years.
For optimal results, refactoring efforts should be performed concurrently with producing business knowledge. You can analyze each and every area of a module individually over an extended period of time while shaping a repeatable refactoring process for the whole app.
As you can see, there are hardly any shortcuts. You need to have a viable refactoring plan.
What refactoring techniques in PHP can become a part of such a process?
1. Use the one-step-at-a-time approach
Everyone knows this method, even if they have nothing to do with programming. You use it when you learn how to play the piano, or ride a bike. What does it mean in refactoring?
The gist of it is to try to improve the code by making small changes. It involves bits and pieces of all the other methods. There is no straightforward way of doing this.
If you spot poorly name variables – correct them! If you find out that the structure of a given class is incorrect – change it! Are there missing types somewhere? Add them!
The point is not to spend too much time on it. By making a couple of small positive changes at a time, you gradually improve the codebase. If you get your teammates to do the same thing, the result is even better. Over time, you can even refactor the whole app using the process.
2. Consider the importance of business
Refactoring and business have a troubled relationship.
It’s true that a refactored app clearly works better and has a far more promising future after being made efficient and scalable. On the other hand, refactoring often eats away a lot of programmer’s billable hours and it is not always easy to sell its benefits to non-technical people. After all, the changes made during refactoring have no bearing on how the app looks to the user and its external behavior. To a non-technical person, they are largely invisible.
From the perspective of developers themselves, business knowledge is also crucial. Once you know which modules of the system are most important from a business perspective, you can plan your refactoring process better.
Personally, I like to start with the smallest and least important modules. That way, you can get the hang of the system and the style of its code by working with the least essential parts of it. The probability of breaking the entire system and having to revert entirely to the old codebase shrinks.
3. Take care of your app structure
Learning all there is to know about your system’s code structure is essential. Regardless of how poorly made it is, you need to study it before you get serious about refactoring.
To show you just how important it is, let’s analyze an example app structure inspired by one of my many past projects.
The structure above has two basic parts:
- a proper app structure based on the Symfony framework,
- elements of a subpar app structure inherited from an obsolete part of the app.
If you want to refactor a structure like this, you need to focus on the obsolete part. Often, it includes key elements of the app. This particular obsolete structure contains the CRUD part of the system that has to do with basic data management. Unfortunately, it also contains important business logic, including calculations, and processing rules for form data.
If you work with projects such as this one, you’re bound to come across a situation in which a part of the logic is refactored, while another part is still provided by the old system. If you want to know exactly where every bit of functionality comes from, you need to learn the whole structure.
4. Correct any bug you spot on the fly
This one has to be the shortest piece of advice I will ever give on this subject.
One of the oldest rules of refactoring goes like this: if you change your code in any place of your system, make sure to improve at least the basic version of it.
Picture a function, method, or file you need to change. The code is plain ugly. It has both tabs and spaces, variable names are in multiple languages, naming is non-intuitive, and so on.
Correct at least some of the many problems you are able to find. That way, when another programmer works on that code, they will have fewer problems to deal with. If your team follows that rule, soon the quality of the code will improve significantly.
5. Update your PHP version
One of the first things I do as part of refactoring is update my PHP version. Not only will it speed up the app, but will also make it easier to spot outdated code. This is extremely important, as some of the outdated pieces of your software might lead to security issues that may be exploited. To put it another way, updating your PHP version improves:
- app speed,
- app stability,
- and the safety of your software and your entire business.
Not bad for something that takes a couple of clicks to complete, right?
When you use an IDE such as PHPStorm to do the update, you’re also going to get smart suggestions (including those about the null coalescing operator) without the extra third-party tools.
This simple example shows that making sure to update your PHP version has the potential to not only make your app better but also to change your very approach to writing PHP code.
There are some handy tools that can assist you in upgrading your PHP version. Rector is definitely worth checking out. It may help you move from PHP 5.3 all the way to PHP 8.1.
Once PHP is updated, errors removed, obsolete functions rewritten, and other problematic pieces of code simplified, you can move to another stage of refactoring that has to do with libraries.
6. Update your libraries
When you are in the early stages of PHP refactoring, inspecting all of the libraries in your project is a must.
If your system is large and old, chances are that some of your libraries are not only outdated but entirely abandoned by their authors.
Updating is quite straightforward. The composer tool detects outdated/abandoned ones. Such libraries often recommend how best to update or replace them. The difficult part is finding all the pieces of code affected by the libraries.
Thankfully, our favorite IDEs come again to the rescue. Modern tools make it possible to detect all specific instances of the old library being used in your code. Of course, you need to know enough about the library to recognize its main classes, namespaces, constants, or functions.
Tackling and improving these outdated elements will add a lot of vigor and quality to your code. The performance will be improved as well.
That’s for the pros. Unfortunately, going through the process is not always such a walk in the park even with the best of IDEs. The problem is that not every outdated library has a proper one-to-one replacement. Pieces of code affected by the library cannot be refactored. It is also plausible that a replacement does exist, but it doesn’t allow you to do everything the way it was done previously.
The latter situation makes refactoring possible but costly. A big part of the app needs to be rewritten. Refactoring of this kind poses some risks. The more obscure the original library is, the harder it is for your IDE or even for your own set of eyes to find all the pieces of code affected. On several occasions, I saw some code being triggered by a set of seemingly random signs. There is no way to find and understand such code if neither you nor any of your teammates worked on it originally.
7. Get rid of outdated solutions
This one is less straightforward than you might think at first glance. It’s hard to decide what’s new enough to stay and old enough to get rid of objectively.
One simple refactoring trick that can help is to focus on removing outdated database connections, e.g. those that use the MySQL library phased out in PHP 7.0.
Another good practice is to turn on the error display by switching the “display_errors = true” option in php.ini, the. htaccess file, or the ini_set function. You will also find depreciated solution reports from error_reporting(E_DEPRECATED) useful, so turn them on as well. Learn more about how PHP can help you with error reporting.
Even with that help, this will not be a straightforward job. Often, you will be left frustrated, moving through a mass of code, analyzing it piece by piece. Still, it’s worth doing it if you find:
- a class,
- a function,
- or a whole PHP file in need of a replacement for a newer one.
It’s much easier to correct a bug like this now than suffer the consequences of it in the future together with all of your teammates.
8. Ensure the separation of technologies
The project you inherit might be a weird mix of different technologies and styles. Its readability, extendability, and general developer experience working with it are going to be poor. Here is an example of mixed code in the index.php file
You are bound to come across code like this in some older projects. When you tackle it, the first and best thing you can do is try to separate one technology from another.
The following example illustrates the solution for separation of technologies:
While the example illustrates the general idea well, you can definitely improve upon it. In a perfect world, the entire HTML section would be moved to a template file such as twig, while all the PHP code would land in the controller. It could end up looking like this:
Although the code here is quite simple, the separation clearly improves its readability and extendability. The more complex your starting code is, the more you will improve it by using this technique.
The key is to set up an acceptable minimal level of readability and continue the refactoring efforts until you reach it. When you do that, you will definitely get to a point where you have an easy-to-work-with system.
9. Maintain your code with static analysis tools
Static code analysis is the mainstay of your refactoring efforts. It holds your code together and makes it easier to eliminate basic errors and misunderstandings (e.g. in case of an indefinite type taken or returned by a function).
In the context of refactoring, a big contribution of static analysis is the way it can smooth out all the inconsistencies in your code. If it was created by many different developers over time, it may include different coding styles. When the code structure is decent, the static analysis tool can even find and correct all of the inconsistencies at the same time.
Even a single space, missing or needlessly added, can mess up the code and give you quite a headache. Static analysis may go a long way to amend such shortcomings.
And if someone you know needs to brush up on PHP coding standards, have them read the latest version of PHP Standards Recommendations (PSR).
Read more on static code analysis in PHP from an article from my colleagues!
10. Try out the Strangler Fig Pattern
There are some people out there who think of the Strangler Fig Pattern as a solution to all refactoring problems.
As you may already suspect, this isn’t quite the case. In software development, there are no one-size-fits-all solutions. Still, the Strangler Fig Pattern may be useful to your refactoring process.
Strangler Fig Pattern – definition
What is the Strangler Fig Pattern? It is all about building a new system around the old one (strangling the old one with the new one) until the latter is not needed anymore.
Strangler Fig Pattern – benefits
This approach has some major benefits.
The system always works
The new system in development shouldn’t collide with the existing one. As the new one matures, its individual modules can gradually replace the old system.
Monitoring is easy
As the tasks are being transferred to the new system, it’s easy to monitor and compare their performance. You can gradually eliminate all the problems should they arise.
In most cases, monitoring is more of a necessity than an optional assignment. It allows your team to learn more about bits and pieces of the old system that you don’t know about either due to incomplete business knowledge or the unusual nature of the legacy code.
How to use the Strangler Fig Pattern?
You should be fine as long as you stick to a couple of basic steps:
Define the interface of what you want to move
It’s an essential step because the interface includes directions on how the new code and modules are supposed to work.
Delegate tasks from the old system to the new one
If there is a piece of code in your new system that can replace a piece of code from the old system, make the switch as soon as possible.
Define a new single source of truth
A well-designed system has a single source of truth (SSOT). It should have all data required by your system’s modules. As you move to the new system, make sure not to compromise your SSOT.
Add all the new functionalities in the new system only
It should go without saying, but unfortunately, it doesn’t always happen. If you continue to expand your old codebase, you delay the moment when the new system takes over completely and risk having to write the same functionality twice.
Turn off the old code altogether when the time comes
Once all the functionalities are moved to the new system, turn off or remove the old code.
The Strangler Fig Patten is a modern solution to the problem of outdated code. It’s worth remembering that you don’t always have to move the entire codebase. Sometimes, you may choose to use the pattern for specific modules or functionalities.
There is no hard-and-fast rule for using the pattern. The best course of action needs to be decided individually for each and every project.
* Extra tip – testing new code
As cliche as it sounds, testing is a continuous process. Each new testable functionality in your system should have:
- unit tests,
- functional tests,
- integration tests.
In addition to that, you should still test manually. That lets you find bugs that might be missed by the tools you use.
The key word here is “testable” – not every piece of code meets this requirement. Don’t fall into the trap of testing literally everything. It will take more time and it is worth it.
Refactoring PHP code – 3 key takeaways
As you can, there is a lot to consider when refactoring your existing code:
- The business reality of your software may determine just how much you can refactor and where to begin.
- The quality, structure, and age of your code will decide the approach and how much work you really have on hand. And as you know very well, those factors may vary a lot when it comes to the PHP programming language.
- The size of your team and the extent to which you prioritize scalability, as well as the continuous availability of your software, will make a big difference.
Even putting that aside, refactoring is still unpredictable. Once you start working with a large piece of legacy code, you might find out that your changes cause other unexpected changes. It may take a lot of time and PHP examples to figure out everything. Just how much? Not even your project manager dares to guess!
That’s why my final piece of advice is – to spend as much as you can afford on the initial analysis.
Design a custom refactoring process for your project that gets the most of the limited time and resources you have. This is the way to achieve sustainable code!
Are you looking for skilled PHP developers to refactor your system?
The Software House provides access to many skilled PHP devs that participated in large commercially successful projects just like those described in the article.