Writing usableap isinpractice

66
© Asprotunity Ltd email: [email protected] twitter: @gasproni linkedin: http://www.linkedin.com/in/gasproni Writing usable APIs in practice /dev/summer/2014

description

Talk given at /dev/summer 2014 in Cambridge, UK

Transcript of Writing usableap isinpractice

Page 1: Writing usableap isinpractice

© Asprotunity Ltd

email: [email protected] twitter: @gasproni linkedin: http://www.linkedin.com/in/gasproni

Writing usable APIs in practice

/dev/summer/2014

Page 2: Writing usableap isinpractice

Have you been a victim of bad APIs?

© Asprotunity Ltd

Page 3: Writing usableap isinpractice

Have you written bad APIs?

© Asprotunity Ltd

Page 4: Writing usableap isinpractice

© Asprotunity Ltd

When we talk about good code we always mean usable code as well

Page 5: Writing usableap isinpractice

© Asprotunity Ltd

API

From: “Sometimes You Need to See Through Walls — A Field Study of Application Programming Interfaces”, Cleidson R. B. de Souza et al., http://www.ufpa.br/cdesouza/pub/p390-desouza.pdf

“Any well-defined interface that defines the service that one component, module, or application provides to other software elements”

Page 6: Writing usableap isinpractice

© Asprotunity Ltd

Example: java.io package (Java 7)

• Interfaces: Closeable, DataInput, DataOutput, Externalizable, FileFilter, FilenameFilter, Flushable, ObjectInput, ObjectInputValidation, ObjectOutput, ObjectStreamConstants, Serializable

• Classes: BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter, ByteArrayInputStream, ByteArrayOutputStream, CharArrayReader, CharArrayWriter, Console, DataInputStream, DataOutputStream, File, FileDescriptor, FileInputStream, FileOutputStream, FilePermission, FileReader, FileWriter, FilterInputStream, FilterOutputStream, FilterReader, FilterWriter, InputStream, InputStreamReader, LineNumberInputStream, LineNumberReader, ObjectInputStream, ObjectInputStream.GetField, ObjectOutputStream, ObjectOutputStream.PutField, ObjectStreamClass, ObjectStreamField, OutputStream, OutputStreamWriter, PipedInputStream, PipedOutputStream, PipedReader, PipedWriter, PrintStream, PrintWriter, PushbackInputStream, PushbackReader, RandomAccessFile, Reader, SequenceInputStream, SerializablePermission, StreamTokenizer, StringBufferInputStream, StringReader, StringWriter, Writer

• Exceptions: CharConversionException, EOFException, FileNotFoundException, InterruptedIOException, InvalidClassException, InvalidObjectException, IOException, NotActiveException, NotSerializableException, ObjectStreamException, OptionalDataException, StreamCorruptedException, SyncFailedException, UnsupportedEncodingException, UTFDataFormatException, WriteAbortedException

• Errors: IOError

Page 7: Writing usableap isinpractice

© Asprotunity Ltd

Any non trivial software application involves writing one or more APIs

Page 8: Writing usableap isinpractice

© Asprotunity Ltd

Usability

• Efficiency

• Effectiveness

• Error prevention

• Ease of learning

Page 9: Writing usableap isinpractice

© Asprotunity Ltd

Giovanni’s usability equation

• u is the usability

• b > 0. The brain power necessary to achieve your goals

• c > 0. A constant value

u = cb

Page 10: Writing usableap isinpractice

© Asprotunity Ltd

Why bother (company’s perspective)

• APIs can be among a company's greatest assets

• Can also be among company's greatest liabilities

Adapted from: “How to Design a Good API and Why it Matters”, Joshua Bloch, http://lcsd05.cs.tamu.edu/slides/keynote.pdf

Page 11: Writing usableap isinpractice

© Asprotunity Ltd

Why bother (programmer’s perspective)

• Fewer bugs to take care of

• Code of higher quality

• Solve more interesting problems

• More productivity

Page 12: Writing usableap isinpractice

© Asprotunity Ltd

Public and private APIs

• Public APIs

• Given to third parties

• Private APIs

• For internal use

Page 13: Writing usableap isinpractice

Some usability concepts

© Asprotunity Ltd

Page 14: Writing usableap isinpractice

© Asprotunity Ltd

Affordances

An affordance is a quality of an object, or an environment, that allows an individual to perform an action.

From: http://en.wikipedia.org/wiki/Affordance

Page 15: Writing usableap isinpractice

© Asprotunity Ltd

import java.io.BufferedReader;

import java.io.File;

