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

From Angular to React

From Angular to React

Jack talks about the reasons why he's moving a large scale app from Angular to React and the practicalities of doing so in a less risky, flexible manner that hasn’t left him and his team rebuilding from scratch, but implementing incrementally.

Frontend NE

April 06, 2017
Tweet

More Decks by Frontend NE

Other Decks in Technology

Transcript

  1. — Started 2 years ago, built on Angular 1 —

    Primarily used within iframes — Added Redux to be used alongside Angular — 2 main developers no longer with the company — Very heavy on features — Our main source of revenue — Tickets being sold constantly — Used by our flagship clients (Adele, RHCP and others)
  2. — Test coverage not bad, but not great. — 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 wwouldn'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 — Apparently David Moyes likes Angular
  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 — We can experiment by moving piece by piece and try different approaches — Current stores continue to function correctly — We can move quickly and release small parts at once - avoiding risky big bang releases — Downside: introduce some migration specific code
  9. ! 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 ! It's Rafa Benitez's framework of choice
  10. 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.
  11. 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)
  12. Angular 1 was built in a time where we had

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

    for us) const Foo = {...} export default Foo // in another file import Foo from './foo'
  15. 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.
  16. So, we can first pull the logic into a standalone

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

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

    { ... } export default localStorage angular.service('LocalStorage', function() { return localStorage })
  19. ! 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
  20. 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.
  21. But what about Angular's built ins? — $timeout is a

    wrapper around setTimeout — $window is a wrapper around $window — $q is a wrapper around Promise
  22. import React, { Component } from 'react' class ListingsButton extends

    Component { render() { return ( <a href="" className="">Buy now!</a> ) } }
  23. But how do we embed this within Angular? <li> <h2>Rihanna

    at The O2</h2> <listings-button url="...">Buy now</listings-button> </li>
  24. 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.
  25. We can continue this strategy, migrating the other components to

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

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

  33. To date we've had 3 fires caused by migration: —

    One was down to bad data in our test environment not matching "real life" — we have now fixed the test data to replicate "real life"
  34. To date we've had 3 fires caused by migration: —

    One was down to a particular journey not being covered by any unit or acceptance tests — it is now covered by tests
  35. To date we've had 3 fires caused by migration: —

    Another was down to timezones ! — we know to check our site on different timezones when making changes that might hit this problem
  36. 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.
  37. Momentum and prioritisation We knew this migration was going to

    be at least 6 months, more likely closer to a year.
  38. Prioritisation 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.
  39. Pick work based on bugs and churn rate, not code

    quality Churn rates: the amount of times a file is changed
  40. 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
  41. Remember: you can go back and rewrite migrated code Don't

    aim for perfection when you migrate.
  42. "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" "It will enable us to onboard new developers to the team easily and have them contributing quicker than with the current codebase"
  43. Tooling Because you can't do a JavaScript talk without talking

    about tools. 1 1 And I don't mean John O'Shea or David Moyes
  44. Migrations are a good excuse to reconsider tooling And also

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

    "under the hood", tooling), we're able to keep work fun and interesting over a (so far) 6 month period, with probably 6 more months to go.
  48. How's it going so far? We estimate to be ~

    35% 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.
  49. 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.
  50. 1. Don't migrate for the sake of it "Framework X

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