Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

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. — 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
  2. — 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
  3. We want to be in a position to iterate quickly

    on this product, but with the current codebase we can't.
  4. 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
  5. 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
  6. 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
  7. Migrate from Angular 1 to 2 and beyond ! —

    Not much developer expertise in Angular across Songkick — Not many Angular evangelists at Songkick
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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.
  13. 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)
  14. Angular 1 was built in a time where we had

    no module system in JavaScript, so it provided one.
  15. // 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.
  16. In ES2015 we have a module system (which Babel transpiles

    for us) const Foo = {...} export default Foo // in another file import Foo from './foo'
  17. ES2015 modules are brilliant And have loads of advantages over

    dependency injection that this migration gives you for free.
  18. 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.
  19. So, we can first pull the logic into a standalone

    thing: const localStorage = { ... }
  20. And then export it so any non Angular code can

    just import it: const localStorage = { ... } export default localStorage
  21. And then leave the small Angular wrapper: const localStorage =

    { ... } export default localStorage angular.service('LocalStorage', function() { return localStorage })
  22. ! 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
  23. 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.
  24. import React, { Component } from 'react' class ListingsButton extends

    Component { // code left out to save space :) render() { return ( <a href="" className="">Buy now!</a> ) } }
  25. But how do we embed this within Angular? Angular directives

    are used as if they were custom HTML elements: <li> <h2>Rihanna at The O2</h2> <listings-button url="...">Buy now</listings-button> </li>
  26. 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.
  27. We can continue this strategy, migrating the other components to

    React: <li> <react-component name="ListingsTitle" props="..." /> <react-component name="ListingsButton" props="..." /> </li>
  28. 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 ( <li> <ListingsTitle ... /> <ListingsButton ... /> </li> ) } }
  29. 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
  30. Unit Tests — Existing Angular tests can be migrated as

    the services move away from Angular — Used as an opportunity to add test coverage
  31. 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') }) }) })
  32. 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.
  33. 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
  34. !

  35. 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"
  36. To date we've had 5 fires caused by migration: —

    Two were particular journeys not being covered by any unit or acceptance tests
  37. 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...
  38. 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.
  39. 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.
  40. Pick work based on bugs and churn rate, not code

    quality Churn rates: the amount of times a file is changed
  41. 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
  42. The next time you touch some code, you'll know more

    about it than before. So why make it perfect now?
  43. Keeping people happy and productive We knew this migration was

    going to be at least 6 months, more likely closer to a year.
  44. Release early, release often ! Easier to react 1 to

    a bug if release causes it 1 pun very much intended
  45. Migrations are a good excuse to reconsider tooling And also

    provide a break from working on the actual app codebase.
  46. 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!
  47. 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.
  48. By providing a mix of migration tasks (big, small, visual,

    "under the hood", tooling), we're able to keep work fun and interesting
  49. 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"
  50. 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"
  51. 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.
  52. 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.
  53. 1. Don't migrate for the sake of it "Framework X

    is now out of date" is not a valid reason!