import java.io.FileReader;

BufferedReader reader;

try { reader = new BufferedReader(new FileReader(“filename“)); while (true) { String line = reader.readLine(); if (line == null) { break;

} processLine(line); } } catch (Exception exc) { // Do something here... } finally { if (reader != null) { reader.close(); } }

Page 16: Writing usableap isinpractice

© Asprotunity Ltd

import java.nio.file.Path;

import java.nio.FileSystems;

import java.nio.charset.Charset;

try {

Path filePath = FileSystems.getDefault().getPath("filename");

Charset charset = Charset.defaultCharset();

for (String line : Files.readAllLines(filePath, charset)) {

processLine(line);

}

} catch (IOException e) {

// Do something here

}

Page 17: Writing usableap isinpractice

© Asprotunity Ltd

import java.io.File;

import java.util.Scanner;

File file = new File("filename");

try {

Scanner scanner = new Scanner(file);

while (scanner.hasNextLine()) {

String line = scanner.nextLine();

processLine(line);

}

scanner.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

}

}

Page 18: Writing usableap isinpractice

© Asprotunity Ltd

with open("filename") as infile: for line in infile.readlines(): processLine(line)

Page 19: Writing usableap isinpractice

© Asprotunity Ltd

Some cognitive dimensions• Abstraction level. The minimum and maximum levels of abstraction

exposed by the API

• Working framework. The size of the conceptual chunk (developer working set) needed to work effectively

• Progressive evaluation. To what extent partially completed code can be executed to obtain feedback on code behaviour

• Penetrability. The extent to which a developer must understand the underlying implementation details of an API

• Consistency. How much of the rest of an API can be inferred once part of it is learned

Adapted from: “Measuring API Usability”, Steven Clarke, http://drdobbs.com/windows/184405654

Page 20: Writing usableap isinpractice

© Asprotunity Ltd

import java.io.BufferedReader;

import java.io.File;

import java.io.FileReader;

BufferedReader reader;

try { reader = new BufferedReader(new FileReader(“filename“)); while (true) { String line = reader.readLine(); if (line == null) { break;

} processLine(line); } } catch (Exception exc) { // Do something here... } finally { if (reader != null) { reader.close(); } }

• Abstraction level

• Working framework

• Progressive evaluation

• Penetrability

• Consistency

Page 21: Writing usableap isinpractice

© Asprotunity Ltd

• Abstraction level

• Working framework

• Progressive evaluation

• Penetrability

• Consistency

import java.nio.file.Path;

import java.nio.FileSystems;

import java.nio.charset.Charset;

try {

Path filePath = FileSystems.getDefault().getPath("filename");

Charset charset = Charset.defaultCharset();

for (String line : Files.readAllLines(filePath, charset)) {

processLine(line);

}

} catch (IOException e) {

// Do something here

}

Page 22: Writing usableap isinpractice

© Asprotunity Ltd

• Abstraction level

• Working framework

• Progressive evaluation

• Penetrability

• Consistency

import java.io.File;

import java.util.Scanner;

File file = new File("filename");

try {

Scanner scanner = new Scanner(file);

while (scanner.hasNextLine()) {

String line = scanner.nextLine();

processLine(line);

}

scanner.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

}

}

Page 23: Writing usableap isinpractice

© Asprotunity Ltd

• Abstraction level

• Working framework

• Progressive evaluation

• Penetrability

• Consistency

with open("filename") as infile: for line in infile.readlines(): processLine(line)

Page 24: Writing usableap isinpractice

Some techniques

© Asprotunity Ltd

Page 25: Writing usableap isinpractice

“None of the ideas presented here are new; they are just forgotten from time to time.”

!

Alan J. Perlis, 1966 Turing Award Lecture.

© Asprotunity Ltd

Page 26: Writing usableap isinpractice

© Asprotunity Ltd

• User’s perspective

• Naming

• Explicit context

• Error reporting

• Incremental design

Page 27: Writing usableap isinpractice

© Asprotunity Ltd

• User’s perspective

• Naming

• Explicit context

• Error reporting

• Incremental design

Page 28: Writing usableap isinpractice

© Asprotunity Ltd

“Ask, ‘What Would the User Do?’ (You Are Not the User)”

!

Giles Colborne, “97 Things Every Programmer Should Know”

Page 29: Writing usableap isinpractice

Use language constructs to make intent clear

© Asprotunity Ltd

Page 30: Writing usableap isinpractice

makeTV(true, false);

© Asprotunity Ltd

From: “API Design Matters”, Michi Henning, http://queue.acm.org/detail.cfm?id=1255422

