Beautiful APIs

UX for developers

Design is about communication.

There are tons of ways of telling the same thing.

But some ways say better than others.

What do you prefer?

glBegin (GL_TRIANGLES);
  glVertex (0,0,0);
  glVertex (1,1,0);
  glVertex (2,0,0);
glEnd ();
v = &buffer.vertexes[0];
v->x = 0; v->y = 0; v->z = 0;
v++;
v->x = 1; v->y = 1; v->z = 0;
v++;
v->x = 2; v->y = 0; v->z = 0;
c = &buffer.commands;
c->operation = DRAW_TRIANGLE;
c->vertexes[0] = 0;
c->vertexes[1] = 1;
c->vertexes[2] = 2;
IssueExecuteBuffer (buffer);
It’s not uncommon for designers to confuse a beautiful looking product with one that works beautifully.

Braden Kowitz

Samples of beauty

Jasmine

A Behaviour-Driven Development framework

Includes test management, an expectation library and function spies.

Check it! http://pivotal.github.io/jasmine/

Offers a micro internal DSL

describe("A suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
  });
});

Note this metaphore:

xdescribe("A spec", function() {
  var foo;

  xit("is just a function, so it can contain any code", function() {
    expect(foo).toEqual(1);
  });
})

The x prefix crosses out and disables the test

"not" tests are naturally derived from positive ones:

expect(this).not.toBe(true);
expect(this).not.toMatch(/^https?/);
expect(this).not.toContain('www');

There is no toBeGreaterOrEqualThan().

But there is a toBeLessThan().

So we can write not.toBeLessThan().

VerbalExpressions

A library to build meaningful regular expressions.

Translate the obscure regexp syntax to a human-friendly format.

Check it! https://github.com/VerbalExpressions/JSVerbalExpressions

Consider this pattern ^http(s)?://(www.)?[^ ]+$

var tester = VerEx()
            .startOfLine()
            .then( "http" )
            .maybe( "s" )
            .then( "://" )
            .maybe( "www." )
            .anythingBut( " " )
            .endOfLine();

Date.js

Unfortunately, a discontinued library for handling dates.

Extend JS native objects to improve readability.

Check what remains. https://code.google.com/p/datejs/

A fluent API

Date.today().first().thursday() // Returns the first Thursday of the current month.
Date.today().second().thursday()// Returns the second Thursday of the current month.

Date.march().third().thursday() // Returns the third Thursday in March of the current year.
Date.october().fourth().sunday()// Returns the fourth Sunday in October.

Date.today().fifth().sunday()   // Returns the fifth Sunday in the current month, or throws a RangeError exception if there are not 5 Sundays in the current month.
Date.october().final().sunday() // Returns the final Sunday in October.

Extensions in other types

(1).day().fromNow()  // One (1) day from now.
(3).months().ago()   // Three (3) months ago.

Algebraical date expressions

Date.parse('t + 5d')          // Adds 5 days to today.
Date.parse('today - 1 month') // Subtracts 1 month from today.
Date.parse('+')               // Add 1 day to today = tomorrow.
Date.parse('- 3months')       // Subtract 3 months.

Promises

A pattern to control asynchronous flows.

The promise is the unit of asynchronous flow.

Check the polyfill! https://github.com/slightlyoff/Promises

A promise retains a future value.

function getAnswerToEverything() {
  return new Promise(function (resolver) {
    doHeavyCalculations();

    function doHeavyCalculations() {
      setTimeout(function () {
        // fulfill once we get the answer
        resolver.fulfill(42);
      }, 2000);
    }
  });
}

.then() allows to chain execution.

getAnswerToEverything().then(guessTheQuestion);

.then() returns a promise. So more chaining is possible.

getAnswerToEverything()
  .then(guessTheQuestion).
  .then(reachOmnisciency);

Zepto

A light-weight implementation of jQuery.

Let's focus on the query engine and chainability only.

Check it! http://zeptojs.com/

Reusing CSS knowledge...

$('article aside.comments');
$('span[data-tooltip]');
$('#navigation ul > li');

...the developer becomes more productive.

Improved chainability...

$('#navigation ul')
  .find('li:nth-of-type(1)')
    .css('background-color', 'red')
  .end()
  .find('li:nth-of-type(2)')
    .toogle('background-color', 'green')
  .end();

...allowing independant chains of operations

by maintaining a stack of states.

The principles of design

I think not all design principles can be applied
directly to the API design.

But with some adaptation...

(roughly based on Secrets of Awesome JavaScript API design)

Unity / Goal

In design, the concept behind the work.

