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

JavaScript API Design Principles

JavaScript API Design Principles

First day closing keynote for http://yuilibrary.com/yuiconf/2013/

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

November 06, 2013
Tweet

More Decks by Ariya Hidayat

Other Decks in Programming

Transcript

  1. JavaScript API
    Design Principles
    @ariyahidayat
    November 6, 2013 CONF 2013

    View Slide

  2. /usr/bin/whoami
    PhantomJS
    shapesecurity.com

    View Slide

  3. “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

  4. Modular Application Architecture
    Module A
    Module B
    interface

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  8. Static Polymorphism

    View Slide

  9. 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';
    Progress Bar vs Slider

    View Slide

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

    View Slide

  11. 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

    View Slide

  12. var x = NaN;
    isNaN(x) true
    x === NaN false

    View Slide

  13. Convenience & Traps

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  18. 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

  19. 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

  20. 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

  21. View Slide

  22. Ambiguity Begets Insanity

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. String Extraction
    'yuiconf2013'.substr(3, 8) "conf2013"
    'yuiconf2013'.substr(8, 3) "013"
    'yuiconf2013'.substring(3, 8) "conf2"
    'yuiconf2013'.substring(8, 3) "conf2"
    'yuiconf2013'.slice(3, 8) "conf2"
    'yuiconf2013'.slice(8, 3) ""

    View Slide

  29. 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

    View Slide

  30. 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 = ' yuiconf ';
    s.trim();

    View Slide

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

    View Slide

  32. Best Practices

    View Slide

  33. #1: Private by Default
    Public = Irreversible

    View Slide

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

    View Slide

  35. View Slide

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

    View Slide

  37. View Slide

  38. #3: Mandatory API Review

    View Slide

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

    View Slide

  40. View Slide

  41. #4: API Tools
    Tools separate us

    View Slide

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

    View Slide

  43. {
    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

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

    View Slide

  45. 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

  46. 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

  47. 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

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

    View Slide

  49. What About API Usage?
    “You call this immutable function
    but you never assign its return
    value.”
    “You check the return value of this
    function 2000 times. Why don’t you
    do it this time (line = ….)?”

    View Slide

  50. 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

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

    View Slide