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

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

Jacques Favreau

October 25, 2016
Tweet

Other Decks in Programming

Transcript

  1. 1
    The Road to
    ES2015 in
    Production
    Jacques Favreau

    View Slide

  2. 2
    What time is it?
    Doom time!

    View Slide

  3. 3
    • Const & Let
    • Arrow Functions
    • Destructuring
    • Backtick/Template Strings
    • Default Parameters

    Where we are today:
    Limited ES2015

    View Slide

  4. 4
    What are we trying to do?
    Goals
    Best experience for our users.
    Best experience for our developers.

    View Slide

  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);
    };

    View Slide

  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

    View Slide

  7. 7
    With great transpilation comes great…
    Hidden Cost
    http://enomis94.deviantart.com

    View Slide

  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

    View Slide

  9. 9
    Where we are today
    Heavy on DX










    View Slide

  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

    View Slide

  11. 11
    Ok, so let’s go the other way

    View Slide

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

    View Slide

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





    View Slide

  14. 14
    How can we do better?

    View Slide

  15. 15 Things change
    1.Modern browsers speak
    ES2015
    2.Not all browsers are
    modern browsers
    http://sanlaris.deviantart.com

    View Slide

  16. 16 Things change
    Is getting ES2015 into the browser a
    good thing for our users?

    View Slide

  17. 17 Things change
    20 to 40% over-the-wire

    payload reduction!

    View Slide

  18. 18 Things change
    Browsers have varying support.

    View Slide

  19. 19
    Haven’t we done this?
    Shoulders of Giants!
    We have a long history of
    polyfilling in the browser

    View Slide

  20. 20
    ES5 Code
    Modern
    ES5 Code
    Polyfill ES5
    Shim/Shams
    Legacy
    No
    Support
    Unsupported
    Flatten the earth:
    Polyfilling

    View Slide

  21. 21
    • Write expecting features
    • Trade some performance for
    legacy browser support
    Flatten the earth:
    Polyfilling

    View Slide

  22. 22

    Polyfills:
    Ship only what you need
    Flatten the earth:
    Polyfilling

    View Slide

  23. 23
    What if we just shipped everything?

    I do
    not like
    the cone of shame
    I do
    not like
    the cone of shame

    View Slide

  24. 24
    Why is transpilation different?
    (It’s not.)

    View Slide

  25. 25
    Best experience for our users.
    Best experience for our developers.
    Goals

    View Slide

  26. 26
    So here it comes…

    View Slide

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

    View Slide

  28. 28
    How?

    View Slide

  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

    View Slide

  30. 30
    Our future:
    Progressive Transpilation
    Best experience for our users.
    Best experience for our developers.

    View Slide

  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

    View Slide

  32. 32
    ES2015
    Modern
    Legacy
    No
    Support
    Unsupported
    Transpiled to ES5
    Polyfill ES2015
    Polyfill ES5
    Trailing
    Fallback
    Sidebar:
    We ship fallback to legacy browsers

    View Slide

  33. 33
    Our future:
    Progressive Transpilation
    Best experience for our users.
    Best experience for our developers.

    View Slide

  34. 34
    Let’s do it!

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  39. 39
    Issue #1:
    Build systems

    Issue #1:
    Build systems lack ES2015 support

    View Slide

  40. 40
    Issue #1:
    Build systems
    Solution #1:
    There are some build systems that do
    support ES2015 as a target!

    View Slide

  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

    View Slide

  42. 42
    {
    "compilerOptions": {
    "module": "amd",
    "target": "es6"
    }
    }
    ES2015 supported as
    a target.
    Solutions to issue #1:
    Workflows that work

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  46. 46
    Solutions to Issue #2:
    Bable as a minifier
    While we wait…
    babel-preset-minify-es2015

    View Slide

  47. 47
    It’s still a win!
    Solutions to Issue #2:
    Bable as a minifier

    View Slide

  48. 48
    Issue #3:
    Who gets what?
    Issue #3: Targeting browsers is hard

    View Slide

  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

    View Slide

  50. 50
    On first load, it’s all we have
    ¯\_(ϑ)_/¯
    Solution to Issue #3:
    Our old friend, UA

    View Slide

  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

    View Slide

  52. 52
    Issue #4:
    Browsers lie
    Issue #4: Browsers can and do lie.

    View Slide

  53. 53
    Solution to issue #4:
    Test for lying browsers
    Execute a bit of Javascript
    and test for lies.

    View Slide

  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

    View Slide

  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.

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  61. 61
    Our future:
    Progressive Transpilation
    ES2015
    Modern
    Legacy
    No
    Support
    Unsupported
    Transpiled to ES5
    Polyfill ES2015
    Trailing
    Fallback

    View Slide

  62. 62
    Mission accomplished
    Goals balanced ⚖
    *For now
    *
    Best experience for our users.
    Best experience for our developers.

    View Slide

  63. 63
    Jem Young
    @jemyoung
    Jacques Favreau
    @betaorbust
    Questions?
    Come ask us!

    View Slide