Design Strategies for JavaScript API

Design Strategies for JavaScript API

Presented at O'Reilly Fluent Conference, San Francisco (March 12, 2014):
http://fluentconf.com/fluent2014/public/schedule/detail/32527

Modular design of a large-scale web application encourages the right level of component coupling and at the same time it mandates a reasonable set of APIs (application programming interfaces) between those modules. The way a particular module is being used by other modules define the success of those APIs. In this talk, best practices on the interactions between modules, particularly on the influence of an interface on the code patterns, will be analyzed and used to formulate the following strategies:

* Using static polymorphism to ensure consistent naming
* Preventing unnecessary and dangerous convenience (e.g. Boolean traps)
* Avoiding unreadable code due to confusing semantics

Examples, both on the good and bad sides, will be taken from real-world application code. In addition, a few language tools to recognize the ambiguous code pattern will be demonstrated.

For links to follow-up resources, read also:
http://ariya.ofilabs.com/2014/03/the-phenomenal-fluent-2014.html.

0284b8950e0f4a57bcc092d4dbb98d97?s=128

Ariya Hidayat

March 12, 2014
Tweet

Transcript

  1. 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
  2. 8.

    Using static polymorphism to ensure consistency Preventing dangerous convenience such

    as Boolean trap Avoiding unreadable code due to confusing semantics
  3. 10.

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

    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;
  5. 13.

    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
  6. 14.

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

    https://twitter.com/AriyaHidayat/status/393034774245171200
  7. 17.

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

    Boolean Trap // Horizontal s1 = new Slider(true); // Vertical

    s2 = new Slider(false); s1 = new Slider({ orientation: 'horizontal' }); s2 = new Slider({ orientation: 'vertical' });
  9. 21.

    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 });
  10. 22.

    Ping-pong, Anyone? // Expand + animate treeItem.setState(true, true); // Expand

    + don't animate treeItem.setState(true, false); // Collapse + animate treeItem.setState(false, true);
  11. 23.

    Ping-pong, Anyone? // Expand + animate treeItem.setState(true, true); // Expand

    + don't animate treeItem.setState(true, false); // Collapse + animate treeItem.setState(false, true);
  12. 26.
  13. 30.

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

    = "Adam"; flight.departure = SFO; flight.destination = JFK;
  14. 31.

    String Extraction 'fluentconf2014'.substr(6, 9) "conf2014" 'fluentconf2014'.substr(9, 6) "f0214" 'fluentconf014'.substring(6, 9)

    "con" 'fluentconf014'.substring(9, 6) "con" 'fluentconf2014'.slice(6, 9) "con" 'fluentconf2014'.slice(9, 6) "" http://ariya.ofilabs.com/2014/02/javascript-string-substring-substr-slice.html
  15. 32.

    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
  16. 33.

    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 = ' fluentconf '; s.trim();
  17. 34.

    Explicit (Im)mutability var p = new Point(14, 3); p.translate(4, 4);

    p.translated(4, 4); var s = ' fluentconf '; s.trim(); s.trimmed(); p does not change Returns a translated version of p
  18. 37.

    SHStripMneumonic “Why is the function SHStripMneumonic misspelled?” Raymond Chen, Microsoft

    http://blogs.msdn.com/b/oldnewthing/archive/2008/05/19/8518565.aspx
  19. 38.
  20. 40.
  21. 47.

    Syntax Parser var answer = 42 keyword equal sign identifier

    number Variable Declaration Identifier Literal Constant Tokenization → Tokens Parsing → Syntax Tree
  22. 48.

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

    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
  24. 51.

    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); } } });
  25. 52.

    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'); } }
  26. 54.

    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 = ….)?”
  27. 55.

    Final Words Practice the rituals: • Apply static polymorphism •

    Judge every convenient shortcut • Read aloud (and often) Build, write, tweak API tools Apply best practices