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

Improving JavaScript Code Quality: Strategies and Tools

Improving JavaScript Code Quality: Strategies and Tools

Presented at O'Reilly Fluent 2013, San Francisco.

The quality of JavaScript-based web applications can be improved by choosing the development strategies which minimize common mistakes, avoid API ambiguities, and reduce any syntax confusion. Applying these approaches systematically as part of the development strategies also requires the use of advanced code quality tools. This talk highlights the recent developments on language tools which aim at solving near-future JavaScript quality analysis, everything from run-time complexity profiling to framework-aware static code inspection.

Ariya Hidayat

May 30, 2013
Tweet

More Decks by Ariya Hidayat

Other Decks in Technology

Transcript

  1. Improving JavaScript
    Code Quality:
    Strategies and Tools
    Ariya Hidayat
    @ariyahidayat
    1

    View full-size slide

  2. Multi-Layer Defense
    3

    View full-size slide

  3. Defensive = Paranoid
    http://ariya.ofilabs.com/2012/12/quality-code-via-multiple-layers-of-defense.html
    4

    View full-size slide

  4. Placement of Layers
    Code Editor
    CI Server
    VCS Hooks
    Smoke Tests
    Pull Request
    Build Tasks
    5

    View full-size slide

  5. Feedback is Important
    Engineer
    Tools
    Feedback Cycle
    Boring
    Repetitive
    Time-consuming
    6

    View full-size slide

  6. Track Quality Metrics
    Application revision
    Execution time
    Baseline
    7

    View full-size slide

  7. Metrics Quadrant
    http://ariya.ofilabs.com/2012/11/optimization-journey-vs-destination.html
    8

    View full-size slide

  8. Be Reasonable
    “First Things First”, Steven Covey
    “The Big Rock”
    9

    View full-size slide

  9. Foundation
    10

    View full-size slide

  10. JavaScript in the Browser
    User Interface
    Browser Engine
    Graphics
    Stack
    Data Persistence
    Render Engine
    JavaScript
    Engine
    Networking I/O
    11

    View full-size slide

  11. JavaScript Engines
    SpiderMonkey Firefox
    JavaScriptCore/Nitro Safari
    V8 Node.js, Chrome
    JScript/Chakra Internet Explorer
    Carakan Opera
    12

    View full-size slide

  12. Engine Building Blocks
    Virtual
    Machine/
    Interpreter
    Parser
    Runtime
    Source
    Syntax Tree
    Built-in objects,
    host objects, ...
    Fast and conservative
    13

    View full-size slide

  13. Parser
    var answer = 42
    keyword equal sign
    identifier number
    Variable Declaration
    Identifier
    Literal Constant
    Tokenization →
    Tokens
    Parsing → Syntax Tree
    14

    View full-size slide

  14. Syntax Visualization http://esprima.org/demo/parse.html
    Try online!
    http://ariya.ofilabs.com/2012/04/javascript-syntax-tree-visualization-with-esprima.html
    15

    View full-size slide

  15. Execution Visualization
    http://int3.github.io/metajs/
    16

    View full-size slide

  16. Live Coding/Editing
    http://nornagon.github.com/scrubby/
    17

    View full-size slide

  17. Composition: Chains of Responsibilities
    https://speakerdeck.com/constellation/escodegen-and-esmangle-using-mozilla-javascript-ast-as-an-ir
    Parser
    Code
    Generator
    Source
    Syntax Tree
    Source
    Obvious examples: minifier, obfuscator, ...
    18

    View full-size slide

  18. Quasi-Endless Possibilities
    Static analysis
    Inspection
    Dynamic analysis
    Transformation
    19

    View full-size slide

  19. Standard, Off-the-Shelf Tools
    20

    View full-size slide

  20. Syntax Validation
    http://ariya.ofilabs.com/2012/10/javascript-validator-with-esprima.html
    http://esprima.org/demo/validate.html
    Try online!
    Also available:
    Grunt plugin, Ant task, ...
    21

    View full-size slide

  21. Strict Mode Validator
    'use strict';
    block = {
    color: 'blue',
    height: 20,
    width: 10,
    color: 'red'
    };
    Duplicate data property in object
    literal not allowed in strict mode
    http://ariya.ofilabs.com/2012/10/validating-strict-mode.html
    22

    View full-size slide

  22. Linter vs Validator
    Validator
    Linter
    Looks for specification violations
    Does not care about coding style
    Works well on generated/minified code
    Searches for suspicious pattern
    Warns on style inconsistencies
    Works well on hand-written code
    23

    View full-size slide

  23. Polluting Variables
    var height;
    // some fancy processing
    heigth = 200;
    Leaks to
    global
    test.js:3
    heigth = 200;
    ^
    LeakError: global leak
    detected: heigth
    https://github.com/kesla/node-leaky
    http://ariya.ofilabs.com/2012/11/polluting-and-unused-javascript-variables.html
    24

    View full-size slide

  24. Unused Variables
    var height;
    // some fancy processing
    heigth = 200;
    http://ariya.ofilabs.com/2012/11/polluting-and-unused-javascript-variables.html
    Declared
    but not used
    test.js
    height - on line 1
    https://github.com/Kami/node-unused
    25

    View full-size slide

  25. Protection Layers, Again
    JUnit XML + Jenkins
    files=$(git diff-index --name-only HEAD
    | grep -P '\.js$')
    for file in $files; do
    esvalidate $file
    if [ $? -eq 1 ]; then
    echo "Syntax error: $file"
    exit 1
    fi
    done
    Git Precommit Hook
    http://ariya.ofilabs.com/2012/03/git-pre-commit-hook-and-smoke-testing.html
    26

    View full-size slide

  26. Code Complexity
    if (true) "foo"; else "bar";
    ◦ Maintainability index: 139.99732896539635
    ◦ Physical LOC: 1
    ◦ Logical LOC: 4
    ◦ Aggregate cyclomatic complexity: 2
    http://jscomplexity.org/
    28

    View full-size slide

  27. Continuous Monitoring of Complexity
    http://ariya.ofilabs.com/2013/05/continuous-monitoring-of-javascript-code-complexity.html
    var cr = require('complexity-report'),
    content = require('fs').readFileSync('index.js', 'utf-8'),
    list = [];
    cr.run(content).functions.forEach(function (entry) {
    list.push({ name: entry.name, value: entry.complexity.cyclomatic });
    });
    list.sort(function (x, y) {
    return y.value - x.value;
    });
    console.log('Most cyclomatic-complex functions:');
    list.slice(0, 6).forEach(function (entry) {
    console.log(' ', entry.name, entry.value);
    });
    29

    View full-size slide

  28. Complexity Visualization with Plato
    https://ariya.ofilabs.com/2013/01/javascript-code-complexity-visualization.html
    https://github.com/jsoverson/plato
    30

    View full-size slide

  29. Editing Autocomplete
    http://ariya.ofilabs.com/2013/03/javascript-editing-with-autocomplete.html
    http://esprima.org/demo/autocomplete.html
    Try online!
    31

    View full-size slide

  30. Scope and Highlight
    http://ariya.ofilabs.com/2013/04/javascript-variable-scope-and-highlight.html
    http://esprima.org/demo/highlight.html
    Try online!
    32

    View full-size slide

  31. Scope Visualization http://mazurov.github.io/eslevels-demo/
    33

    View full-size slide

  32. Rename Refactoring Assistant
    http://ariya.ofilabs.com/2013/04/rename-refactoring-for-javascript-code.html
    http://esprima.org/demo/rename.html
    Try online!
    34

    View full-size slide

  33. Source Transformation
    Parser
    Code
    Generator
    Source
    Syntax Tree
    Source
    In-place
    Modification
    Modified Source
    Regenerative
    Non-Destructive
    35

    View full-size slide

  34. Dynamic Code Coverage via Instrumentation
    var answer = 42;
    alert(answer);
    var __cov_l$4m7m$L464yvav5F$qhNA = __coverage__['hello.js'];
    __cov_l$4m7m$L464yvav5F$qhNA.s['1']++;
    var answer = 42;
    __cov_l$4m7m$L464yvav5F$qhNA.s['2']++;
    alert(answer);
    http://gotwarlost.github.io/istanbul/
    36

    View full-size slide

  35. Statement Coverage and Latent Trap
    http://ariya.ofilabs.com/2012/09/the-hidden-trap-of-code-coverage.html
    function inc(p, q) {
    if (q == undefined) q = 1;
    return p + q/q;
    }
    assert("inc(4) must give 5", inc(4) == 5);
    function inc(p, q) {
    if (q == undefined)
    return p + 1;
    return p + q/q;
    }
    assert("inc(4) must give 5", inc(4) == 5);
    Does not catch the missing
    code sequence
    37

    View full-size slide

  36. Branch Coverage
    http://ariya.ofilabs.com/2012/12/javascript-code-coverage-with-istanbul.html
    function inc(p, q) {
    if (q == undefined) q = 1;
    return p + q/q;
    }
    assert("inc(4) must give 5", inc(4) == 5);
    E = Else is not taken
    38

    View full-size slide

  37. Hard Thresholds on Code Coverage
    http://ariya.ofilabs.com/2013/05/hard-thresholds-on-javascript-code-coverage.html
    istanbul check-coverage --statement -5 --branch -3 --function 100
    39

    View full-size slide

  38. If you think JSLint hurts your feelings,
    wait until you use Istanbul.
    @davglass
    40

    View full-size slide

  39. BYOT (Bring Your Own Tools)
    41

    View full-size slide

  40. Consistency
    Convention
    Scalability
    42

    View full-size slide

  41. String Literal Quotes
    [
    { type: "Identifier", value: "console", range: [0, 7] },
    { type: "Punctuator", value: ".", range: [7, 8] },
    { type: "Identifier", value: "log", range: [8, 11] },
    { type: "Punctuator", value: "(", range: [11, 12] },
    { type: "String", value: "\"Hello\"", range: [12, 19] },
    { type: "Punctuator", value: ")", range: [19, 19] }
    ]
    console.log('Hello')
    console.log("Hello")
    List of tokens
    http://ariya.ofilabs.com/2012/02/from-double-quotes-to-single-quotes.html
    43

    View full-size slide

  42. Style Formatter
    CodePainter
    Source
    Sample code
    Formatted
    Infer
    coding
    styles
    Indentation
    Quote for string literal
    Whitespace
    esformatter
    Source Formatted
    Style options
    Indentation
    Line breaks
    Whitespaces
    https://github.com/millermedeiros/esformatter
    44

    View full-size slide

  43. Application Structure
    MyApp.create('MyApp.Person', {
    name: 'Joe Sixpack',
    age: 42,
    constructor: function(name) {},
    walk: function(steps) {}
    run: function(steps) {}
    });
    {
    objectName: 'MyApp.Person',
    functions: ['walk', 'run'],
    properties: ['name', 'age']
    }
    Metadata
    45

    View full-size slide

  44. Custom Linting
    var fs = require('fs'),
    esprima = require('./esprima'),
    files = process.argv.splice(2);
    files.forEach(function (filename) {
    var content = fs.readFileSync(filename, 'utf-8'),
    syntax = esprima.parse(content, { loc: true });
    JSON.stringify(syntax, function (key, value) {
    if (key === 'test' && value.operator === '==')
    console.log('Line', value.loc.start.line);
    return value;
    });
    });
    if (x == 9) {
    // do Something
    }
    Not a strict equal
    46

    View full-size slide

  45. “Boolean Trap” Finder
    Can you make up your mind? treeItem.setState(true, false);
    event.initKeyEvent("keypress", true, true, null, null,
    false, false, false, false, 9, 0);
    The more the merrier?
    Obfuscated choice
    var volumeSlider = new Slider(false);
    Double-negative
    component.setHidden(false);
    filter.setCaseInsensitive(false);
    http://ariya.ofilabs.com/2012/06/detecting-boolean-traps-with-esprima.html
    47

    View full-size slide

  46. Nested Ternary Conditionals
    var str = (age < 1) ? "baby" :
    (age < 5) ? "toddler" :
    (age < 18) ? "child": "adult";
    http://ariya.ofilabs.com/2012/10/detecting-nested-ternary-conditionals.html
    48

    View full-size slide

  47. Performance: Measurement Confidence
    Accuracy
    Precision
    49

    View full-size slide

  48. Fast = Enough?
    Alice
    Bob
    Chuck
    Dan
    ...
    Bob
    Alice
    Dan
    Chuck
    ...
    Address Book Application
    Sort
    How’s the speed?
    2 ms to sort 10 contacts
    50

    View full-size slide

  49. Array.prototype.swap = function (i, j) {
    var k = this[i]; this[i] = this[j]; this[j] = k;
    }
    function sort(list) {
    var items = list.slice(0), swapped = false, p, q;
    for (p = 1; p < items.length; ++p) {
    for (q = 0; q < items.length - p; ++q) {
    if (items[q + 1] < items[q]) {
    items.swap(q, q + 1);
    swapped =true;
    }
    }
    if (!swapped) break;
    }
    return items;
    }
    Bubble Sort ???
    51

    View full-size slide

  50. Empirical Run-time Complexity
    Array.prototype.swap = function (i, j) {
    var k = this[i]; this[i] = this[j]; this[j] = k;
    }
    Array.prototype.swap = function (i, j) {
    Log({ name: 'Array.prototype.swap', lineNumber: 1, range: [23, 94] });
    var k = this[i]; this[i] = this[j]; this[j] = k;
    }
    http://esprima.org/demo/functiontrace.html
    Try online!
    52

    View full-size slide

  51. Run-time Analysis
    0
    250000
    500000
    0 500 1000
    Calls to swap()
    Input Size
    http://ariya.ofilabs.com/2012/01/scalable-web-apps-the-complexity-issue.html
    53

    View full-size slide

  52. Execution Tracing
    http://ariya.ofilabs.com/2012/02/tracking-javascript-execution-during-startup.html
    https://gist.github.com/1823129
    jQuery Mobile startup log
    4640 function calls
    jquery.js 26 jQuery
    jquery.js 103 init undefined, undefined,
    [object Object]
    jquery.js 274 each (Function)
    jquery.js 631 each [object Object],
    (Function), undefined
    jquery.js 495 isFunction [object Object]
    jquery.js 512 type [object Object]
    jquery.mobile.js 1857 [Anonymous]
    jquery.mobile.js 642 [Anonymous]
    jquery.mobile.js 624 enableMouseBindings
    jquery.mobile.js 620 disableTouchBindings
    54

    View full-size slide

  53. Embrace the Future
    55

    View full-size slide

  54. Copy Paste (Mistake) Detector
    function inside(point, rect) {
    return (point.x >= rect.x1) && (point.y >= rect.y1) &&
    (point.x <= rect.x2) && (point.y <= rect.y1);
    }
    Wrong check
    56

    View full-size slide

  55. Syntax Augmentation
    ES.Future:
    Polyfills, Transpiler, ...
    Exoskeleton:
    LLJS, Sweet.js, ...
    57

    View full-size slide

  56. Adaptive Tools
    Explicit Implicit
    Customize analysis options
    Define new sets of rules
    Infer from high-quality sample
    Observe the engineer’s behavior
    58

    View full-size slide

  57. Syntax Query
    if (x = 0) { /* do Something */ }
    IfStatement.test AssigmentExpression[operator='=']
    Which syntax family should be the model?
    CSS selector? XPath? SQL?
    59

    View full-size slide

  58. And Many More...
    Semantic Diff
    Symbolic execution
    Informative syntax error
    Declarative transformation
    Pattern Matching
    60

    View full-size slide

  59. Her five-year mission: to explore strange new worlds, to
    seek out new lifeforms and new civilizations;
    to boldly go where no one has gone before.
    61

    View full-size slide

  60. Thank You
    [email protected]
    @AriyaHidayat
    ariya.ofilabs.com/highlights
    speakerdeck.com/ariya
    Credits: Some artworks are from http://openclipart.org
    62

    View full-size slide