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

80d92d5d0bd0170aebf6e07589a0b571?s=128

Bilal Çınarlı

June 20, 2018
Tweet

Transcript

  1. Future Proof Frontend Coding De-couple Your Dependencies

  2. Bilal Çınarlı Frontend Architect Software Engineer @Adidas @bcinarli github.com/bcinarli bcinarli.com

  3. None
  4. Frontend Development

  5. Some think, it is an art Some think, it is

    the biggest pain
  6. However, everybody agrees on the constant change in our landscapes

  7. None
  8. None
  9. None
  10. Keys to keep up with the change; Organization, Standardisation, De-coupling

  11. Organization

  12. Define your folder structure based on their functionalities

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

    cases, when you remove the folder, you should expect no traces left.
  14. // 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
  15. …and tests are a part of your components.

  16. // add test specs . ├── cart | ├── index.js

    | ├── test.spec.js | └── template.pug └── checkout ├── index.js ├── test.spec.js └── template.pug
  17. Separate config and scripts away from your components.

  18. . ├── 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
  19. Standardisation

  20. Standards help to keep your code in order.

  21. None
  22. Test different coding standards, pick the best according to your

    needs.
  23. Idiomatic.js

  24. Use linters to have a shared, automatically controlled coding style

    across the project.
  25. None
  26. Use babel to get the benefits of modern coding

  27. [1, 2, 3].map(n => n ** 2); [1, 2, 3].map(function

    (n) { return Math.pow(n, 2); });
  28. Clean Coding reduces your technical debt

  29. Cost of Chance (CoC) Time Actual CoC Responsiveness to Change

    Optimal Responsiveness Optimal CoC Responsiveness Technical
 Debt
  30. Follow a naming convention, be concise, stay DRY, and constantly

    refactor your code
  31. De-Coupling

  32. JavaScript projects have and average of 650 to 1000 dependent

    modules
  33. None
  34. In each method you add to your app, keep in

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

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

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

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

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

    join('-'), trace('Joined String'), encodeURIComponent );
  40. Rule of thumb: Separate your code from your dependent codes.

  41. Create wrappers for third-party codes that you are going to

    use
  42. 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 };
  43. 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 };
  44. Proxy can be used for between your code and your

    dependency code for having a standardised set of methods.
  45. Proxies are middleware for JavaScript objects

  46. 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
  47. 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); }; } } );
  48. 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); }; } } );
  49. 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); }; } } );
  50. 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); }; } } );
  51. // 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();
  52. What About HTML & CSS?

  53. Keep HTML and CSS separate in your source code 


    and never inline manually
  54. // 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>
  55. Always think about specificity in CSS, try to avoid creating

    specific selectors
  56. // DON’T #main .article .title span { font-size: 32px; font-weight:

    bold; } // DO .main-article-title { font-size: 32px; font-weight: bold; }
  57. Do not write the code you are going to overwrite

  58. // 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; } }
  59. Use Postcss, Autoprefixer, and Variables that you can update your

    support by compiling
  60. // 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; }
  61. Your app should not know what the dependencies are in

    behind the curtains you are using.
  62. It should only aware of which functions are available for

    a particular action.
  63. Every dependency comes with a technical debt for the future.

  64. Thank you @bcinarli