Page 31: Writing usableap isinpractice

void makeTV(bool isColor, bool isCrt);

© Asprotunity Ltd

From: “API Design Matters”, Michi Henning, http://queue.acm.org/detail.cfm?id=1255422

Page 32: Writing usableap isinpractice

enum ColorType { Color, BlackAndWhite } !

enum ScreenType { CRT, FlatScreen };

void makeTV(ColorType col, ScreenType st);

© Asprotunity Ltd

From: “API Design Matters”, Michi Henning, http://queue.acm.org/detail.cfm?id=1255422

Page 33: Writing usableap isinpractice

makeTV(Color, FlatScreen);

© Asprotunity Ltd

From: “API Design Matters”, Michi Henning, http://queue.acm.org/detail.cfm?id=1255422

Page 34: Writing usableap isinpractice

Give control to the caller

© Asprotunity Ltd

Page 35: Writing usableap isinpractice

© Asprotunity Ltd

What’s wrong with these?

public interface Startable { Startable start() throws AlreadyStartedException; } !

public interface Stoppable { Stoppable stop() throws AlreadyStoppedException; }

Page 36: Writing usableap isinpractice

© Asprotunity Ltd

A better alternative

public interface Service { void start() throws AlreadyStartedException; !

void stop() throws AlreadyStoppedException; !

boolean isStarted(); }

Page 37: Writing usableap isinpractice

© Asprotunity Ltd

TDD

• It puts you in the shoes of an user

• Outside-in development

• If writing a test is painful, the design may be wrong

• Tests will provide up to date documentation and examples of use

Page 38: Writing usableap isinpractice

© Asprotunity Ltd

TDD helps with• Abstraction level. It helps to limit the number of

abstractions in mainline scenarios

• Working framework. It helps in keeping it smaller

• Progressive evaluation. The tests themselves are written in a progressive way

• Penetrability. It provides examples on how the various components interact with each other

• Consistency. It is maintained by refactoring the code

Page 39: Writing usableap isinpractice

© Asprotunity Ltd

Back to the first example: potential test with TDD

… List<String> expectedLines = …; File file = new File(“testfile“)); assertEquals(file.readLines(), expectedLines);…

Page 40: Writing usableap isinpractice

More likely outcome

© Asprotunity Ltd

File file = new File(“filename“)); try { for (String line : file.readLines()) { processLine(line);

} }

finally { file.close() }

Page 41: Writing usableap isinpractice

© Asprotunity Ltd

