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

Migrating from Angular to React: Manc React

Migrating from Angular to React: Manc React

Jack Franklin

May 02, 2017
Tweet

More Decks by Jack Franklin

Other Decks in Technology

Transcript

  1. From Angular to React
    by @Jack_Franklin

    View Slide

  2. From X to Y
    My experience migrating complex software
    (Regardless of the tech choices).

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. The Store

    View Slide

  9. — Started 2 years ago, built on Angular 1
    — Primarily used within iframes
    — 2 main developers no longer with the company
    — Heavy on features
    — Tickets being sold constantly
    — Used by our flagship clients

    View Slide

  10. The store works well.

    View Slide

  11. But changing it is very tricky.

    View Slide

  12. — Unclear which features are actually needed
    — Many different use cases
    — Regular general admission tickets
    — Reserved seating tickets
    — Queued tickets for big sales
    — VIP tickets ("meet and greet")
    — Merchandise addons (Ticket + album = 10%
    discount)
    — We have a lot of stores in the wild

    View Slide

  13. We want to be in a position to iterate quickly on this
    product, but with the current codebase we can't.

    View Slide

  14. What do we do?

    View Slide

  15. 1. Start completely from scratch
    2. Stick with Angular 1, and rewrite
    complex parts.
    3. Migrate from Angular 1 to 2/3/4
    4. Migrate bit by bit to something
    else

    View Slide

  16. Start completely from scratch !
    — We can't afford to not work on bugs, improvements
    on the existing product
    — The Angular app began as a big rewrite - business
    wouldn't like it to happen again

    View Slide

  17. Stick with Angular 1 !
    — Angular 1 is reaching its end - focus is now on
    Angular 2 and beyond.
    — Not much developer expertise in Angular across
    Songkick

    View Slide

  18. Migrate from Angular 1 to 2 and beyond !
    — Not much developer expertise in Angular across
    Songkick
    — Not many Angular evangelists at Songkick

    View Slide

  19. Migrate bit by bit to something else !
    — Allows us to work on bugs in existing code whilst
    migrating others
    — Allows us to build new features whilst migrating

    View Slide

  20. Migrate bit by bit to something else !
    — We can experiment by moving piece by piece and
    try different approaches
    — Current stores continue to function correctly

    View Slide

  21. Migrate bit by bit to something else !
    — We can move quickly and release small parts at
    once - avoiding risky big bang releases
    — Small downside: migration specific code - plumbing

    View Slide

  22. What to migrate to?

    View Slide

  23. View Slide

  24. Why React?
    ! I and my team are all familiar with React
    ! Another, newer, product at Songkick was built with
    React
    ! React leaves you with less framework specific code

    View Slide

  25. Note: I am not saying that Angular is a bad choice, or
    that you shouldn't use it.
    Just for us, it wasn't the best fit.

    View Slide

  26. So, we've decided to migrate,
    and what to migrate to.

    View Slide

  27. Now for the hard part
    — 40,000 lines of JavaScript
    — Most of those lines are tied directly to Angular
    — Many dependencies outdated due to lack of
    maintanance over past 6 months
    — A store that cannot be broken at any point (we sell
    tickets pretty much continuously)

    View Slide

  28. The Theory
    Components (or directives in Angular)

    View Slide

  29. View Slide

  30. View Slide

  31. View Slide

  32. The Plan

    View Slide

  33. View Slide

  34. View Slide

  35. View Slide

  36. The theory is easy!

    View Slide

  37. But more complicated in
    practice...

    View Slide

  38. Angular and dependency
    injection

    View Slide

  39. Angular 1 was built in a time where we had no module
    system in JavaScript, so it provided one.

    View Slide

  40. // define your Foo object
    const Foo = {...}
    angular.service('Foo', Foo)
    // in another Angular thing
    function SomeController(Foo) {
    Foo.bar()
    ...
    }
    Angular will inject the Foo dependency into
    SomeController at runtime.

    View Slide

  41. In ES2015 we have a module system (which Babel transpiles for us)
    const Foo = {...}
    export default Foo
    // in another file
    import Foo from './foo'

    View Slide

  42. ES2015 modules are brilliant
    And have loads of advantages over dependency
    injection that this migration gives you for free.

    View Slide

  43. The step by step process of
    migrating an Angular directive
    to a React component

    View Slide

  44. 1. Find all dependencies that the directive (or a
    dependency of the directive) injects.

    View Slide

  45. 2. Migrate all of those one by
    one to React

    View Slide

  46. But wait!
    You need to migrate localStorage-service.js away from
    Angular.
    angular.service('LocalStorage', function() {
    ...
    })
    But this service is used in ~15 places in the app.
    And we want to release the migration in tiny chunks.

    View Slide

  47. So, we can first pull the logic into a standalone thing:
    const localStorage = {
    ...
    }

    View Slide

  48. And then export it so any non Angular code can just import
    it:
    const localStorage = {
    ...
    }
    export default localStorage

    View Slide

  49. And then leave the small Angular wrapper:
    const localStorage = {
    ...
    }
    export default localStorage
    angular.service('LocalStorage', function() {
    return localStorage
    })

    View Slide

  50. View Slide

  51. ! Existing Angular code can still use the local storage
    code without being aware it's changed
    ! New React components can import the module
    without any of the Angular boilerplate
    " We had to write a small amount of migration
    specific code

    View Slide

  52. Migration specific code
    Code that you know will be deleted when the
    migration is finished.
    A neccessary part of the migration but you should
    keep it to a minimum.
    You should also group it so it's easily found and
    removed later.

    View Slide

  53. So, we've migrated all dependencies away from Angular

    View Slide

  54. Now we can migrate listingsButton-
    directive.js to React

    View Slide

  55. import React, { Component } from 'react'
    class ListingsButton extends Component {
    // code left out to save space :)
    render() {
    return (
    Buy now!
    )
    }
    }

    View Slide

  56. But how do we embed this within Angular?
    Angular directives are used as if they were custom HTML elements:

    Rihanna at The O2
    Buy now

    View Slide

  57. View Slide

  58. ngReact lets you embed React components within Angular
    applications
    It is the piece of code that enables our entire migration
    strategy to work.
    Remember: you can render many React apps on a
    page in any place you want.

    View Slide

  59. Aside: React is really malleable

    View Slide

  60. With ngReact

    Rihanna at The O2


    View Slide

  61. We can continue this strategy, migrating the other
    components to React:




    View Slide

  62. View Slide

  63. And now we can migrate this into one big React
    component!
    import React, { Component } from 'react'
    import ListingsTitle from './listings-title'
    import ListingsButton from './listings-button'
    class ListingsItem extends Component {
    render() {
    return (




    )
    }
    }

    View Slide

  64. And update our template

    View Slide

  65. View Slide

  66. https://www.sitepoint.com/organize-large-react-
    application/

    View Slide



  67. View Slide

  68. Thanks for coming back!

    View Slide

  69. Maintaining functionality
    — At no point can the store be broken.
    — Newly migrated code must have exactly the same
    functionality
    — Put simply: users must not notice any difference

    View Slide

  70. Unit Tests
    — Existing Angular tests can be migrated as the
    services move away from Angular
    — Used as an opportunity to add test coverage

    View Slide

  71. React components are very testable
    import React from 'react'
    import ListingsDate from './listings-date'
    import { mount } from 'enzyme'
    describe('ListingsDateComponent', () => {
    describe('an event with just a start date', function() {
    const event = { event_date: { date: '2016-01-01' } }
    const wrapper = mount(React.createElement(ListingsDate, { event }))
    it('formats the month correctly', function() {
    expect(wrapper.find('.date-block__seg--month').text()).toEqual('Jan')
    })
    it('formats the day correctly', function() {
    expect(wrapper.find('.date-block__seg--day').text()).toEqual('1')
    })
    it('formats the year correctly', function() {
    expect(wrapper.find('.date-block__seg--year').text()).toEqual('2016')
    })
    })
    })

    View Slide

  72. https://www.sitepoint.com/test-react-components-jest

    View Slide

  73. Unit tests are good because they
    provide quick feedback and can
    test at a very granular level.

    View Slide

  74. Unit tests (in our case) are bad because when you migrate
    code, you migrate tests
    Therefore, they are not technically proof that
    everything works as before.

    View Slide

  75. Acceptance tests

    View Slide

  76. View Slide

  77. Automated tests that are run using Protractor & Selenium
    — ! They test the entire system, with no faked API
    calls or fake data
    — ! This means they are entirely independent of the
    migration
    — " They can flake sometimes and are slow to run

    View Slide

  78. Because they are slow to run, we
    keep acceptance tests to our
    core user journeys

    View Slide

  79. They run on every single build,
    and they have to pass to enable
    us to deploy

    View Slide

  80. The pipeline

    View Slide

  81. View Slide

  82. Making sure deploys have
    succeeded

    View Slide

  83. Track user activity to ensure deploys are successful

    View Slide

  84. View Slide

  85. !

    View Slide

  86. Unfortunately, fires will happen
    during this process.

    View Slide

  87. To date we've had 5 fires caused by migration:
    — Two were down to bad data in our test / dev
    environment not matching "real life"

    View Slide

  88. To date we've had 5 fires caused by migration:
    — Two were particular journeys not being covered by
    any unit or acceptance tests

    View Slide

  89. To date we've had 5 fires caused by migration:
    — Another was down to timezones !
    — 3 people in Japan bought tickets for an event on the
    wrong day because of me...

    View Slide

  90. You have to communicate how and why a fire happened,
    but how you'll prevent it next time
    Because you can never ever release bug free so!ware.

    View Slide

  91. Momentum & Prioritisation

    View Slide

  92. One of the core goals of this migration was to make
    the code easier to work on, add features to and fix bugs
    with more confidence.

    View Slide

  93. Pick work based on bugs and churn rate, not code quality
    Churn rates: the amount of times a file is changed

    View Slide

  94. Code quality Churn rate Bug rate Priority
    Bad Low Low Low
    Good Low Low Lowest
    Bad Medium High High
    Good Low High High
    Bad High High Highest

    View Slide

  95. A migrated codebase is not a
    perfect code base

    View Slide

  96. 1. Migrate from X to Y
    2. Refactor or redesign Y based on new learnings

    View Slide

  97. The next time you touch some code, you'll know more about it than before.
    So why make it perfect now?

    View Slide

  98. Keeping people happy and productive
    We knew this migration was going to be at least 6
    months, more likely closer to a year.

    View Slide

  99. And in any large software migration...

    View Slide

  100. There will be good times...

    View Slide

  101. And bad ones...

    View Slide

  102. But, remember, it can always be
    worse...

    View Slide

  103. But, remember, it can always be worse...

    View Slide

  104. View Slide

  105. Anyway; where was I?!

    View Slide

  106. Momentum & Prioritisation

    View Slide

  107. Mix larger, multi-week work with
    short, 1-2 day work, to keep
    momentum.

    View Slide

  108. <1 day's worth of work

    View Slide

  109. 1 week's worth of work

    View Slide

  110. Break down large work into small PRs

    View Slide

  111. Release early, release often

    View Slide

  112. Release early, release often
    ! Encourages small pull requests and units of work

    View Slide

  113. Release early, release often
    ! Keeps momentum up in the team

    View Slide

  114. Release early, release often
    ! Easier to react 1 to a bug if release causes it
    1 pun very much intended

    View Slide

  115. The scout's rule
    Always leave things better than when you found them.

    View Slide

  116. Mix visual work + "under the
    hood work"

    View Slide

  117. Have some form of tracking or
    metrics, however rough it is

    View Slide

  118. View Slide

  119. View Slide

  120. View Slide

  121. Tooling
    Because you can't do a JavaScript talk without talking
    about tools. #fatigue

    View Slide

  122. Migrations are a good excuse to reconsider tooling
    And also provide a break from working on the actual
    app codebase.

    View Slide

  123. From Karma to Jest
    Our tests were running on Karma, but we wanted to try
    Jest from Facebook.
    We had (at the time) ~600 tests across ~130 files. So we
    started migrating them!

    View Slide

  124. View Slide

  125. From Browserify to Webpack
    The store was built using a combination of Makefiles,
    misc command line tools and Browserify. This was
    hard for us to follow and was very brittle.
    We moved to Webpack which lead to reduce build
    times, smaller bundles and got us more inline with the
    rest of the community.

    View Slide

  126. By providing a mix of migration
    tasks (big, small, visual, "under
    the hood", tooling), we're able to
    keep work fun and interesting

    View Slide

  127. Communicate to the business
    Specifically: Why?

    View Slide

  128. Take 1
    "Well, Angular 1 is reaching end of life and React offers
    a much better component model that fits our ideas of
    how to build so#ware"
    "React's lifecycle methods and small API is easier for
    developers to learn"
    "React's state model is less magical; its unidirectional
    data flow really simplifies code and makes it easier to
    reason about"

    View Slide

  129. Take 2
    "Right now when you ask us for a new feature, or bug
    fix, it's hard and takes a long time to fix and have
    confidence that we've not inadvertently broken
    anything else"
    "This migration will enable us to have a leaner, stable
    codebase which we will have more confidence in and
    be able to build features and fix bugs more quickly"

    View Slide

  130. "So nothing will change in the
    next year on the store?"

    View Slide

  131. Long term user advantages

    View Slide

  132. xf buddies

    View Slide

  133. How's it going so far?
    We estimate to be ~51% done.
    Most of the core visual journey is done. All forms that
    users interact with are now in React.
    We've had very few bug reports on any of our new
    React code, compared to the old Angular code.

    View Slide

  134. We've done some stuff well, and
    I expect to learn we've done
    some stuff badly.

    View Slide

  135. Will you ever be done?
    Our natural "finish" point is when we've removed
    Angular from our codebase.
    But when that happens we'll keep refactoring &
    revisiting old code as we fix bugs and introduce new
    features.

    View Slide

  136. Takeaways

    View Slide

  137. 1. Don't migrate for the sake of it
    "Framework X is now out of date" is not a valid reason!

    View Slide

  138. 2. Plan, plan and plan again

    View Slide

  139. 3. Cross business
    communication is more
    important than you realise at
    first

    View Slide

  140. 4. Prioritise based on pain points
    in your current application

    View Slide

  141. 5. Mix up tasks based on
    difficulty + visual/tooling/etc to
    keep it interesting

    View Slide

  142. 6. Have some rough metrics that
    you can use to track progress
    internally

    View Slide

  143. 7. Don't be surprised if you end
    up rewriting some migrated
    code

    View Slide

  144. 8. Don't appoint David Moyes.

    View Slide

  145. Thank you!
    — javascriptplayground.com
    — Questions / want a job?!
    [email protected]

    View Slide