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

Something smells IF-y (Javascript Israel)

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for yuvke yuvke
March 28, 2018
46

Something smells IF-y (Javascript Israel)

A cleaner approach to Feature Toggles (using a webpack loader)

Avatar for yuvke

yuvke

March 28, 2018
Tweet

Transcript

  1. Us

  2. toggle example setup code var treatment = client.getTreatment(userData, toggleName); if

    (treatment) { // insert code here to show on treatment } else { // insert code here to show off treatment }
  3. header.js episodesList.js header.js footer.js show.js gallery.js if (feature) { }

    else { } if (feature) { } else { } if (feature) { } else { } if (feature) { } else { }
  4. header.js episodesList.js header.js footer.js show.js gallery.js if (feature) { }

    else { } if (feature) { } else { } if (feature) { } else { } if (feature) { } else { } Unreadable
  5. header.js episodesList.js header.js footer.js show.js gallery.js if (feature) { }

    else { } if (feature) { } else { } if (feature) { } else { } if (feature) { } else { } Unreadable Unmaintainable
  6. header.js episodesList.js header.js footer.js show.js gallery.js if (feature) { }

    else { } if (feature) { } else { } if (feature) { } else { } if (feature) { } else { } Unreadable Unmaintainable Untestable
  7. New Feature == Feature Branch • Add the feature in

    a new branch image source: http://endoflineblog.com/oneflow-a-git-branching-model-and-workflow
  8. New Feature == Feature Branch • Add the feature in

    a new branch image source: http://endoflineblog.com/oneflow-a-git-branching-model-and-workflow
  9. New Feature == Feature Branch • Add the feature in

    a new branch • change gallery.js image source: http://endoflineblog.com/oneflow-a-git-branching-model-and-workflow
  10. New Feature == Feature Branch • Add the feature in

    a new branch • change gallery.js • Merge back to master / trunk image source: http://endoflineblog.com/oneflow-a-git-branching-model-and-workflow
  11. New Feature == Feature Branch • Add the feature in

    a new branch • change gallery.js • Merge back to master / trunk image source: http://endoflineblog.com/oneflow-a-git-branching-model-and-workflow
  12. New Feature == Feature Branch • Add the feature in

    a new branch • change gallery.js • Merge back to master / trunk image source: http://endoflineblog.com/oneflow-a-git-branching-model-and-workflow
  13. Our solution - Multiquire • Duplicate gallery.js to a new

    file gallery_bigThumbs.js • Make the changes you want in gallery_bigThumbs.js
  14. Our solution - Multiquire • Duplicate gallery.js to a new

    file gallery_bigThumbs.js • Make the changes you want in gallery_bigThumbs.js • load both gallery.js and gallery_bigThumbs.js to memory
  15. Our solution - Multiquire • Duplicate gallery.js to a new

    file gallery_bigThumbs.js • Make the changes you want in gallery_bigThumbs.js • load both gallery.js and gallery_bigThumbs.js to memory • In runtime use the appropriate module according to feature toggle state
  16. var gallery = require('./gallery.js'); using require() Our solution - Multiquire

    var gallery = multiquire('./gallery.js'); using multirequire() loads ‘gallery.js’ to memory
  17. var gallery = require('./gallery.js'); using require() Our solution - Multiquire

    var gallery = multiquire('./gallery.js'); using multirequire() loads ‘gallery.js’ to memory loads both ‘gallery.js’ and ‘gallery_bigThumbs.js’ to memory
  18. the “merged” gallery.js var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') };

    var activeToggle = getActiveFeatureToggle(); if (activeToggle && moduleVersions[activeToggle]) { module.exports = moduleVersions[activeToggle]; } else { module.exports = /// original gallery.js } gallery.js
  19. What we want var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') };

    var activeToggle = getActiveFeatureToggle(); if (activeToggle && moduleVersions[activeToggle]) { module.exports = moduleVersions[activeToggle]; } else { module.exports = /// original gallery.js } gallery.js
  20. What we want var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') };

    var activeToggle = getActiveFeatureToggle(); if (activeToggle && moduleVersions[activeToggle]) { module.exports = moduleVersions[activeToggle]; } else { module.exports = /// original gallery.js } gallery.js
  21. What we want var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') };

    var activeToggle = getActiveFeatureToggle(); if (activeToggle && moduleVersions[activeToggle]) { module.exports = moduleVersions[activeToggle]; } else { module.exports = /// original gallery.js } gallery.js
  22. webpack loaders 101 // the identity loader module.exports = function(source)

    { console.log(this.resourcePath); // output: '~/full/path/to/gallery.js' return source }
  23. webpack loaders 101 // the identity loader module.exports = function(source)

    { console.log(this.resourcePath); // output: '~/full/path/to/gallery.js' console.log(this.context); // output '~/full/path/to' return source }
  24. webpack loaders 101 // the identity loader module.exports = function(source)

    { console.log(this.resourcePath); // output: '~/full/path/to/gallery.js' console.log(this.context); // output: '~/full/path/to' console.log(this.options); return source }
  25. What we want var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') };

    var activeToggle = getActiveFeatureToggle(); if (activeToggle && moduleVersions[activeToggle]) { module.exports = moduleVersions[activeToggle]; } else { module.exports = /// original gallery.js } gallery.js
  26. multiquire webpack loader module.exports = function(source) { const options =

    this.options; const moduleName = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); };
  27. multiquire webpack loader module.exports = function(source) { const options =

    this.options; const moduleName = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); }; variants = [ 'bigThumbs' ]
  28. module.exports = function(source) { const options = this.options; const moduleName

    = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); if (variants.length < 1) { return source; } }; variants = [ 'bigThumbs' ] multiquire webpack loader
  29. module.exports = function(source) { const options = this.options; const moduleName

    = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); if (variants.length < 1) { return source; } let requireStr = buildVersionsObject(variants, moduleName); }; variants = [ 'bigThumbs' ] multiquire webpack loader
  30. module.exports = function(source) { const options = this.options; const moduleName

    = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); if (variants.length < 1) { return source; } let requireStr = buildVersionsObject(variants, moduleName); }; variants = [ 'bigThumbs' ] requireStr = 'var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') }’; multiquire webpack loader
  31. module.exports = function(source) { const options = this.options; const moduleName

    = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); if (variants.length < 1) { return source; } let requireStr = buildVersionsObject(variants, moduleName); }; variants = [ 'bigThumbs' ] requireStr = 'var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') }’; multiquire webpack loader What about getActiveFeatureToggles?
  32. module.exports = function(source) { const options = this.options; const moduleName

    = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); if (variants.length < 1) { return source; } let requireStr = buildVersionsObject(variants, moduleName); let toggleFuncStr = getActiveFeatureToggles.toString(); }; variants = [ 'bigThumbs' ] requireStr = 'var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') }’; multiquire webpack loader
  33. module.exports = function(source) { const options = this.options; const moduleName

    = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); if (variants.length < 1) { return source; } let requireStr = buildVersionsObject(variants, moduleName); let toggleFuncStr = getActiveFeatureToggles.toString(); return `${requireStr}${toggleFuncStr} var activeToggle = getActiveFeatureTogglesFunc(); if (activeToggle && moduleVersions[activeToggle]) { module.exports = moduleVersions[activeToggle]; } else { ${source} }`; }; variants = [ 'bigThumbs' ] requireStr = 'var moduleVersions = { bigThumbs: require('./gallery_bigThumbs.js') }’; multiquire webpack loader
  34. module.exports = function(source) { const options = this.options; const moduleName

    = path.basename(this.resourcePath, '.js'); const variants = getVariants(moduleName, options); if (variants.length < 1) { return source; } let requireStr = buildVersionsObject(variants, moduleName); let toggleFuncStr = getActiveFeatureToggles.toString(); return `${requireStr}${toggleFuncStr} var activeToggle = getActiveFeatureTogglesFunc(); if (activeToggle && moduleVersions[activeToggle]) { module.exports = moduleVersions[activeToggle]; } else { ${source} }`; }; multiquire webpack loader
  35. Summary • Feature Toggles are cool • But they can

    get messy if we’re not using abstraction layers to manage our branching • CommonJS already gives us a very good start point • Using multiquire we can create variation files and use the relevant file on runtime
  36. Future Features • Run two variants at the same time

    • Use it for other file types as well (.scss / .jsx …) • Support ‘import’ ‘export’