$30 off During Our Annual Pro Sale. View Details »

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. Future Proof Frontend Coding
    De-couple Your Dependencies

    View Slide

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

    View Slide

  3. View Slide

  4. Frontend Development

    View Slide

  5. Some think, it is an art
    Some think, it is the biggest pain

    View Slide

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

    View Slide

  7. View Slide

  8. View Slide

  9. View Slide

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

    View Slide

  11. Organization

    View Slide

  12. Define your folder structure
    based on their functionalities

    View Slide

  13. Think of everything is sort-of a
    pluggable component.
    In most cases, when you remove the folder,
    you should expect no traces left.

    View Slide

  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

    View Slide

  15. …and tests are a part of your components.

    View Slide

  16. // add test specs
    .
    ├── cart
    | ├── index.js
    | ├── test.spec.js
    | └── template.pug
    └── checkout
    ├── index.js
    ├── test.spec.js
    └── template.pug

    View Slide

  17. Separate config and scripts
    away from your components.

    View Slide

  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

    View Slide

  19. Standardisation

    View Slide

  20. Standards help to keep
    your code in order.

    View Slide

  21. View Slide

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

    View Slide

  23. Idiomatic.js

    View Slide

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

    View Slide

  25. View Slide

  26. Use babel to get the benefits
    of modern coding

    View Slide

  27. [1, 2, 3].map(n => n ** 2);
    [1, 2, 3].map(function (n) {
    return Math.pow(n, 2);
    });

    View Slide

  28. Clean Coding reduces your
    technical debt

    View Slide

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

    Debt

    View Slide

  30. Follow a naming convention,
    be concise, stay DRY,
    and constantly refactor your code

    View Slide

  31. De-Coupling

    View Slide

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

    View Slide

  33. View Slide

  34. In each method you add to your app,
    keep in your mind if it is reusable?

    View Slide

  35. Pipe method is a way to have an abstraction
    with different operations in a function

    View Slide

  36. const toSlug = input => {
    let str = input.toLowerCase();
    str = str.split(' ').join('-');
    str = encodeURIComponent(str);
    return str;
    };

    View Slide

  37. const toSlug = input => encodeURIComponent(
    join('-')(
    map(toLowerCase)(
    split(' ')(
    input
    )
    )
    )
    );
    toSlug('Hello World’);
    // hello-world

    View Slide

  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

    View Slide

  39. const toSlug = pipe(
    trace(input),
    split(' '),
    map(toLowerCase),
    trace('Mapped String'),
    join('-'),
    trace('Joined String'),
    encodeURIComponent
    );

    View Slide

  40. Rule of thumb:
    Separate your code
    from your dependent codes.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  44. Proxy can be used for between
    your code and your dependency code
    for having a standardised set of methods.

    View Slide

  45. Proxies are middleware
    for JavaScript objects

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. What About HTML & CSS?

    View Slide

  53. Keep HTML and CSS separate
    in your source code 

    and never inline manually

    View Slide

  54. // DON’T
    Hello World!
    // DO
    .content-text {
    margin: 10px 0;
    padding: 0;
    }
    Hello
    World!

    View Slide

  55. Always think about specificity in CSS,
    try to avoid creating specific selectors

    View Slide

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

    View Slide

  57. Do not write the code
    you are going to overwrite

    View Slide

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

    View Slide

  59. Use Postcss, Autoprefixer, and Variables
    that you can update your support by compiling

    View Slide

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

    View Slide

  61. Your app should not know
    what the dependencies are
    in behind the curtains you are using.

    View Slide

  62. It should only aware of which
    functions are available for a particular action.

    View Slide

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

    View Slide

  64. Thank you
    @bcinarli

    View Slide