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

Future Proof Frontend Coding

Future Proof Frontend Coding

Almost every six months we have new frameworks or approaches in the front-end world. Sometimes your project’s codebase becomes outdated before it is in production. These rapid changes are tiring and frustrating for developers time and time again.

This talk helps to visualise how to set up your codebase for changes with wrapping your dependencies in a standardised way.

Your internal modules are never aware of your external dependencies and do not even figure out if the dependency has changed. With standardising your wrappers, you can update or change your external dependencies safely at any time.

* pipe method example from https://medium.com/javascript-scene/master-the-javascript-interview-what-is-function-composition-20dfb109a1a0
* Proxy examples from https://medium.com/dailyjs/how-to-use-javascript-proxies-for-fun-and-profit-365579d4a9f8

Bilal Çınarlı

June 20, 2018
Tweet

More Decks by Bilal Çınarlı

Other Decks in Technology

Transcript

  1. Think of everything is sort-of a pluggable component. In most

    cases, when you remove the folder, you should expect no traces left.
  2. // DON’T . ├── controllers | ├── cart.js | └──

    checkout.js ├── models | ├── cart.js | └── checkout.js └── views ├── cart.pug └── checkout.pug // DO . ├── cart | ├── index.js | └── template.pug └── checkout ├── index.js └── template.pug
  3. // add test specs . ├── cart | ├── index.js

    | ├── test.spec.js | └── template.pug └── checkout ├── index.js ├── test.spec.js └── template.pug
  4. . ├── config | ├── index.js | └── server.js ├──

    scripts | ├── build.sh | └── post-install.sh ├── test | ├── index.js | └── setup.spec.js ├── cart | ├── index.js | ├── test.spec.js | └── template.pug └── checkout ├── index.js ├── test.spec.js └── template.pug
  5. [1, 2, 3].map(n => n ** 2); [1, 2, 3].map(function

    (n) { return Math.pow(n, 2); });
  6. Cost of Chance (CoC) Time Actual CoC Responsiveness to Change

    Optimal Responsiveness Optimal CoC Responsiveness Technical
 Debt
  7. In each method you add to your app, keep in

    your mind if it is reusable?
  8. Pipe method is a way to have an abstraction with

    different operations in a function
  9. const toSlug = input => { let str = input.toLowerCase();

    str = str.split(' ').join('-'); str = encodeURIComponent(str); return str; };
  10. const toSlug = input => encodeURIComponent( join('-')( map(toLowerCase)( split(' ')(

    input ) ) ) ); toSlug('Hello World’); // hello-world
  11. const pipe = (...fns) => x => fns.reduce((v, f) =>

    f(v), x); const toSlug = pipe( split(' '), map(toLowerCase), join('-'), encodeURIComponent ); toSlug('Hello World’); // hello-world
  12. const toSlug = pipe( trace(input), split(' '), map(toLowerCase), trace('Mapped String'),

    join('-'), trace('Joined String'), encodeURIComponent );
  13. const {logs: showLogs} = require('./config'); const chalk = require('chalk'); const

    log = (...args) => { if(showLogs === 'none') { return; } console.log(...args); }; const info = (...message) => { log(chalk.blue('info'), ...message); }; const success = (...message) => { log('', chalk.green('success'), ...message); }; const error = (...message) => { log(chalk.red('error'), ...message); }; const warn = (...message) => { log(chalk.yellow('notice'), ...message); }; module.exports = { log, info, success, error, warn };
  14. const depend = require('depended-module'); const getResource = url => {

    // your logic // depended code let fetched = depend.get(url, options); // some more logic return data; }; const putData = (url, data) => { // your logic // depended code let fetched = depend.put(url, dataToStore); // some more logic return result; }; module.exports = { getResource, putData };
  15. Proxy can be used for between your code and your

    dependency code for having a standardised set of methods.
  16. const wrap = obj => { return new Proxy(obj, {

    get(target, propKey) { console.log(`Reading property "${propKey}"`); return target[propKey]; } }); }; const object = {message: 'hello world'}; const wrapped = wrap(object); console.log(wrapped.message); // Reading property "message" // hello world
  17. const {METHODS} = require('http'); const api = new Proxy({}, {

    get(target, propKey) { const method = METHODS.find(method => propKey.startsWith(method.toLowerCase())); if (!method) { return; } const path = '/' + propKey .substring(method.length) .replace(/([a-z])([A-Z])/g, '$1/$2') .replace(/\$/g, '/$/') .toLowerCase(); return (...args) => { const finalPath = path.replace(/\$/g, () => args.shift()); const queryOrBody = args.shift() || {}; // You could use fetch here // return fetch(finalPath, { method, body: queryOrBody }) info(method, finalPath, queryOrBody); }; } } );
  18. const {METHODS} = require('http'); const api = new Proxy({}, {

    get(target, propKey) { const method = METHODS.find(method => propKey.startsWith(method.toLowerCase())); if (!method) { return; } const path = '/' + propKey .substring(method.length) .replace(/([a-z])([A-Z])/g, '$1/$2') .replace(/\$/g, '/$/') .toLowerCase(); return (...args) => { const finalPath = path.replace(/\$/g, () => args.shift()); const queryOrBody = args.shift() || {}; // You could use fetch here // return fetch(finalPath, { method, body: queryOrBody }) info(method, finalPath, queryOrBody); }; } } );
  19. const {METHODS} = require('http'); const api = new Proxy({}, {

    get(target, propKey) { const method = METHODS.find(method => propKey.startsWith(method.toLowerCase())); if (!method) { return; } const path = '/' + propKey .substring(method.length) .replace(/([a-z])([A-Z])/g, '$1/$2') .replace(/\$/g, '/$/') .toLowerCase(); return (...args) => { const finalPath = path.replace(/\$/g, () => args.shift()); const queryOrBody = args.shift() || {}; // You could use fetch here // return fetch(finalPath, { method, body: queryOrBody }) info(method, finalPath, queryOrBody); }; } } );
  20. const {METHODS} = require('http'); const api = new Proxy({}, {

    get(target, propKey) { const method = METHODS.find(method => propKey.startsWith(method.toLowerCase())); if (!method) { return; } const path = '/' + propKey .substring(method.length) .replace(/([a-z])([A-Z])/g, '$1/$2') .replace(/\$/g, '/$/') .toLowerCase(); return (...args) => { const finalPath = path.replace(/\$/g, () => args.shift()); const queryOrBody = args.shift() || {}; // You could use fetch here // return fetch(finalPath, { method, body: queryOrBody }) info(method, finalPath, queryOrBody); }; } } );
  21. // GET / api.get(); // GET /users api.getUsers(); // GET

    /users/1234/likes api.getUsers$Likes('1234'); // GET /users/1234/likes?page=2 api.getUsers$Likes('1234', { page: 2 }); // POST /items with body api.postItems({ name: 'Item name' }); // api.foobar is not a function api.foobar();
  22. // DON’T <p style="margin: 10px 0; padding: 0;">Hello World!</p> //

    DO .content-text { margin: 10px 0; padding: 0; } <p class="content-text">Hello World!</p>
  23. // DON’T #main .article .title span { font-size: 32px; font-weight:

    bold; } // DO .main-article-title { font-size: 32px; font-weight: bold; }
  24. // DON’T .content { display: flex; max-width: 1280px; margin: 0

    auto; } .article { width: 900px; } .supplementary { width: 380px; } @media screen and (min-width: 48.0625em) and (max-width: 64em) { .article { width: 644px; } } @media screen and (max-width: 48em) { .content { flex-direction: column; } .article, .supplementary { width: 100%; } } // DO .content { max-width: 1280px; margin: 0 auto; } @media screen and (min-width: 48.0625em) { .content { display: flex; } .article { flex: 1; } .supplementary { width: 380px; } }
  25. // In your CSS ::placeholder { color: gray; } //

    Output after compiling ::-webkit-input-placeholder { color: gray; } :-ms-input-placeholder { color: gray; } ::-ms-input-placeholder { color: gray; } ::placeholder { color: gray; }
  26. Your app should not know what the dependencies are in

    behind the curtains you are using.