BufferedReader reader; try { reader = new BufferedReader(new FileReader(“filename“)); while (true) { String line = reader.readLine(); if (line == null) { break;

} processLine(line); } } catch (Exception exc) { // Do something here... } finally { if (reader != null) { reader.close(); } }

Less likely outcome

Page 42: Writing usableap isinpractice

© Asprotunity Ltd

• User’s perspective

• Naming

• Explicit context

• Error reporting

• Incremental design

Page 43: Writing usableap isinpractice

"There are only two hard problems in Computer Science: cache invalidation, naming things and off-by-one errors.” !

Variation on a Phil Karlton quote

© Asprotunity Ltd

Page 44: Writing usableap isinpractice

© Asprotunity Ltd

Naming

• Reserve the simplest and most intuitive names for the entities used in the most common scenarios

• Pick one word per concept

• Use easy to remember conventions

• Don’t be cute!

Page 45: Writing usableap isinpractice

© Asprotunity Ltd

What not to do (1/2)

• java.io.ObjectStreamConstants

• Constants written into the Object Serialization Stream

Page 47: Writing usableap isinpractice

© Asprotunity Ltd

• User’s perspective

• Naming

• Explicit context

• Error reporting

• Incremental design

Page 48: Writing usableap isinpractice

© Asprotunity Ltd

Explicit context

• Assumptions about the external environment

• There are two kinds of context we are interested in

• Deployment context

• Runtime context

Page 49: Writing usableap isinpractice

© Asprotunity Ltd

Deployment context

• Dependencies on other APIs

• Assumptions on deployment paths

• User permissions

• etc.

Page 50: Writing usableap isinpractice

© Asprotunity Ltd

Runtime context

• Initialization (and finalization) steps

• Preconditions for calling methods (or functions) or instantiating classes

• Assumptions about global application state

Page 51: Writing usableap isinpractice

© Asprotunity Ltd

Global state

• Difficult to use in a concurrent environment

• Can make test setup extremely hard

• Can make penetrability really hard by hiding dependencies

Page 52: Writing usableap isinpractice

© Asprotunity Ltd

• User’s perspective

• Naming

• Explicit context

• Error reporting

• Incremental design

Page 53: Writing usableap isinpractice

© Asprotunity Ltd

Error reporting

• Error reporting code is important for usability

• Users need to know

• How errors are reported

• What is reported when

• What they can do about them

Page 54: Writing usableap isinpractice

© Asprotunity Ltd

Error recovery

• Make recovery easy to do

• Error codes

• Exception classes

• A mix of the above

• Text messages are usually not good enough

Page 55: Writing usableap isinpractice

Error management and reporting need careful design from the very beginning

© Asprotunity Ltd

Page 56: Writing usableap isinpractice

© Asprotunity Ltd

What is an error at one level....

...May not be an error at another one

Page 57: Writing usableap isinpractice

© Asprotunity Ltd

• User’s perspective

• Naming

• Explicit context

• Error reporting

• Incremental design

Page 58: Writing usableap isinpractice

© Asprotunity Ltd

I will contend that conceptual integrity is the most important consideration in system design. It is better to have a system omit certain anomalous features and improvements, but to reflect one set of design ideas, than to have one that contains many good but independent and uncoordinated ideas. !Fred Brooks, “The Mythical Man Month”

ptg6931361

From the Library of Giovanni Asproni

Page 59: Writing usableap isinpractice

© Asprotunity Ltd

Start specific and small

• Start with the 80% case first

• It is easier to remove constraints rather than to add them later

• YAGNI

Page 60: Writing usableap isinpractice

© Asprotunity Ltd

A caveat

• Public APIs are more difficult to refactor. In fact, some errors may actually become features

• Techniques to refactor them usually involve some form of deprecation and versioning

Page 61: Writing usableap isinpractice

Surprising study results

© Asprotunity Ltd

Page 62: Writing usableap isinpractice

© Asprotunity Ltd

A user study comparing the usability of the factory pattern and constructors in API designs found highly significant results indicating that factories are detrimental to API usability in several varied situations. The results showed that users require significantly more time (p = 0.005) to construct an object with a factory than with a constructor while performing both context-sensitive and context free tasks

From: “The Factory Pattern in API Design: A Usability Evaluation”

Factory pattern

Page 63: Writing usableap isinpractice

© Asprotunity Ltd

It was hypothesized that required parameters would create more usable and self documenting APIs by guiding programmers toward the correct use of objects and preventing errors. However, in the study, it was found that, contrary to expectations, programmers strongly preferred and were more effective with APIs that did not require constructor parameters. Participants’ behavior was analyzed using the cognitive dimensions framework, and revealing that required constructor parameters interfere with common learning strategies, causing undesirable premature commitment. !From: “Usability Implications of Requiring Parameters in Objects’ Constructors”

Constructors

Page 64: Writing usableap isinpractice

© Asprotunity Ltd

Conclusion

• User’s perspective

• Naming

• Explicit context

• Error reporting

• Incremental design

Page 65: Writing usableap isinpractice

© Asprotunity Ltd

Links• http://www.apiusability.org

• “Sometimes You Need to See Through Walls — A Field Study of Application Programming Interfaces”, Cleidson R. B. de Souza et al., http://www.ufpa.br/cdesouza/pub/p390-desouza.pdf

• “Measuring API Usability”, Steven Clarke, http://drdobbs.com/windows/184405654

• “API Design Matters”, Michi Henning, http://queue.acm.org/detail.cfm?id=1255422

• http://en.wikipedia.org/wiki/Affordance

• “How to Design a Good API and Why it Matters”, Joshua Bloch, http://lcsd05.cs.tamu.edu/slides/keynote.pdf

• “What Makes APIs Difficult to Use?”, Minhaz Fahim Zibran, http://paper.ijcsns.org/07_book/200804/20080436.pdf

• http://www.codeproject.com/Articles/8707/API-Usability-Guidelines-to-improve-your-code-ease

• “Usability Implications of Requiring Parameters in Objects’ Constructors”, Jeffrey Stylos and Steven Clarke, http://www.cs.cmu.edu/~NatProg/papers/Stylos2007CreateSetCall.pdf

• “The Factory Pattern in API Design: A Usability Evaluation”, Brian Ellis, Jeffrey Stylos, and Brad Myers, http://www.cs.cmu.edu/~NatProg/papers/Ellis2007FactoryUsability.pdf

Page 66: Writing usableap isinpractice

© Asprotunity Ltd

Books

ptg6931361

From the Library of Giovanni Asproni