Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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
Tweet

More Decks by Steve Winter

Other Decks in Technology

Transcript

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

    View full-size slide

  2. @steveWinterNZ
    Who am I?
    Former school teacher and science educator
    PHP developer since BGH
    Work a lot with FileMaker as backend
    Freelance developer

    View full-size slide

  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.

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  6. @steveWinterNZ
    Client requirements
    Updates to templates / layout of app
    Enhancements to some functionality e.g. persisting of favourites
    New features to implement

    View full-size slide

  7. @steveWinterNZ
    Our requirements
    Implement proper deployment processes
    Get the application testable (and tested)
    Fix the build chain

    View full-size slide

  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

    View full-size slide

  9. @steveWinterNZ
    Solution attempt 1
    Began to work on feature modification
    Tried to add new dependencies (both JavaScript and PHP)

    View full-size slide

  10. @steveWinterNZ

    View full-size slide

  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

    View full-size slide

  12. @steveWinterNZ
    Solution attempt 2
    Upgrade to the latest version of the ‘other’ framework

    View full-size slide

  13. @steveWinterNZ

    View full-size slide

  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

    View full-size slide

  15. @steveWinterNZ
    Thank you Symfony!
    Semver FTW
    Backwards compatible promise super important

    View full-size slide

  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

    View full-size slide

  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')

    View full-size slide

  18. @steveWinterNZ
    Apache proxy rules

    ...
    ProxyPass /public/ http://project.backend:808/
    ProxyPassReverse /public/ http://project.backend:808/
    ...

    View full-size slide

  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’

    View full-size slide

  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

    View full-size slide

  21. @steveWinterNZ
    But now we have a new problem
    How does the legacy app know the number of reminders?

    View full-size slide

  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

    View full-size slide

  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!

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  27. @steveWinterNZ
    Remaining challenges
    Templating / CSS
    currently have two versions of everything
    next step is to move all CSS to Symfony app

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  30. @steveWinterNZ
    Outcome
    Modern, tested, reliable app meeting client needs in
    which it’s easier to continue development.

    View full-size slide

  31. @steveWinterNZ
    Is this approach right for you?
    It depends!

    View full-size slide

  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

    View full-size slide

  33. @steveWinterNZ
    Questions?

    View full-size slide

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

    View full-size slide