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

Enforcing coding standards in a JS project

Enforcing coding standards in a JS project

Talk given at JSConf Budapest (Budapest, Hungary) - May 2015

Sebastiano Armeli

May 14, 2015
Tweet

More Decks by Sebastiano Armeli

Other Decks in Programming

Transcript

  1. ENFORCING 

    CODING 

    STANDARDS 

    in a JS PROJECT

    Sebastiano Armeli
    @sebarmeli 14/05/2015 - JSConf BD

    View Slide

  2. to enforce
    verb (used with object), enforced, enforcing.
    to put or keep in force; to compel obedience to:

    “to enforce a rule; Traffic laws will be strictly
    enforced.”

    View Slide

  3. View Slide

  4. View Slide

  5. standard
    noun
    a rule or principle that is used as a basis for judgment:

    “They tried to establish standards for a new approach.”

    View Slide

  6. View Slide

  7. commit 111111
    Author: Sebastiano Armeli
    Date: Sun Dec 21 22:08:00
    2014 -0500
    adding something
    commit 2222222
    Author: Sebastiano Armeli
    Date: Thu Dec 18 15:35:39
    2014 -0500
    it will work, trust me

    View Slide

  8. myProject
    |
    |— module1.js
    |— module2.js
    |— module_3.js
    |— module_4.js
    |— module5.js
    |— package.json

    View Slide

  9. View Slide

  10. defined by your team
    Automate

    View Slide

  11. http://facilitationjapan.com/wp-content/uploads/2013/09/consensus_building.jpg

    View Slide

  12. AD LIBRARY

    View Slide

  13. View Slide

  14. View Slide

  15. ! IDE (Editorconfig)
    ! Quality & Style tools (JSHint, JSCS, ESLint)
    ! Git commits standards
    ! Build tools (Grunt, Gulp)
    ! Language - Transpiler (ES6 - Babel)
    ! Complexity tool (Plato)
    Summary 1/2

    View Slide

  16. ! Testing (Mocha, Karma)
    ! Automated Release Flow (Jenkins, NPM)
    ! Setup script
    ! Documentation
    Summary 2/2

    View Slide

  17. View Slide

  18. root = true
    [*]
    indent_style = space
    indent_size = 2
    end_of_line = lf
    charset = utf-8
    trim_trailing_whitespace = true
    insert_final_newline = true
    .editorconfig

    View Slide

  19. Coding Style
    & Quality Tools

    View Slide

  20. Prevent bugs
    Improve code maintainability 

    & readability
    Easy to use

    View Slide

  21. function increment(a) {
    return
    a + 1;
    }
    increment(1); // undefined
    BUG!!

    View Slide

  22. var x = y = z = "example";
    Leaking Variables!!

    View Slide

  23. View Slide

  24. {
    "curly": true,
    "eqeqeq": false,
    "latedef": true,
    "newcap": true,
    "noarg": true,
    "sub": true,
    "boss": true,
    "indent": 2,
    "noempty": true,
    "expr": true,
    "eqnull": true,
    "esnext": true,
    "browser": true,
    "white": true,
    "undef": true,
    "predef": [ “require”, "module", “exports", "CustomEvent"]
    }
    .jshintrc

    View Slide

  25. {
    "curly": true,
    "eqeqeq": false,
    "latedef": true,
    "newcap": true,
    "noarg": true,
    "sub": true,
    "boss": true,
    "indent": 2,
    "noempty": true,
    "expr": true,
    "eqnull": true,
    "esnext": true,
    "browser": true,
    "white": true,
    "undef": true,
    "predef": [ “require”, "module", “exports", "CustomEvent"]
    }
    .jshintrc

    View Slide

  26. {
    "curly": true,
    "eqeqeq": false,
    "latedef": true,
    "newcap": true,
    "noarg": true,
    "sub": true,
    "boss": true,
    "indent": 2,
    "noempty": true,
    "expr": true,
    "eqnull": true,
    "esnext": true,
    "browser": true,
    "white": true,
    "undef": true,
    "predef": [ “require”, "module", “exports", "CustomEvent"]
    }
    .jshintrc

    View Slide

  27. JSCS

    View Slide

  28. {
    "requireSpaceAfterKeywords": ["if", "else",
    "for", "while", "do", "switch", "return", "try"],
    "disallowImplicitTypeConversion": ["string"],
    "disallowMultipleLineBreaks": true,
    "disallowMixedSpacesAndTabs": true,
    "disallowKeywords": ["with"],
    "disallowMultipleVarDecl": true,
    "disallowTrailingComma": true,
    "disallowTrailingWhitespace": true,
    "maximumLineLength": 80,
    "esnext": true
    }
    .jscsrc

    View Slide

  29. {
    "requireSpaceAfterKeywords": ["if", "else",
    "for", "while", "do", "switch", "return", "try"],
    "disallowImplicitTypeConversion": ["string"],
    "disallowMultipleLineBreaks": true,
    "disallowMixedSpacesAndTabs": true,
    "disallowKeywords": ["with"],
    "disallowMultipleVarDecl": true,
    "disallowTrailingComma": true,
    "disallowTrailingWhitespace": true,
    "maximumLineLength": 80,
    "esnext": true
    }
    .jscsrc

    View Slide

  30. {
    "requireSpaceAfterKeywords": ["if", "else",
    "for", "while", "do", "switch", "return", "try"],
    "disallowImplicitTypeConversion": ["string"],
    "disallowMultipleLineBreaks": true,
    "disallowMixedSpacesAndTabs": true,
    "disallowKeywords": ["with"],
    "disallowMultipleVarDecl": true,
    "disallowTrailingComma": true,
    "disallowTrailingWhitespace": true,
    "maximumLineLength": 80,
    "esnext": true
    }
    .jscsrc

    View Slide

  31. View Slide

  32. ---
    parser: babel-eslint
    env:
    browser: true
    node: true
    mocha: true
    es6: true
    .eslintrc

    View Slide

  33. rules:
    space-before-blocks: 2
    eqeqeq: [2, 'smart']
    curly: [2, 'multi-line']
    quotes: [2, 'single']
    space-after-keywords: 2
    no-unused-vars: [2, args: none]
    no-comma-dangle: 2
    no-unused-expressions: 0
    no-multi-spaces: 2

    View Slide

  34. rules:
    space-before-blocks: 2
    eqeqeq: [2, 'smart']
    curly: [2, 'multi-line']
    quotes: [2, 'single']
    space-after-keywords: 2
    no-unused-vars: [2, args: none]
    no-comma-dangle: 2
    no-unused-expressions: 0
    no-multi-spaces: 2

    View Slide

  35. no-unused-vars: [2, args: none]
    function test1(a, b) {
    var c, d = 2;
    return a + d;
    }
    test1(1, 2);
    Error!!
    function test2(a, b, c) {
    return a + b;
    }
    test2(1, 2);
    Ok

    View Slide

  36. no-trailing-spaces: 2
    no-mixed-spaces-and-tabs: 2
    quotes: [2, ‘single’]
    indent: [2, 2]

    View Slide

  37. "use strict”;
    module.exports = function(context) {
    return {
    "NewExpression": function(node) {
    if (node.callee.name === "Object") {
    context.report(node, “Error …”);
    }
    }
    };
    };
    no-new-object.js
    let obj = new Object(); let obj = {};

    View Slide

  38. "use strict”;
    module.exports = function(context) {
    return {
    "NewExpression": function(node) {
    if (node.callee.name === "Object") {
    context.report(node, “Error …”);
    }
    }
    };
    };
    no-new-object.js
    let obj = {};

    View Slide

  39. View Slide

  40. Git Commits
    (feat | fix | docs | style | refactor | test | chore)():


    E.g.

    doc(readme): update with additional links.

    View Slide

  41. Changelog
    conventional-changelog Changelog.md
    commit 7aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Author: Sebastiano Armeli
    Date: Tue Jan 6 11:48:59 2015 -0500
    refactor(BaseAd): Removed addToStreamTime method from
    BaseAd
    commit 7bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    Author: Sebastiano Armeli
    Date: Tue Jan 6 00:04:49 2015 -0500
    style(gpt): rearrange for better readability

    View Slide

  42. CHANGELOG.md

    View Slide

  43. Build tool
    gulp test / gulp dev

    View Slide

  44. var gulp = require('gulp');
    var plugins = require(‘gulp-load-plugins')();

    gulp.task('eslint', function() {
    return gulp.src(['src/**/*.js'])
    .pipe(plugins.eslint())
    .pipe(plugins.eslint.format())
    .pipe(plugins.eslint.failOnError());
    });

    gulpfile.js

    View Slide

  45. View Slide

  46. gulp es6
    /src /dist
    ES6 ES5

    View Slide

  47. Plato
    gulp plato

    View Slide

  48. Testing

    View Slide

  49. describe('#_onContainerResume', function() {
    it('should call play when container resumes', function() {
    videoAd.views.set(mockedVideoAdMetadata.id, {
    play: function() {},
    hasBeenPlayed: true
    });
    sinon.stub(videoAd.views.get(mockedVideoAdMetadata.id), 'play');
    videoAd._onContainerResume();
    expect(videoAd.views.get(mockedVideoAdMetadata.id).play).to.have
    .been.called;
    });
    });

    View Slide

  50. module.exports = function(config) {
    config.set({
    basePath: '../',
    frameworks: ['mocha', 'fixture'],
    files: [
    ….
    ],
    browsers: ‘Chrome’,
    singleRun: false,
    preprocessors: {
    '**/*.html': ['html2js'],
    '**/*.json': ['html2js']
    },
    sauceLabs: {

    }
    });
    };
    karma.conf.js

    View Slide

  51. module.exports = function(config) {
    config.set({
    basePath: '../',
    frameworks: ['mocha', 'fixture'],
    files: [
    ….
    ],
    browsers: ‘Chrome’,
    singleRun: false,
    preprocessors: {
    '**/*.html': ['html2js'],
    '**/*.json': ['html2js']
    },
    sauceLabs: {

    }
    });
    };
    karma.conf.js

    View Slide

  52. module.exports = function(config) {
    config.set({
    basePath: '../',
    frameworks: ['mocha', 'fixture'],
    files: [
    ….
    ],
    browsers: ‘Chrome’,
    singleRun: false,
    preprocessors: {
    '**/*.html': ['html2js'],
    '**/*.json': ['html2js']
    },
    sauceLabs: {

    }
    });
    };
    karma.conf.js

    View Slide

  53. Automated Release flow
    gulp test:ci gulp bump:path
    gulp bump:minor
    gulp bump:major
    Changelog.md

    View Slide

  54. ./setup.sh
    pre-commit hook
    +
    npm i && gulp test

    View Slide

  55. Documentation
    README.md
    CONTRIBUTING.md

    View Slide

  56. Documentation
    README.md
    CONTRIBUTING.md

    View Slide

  57. /doc

    View Slide

  58. Sebastiano Armeli <…@spotify.com>
    Brice Lin <[email protected]> <…@gmail.com>
    Jason Palmer <[email protected]> <[email protected]>
    Joseph Werle <[email protected]> <…@gmail.com>
    Olof Kihlberg <…@spotify.com>
    Sigfrido Chirinos <…@spotify.com>
    .mailmap

    View Slide

  59. View Slide

  60. Sebastiano Armeli
    @sebarmeli

    View Slide