So the Guy (or Gal, it doesn’t really matter) comes to you and says:
I have this awesome project, which is really nice and all, but it’s a bit slow. Could you please make it work faster?
Then your brain starts racing… Database indexes, search server, PHP7, message queues, all the good stuff. But the client grants you SSH and GitHub access and you get to see the code.
Vanilla PHP or, if you’re among the lucky kind, some sort of an open-source framework (documented, hopefully) which is totally misused.
Now it’s time to get to work, and first thing’s first, migrate this … THING … to a real framework.
But that’s easier said than done. Let’s say you have about 150 000 lines of code which has to be migrated, and classes (if you’re lucky and it’s not procedural programming) spread all over the place and do all kinds of crazy stuff. After the facepalms are consumed, you look at the routing and how the request flows through your application.
If you’re among the unfortunate ones, each PHP file will start with “include” or “require”. That’s a no-go. Get some autoloading in there ASAP. Any PSR-4 autoloader (e.g., Composer) should get the job done. You can’t do it all at once but keep this in mind when starting to touch your classes!
In a perfect world, you’ll end up using Symfony’s Routing component and move through, so after 2 months of eye bleeding you manage to completely get rid of the old clunky routing. It’s doable, you can make it happen, and it will give you the first insight on how the previous devs structured the architecture of the project.
If you find that a MVC approach will fit your needs (in most cases it will), go ahead and create the 3 folders for Controllers, Models and Views. Having the routing in place creates a good opportunity for you to find what’s supposed to be controller code. For now, just dump it in there and carry on. I know it’s messy, spaghetti, unreadable, but it’s something. It’s better to have a fat controller than no controller at all.
Back to the classes, now comes the fun part 🙂 It’s easy enough to make the router point to some actions. Problem solved. But the next step is understanding which component does what. So you start dividing code into granulated components and hopefully shape them on a piece of paper so that they can at least TRY to respect SOLID.
Once this is done, you’ll be a happy dev. BUT then reality hits and you realize you have to actually understand the business logic behind all the components, and during their refactoring, you have to maintain the same output, “bug-free”. So there’s more to it than moving code from one class to another.
At first, just throw a DIC in there. Raw Pimple or something Pimple based. It’s going to be your best friend. For now it’s empty, but not for long 🙂 Now let’s carry on…
So your obvious choice is to look for tests … but wait … of course it doesn’t have unit tests and Behat wasn’t even a thing when the project started… Whelp, first thing’s first, start sketching down some SERVICES, by first writing unit tests for them according to the current behavior of the code.
Without tests, your end result WILL NOT MATCH the current application no matter how hard you try. You will spend 10 hours on debugging something that a test could have revealed in a few seconds. Moreover, by the time you finish writing a test class, you already anticipate the content of the class, and you have a PLAN. Plans are good. You like plans.
Yes, this will hurt. Indeed, you have to keep in touch with the Guy in order to find out what the darkest corners of the app do. But when you’re done, the refactoring process will have been almost complete. ALMOST.
Service by service, you start enjoying the moment when you delete an old class and insert your new pretty service in the DIC. You learn the app more and more. Suddenly “IT’S NOT THAT HARD”. But then you see:
$query = ‘
Well, !@#$ …. This query is repeated in the same class 3 times, 325 times in other classes, and only some variables change. IT IS GONNA PAY OFF if you start implementing a repository pattern or something to keep the queries in one place! Don’t be lazy! Your query results will look so much better when you’re done. Instead of 15 lines of boilerplate code that hydrates the result, you only call a method from a repository and that’s it, you’re good to go.
If you want to go the extra mile, go ahead and use Doctrine.
Okay, I got it, but you’re talking !@#$
Well, it’s not me saying these things. They’re just a compilation of stuff that really smart people produced.
Here’s some more stuff to see before starting:
There’s more to refactoring than what you just read, but this is a baseline for your battle plan.
If you manage to implement this, brag about it in the comments!
You could have an insight on how we did it. Just Ask!
Article written by Calin Pristavu