Save 37% off PRO during our Black Friday Sale! »

Progressive Transpilation at Netflix and the road to running native ES2015 in production

Progressive Transpilation at Netflix and the road to running native ES2015 in production

A talk I gave at Netflix HQ discussing my solution for shipping native ES2015 to users of netflix.com. It lays out the concept of Progressive Transpilation, and goes through the hurdles I have run into and the solutions I've found.

More information on twitter at https://twitter.com/betaorbust

Aac9bc529868ea8e36effedd5ce4ff58?s=128

Jacques Favreau

October 25, 2016
Tweet

Transcript

  1. 1 The Road to ES2015 in Production Jacques Favreau

  2. 2 What time is it? Doom time!

  3. 3 • Const & Let • Arrow Functions • Destructuring

    • Backtick/Template Strings • Default Parameters Where we are today: Limited ES2015
  4. 4 What are we trying to do? Goals Best experience

    for our users. Best experience for our developers.
  5. 5 Senior UI Engineer @ Netflix Squad lead for the

    Web Core team Travel Clever code Who? Jacques Favreau formComplete = function(){ var u = user.getUser(); var ct = 0 + (typeof(u.nickname)==='string' && u.nickname.length>0) + (typeof(u.websiteUrl)==='string' && u.websiteUrl.length>0) + (typeof(u.linkedin)==='string' && u.linkedin.length>0) + (typeof(u.locationString)==='string' && u.locationString.length>0) + (typeof(u.title)==='string' && u.title.length>0) + (typeof(u.bio)==='string' && u.bio.length>0) + (typeof(u.imageUrl)==='string' && u.imageUrl.length>0) + (typeof(u.resume)==='string' && u.resume.length>0) + (u.employerSharing && true); return Math.ceil(ct/0.09); };
  6. 6 What’s the future look like? 1.Are we stuck? 2.Rethinking

    what we ship, and why 3.ES2015 in the browser 4.Balancing UX and DX now and in the future
  7. 7 With great transpilation comes great… Hidden Cost http://enomis94.deviantart.com

  8. 8 Where we are today Heavy on DX Transpiled to

    ES5 Polyfill ES2015 Modern Transpiled to ES5 Polyfill ES2015 Polyfill ES5 Legacy No Support Unsupported
  9. 9 Where we are today Heavy on DX

  10. 10 Where we are today Heavy on DX Transpiled to

    ES5 Polyfill ES2015 Modern Transpiled to ES5 Polyfill ES2015 Polyfill ES5 Legacy No Support Unsupported
  11. 11 Ok, so let’s go the other way

  12. 12 We could go the other way Heavy on UX

    ES3 Supported No Support Unsupported ES5 ES5 Polyfill
  13. 13 We could go the other way Heavy on UX

  14. 14 How can we do better?

  15. 15 Things change 1.Modern browsers speak ES2015 2.Not all browsers

    are modern browsers http://sanlaris.deviantart.com
  16. 16 Things change Is getting ES2015 into the browser a

    good thing for our users?
  17. 17 Things change 20 to 40% over-the-wire
 payload reduction!

  18. 18 Things change Browsers have varying support.

  19. 19 Haven’t we done this? Shoulders of Giants! We have

    a long history of polyfilling in the browser
  20. 20 ES5 Code Modern ES5 Code Polyfill ES5 Shim/Shams Legacy

    No Support Unsupported Flatten the earth: Polyfilling
  21. 21 • Write expecting features • Trade some performance for

    legacy browser support Flatten the earth: Polyfilling
  22. 22 <script src="//polyfill.io/v2/polyfill.js?features=es6,es5&flags=gated"></script> Polyfills: Ship only what you need Flatten

    the earth: Polyfilling
  23. 23 What if we just shipped everything? I do not

    like the cone of shame I do not like the cone of shame
  24. 24 Why is transpilation different? (It’s not.)

  25. 25 Best experience for our users. Best experience for our

    developers. Goals
  26. 26 So here it comes…

  27. 27 Netflix will start shipping native ES2015 to eligible browsers.

  28. 28 How?

  29. 29 Our future: Progressive Transpilation ES2015 Modern Legacy No Support

    Unsupported Transpiled to ES5 Polyfill ES2015 Trailing Transpiled to ES5 Polyfill ES2015 Polyfill ES5 Shims
  30. 30 Our future: Progressive Transpilation Best experience for our users.

    Best experience for our developers.
  31. 31 Sidebar: Look for other win opportunities ES2015 Modern Legacy

    No Support Unsupported Transpiled to ES5 Polyfill ES2015 Polyfill ES5 Trailing Transpiled to ES5 Polyfill ES2015 Polyfill ES5 Shims
  32. 32 ES2015 Modern Legacy No Support Unsupported Transpiled to ES5

    Polyfill ES2015 Polyfill ES5 Trailing Fallback Sidebar: We ship fallback to legacy browsers
  33. 33 Our future: Progressive Transpilation Best experience for our users.

    Best experience for our developers.
  34. 34 Let’s do it!

  35. 35 On Let’s do this! Well… There are a few

    hurdles.
  36. 36 On Let’s do this! 1. Build systems lack support

    2. Minifiers don’t work 3. Targeting browsers is hard 4. Browsers are broken and lie
  37. 37 Issue #1: Build systems Issue #1: Build systems lack

    ES2015 support
  38. 38 Issue #1: Build systems Issue #1: Build systems lack

    ES2015 support
  39. 39 Issue #1: Build systems Issue #1: Build systems lack

    ES2015 support
  40. 40 Issue #1: Build systems Solution #1: There are some

    build systems that do support ES2015 as a target!
  41. 41 const babelConfig = { modern: { presets: [] },

    trailing: { presets: ['es2015'] } }; return gulp.src(glob) // Run babel with the modern config .pipe(babel(babelConfig.modern)) // Save the modern JS version. .pipe(gulp.dest('generated/modern/')) // Run Babel with trailing config. .pipe(babel(babelConfig.trailing)) // Save the trailing JS version. .pipe(gulp.dest('generated/trailing/')); Just don’t use the ES2015 preset. Solutions to issue #1: Workflows that work
  42. 42 { "compilerOptions": { "module": "amd", "target": "es6" } }

    ES2015 supported as a target. Solutions to issue #1: Workflows that work
  43. 43 Issue #2: Minfiers Issue #2: Minifiers don’t work

  44. 44 Issue #2: Minifiers don’t work Issue #2: Minfiers

  45. 45 Issue #2: Minfiers …but they’re working on it!

  46. 46 Solutions to Issue #2: Bable as a minifier While

    we wait… babel-preset-minify-es2015
  47. 47 It’s still a win! Solutions to Issue #2: Bable

    as a minifier
  48. 48 Issue #3: Who gets what? Issue #3: Targeting browsers

    is hard
  49. 49 We unabashedly use the User Agent string “[A]n ever-growing

    pack of lies[…]“ - Patrick H. Lauke Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136 Solution to Issue #3: Our old friend, UA
  50. 50 On first load, it’s all we have ¯\_(ϑ)_/¯ Solution

    to Issue #3: Our old friend, UA
  51. 51 Select browsers with ES2015 support. kangax.github.io/compat-table/es6/ At this time,

    our ES2015 browsers are: - Chrome 51 - Firefox 49 - Edge 14 - Safari 10 Solution to Issue #3: Our old friend, UA
  52. 52 Issue #4: Browsers lie Issue #4: Browsers can and

    do lie.
  53. 53 Solution to issue #4: Test for lying browsers Execute

    a bit of Javascript and test for lies.
  54. 54 ✓ unicode ✓ proxies ✓ symbols ✓ subclassable built-ins

    ✓ promises ✓ math + number + string APIs ✓ array + object APIs ✓ binary and octal literals Solution to issue #4: Test for lying browsers ✓ arrows ✓ classes ✓ enhanced object literals ✓ template strings ✓ destructuring ✓ default + rest + spread ✓ let + const ✓ iterators + for..of ✓ generators
  55. 55 class ಠ_ಠ extends Array{ constructor(j = 'a', ...c) {

    const q = (({u: e}) => { return { [`s${c}`]: Symbol(j) }; })({}); super(j, q, ...c); } } new Promise((f) => { const a = function* (){ return "\u{20BB7}".match(/./u)[0].length === 2 || true; }; for(let vre of a()){ const [uw, as, he, re] = [new Set(), new WeakSet(), new Map(), new WeakMap()]; break; } f(new Proxy({}, {get: (han, h) => h in han ? han[h] : "42".repeat(0o10)})); }).then(bi => new ಠ_ಠ(bi.rd)); Solution to issue #4: Test for lying browsers Execute a bit of Javascript and test for lies.
  56. 56 eval( 'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{[' + '`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const

    a=function' + '*(){return"\u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' + 'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' + 'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));' ); Execute a bit of Javascript and test for lies. Solution to issue #4: Test for lying browsers
  57. 57 try { eval( 'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{[' +

    '`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const a=function' + '*(){return"\u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' + 'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' + 'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));' ); } catch (e) { } Execute a bit of Javascript and test for lies. Solution to issue #4: Test for lying browsers
  58. 58 try { eval( 'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{[' +

    '`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const a=function' + '*(){return"\u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' + 'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' + 'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));' ); } catch (e) { document.cookie = 'esSupportLevel=5; expires=' + (new Date((new Date()).getTime() + 2678400000)).toGMTString() + '; path=/ ;domain=netflix.com'; if(location.reload){location.reload(true);}else{location.href = location.href;} } Execute a bit of Javascript and test for lies. Solution to issue #4: Test for lying browsers
  59. 59 <script> try { eval( 'class ಠ_ಠ extends Array{constructor(j="a",...c){const q=(({u:e})=>{return{['

    + '`s${c}`]:Symbol(j)}})({});super(j,q,...c)}}new Promise(f=>{const a=function' + '*(){return"\u{20BB7}".match(/./u)[0].length===2||true};for(let vre of a()){' + 'const[uw,as,he,re]=[new Set,new WeakSet,new Map,new WeakMap];break} f(new ' + 'Proxy({},{get:(han,h)=>h in han?han[h]:"42".repeat(8)}))}).then(bi=>new ಠ_ಠ(bi.rd));' ); } catch (e) { document.cookie = 'esSupportLevel=5; expires=' + (new Date((new Date()).getTime() + 2678400000)).toGMTString() + '; path=/ ;domain=netflix.com'; if(location.reload){location.reload(true);}else{location.href = location.href;} } </script> Execute a bit of Javascript and test for lies. Solution to issue #4: Test for lying browsers
  60. 60 What we’re doing (for now) 1. Build Babel as

    a transpiler. 2. Minifiers Babel plugins > babel-minify 3. Targeting User Agent to target supporting browsers 4. Lies Evaluated ES2015 to test; cookie fallback
  61. 61 Our future: Progressive Transpilation ES2015 Modern Legacy No Support

    Unsupported Transpiled to ES5 Polyfill ES2015 Trailing Fallback
  62. 62 Mission accomplished Goals balanced ⚖ *For now * Best

    experience for our users. Best experience for our developers.
  63. 63 Jem Young @jemyoung Jacques Favreau @betaorbust Questions? Come ask

    us!