Rewriting Legacy Code

Rewriting Legacy Code

Did you ever have to maintain a 15-year-old application? Dead code everywhere, hard dependencies, null pointer exceptions, database queries mixed with HTML, no tests and most libraries have been discontinued.

This presentation will show you how to modernize your application using a variety of strategies. You will learn how to avoid common pitfalls, automate legacy testing, manage your development environment and overcome various challenges that arise from dealing with legacy.


Anna Filina

August 20, 2020


  1. <animés par la passion> Rewriting Legacy Code SKILLS MATTER |

    AUG 20, 2020 @afilina
  2. Anna Filina ‣ Coding since 1997. ‣ Legacy archaeology. ‣

    Test automation. ‣ Talks and workshops. ‣ Twitter advice (@afilina) ‣ YouTube videos.
  3. You Inherited a
 10-Year-Old Codebase

  4. You Inherited a
 15-Year-Old Codebase

  5. You Inherited a
 20-Year-Old Codebase

  6. None
  7. History Since 2000 2004 PHP 5.0 2005 Symfony 2006 Zend

    Framework 2008 Clean Code 2011 Laravel 2001 PHPUnit
  8. Legacy Sadness

  9. The Methods ‣ Can easily exceed a thousand lines. ‣

    Mixing SQL, PHP and HTML. ‣ Receiving dozens of arguments of arbitrary type. ‣ Accessing hundreds of potentially undefined properties. ‣ Expect properties to have been set 10 children down. ‣ methodName_new, methodName_new2
  10. The Dynamic Types ‣ Null pointer exceptions of every flavor.

    ‣ Attempting to split an array. ‣ Attempting to divide by a null. ‣ Attempting to count the number of elements in a float. ‣ Attempting to max($array1, $array2),
 where array items are of mixed types.
  11. How to Fail a Rewrite

  12. Microservices AWS GitHub Flutter CircleCI ElasticSearch Ansible Docker Symfony 5

  13. Start With the Strategy

  14. Strategy ‣ Version upgrade. ‣ New framework. ‣ Progressive rewrite.

    ‣ Refactoring only. ‣ Full rewrite.
  15. Version Upgrade ‣ No need to freeze development. ‣ PHPCodeSniffer

    can detect incompatibilities. ‣ Also need to patch libs & framework. ‣ Some libs might be easier to swap than to fix (PHPExcel). ‣ Regexes are your friends. ‣ Rector can automatically refactor.
  16. None
  17. New Framework ‣ Use dependency injection. ‣ Automate large chunks

    with Rector. ‣ Use adapters. ‣ Need to freeze development.
  18. Product/IndexHandler ProductController indexAction viewAction Product/ViewHandler Routes Routes Product/BaseHandler

  19. class IndexHandler extends BaseHandler implements RequestHandlerInterface { public function __construct(

    TemplateRendererInterface $template, ProductModel $productModel ) { //... } public function handle(ServerRequestInterface $request): ResponseInterface { return new HtmlResponse(
 $this->template->render('admin::product/index', $view)
 ); } }
  20. Progressive Rewrite ‣ By endpoint/feature • Can switch language. •

    Use design extraction.
  21. Old Code New Design Old Design New Code

  22. ‣ Remove dead code. ‣ Move logic to a better

    location. ‣ Split long functions. Techniques
  23. ‣ Some criteria for refactoring: • Need to change. •

    Hard to understand, but need to work with it. Refactoring Only
  24. Full Rewrite ‣ Discouraged. ‣ My criteria: • Can disregard

    existing features. • Few or no integrations. • Can trash old database. • If not, need a team experienced in full rewrites.
  25. Testing Approaches

  26. Version Upgrade Characterization tests Fix code Should work
 for old

    and new versions. Unit tests New/refactored code. What failed in E2E.
  27. New Framework Characterization tests Migrate Should work
 for old and

    new versions. Unit tests New/refactored code. What failed in E2E.
  28. Progressive Rewrite For new code only. Characterization tests Unit tests

    Should work
 for old and new versions. New code
  29. Refactoring Only For the 
 upcoming code. Characterization tests Unit

    tests Should work
 for old and new versions. Refactor
  30. Preparing the Environments

  31. Local Environment ‣ Docker. ‣ Baseline image. ‣ Migration image.

    ‣ Match server / vagrant.
  32. services: php_apache: //... mysql: //... acceptance: //... mysql_test: //...

  33. Gotchas ‣ Pay attention to MySQL mode. ‣ Run composer

    inside Docker, commit composer.lock ‣ Anonymized production database. ‣ Send e-mails to relay: DebugMail, MailTrap, etc.
  34. Staging Environment ‣ Baseline server. ‣ Migration server. ‣ Staging

    config: point to sandboxes.
  35. <animés par la passion> @afilina @afilina