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

JavaScript API Hall of Shame

JavaScript API Hall of Shame

In this day and age it is pretty common to design a large-scale web application as a collection of modular and reusable components. These components need to have a reasonable set of APIs (application programming interfaces) which deliver against the stipulated engineering criteria. In addition this API set needs to retain ease of use. The success of a module is thus reflected in the way it is being used in other modules. This talk is intended to initiate the discussion on finding the principles for designing a good JavaScript API by observing the interactions between modules, particularly on the influence of an interface on the code patterns. Some good and bad examples found in real-world libraries will be shown, along with a collection of language tools designed to detect and analyze those patterns.

Ariya Hidayat

April 30, 2014
Tweet

More Decks by Ariya Hidayat

Other Decks in Programming

Transcript

  1. JavaScript API
    Hall of Shame
    @ariyahidayat
    April 30, 2014

    View Slide

  2. /usr/bin/whoami
    shapesecurity.com

    View Slide

  3. .setState( );
    treeItem true, false

    View Slide

  4. https://twitter.com/AriyaHidayat/status/455892955765551105

    View Slide

  5. “How to Design a Good API and Why It Matters”
    Joshua Bloch
    “Qt API Design Principles”
    http://qt-project.org/wiki/API-Design-Principles
    Matthias Ettrich, Jasmin Blanchette

    View Slide

  6. https://twitter.com/AriyaHidayat/status/429111596627918848

    View Slide

  7. Modular Application Architecture
    Module A
    Module B
    interface

    View Slide

  8. WHY?
    Nobody reads API documentation
    Code is written once, read many times

    View Slide

  9. Readable, consistent,
    crystal clear
    Thank you, Captain Obvious!

    View Slide

  10. Using static polymorphism
    to ensure consistency
    Preventing dangerous convenience
    such as Boolean trap
    Avoiding unreadable code
    due to confusing semantics

    View Slide

  11. Static Polymorphism

    View Slide

  12. X1.value = 'Rare';
    X2.value = 'Medium';
    X3.value = 'Well done';
    Y.option = 'Fries';
    Z.caption = 'Order';
    X1.value = 'Rare';
    X2.value = 'Medium';
    X3.value = 'Well done';
    Y.value = 'Fries';
    Z.value = 'Order';
    http://ariya.ofilabs.com/2014/03/api-names-and-static-polymorphism.html

    View Slide

  13. slider.maxValue = 100;
    slider.minValue = 0;
    slider.value = 34;
    indicator.range = [100, 0];
    indicator.progress = 61;
    slider.maximum = 100;
    slider.minimum = 0;
    slider.value = 34;
    indicator.maximum = 100;
    indicator.minimum = 0;
    indicator.value = 61;

    View Slide

  14. point.translate(14, -3)
    rect.translateBy(26, 4)
    point.translate(14, -3)
    rect.translate(26, 4)

    View Slide

  15. corner = new Point(10, 10);
    dim = new Size(70, 50);
    R = new Rect(corner, dim);
    Q = new Rect(10, 10, 80, 60);
    corner = new Point(10, 10);
    dim = new Size(70, 50);
    R = new Rect(corner, dim);
    Q = new Rect(10, 10, 70, 50);
    corner
    dimension
    x1, y1, x2, y2 x, y, w, h
    http://ariya.ofilabs.com/2014/03/api-names-and-static-polymorphism.html

    View Slide

  16. var x = NaN;
    isNaN(x) true
    x === NaN false
    https://twitter.com/AriyaHidayat/status/393034774245171200

    View Slide

  17. GarlicNaN != NaN
    ES 7

    View Slide

  18. Convenience & Traps

    View Slide

  19. What’s in a name?
    that which we call a rose
    by any other name
    would smell as sweet...
    Romeo and Juliet
    Act II. Scene II

    View Slide

  20. Can We Have Some Vowels, Please?
    BrCtl →
    WebView

    View Slide

  21. DOM Event
    event.initKeyEvent("keypress", true, true,
    null, null, false, false, false, false, 9, 0);

    View Slide

  22. Boolean Trap
    // Horizontal
    s1 = new Slider(true);
    // Vertical
    s2 = new Slider(false);
    s1 = new Slider({
    orientation: 'horizontal'
    });
    s2 = new Slider({
    orientation: 'vertical'
    });

    View Slide

  23. Boolean Modifier Trap
    // Animate the resize
    w.resize(250, 150, true);
    // Do not animate the resize
    w.resize(250, 150, false);
    w.resize(250, 150, {
    animate: true
    });

    View Slide

  24. Ping-pong, Anyone?
    // Expand + animate
    treeItem.setState(true, true);
    // Expand + don't animate
    treeItem.setState(true, false);
    // Collapse + animate
    treeItem.setState(false, true);

    View Slide

  25. https://twitter.com/ID_AA_Carmack/status/367485612627984385

    View Slide

  26. Ambiguity Begets Insanity

    View Slide

  27. Double Negatives
    X.disabled = false;
    Y.setHidden(true);
    filter.caseInsensitive = true;
    X.enabled = true;
    Y.setVisible(false);
    filter.caseSensitive = false;

    View Slide

  28. cube.translucency = 0.2;
    20% translucent
    cube.opacity = 0.8;
    80% opaque

    View Slide

  29. Nonverbal Actions
    status.message("Ready");
    page.forward();
    page.backward();
    status.showMessage("Ready");
    page.goForward();
    page.goBackward();

    View Slide

  30. picker.yearFrom = 1980;
    picker.yearTo = 2020;
    picker.minimumYear = 1980;
    picker.maximumYear = 2020;

    View Slide

  31. this.callMe = "Adam";
    flight.from = SFO;
    flight.to = JFK;
    this.name = "Adam";
    flight.departure = SFO;
    flight.destination = JFK;

    View Slide

  32. String Extraction
    'forcedotcom2014'.substr(5, 6) "dotcom"
    'forcedotcom2014'.substr(6, 5) "otcom"
    'forcedotcom2014'.substring(5, 6) "d"
    'forcedotcom2014'.substring(6, 5) "d"
    'forcedotcom2014'.slice(5, 6) "d"
    'forcedotcom2014'.slice(6, 5) ""
    http://ariya.ofilabs.com/2014/02/javascript-string-substring-substr-slice.html

    View Slide

  33. Array Extraction
    var a = [14, 3, 77]
    var b = a.slice(1, 2)
    a [14, 3, 77]
    b [3]
    var a = [14, 3, 77]
    var b = a.splice(1, 2)
    a [14]
    b [3, 77]
    Immutable vs Mutable
    http://ariya.ofilabs.com/2014/02/javascript-array-slice-vs-splice.html

    View Slide

  34. Verb = Action
    var p = new Point(14, 3);
    p.translate(4, 4);
    Does this change my string or
    return a fresh new string?
    var s = ' hal9000 ';
    s.trim();

    View Slide

  35. Explicit (Im)mutability
    var p = new Point(14, 3);
    p.translate(4, 4);
    p.translated(4, 4);
    var s = ' hal9000 ';
    s.trim();
    s.trimmed();
    p does not change
    Returns a translated version of p

    View Slide

  36. Best Practices

    View Slide

  37. View Slide

  38. #1: Private by Default
    Public = Irreversible

    View Slide

  39. -- yours truly
    “You’d fail your first
    two attempts anyway.”

    View Slide

  40. SHStripMneumonic
    “Why is the function
    SHStripMneumonic misspelled?”
    Raymond Chen, Microsoft
    http://blogs.msdn.com/b/oldnewthing/archive/2008/05/19/8518565.aspx

    View Slide

  41. View Slide

  42. “Write 3 examples which
    use the API”
    #2: Public by Justification

    View Slide

  43. View Slide

  44. #3: Mandatory API Review

    View Slide

  45. “YOU SHALL NOT PASS!”
    — Darth Vader

    View Slide

  46. #4: API Tools
    Tools separate us

    View Slide

  47. API Detective

    View Slide

  48. T D D B D D
    P D D

    View Slide

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

    View Slide

  50. {
    type: "Program",
    body: [
    {
    type: "VariableDeclaration",
    declarations: [
    {
    type: "VariableDeclarator",
    id: {
    type: "Identifier",
    name: "answer"
    },
    init: {
    type: "Literal",
    value: 42,
    raw: "42"
    }
    }
    ],
    kind: "var"
    }
    ]
    }
    Syntax Tree
    var answer = 42;
    Terms → ECMAScript 5.1 Specification
    https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API

    View Slide

  51. Syntax Visualization
    http://esprima.org/demo/parse.html

    View Slide

  52. Module 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
    Meta-object

    View Slide

  53. Collecting Member Expression
    foo.bar
    syntax = esprima.parse(code);
    traverse(syntax, function (node) {
    if (node.type === 'MemberExpression') {
    if (node.property) {
    console.log(node.property.name);
    }
    }
    });

    View Slide

  54. Detecting Boolean Traps
    reload(x, y, false)
    function checkLastArgument(node) {
    var args = node['arguments'];
    if (args.length < 2) {
    return;
    }
    if (typeof args[args.length - 1].value === 'boolean') {
    report(node, 'Dangerous Boolean literal');
    }
    }

    View Slide

  55. Blacklisting Function Names
    setDisabled(false)
    This block is left intentionally blank.
    Exercise for the brave reader!

    View Slide

  56. Future API-Cop
    “You call this immutable function, yet
    you never use its return value.”
    “You check the return value of this
    function 2000 times. Why don’t you
    do it this time (line = ….)?”

    View Slide

  57. Final Words
    Practice the rituals:
    ● Apply static polymorphism
    ● Judge every convenient shortcut
    ● Read aloud (and often)
    Build, write, tweak API tools
    Apply best practices

    View Slide

  58. Thank You
    @ariyahidayat
    ariya.ofilabs.com
    speakerdeck.com/ariya
    Some artworks are from http://openclipart.org.

    View Slide