In software design, the goal of the library.

  • Jasmine is a BDD framework.
  • VerbalExpressions is regexp library.
  • Date.js is a time library.
  • Promises are primitives for handling asynchronous flows.
  • Zepto a library for DOM manipulation.

Harmony / Predictability

The coexistence of non-conflictive elements in a work.

In software design, regularity and predictability of the API.

  • In Jasmine, the use of functions to provide the body of the distinct units (suites, tests, set-up, tear-down...).
  • VerbalExpressions allows same expressivity as regular expressions.
  • Zepto reuse of the CSS selectors in the query engine.

Balance / Completeness

In design, the distribution of the work elements
according to their visual weight.

In software design, the completeness of the API methods.

  • Jasmine provides all the functionality you need to make BDD: the runner, the expectation library and the spies library.
  • Date.js provide addYears(), addMonths(), addDays()... allowing total control of the date parameters.
  • Promises gives static constructors any(), all() and some() covering most of the common asynchronous situations.

Hierarchy / Modularity

In design, the order of significance.

In software design, modularity and organisation.

  • Jasmine integrates and polutes the global namespace but offers a clear differentation when using the runner, expectations and spies.
  • VerbalExpressions is self-contained and all the access is through the VerEx object.
  • Zepto exposes (somewhat) separation between general purpose utilities (all inside the Zepto namespace) and DOM manipulation (called from Zepto collections).

Proportion / Scope

In design, the relative size of elements againts each other.

In software design, the scope of each component.

  • Jasmine (again) is a BDD framework with a runner, expectations and spies but none of its components is oversized nor dominates over other company.
  • Zepto provides DOM manipulation and utility functions but DOM manipulation API is notably richer.
  • Date.js is a little bit oversized but keeping the focus on time management.

Emphasis / Features

In design, the focal point of the work outstanding by the use of contrast.

In software design, the major structures around which the library structures itself.

  • .then() and Promise chainability.
  • The collections and fn in Zepto.
  • Expressiveness in VerbalExpressions.

Fluent Interfaces

APIs simulating natural language.

They have contextual meaning.

By method chaining.

By micro embedded DSLs.

The good ,)

  • More readability.
  • Greater level of abstraction.
  • Versatile if careful designed.

The bad :(

  • Functions are meaningless without context.
  • Magic behind make them hard to debug.
  • Lots of side effects.

color·k

An example of fluent API

A colour manipulating library.

Storytelling

Start by creating user stories.

You will realize you are speaking a specific language.

Adapt this language to an API.

In color·k

// I want to create colors using CSS constructors.
k.color('red');
k.color('#F00');
k.color('#FA0000');
k.color('rgba(255, 0, 0, 0.5)');

// I want to make a color lighter or darker
// by a fixed amount.
k.color('red').lighter();
k.color('green').darker();

// I want to use the color entities in CSS contexts.
k.color('red').toString() // returns 'hsla(0, 50%, 50%, 1.0)'

Design patterns

The façade pattern: simplify complex APIs.

The proxy pattern: dynamic access to properties.

The interpreter pattern: execution of DSLs.

In color·k

// A prefix-based DSL to build colors
k.color('light-red');
k.color('web-violet');
k.color('dull-transparent-orange');
k.color('dark-#0F0');

(Simple) Method chaining

Allows methods to form natural language sentences.

You must violate the Command / Query principle.

Main limitation: one unique context to modify.

In color·k

k.color('red')
  .lighter()
  .more.red()
  .less.blue()
  .brighter();

(Structured) Method chaining

Used in jQuery / Zepto: .find() and .end().

Implemented by maintaining a stack of contexts.

Some methods save the current context, .end() restores the last context.

In color·k (experimental feature)

var variations = k.color('red')
  .change().lighter()._
  .change().darker()._
  .change().alpha(0).$

By using special methods and descriptors:

  • Some methods save the context into a stack: change()
  • The special property _ is a descriptor that saves the current state into the result stack and restore the context state.
  • The special property $ do the same but returning the result stack.

Documentation

  • Try to avoid autodoc tools: functions without context are meaningless.
  • Take example of Jasmine: include your behaviour test-suite as part of your documentation and use literate documentation+ tools like docco.
  • If providing a formal documentation: document contexts, then methods in each context.

In color·k

Check the ongoing specs!

Versioning

Check Semanting Versioning. In semver you use three numbers M.m.p standing for Major version, minor version and patch version.

  • Each time you improve you current implementation without altering the API, increase the p number.
  • Each time you change your API preserving compatibility, increase the m number.
  • Each time you change your API breaking compatibility intentionally, increase the M number.

Sources & further reading

About me

me
Salvador de la Puente González
twitter
@salvadelapuente
My sites
http://unoyunodiez.com
http://github.com/delapuente