This post is a summary of a couple of years spent battling a legacy application. This is not (necessarily) a how-to because I have yet to declare a "mission accomplished" here and whether it's a success or not, it's too soon to call.
While this represents a particular set of circumstances, I believe it can help you ask the right questions in the beginning, take into account a variety of paths and also give the benefit of the doubt to those whose mess you've inherited (they were doing their best at the time).
- legacy PHP, started with early 5.x versions and patched just enough so that it runs on 7.2
- really old-school: html is formed by echo-ing stuff in random scripts that are "require'd" in the right order. Warnings are totally silenced, nobody cared about a missing array item when fetching a deeper array entry.
- evidence that someone tried to MVC the whole thing, but not by introducing, say, Symfony components but by creating scripts that act like views (just echo stuff, but without buffering it into a final string - echo wherever/whenever) and some classes - but missing a proper service layer
- database access done via a class that mysqli_connects, no caching of any kind
- some models carry state but still choke-full of static methods that bring fresh data, as if someone wanted to go more like Doctrine-style but someone else later wanted a poor man's Eloquent Active Record
- sometimes the same query ended up being done against the DB 10s of times during a request (well, 29 was the top I've seen myself)
- running on manually maintained VMs, deploy manually via "git pull" then clear some local static files then restart Apache
- the whole thing was becoming multi-tenant
- the application was heavily dependent on data fetched from remote APIs and had to push data back to the same APIs (some where REST, some SOAP)
- non-existent error handling, WARNING were silent, catching was used to ignore Errors (for the most part), virtually no logging.
- objects were used for data models only for the DB, for SOAP/REST(JSON) only arryas/stdClass were used to serialise/deserialise (always assuming that some item exists via the developer's knowledge of the structure of a given request).
- migrate to a cloud provider and add scalability (automatic, preferably)
- create a layered system of environments for each tenant with automatic deployment on a QA environment and manual promotion for local (our) acceptance then customer acceptance then prod.
- tackle a number of bugs in the code. Right off the bat it was clear most were linked to the hidden warnings, where the code would for example expect
$array['level1']['level2']but 'level2' may be missing due to some buggy change yet that fact was hidden from developers since warnings were silenced.
- create a system of aggregated structure logging
- integrate more external APIs
- tackle an existing feature backlog
- improve performance
Let's explore them, as part of the series!