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.

0284b8950e0f4a57bcc092d4dbb98d97?s=128

Ariya Hidayat

April 30, 2014
Tweet

Transcript

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

  2. /usr/bin/whoami shapesecurity.com

  3. .setState( ); treeItem true, false

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

  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
  6. https://twitter.com/AriyaHidayat/status/429111596627918848

  7. Modular Application Architecture Module A Module B interface

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

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

  10. Using static polymorphism to ensure consistency Preventing dangerous convenience such

    as Boolean trap Avoiding unreadable code due to confusing semantics
  11. Static Polymorphism

  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
  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;
  14. point.translate(14, -3) rect.translateBy(26, 4) point.translate(14, -3) rect.translate(26, 4)

  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
  16. var x = NaN; isNaN(x) true x === NaN false

    https://twitter.com/AriyaHidayat/status/393034774245171200
  17. GarlicNaN != NaN ES 7

  18. Convenience & Traps

  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
  20. Can We Have Some Vowels, Please? BrCtl → WebView

  21. DOM Event event.initKeyEvent("keypress", true, true, null, null, false, false, false,

    false, 9, 0);
  22. Boolean Trap // Horizontal s1 = new Slider(true); // Vertical

    s2 = new Slider(false); s1 = new Slider({ orientation: 'horizontal' }); s2 = new Slider({ orientation: 'vertical' });
  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 });
  24. Ping-pong, Anyone? // Expand + animate treeItem.setState(true, true); // Expand

    + don't animate treeItem.setState(true, false); // Collapse + animate treeItem.setState(false, true);
  25. https://twitter.com/ID_AA_Carmack/status/367485612627984385

  26. Ambiguity Begets Insanity

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

    = true; Y.setVisible(false); filter.caseSensitive = false;
  28. cube.translucency = 0.2; 20% translucent cube.opacity = 0.8; 80% opaque

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

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

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

    = "Adam"; flight.departure = SFO; flight.destination = JFK;
  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
  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
  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();
  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
  36. Best Practices

  37. None
  38. #1: Private by Default Public = Irreversible

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

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

    http://blogs.msdn.com/b/oldnewthing/archive/2008/05/19/8518565.aspx
  41. None
  42. “Write 3 examples which use the API” #2: Public by

    Justification
  43. None
  44. #3: Mandatory API Review

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

  46. #4: API Tools Tools separate us

  47. API Detective

  48. T D D B D D P D D

  49. Syntax Parser var answer = 42 keyword equal sign identifier

    number Variable Declaration Identifier Literal Constant Tokenization → Tokens Parsing → Syntax Tree
  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
  51. Syntax Visualization http://esprima.org/demo/parse.html

  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
  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); } } });
  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'); } }
  55. Blacklisting Function Names setDisabled(false) This block is left intentionally blank.

    Exercise for the brave reader!
  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 = ….)?”
  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
  58. Thank You @ariyahidayat ariya.ofilabs.com speakerdeck.com/ariya Some artworks are from http://openclipart.org.