$30 off During Our Annual Pro Sale. View Details »

Migrating to Symfony one route at a time

Steve Winter
September 13, 2019

Migrating to Symfony one route at a time

We recently acquired a new client and a new project. Unfortunately this was built in a fairly old version of a framework we're not so familiar with.

There were several bugs to fix and plenty of new features the client wanted 'yesterday'!

Initially we attempted to build these using the existing codebase, but very quickly began running into dependency hell.

Our next attempt was to work through the migration pathway of that framework, but every time we tried to move forward something else seemed to go wrong, and with no tests that was pretty scary!

The client didn't have the budget for a complete rebuild so we began by building all new features in Symfony then as other functionality needed work moving that across as well.

In this session we'll look at the challenges we faced and how we resolved them to allow us to leave the legacy application doing what it does but still being able to work with current packages and methodologies for new development.

Steve Winter

September 13, 2019

More Decks by Steve Winter

Other Decks in Technology


  1. Migrating to Symfony one route at a time Steve Winter

  2. @steveWinterNZ Who am I? Former school teacher and science educator

    PHP developer since BGH Work a lot with FileMaker as backend Freelance developer
  3. @steveWinterNZ One route at a time Real world story of

    a problem we needed to solve This may not be the best way, but it worked for us and our client Currently in production and working well.
  4. @steveWinterNZ The situation New client with an old application Built

    in a (much) older version of another popular PHP framework Questionable coding practices in the application Poorly set up build chain, with unusual dependency management No tests, no version control, deployment via FTP
  5. @steveWinterNZ The application Subscription-based index of suppliers in particular field

    Search / filter by name, location, type etc View details, including location etc ‘Faviourite’ results to a session-based list, download export etc
  6. @steveWinterNZ Client requirements Updates to templates / layout of app

    Enhancements to some functionality e.g. persisting of favourites New features to implement
  7. @steveWinterNZ Our requirements Implement proper deployment processes Get the application

    testable (and tested) Fix the build chain
  8. @steveWinterNZ Solution attempt 1 Work within the existing version of

    the current framework Addressed the easy / critical things added code to git implemented Ansible-based deployment added functional (end-to-end) tests for key functionality made some minor template / design changes
  9. @steveWinterNZ Solution attempt 1 Began to work on feature modification

    Tried to add new dependencies (both JavaScript and PHP)
  10. @steveWinterNZ

  11. @steveWinterNZ Solution attempt 1 JavaScript build chain issues were circular,

    confusing, frustrating and seemingly unsolvable! PHP library versions in use were incompatible with desired new dependencies
  12. @steveWinterNZ Solution attempt 2 Upgrade to the latest version of

    the ‘other’ framework
  13. @steveWinterNZ

  14. @steveWinterNZ Solution attempt 2 Multiple BC breaks from version to

    version Semver didn’t seem to apply Many manual steps required Functionality stopped working, sometimes for no apparent reason
  15. @steveWinterNZ Thank you Symfony! Semver FTW Backwards compatible promise super

  16. @steveWinterNZ Third time lucky! Reverted the legacy codebase to the

    original framework version Introduced a Symfony app to run alongside the legacy app Added Apache proxy rules Made minor changes to the legacy app to integrate between the two Added data-transfer mechanisms (mostly API on the Symfony side) All new and updated functionality developed in Symfony
  17. @steveWinterNZ Introduced a Symfony app Installed on the same server

    Has it’s own virtual host as ‘project.backend’ (with local hosts file entry) Runs on port 808, not publicly accessible (through firewall rules) Deployable independently of legacy app Minor difference to standard app in webpack.config.js .setPublicPath('/public/build')
  18. @steveWinterNZ Apache proxy rules <VirtualHost *:443> ... ProxyPass /public/ http://project.backend:808/

    ProxyPassReverse /public/ http://project.backend:808/ ... </VirtualHost>
  19. @steveWinterNZ Legacy app changes Created a shared cookie which was

    set on login user account details (e.g. ID, name, affiliation) session details (e.g. login time) (initially) details of count of items in ‘cart’
  20. @steveWinterNZ Added functionality Built entirely in the Symfony app New

    proxy routes in Apache config ProxyPass /reminder/ http://project.backend:808/reminder ProxyPassReverse /reminder/ http://project.backend:808/reminder Added to the navigation of the legacy app
  21. @steveWinterNZ But now we have a new problem How does

    the legacy app know the number of reminders?
  22. @steveWinterNZ Symfony-based API Simple RESTful API on the Symfony side,

    returns JSON object of data Minor update in the legacy app to source data from that API Expanded over time to provide additional two-way state transfer data
  23. @steveWinterNZ Migrating routes (functionality) Rebuild / extend in Symfony app

    Test and deploy Update Apache config to switch functionality to Symfony ProxyPass /company-details/ http://project.backend:808/company ProxyPassReverse /company-details/ http://project.backend:808/company Rinse and repeat!
  24. @steveWinterNZ A ‘gotcha’ we struck Legacy app uses CSRF tokens

    Needed to track that token in Symfony in order to access some endpoints through AJAX from the Symphony app Added it to our shared cookie on login
  25. @steveWinterNZ Storing data Both apps using the same (MySQL) database

    Each app has its own user credentials Some tables are used by both, others only by one app or the other
  26. @steveWinterNZ Remaining challenges User session management still handled only by

    the legacy app inactive user sessions don’t expire (as they should) in the Symfony app sometimes users spend so long in Symfony they exceed legacy timeout
  27. @steveWinterNZ Remaining challenges Templating / CSS currently have two versions

    of everything next step is to move all CSS to Symfony app
  28. @steveWinterNZ Remaining challenges Authentication is still handled by legacy app

    right now overall app is about 50:50 legacy / Symfony need to ‘bite the bullet’ and move the user management to Symfony rest of app will ‘fall quickly’ from that point
  29. @steveWinterNZ Summary Introduced a Symfony app to run alongside the

    legacy app Used Apache proxy rules to migrate routes Added data-transfer mechanisms (mostly API on the Symfony side) Developed all new and updated functionality in Symfony
  30. @steveWinterNZ Outcome Modern, tested, reliable app meeting client needs in

    which it’s easier to continue development.
  31. @steveWinterNZ Is this approach right for you? It depends!

  32. @steveWinterNZ Is this approach right for you? This app is

    of ‘modest’ size On a larger scale: Use a ‘real’ proxy server Run each app on different server(s) Look at master-master database replication
  33. @steveWinterNZ Questions?

  34. Migrating to Symfony one route at a time Steve Winter