developers are users too #yourapisucks

28
Developers are users too Why the user experience of your API sucks @craignicol #yourapisucks

Upload: craig-nicol

Post on 22-Jan-2018

203 views

Category:

Software


2 download

TRANSCRIPT

Developers are users tooWhy the user experience of your API sucks

@craignicol #yourapisucks

Developing Android Apps With Xamarin in C#

http://bit.ly/xamarin-edin

2 Days : 10th – 11th November, Edinburgh

Discount code to get an additional £100 off early bird tickets : EB100

One month to the deadline...

Talk my language

Using your domain terms rather than

mine

Line1: 1A Queen Anne Drive,

Line2: Lochend Ind Estate

City: Edinburgh

Postcode: EH28 8PL

Postcode: EH28 8PL

Easting: 8212096

Northing: -6213914Postcode: EH28 8PL

Weakest link

Using basic, or no authentication. You

should not be my weakest link

http://www.example.com/api/pay

?cardnumber=12341234123412

34&ccv=1234&expirymonth=12&

expiryyear=12&amount=123456

Open kimono

Unsecured authentication

http://www.example.com/api

/pay?username=bob&passw

ord=supersecret

Side effects

Not understanding idempotence. I can

just about forgive post everything, but

not get with side effects

http://www.example.com/api/createTransaction?...

Data obesity

Don’t assume fat pipes, send as little

data as possible

Busy waiting?

Don’t keep the caller waiting. If it’s

going to take a while, use callbacks or

another form of notification.

Exceptional validation

Returning exceptions as validation.

Validate everything, and return

multiple errors.

And don’t return errors on an

alternative channel (such as email).

NOT:

HTTP 500 - Credit Card number invalid

BETTER:

HTTP 400

{

validationErrors: [

{“CreditCardNumber” : “Must be 16

characters”}

{“ExpiryDate” : “Must be in the future”}

{“CCV” : “Is required”}

]

}

https://http.cat/404

A forest of confusion

Multiple, inconsistent APIs, especially

inconsistent authentication

GET /cat

{ Name : “Garfield” }

GET /food/index.php/fooditem/1

🍌

Inconsistent behaviour

Inconsistent behavior

GET /cat

{ Name : “Garfield” }

GET /cat

{ Name : “Jatt” }

GET /cat

{ Name : “Lion-o” }

GET /cat

🍌

Breaking the contract

Adding versioning to an existing API,

poorly - breaking the contract. See

http://www.troyhunt.com/2014/02/yo

ur-api-versioning-is-wrong-which-

is.html

Today:

http://www.example.com/api/getAddress?postcode=

EH28%208PL

Tomorrow:

HTTP POST

http://www.example.com/api/getAddress

X-www-form-urlencoded

API-version: “v1.1”

Search-type: “postcode-lookup”

Input: “EH28 8PL”

Late notice

Or even negative notice of changes -

“we switched this off 2 weeks ago”

⌛🚫

Not Invented Here

Not Invented Here

https://xkcd.com/927/

Verbosity compounds reducible complexity

E.g. Win32 API is often cryptic and

long-winded

Use sensible defaults

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));wcex.hCursor = LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName = NULL;wcex.lpszClassName = szWindowClass;wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

if (!RegisterClassEx(&wcex)){

MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 Guided Tour"), NULL);return 1;

}

hInst = hInstance; // Store instance handle in our global variableHWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,500, 100, NULL, NULL, hInstance, NULL );

if (!hWnd){

MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 Guided Tour"), NULL);

return 1;}

ShowWindow(hWnd,nCmdShow);UpdateWindow(hWnd);

MSG msg;while (GetMessage(&msg, NULL, 0, 0)){

TranslateMessage(&msg);DispatchMessage(&msg);

}return (int) msg.wParam;

}

HWND hWnd = CreateWindow(

szWindowClass,szTitle,

WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,500, 100,NULL,NULL,hInstance,NULL

);

https://msdn.microsoft.com/en-

us/library/bb384843.aspx

Errors as a side effect

Return code is a constant, errors are a

side effect (HTTP 200, or return -1 in

C)

Success : ERROR -

Credit Card Expiry Date is in the past

Illegal Character

Bad security - ask anyone with a Flat

a/b address or a Paddy O'Malley name

Hidden Prerequisites

Hidden prerequisites - throwing an

error when submitting an order

because you didn't call

ExpectCreditCard first

You said what?

Documentation out of sync with code.

Try swashbuckling

getPostcode(double opt)

This method takes returns

the current longitude and

latitude of the user based

on the information provided

by their device.

Sucking Eggs

Teaching Granny to suck eggs

getAddressFromPostcode

(string postcode)

This method takes a

postcode and returns the

associated address.

Be Explicit

Documentation should be explicit

Use the platform

Legacy models VS JSON-native {

Name : “Your Woman”

Address : “<Line1>My House</Line1>

<Line2>My Street</Line2>

<Town>White Town</Town>

<Postcode>EE11EE</Postcode>”

}

Strawman Testing

Test data / API out of sync with live

Testing the wrong thing

Planet Earth (for testing only)

Tests doomed to succeed

Incomplete test data: imagine

postcode lookup where test addresses

are all SW...

No Flat x/y

No BFPO

No Irish or other EU addresses

Takeaways : avoiding the axe

User first : Design from the outside in

If there’s a standard, use it

Be consistent

Be clear

Be helpful, but only when asked

Over to youHave you seen an API that

sucked?

https://craignicol.wordpress.com

https://twitter.com/craignicol

https://plus.google.com/+CraigNicolGeek

Continue the discussion at theCodeCraftConf, 16th September at CitizenM : http://conf.codecraftuk.org/

10% Ticket Discount with code DevsUsersToo

I’ll be guiding a session on Writing Usable APIs