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. 3.

    3 • Const & Let • Arrow Functions • Destructuring

    • Backtick/Template Strings • Default Parameters Where we are today: Limited ES2015
  2. 4.

    4 What are we trying to do? Goals Best experience

    for our users. Best experience for our developers.
  3. 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); };
  4. 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
  5. 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
  6. 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
  7. 12.

    12 We could go the other way Heavy on UX

    ES3 Supported No Support Unsupported ES5 ES5 Polyfill
  8. 15.

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

    are modern browsers http://sanlaris.deviantart.com
  9. 19.

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

    a long history of polyfilling in the browser
  10. 20.

    20 ES5 Code Modern ES5 Code Polyfill ES5 Shim/Shams Legacy

    No Support Unsupported Flatten the earth: Polyfilling
  11. 21.

    21 • Write expecting features • Trade some performance for

    legacy browser support Flatten the earth: Polyfilling
  12. 23.

    23 What if we just shipped everything? I do not

    like the cone of shame I do not like the cone of shame
  13. 28.
  14. 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
  15. 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
  16. 32.

    32 ES2015 Modern Legacy No Support Unsupported Transpiled to ES5

    Polyfill ES2015 Polyfill ES5 Trailing Fallback Sidebar: We ship fallback to legacy browsers
  17. 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
  18. 40.

    40 Issue #1: Build systems Solution #1: There are some

    build systems that do support ES2015 as a target!
  19. 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
  20. 42.

    42 { "compilerOptions": { "module": "amd", "target": "es6" } }

    ES2015 supported as a target. Solutions to issue #1: Workflows that work
  21. 46.

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

    we wait… babel-preset-minify-es2015
  22. 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
  23. 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
  24. 53.

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

    a bit of Javascript and test for lies.
  25. 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
  26. 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.
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 61.

    61 Our future: Progressive Transpilation ES2015 Modern Legacy No Support

    Unsupported Transpiled to ES5 Polyfill ES2015 Trailing Fallback
  33. 62.

    62 Mission accomplished Goals balanced ⚖ *For now * Best

    experience for our users. Best experience for our developers.