Secrets of Awesome JavaScript API Design
Brandon Satrom@BrandonSatrom
Read it @ bit.ly/ZS5VYv
3 Big Ideas
Three ideas...
Three ideas...
1.APIs are “Developer UX”
Three ideas...
1.APIs are “Developer UX”
2.API Design is a universal practice
Three ideas...
1.APIs are “Developer UX”
2.API Design is a universal practice
3.API Design is a “principle-driven” art form
What is design?
What is design?
“form[ing] a plan or scheme of [some thing]… for later execution”
- Oxford English Dictionary
Design is universal
We judge designs to be...
...elegant
...beautiful
...utilitarian
...simple
...or not...Images from: http://www.greatbuildings.com/ and http://list25.com/25-ugliest-buildings-in-the-world/
We judge designs to be...
...elegant
...beautiful
...utilitarian
...simple
...or not...Images from: http://www.greatbuildings.com/ and http://list25.com/25-ugliest-buildings-in-the-world/
We judge designs to be...
...elegant
...beautiful
...utilitarian
...simple
...or not...Images from: http://www.greatbuildings.com/ and http://list25.com/25-ugliest-buildings-in-the-world/
We judge designs to be...
...elegant
...beautiful
...utilitarian
...simple
...or not...Images from: http://www.greatbuildings.com/ and http://list25.com/25-ugliest-buildings-in-the-world/
“Mediocre design provably wastes the world’s resources, corrupts the environment, affects international
competitiveness. Design is important, teaching design is
important.”
- Fred Brooks
Image of Fred Brooks from: http://en.wikipedia.org/wiki/File:Fred_Brooks.jpg
Poor Design is Costly
We apply design practices to User Interfaces...
Images from: http://pinterest.com/fromupnorth/gui/
We call this practice, User Experience (UX)
design...Images from: http://pinterest.com/fromupnorth/gui/
What about API Design?
API Design is “Developer UX”
API Design is not JUST For Library Authors...
...modular JS is written to be consumed...
...the number of consumers is
irrelevant.
Images from flickr.com/photos/powi/
In JavaScript, API design is critically important...
Dynamic Language...
Anonymity of consumers...
http://i.qkme.me/3qesq1.jpg
Ambiguity of Requirements...
Your users are smart...
Yeah, but my users won’t...
Image from: http://idratherbewriting.com/wp-content/uploads/2012/08/rtfmtractor.jpg
Yeah, but my users won’t...
Image from: http://idratherbewriting.com/wp-content/uploads/2012/08/rtfmtractor.jpg
A Tale of Ice & Water
A Tale of Ice & Water
A Tale of Ice & Water
A Tale of Ice & Water
A Tale of Ice & Water
In Industrial Design, this is NOT an acceptable answer...
Image from: http://idratherbewriting.com/wp-content/uploads/2012/08/rtfmtractor.jpg
... how is Software Different?
Image from: http://www.globalnerdy.com/wordpress/wp-content/uploads/2010/05/makeabetterfm.jpg
... how is Software Different?
Image from: http://www.globalnerdy.com/wordpress/wp-content/uploads/2010/05/makeabetterfm.jpg
It’s Not.
Your users are smart...
http://thebus.net/sites/default/files/americas-next-top-hipster.jpg
...but they are NOT... you.
http://thebus.net/sites/default/files/americas-next-top-hipster.jpg
Design the “pit of success”
Image from: http://darrell.mozingo.net/2011/06/26/the-pit-of-success/
Example:
$("article.blogPost").fadeIn();
Use jQuery to select all article tags with the class “blogPost”
Example:
$("article.blogPost").fadeIn();
Use jQuery to select all article tags with the class “blogPost”
article.blogPost { border-‐radius: 10px; background-‐color: salmon; box-‐shadow: 0px 0px 10px 2px #ccc;}
Goals of API Design
Be Self-Describing
Prevent Errors
Make Users Fast
Image from: http://elliottbrown.files.wordpress.com/2012/04/sandcastles.png
Goals are not enough...
...we need guiding principles...
Principles, not Rules
Principles, not Rules
Rules are rote, often applied without context
Principles encourage application of context
Principles Found in Physical MediaImages from: http://char.txa.cornell.edu/language/principl/principl.htm & http://www.greatbuildings.com/
Principles on display in popular libraries...
Backbone
jQuery
Kendo UI
Modernizr
Moment.js
Underscore
The Four Principles
Unity & Harmony
Balance
Proportion
Emphasis
WARNING! This is ART, not SCIENCE
Unity and Harmony
Image “Portrait of the children of Charles I” by Anthony Van Dyck from: http://char.txa.cornell.edu/language/principl/principl.htm
Unity and Harmony (art)
Unity: The concept behind a work, or how the composer brings everything together into a coherent whole.
Harmony: The placement of similar elements throughout a work, yielding an uncomplicated and simple feel.
Painting by Robert Delauny, from: http://char.txa.cornell.edu/language/principl/principl.htm
Unity & Harmony (API Design):Familiarity & Comfort
Extended Object creation in Backbone
Widget Instantiation in Kendo UI
Use similar and/or unifying elements through your library to create familiarity and comfort
Example: Create Kendo UI Widgets from jQuery-selected DOM Elements
$("ul.tree").kendoTreeView();
$("ul.panel").kendoPanelBar();
$("div").kendoGrid();
Example: Create Kendo UI Widgets from jQuery-selected DOM Elements
Each Widget is prefixed with “kendo” and named in a consistent, camel-cased style
$("ul.tree").kendoTreeView();
$("ul.panel").kendoPanelBar();
$("div").kendoGrid();
Example: Create Extended Objects with Backbone
var Book = Backbone.Model.extend({ initialize: function() { ... }, author: function() { ... }, pubDate: function() { ... },});
var DocumentRow = Backbone.View.extend({ tagName: "li", className: "row", events: { "click .icon": "open", "click .button.edit": "openEditDialog" }, render: function() { ... }});
Example: Create Extended Objects with Backbone
[Object].extend is used to “inherit” the built-in functionality of Backbone Models, Views, Collections and Routers
var Book = Backbone.Model.extend({ initialize: function() { ... }, author: function() { ... }, pubDate: function() { ... },});
var DocumentRow = Backbone.View.extend({ tagName: "li", className: "row", events: { "click .icon": "open", "click .button.edit": "openEditDialog" }, render: function() { ... }});
Balance
Image “Portrait of the children of Charles I” by Anthony Van Dyck from: http://char.txa.cornell.edu/language/principl/principl.htm
Balance (art)
The arrangement of elements to ensure that no one part of the work overpowers other parts, or causes it to feel unstable.
Image of Italian Textile, 18th Centuty from: http://char.txa.cornell.edu/language/principl/principl.htm
Balance (API Design):Weight & Predictability
Browser Feature Tests in Modernizr
DOM Selection Syntax in jQuery
Ensure that each function of your library exhibits consistent behavior, or aids in meeting a complimentary goal.
Example:
Modernizr.geolocationModernizr.localstorageModernizr.webworkersModernizr.canvasModernizr.borderradius
Test Browser Capabilities using Modernizr
Example:
Modernizr.geolocationModernizr.localstorageModernizr.webworkersModernizr.canvasModernizr.borderradius
Test Browser Capabilities using Modernizr
Each property matches an HTML5/CSS-related API and returns a boolean
Example:
$("#grid") // Selects by ID
$("ul.nav > li") // All LIs for UL w/class "nav"
$("ul li:nth-‐child(2)") // 2nd item in each list
Select DOM Elements using jQuery’s Selector Syntax
Example:
$("#grid") // Selects by ID
$("ul.nav > li") // All LIs for UL w/class "nav"
$("ul li:nth-‐child(2)") // 2nd item in each list
Select DOM Elements using jQuery’s Selector Syntax
Many jQuery Selectors map directly to equivalent CSS selectors
Proportion
Image “The Fisherman” by Saul Steinberg from: http://char.txa.cornell.edu/language/principl/principl.htm
Proportion (art)
A measurement of the size and quantity of elements within a work, relative to the whole.
Image of Salisbury Cathedral from: http://char.txa.cornell.edu/language/principl/principl.htm
Proportion (API Design):Scope that matches capability
Moment.js
Underscore
Make sure that every interface of the library matches its intended purpose & that no extraneous elements exist.
Example: Moment.js is working working with dates... and that’s it
moment().format('dddd');moment().startOf('hour').fromNow();moment().format('[Hello from] YYYY'); // Hello from 2013moment().startOf('day').fromNow();
Example: Moment.js is working working with dates... and that’s it
Moment is designed to make working with the JavaScript Date object tolerable, and it provides no functionality beyond that scope.
moment().format('dddd');moment().startOf('hour').fromNow();moment().format('[Hello from] YYYY'); // Hello from 2013moment().startOf('day').fromNow();
_.each(["Todd", "Burke", "Derick"], function(name){ alert(name); });
_.map([1, 2, 3], function(num){ return num * 3; });
_.isNumber("ten"); // False
Example: Underscore.js, designed to add functional programming support to JS
_.each(["Todd", "Burke", "Derick"], function(name){ alert(name); });
_.map([1, 2, 3], function(num){ return num * 3; });
_.isNumber("ten"); // False
Example: Underscore.js, designed to add functional programming support to JS
Underscore provides utility functions that help devs work with JS collections, arrays, functions and objects. Larger API surface, for a broader purpose.
Emphasis
Image of the FlatIron Building from: http://www.greatbuildings.com/
Emphasis (art)
The point of focus or interruption of a work. The use of contrast to cause an aspect of the work to stand out and capture the viewer’s
attention.
Image from: http://char.txa.cornell.edu/language/principl/principl.htm
Emphasis (API Design):Creating a focal point
Plugin development using jQuery’s fn namespace
Method chaining in jQuery
Object extensibility in Backbone
Provide a gateway method that anchors your library, a chained or fluent API, or create extensibility hooks for consuming devs
Example: jQuery enables a fluent programming style by returning a jQuery object from most functions.
$(‘ul.first’).find(‘.overdue’) .css(‘background-‐color’,‘red’) .end() .find(‘.due-‐soon’) .css(‘background-‐color’, ‘yellow’);
Example: jQuery enables a fluent programming style by returning a jQuery object from most functions.
This style enables devs to accomplish a great deal of work in a terse, yet readable manner.
$(‘ul.first’).find(‘.overdue’) .css(‘background-‐color’,‘red’) .end() .find(‘.due-‐soon’) .css(‘background-‐color’, ‘yellow’);
(function($) { $.fn.kittehfy = function() { return this.each(function(idx, el) { var width = el.width, height = el.height; var src= "http://placekitten.com/"; el.src= src + width + "/" + height; }); };})(jQuery);
$("img").kittehfy();
Example: jQuery plugins are connected to jQuery via the fn (“effin”) namespace...
(function($) { $.fn.kittehfy = function() { return this.each(function(idx, el) { var width = el.width, height = el.height; var src= "http://placekitten.com/"; el.src= src + width + "/" + height; }); };})(jQuery);
$("img").kittehfy();
Example: jQuery plugins are connected to jQuery via the fn (“effin”) namespace...
jQuery Plugins “feel” like natural extensions to jQuery itself, and behave in similar ways
Example:
var Book = Backbone.Model.extend({ initialize: function() { ... }, author: function() { ... }, pubDate: function() { ... },});
var DocumentRow = Backbone.View.extend({ tagName: "li", className: "row", events: { "click .icon": "open", "click .button.edit": "openEditDialog" }, render: function() { ... }});
Create Extended Objects with Backbone
Example:
var Book = Backbone.Model.extend({ initialize: function() { ... }, author: function() { ... }, pubDate: function() { ... },});
var DocumentRow = Backbone.View.extend({ tagName: "li", className: "row", events: { "click .icon": "open", "click .button.edit": "openEditDialog" }, render: function() { ... }});
Create Extended Objects with Backbone
[Object].extend is used to “inherit” the built-in functionality of Backbone Models, Views, Collections and Routers
The Four Principles
Unity & Harmony
Balance
Proportion
Emphasis
The HORROR!aka What might go wrong...
What might go wrong...
Inconsistency Disproportionality
Imbalance...Callback signatures on $.map, $.each & $(el).map
Imbalance...Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());});
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());});
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());});
var uppers = $.map(letters, function(val, index) { return (val.toUpperCase());
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());});
var uppers = $.map(letters, function(val, index) { return (val.toUpperCase());});
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());});
var uppers = $.map(letters, function(val, index) { return (val.toUpperCase());});
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());});
var uppers = $.map(letters, function(val, index) { return (val.toUpperCase());});
$(‘li’).map(function(index, val) { //WAT });
Callback signatures on $.map, $.each & $(el).map
Imbalance...
var letters = [“a”, “b”, “c”, “d”, “e”];$.each(letters, function(index, val) { console.log(index + “: “ + val.toUpperCase());});
var uppers = $.map(letters, function(val, index) { return (val.toUpperCase());});
$(‘li’).map(function(index, val) { //WAT });
Callback signatures on $.map, $.each & $(el).map
Not only do $.map and $.each diverge, but $.map and $(el).map order the callback params differently, depending on how the method is called.
Proportion: Too Large or Too Small...
Image of Fred Brooks from: http://www-set.win.tue.nl/UnsungHeroes/files/PTERA-Kosten-Poel.jpg
Image of Fred Brooks from: http://www-set.win.tue.nl/UnsungHeroes/files/PTERA-Kosten-Poel.jpg
“Van der Poel designed a computer with only one operation code...”
Image of Fred Brooks from: http://www-set.win.tue.nl/UnsungHeroes/files/PTERA-Kosten-Poel.jpg
“... Every instruction carried out the same operation. He demonstrated the sufficiency of his operation—his machine could do anything any other computer could do...”
Image of Fred Brooks from: http://www-set.win.tue.nl/UnsungHeroes/files/PTERA-Kosten-Poel.jpg
“...And yet it was very difficult to program....”
Image of Fred Brooks from: http://www-set.win.tue.nl/UnsungHeroes/files/PTERA-Kosten-Poel.jpg
“...the delight that came from using it was similar to... working out a crossword puzzle—a construct of intentional complexity and no intended utility.”
- Fred Brooks
AwesomeLib.do(prams);
AwesomeLib.do(prams);
We can all agree that this is bad
jQuery();
jQuery();
Then, so is this...
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
jQuery() // Empty $ Object
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
jQuery() // Empty $ Object
jQuery( elementArray ) // Wrap
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
jQuery() // Empty $ Object
jQuery( elementArray ) // Wrap
jQuery( jQuery object ) // Clone
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
jQuery() // Empty $ Object
jQuery( elementArray ) // Wrap
jQuery( jQuery object ) // Clone
jQuery( html [, ownerDocument ] ) // Create DOM Elements
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
jQuery() // Empty $ Object
jQuery( elementArray ) // Wrap
jQuery( jQuery object ) // Clone
jQuery( html [, ownerDocument ] ) // Create DOM Elements
jQuery ( html, props ) // Create DOM Elements
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
jQuery() // Empty $ Object
jQuery( elementArray ) // Wrap
jQuery( jQuery object ) // Clone
jQuery( html [, ownerDocument ] ) // Create DOM Elements
jQuery ( html, props ) // Create DOM Elements
jQuery ( callback ) // Bind DOM loaded function
Overloads on the jQuery() method...
jQuery( selector [, context] ) // Select
jQuery( element ) // Wrap
jQuery( object ) // Wrap
jQuery() // Empty $ Object
jQuery( elementArray ) // Wrap
jQuery( jQuery object ) // Clone
jQuery( html [, ownerDocument ] ) // Create DOM Elements
jQuery ( html, props ) // Create DOM Elements
jQuery ( callback ) // Bind DOM loaded function
Overloads on the jQuery() method...
11 ways to call jQuery, with 6 different contexts!
Now, for a question...
jQuery: Good API Design or
Bad API Design?
Image from: http://laughingsquid.com/wp-content/uploads/Kevin-Bacon.jpg
jQuery: Good API Design or
Bad API Design?
Image from: http://laughingsquid.com/wp-content/uploads/Kevin-Bacon.jpg
A baseline example of “Good...”
jQuery: Good API Design or
Bad API Design?
Image from: http://laughingsquid.com/wp-content/uploads/Kevin-Bacon.jpg
A baseline example of “Good...”
YES
Here’s the point...
Image from: http://laughingsquid.com/wp-content/uploads/Kevin-Bacon.jpg
Building and evolving a useful
API is hard...
Image from: http://www.ibiblio.org/xml/slides/xmlone/london2002/schemas/83.html
...you won’t always get it right.
Image from: gorestruly.com2011/09/30/rotting-americana-the-winchester-mystery-house/
But hey, jQuery’s not perfect...
... so you don’t have to be either.