Slide 1

Slide 1 text

@PeterHilton http://hilton.org.uk/ Flat HTTP API design (and documentation)

Slide 2

Slide 2 text

I want two* things: 1. good HTTP API docs 2. good HTTP APIs * good DX !3 @PeterHilton •

Slide 3

Slide 3 text

Why programmers deconstruct APIs

Slide 4

Slide 4 text

public Random(long seed) Создает новый генератор случайных чисел, используя сингл long семя. Семя является начальным значением внутреннего состояния генератора псевдослучайного числа, который сохраняется методом next(int). Параметры: seed - начальное семя См. Также: setSeed(long) Случайный

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

MARK: Raúl, come with me and I’ll introduce you to a few people. RAUL: OK. MARK: Have you met Lucy? LUCY: Yes, we’ve already met. MARK: OK, in that case, meet Sam. Sam and I were at college together. Sam, this is Raúl. Raúl’s a mate of mine from Spain.

Slide 7

Slide 7 text

introduce(subject, target) Introduces one person to someone they might not know. Parameters: subject - the person to introduce target - the person to introduce the subject to See also: introduce-formally Introduction

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

public Random(long seed)   long   # "  ! $  next(int) # ݇හ: seed -  ݚ᧗݇ᥠ: setSeed(long) Random

Slide 12

Slide 12 text

The great HTTP takeover Then CD-ROMs e-mail IRC middleware classes
 Now web pages webmail web-based chat cloud APIs microservices !13 @PeterHilton • ➔

Slide 13

Slide 13 text

What’s wrong with 
 normal API docs

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

@PeterHilton • !17 Elsewhere: Example response Host HTTP method/URL Purpose Parameters Response status Response body

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

@PeterHilton • Elsewhere: Host Authentication Content types Errors !19 Purpose HTTP method, URL Authorisation Parameters Request body Response status Response body

Slide 19

Slide 19 text

Flat HTTP API docs (FHAD)

Slide 20

Slide 20 text

Can’t you generate it? NO!!!

Slide 21

Slide 21 text

http --verbose \ --auth hilton:02ef1e29e3300b94532f \ POST https://api.github.com/user/repos \ < github-repo.json HTTPie.org command line

Slide 22

Slide 22 text

raw HTTP session POST /user/repos HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Authorization: Basic aGlsdG9uOm5vdG15YWN0dWFscGFzc3dvcmQ= Connection: keep-alive Content-Length: 202 Content-Type: application/json Host: api.github.com User-Agent: HTTPie/0.9.6 { "description": "Flat HTTP API Documentation", "has_issues": true, "has_projects": false, "has_wiki": false, "homepage": "https://hilton.org.uk/fhad/", "name": "FHAD", "private": false }

Slide 23

Slide 23 text

POST /user/repos HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Authorization: Basic aGlsdG9uOm5vdG15YWN0dWFscGFzc3dvcmQ= Connection: keep-alive Content-Length: 202 Content-Type: application/json Host: api.github.com User-Agent: HTTPie/0.9.6 { "description": "Flat HTTP API Documentation", "has_issues": true, "has_projects": false, "has_wiki": false, "homepage": "https://hilton.org.uk/fhad/", "name": "FHAD", "private": false } HTTP/1.1 201 Created Access-Control-Allow-Origin: * Access-Control-Expose-Headers: ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval Cache-Control: private, max-age=60, s-maxage=60 Content-Length: 4616 Content-Security-Policy: default-src 'none' Content-Type: application/json; charset=utf-8 Date: Fri, 21 Sep 2018 12:08:36 GMT ETag: "429600554274cc04f28bb186c3a993de" Location: https://api.github.com/repos/hilton/FHAD Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin Server: GitHub.com Status: 201 Created Strict-Transport-Security: max-age=31536000; includeSubdomains; preload Vary: Accept, Authorization, Cookie, X-GitHub-OTP X-Accepted-OAuth-Scopes: public_repo, repo X-Content-Type-Options: nosniff X-Frame-Options: deny X-GitHub-Media-Type: github.v3 X-GitHub-Request-Id: 6F3C:4E27:1593928:2F06A0A:5BA4DF44 X-OAuth-Scopes: gist, notifications, public_repo, read:user X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4998 X-RateLimit-Reset: 1537535112 X-Runtime-rack: 0.633194 X-XSS-Protection: 1; mode=block { "allow_merge_commit": true, "allow_rebase_merge": true, "allow_squash_merge": true, "archive_url": "https://api.github.com/repos/hilton/FHAD/{archive_format}{/ref}", "archived": false, "assignees_url": "https://api.github.com/repos/hilton/FHAD/assignees{/user}", "blobs_url": "https://api.github.com/repos/hilton/FHAD/git/blobs{/sha}", "branches_url": "https://api.github.com/repos/hilton/FHAD/branches{/branch}", "clone_url": "https://github.com/hilton/FHAD.git", "collaborators_url": "https://api.github.com/repos/hilton/FHAD/collaborators{/collaborator}", "comments_url": "https://api.github.com/repos/hilton/FHAD/comments{/number}", "commits_url": "https://api.github.com/repos/hilton/FHAD/commits{/sha}", "compare_url": "https://api.github.com/repos/hilton/FHAD/compare/{base}...{head}", "contents_url": "https://api.github.com/repos/hilton/FHAD/contents/{+path}", "contributors_url": "https://api.github.com/repos/hilton/FHAD/contributors", "created_at": "2018-09-21T12:08:36Z", "default_branch": "master", "deployments_url": "https://api.github.com/repos/hilton/FHAD/deployments", "description": "Flat HTTP API Documentation", "downloads_url": "https://api.github.com/repos/hilton/FHAD/downloads", "events_url": "https://api.github.com/repos/hilton/FHAD/events", "fork": false, "forks": 0, "forks_count": 0, "forks_url": "https://api.github.com/repos/hilton/FHAD/forks", "full_name": "hilton/FHAD", "git_commits_url": "https://api.github.com/repos/hilton/FHAD/git/commits{/sha}", "git_refs_url": "https://api.github.com/repos/hilton/FHAD/git/refs{/sha}", "git_tags_url": "https://api.github.com/repos/hilton/FHAD/git/tags{/sha}", "git_url": "git://github.com/hilton/FHAD.git", "has_downloads": true, "has_issues": true, "has_pages": false, "has_projects": false, "has_wiki": false, "homepage": "https://hilton.org.uk/fhad/", "hooks_url": "https://api.github.com/repos/hilton/FHAD/hooks", "html_url": "https://github.com/hilton/FHAD", "id": 149758160, "issue_comment_url": "https://api.github.com/repos/hilton/FHAD/issues/comments{/number}", "issue_events_url": "https://api.github.com/repos/hilton/FHAD/issues/events{/number}", "issues_url": "https://api.github.com/repos/hilton/FHAD/issues{/number}", "keys_url": "https://api.github.com/repos/hilton/FHAD/keys{/key_id}", "labels_url": "https://api.github.com/repos/hilton/FHAD/labels{/name}", "language": null, "languages_url": "https://api.github.com/repos/hilton/FHAD/languages", "license": null, "merges_url": "https://api.github.com/repos/hilton/FHAD/merges", "milestones_url": "https://api.github.com/repos/hilton/FHAD/milestones{/number}", "mirror_url": null, "name": "FHAD", "network_count": 0, "node_id": "MDEwOlJlcG9zaXRvcnkxNDk3NTgxNjA=", "notifications_url": "https://api.github.com/repos/hilton/FHAD/notifications{?since,all,participating}", "open_issues": 0, "open_issues_count": 0, "owner": { "avatar_url": "https://avatars1.githubusercontent.com/u/232614?v=4", "events_url": "https://api.github.com/users/hilton/events{/privacy}", "followers_url": "https://api.github.com/users/hilton/followers", "following_url": "https://api.github.com/users/hilton/following{/other_user}", "gists_url": "https://api.github.com/users/hilton/gists{/gist_id}", "gravatar_id": "", "html_url": "https://github.com/hilton", "id": 232614, "login": "hilton", "node_id": "MDQ6VXNlcjIzMjYxNA==", "organizations_url": "https://api.github.com/users/hilton/orgs", "received_events_url": "https://api.github.com/users/hilton/received_events", "repos_url": "https://api.github.com/users/hilton/repos", "site_admin": false, "starred_url": "https://api.github.com/users/hilton/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/hilton/subscriptions", "type": "User", "url": "https://api.github.com/users/hilton" }, "permissions": { "admin": true, "pull": true, "push": true }, "private": false, "pulls_url": "https://api.github.com/repos/hilton/FHAD/pulls{/number}", "pushed_at": "2018-09-21T12:08:36Z", "releases_url": "https://api.github.com/repos/hilton/FHAD/releases{/id}", "size": 0, "ssh_url": "[email protected]:hilton/FHAD.git", "stargazers_count": 0, "stargazers_url": "https://api.github.com/repos/hilton/FHAD/stargazers", "statuses_url": "https://api.github.com/repos/hilton/FHAD/statuses/{sha}", "subscribers_count": 1, "subscribers_url": "https://api.github.com/repos/hilton/FHAD/subscribers", "subscription_url": "https://api.github.com/repos/hilton/FHAD/subscription", "svn_url": "https://github.com/hilton/FHAD", "tags_url": "https://api.github.com/repos/hilton/FHAD/tags", "teams_url": "https://api.github.com/repos/hilton/FHAD/teams", "trees_url": "https://api.github.com/repos/hilton/FHAD/git/trees{/sha}", "updated_at": "2018-09-21T12:08:36Z", "url": "https://api.github.com/repos/hilton/FHAD", "watchers": 0, "watchers_count": 0 }

Slide 24

Slide 24 text

POST /user/repos HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Authorization: Basic aGlsdG9uOm5vdG15YWN0dWFscGFzc3dvcmQ= Connection: keep-alive Content-Length: 202 Content-Type: application/json Host: api.github.com User-Agent: HTTPie/0.9.6 HTTP/1.1 201 Created Access-Control-Allow-Origin: * Access-Control-Expose-Headers: ETag, Link, Retry-After, X-GitHub-OTP, 
 X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, 
 X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval Cache-Control: private, max-age=60, s-maxage=60 Content-Length: 4616 Content-Security-Policy: default-src 'none' Content-Type: application/json; charset=utf-8 Date: Fri, 21 Sep 2018 12:08:36 GMT ETag: "429600554274cc04f28bb186c3a993de" Location: https://api.github.com/repos/hilton/FHAD Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin Server: GitHub.com Status: 201 Created Strict-Transport-Security: max-age=31536000; includeSubdomains; preload Vary: Accept, Authorization, Cookie, X-GitHub-OTP X-Accepted-OAuth-Scopes: public_repo, repo X-Content-Type-Options: nosniff X-Frame-Options: deny X-GitHub-Media-Type: github.v3 X-GitHub-Request-Id: 6F3C:4E27:1593928:2F06A0A:5BA4DF44 X-OAuth-Scopes: gist, notifications, public_repo, read:user X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4998 X-RateLimit-Reset: 1537535112 X-Runtime-rack: 0.633194 X-XSS-Protection: 1; mode=block
 POST /user/repos HTTP/1.1 Authorization: Basic aGlsdG9uOm5vdG15YWN0dWFscGFzc3dvcmQ= Content-Type: application/json Host: api.github.com HTTP/1.1 201 Created Content-Length: 4616 Content-Type: application/json; charset=utf-8 Date: Fri, 21 Sep 2018 12:08:36 GMT ETag: "429600554274cc04f28bb186c3a993de" Location: https://api.github.com/repos/hilton/FHAD X-Accepted-OAuth-Scopes: public_repo, repo X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4998 X-RateLimit-Reset: 1537535112

Slide 25

Slide 25 text

Create Create a new repository for the authenticated user. POST /user/repos HTTP/1.1 Authorization: Basic aGlsdG9uOm5vdG15YWN0dWFscGFzc3dvcmQ= Content-Type: application/json Host: api.github.com { "description": "Flat HTTP API Documentation", "has_issues": true, "has_projects": false, "has_wiki": false, "homepage": "https://hilton.org.uk/fhad/", "name": "FHAD", "private": false } HTTP/1.1 201 Created Content-Length: 4616 Content-Type: application/json; charset=utf-8 Date: Fri, 21 Sep 2018 12:08:36 GMT ETag: "429600554274cc04f28bb186c3a993de" Location: https://api.github.com/repos/hilton/FHAD X-Accepted-OAuth-Scopes: public_repo, repo X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4998 X-RateLimit-Reset: 1537535112 { "allow_merge_commit": true, "allow_rebase_merge": true, "allow_squash_merge": true, "archive_url": "https://api.github.com/repos/hilton/FHAD/{archive_format}{/ref}", "archived": false, "assignees_url": "https://api.github.com/repos/hilton/FHAD/assignees{/user}", "blobs_url": "https://api.github.com/repos/hilton/FHAD/git/blobs{/sha}", "branches_url": "https://api.github.com/repos/hilton/FHAD/branches{/branch}", "clone_url": "https://github.com/hilton/FHAD.git", "collaborators_url": "https://api.github.com/repos/hilton/FHAD/collaborators{/collaborator}", "comments_url": "https://api.github.com/repos/hilton/FHAD/comments{/number}", "commits_url": "https://api.github.com/repos/hilton/FHAD/commits{/sha}", "compare_url": "https://api.github.com/repos/hilton/FHAD/compare/{base}...{head}", "contents_url": "https://api.github.com/repos/hilton/FHAD/contents/{+path}", "contributors_url": "https://api.github.com/repos/hilton/FHAD/contributors", "created_at": "2018-09-21T12:08:36Z", "default_branch": "master", "deployments_url": "https://api.github.com/repos/hilton/FHAD/deployments", Purpose HTTP method/URL Authentication Host Parameters Response status Authorisation Rate limits Response body

Slide 26

Slide 26 text

Create Create a new repository for the authenticated user. Request POST /user/repos HTTP/1.1 Authorization: Basic aGlsdG9uOm5vdWFscGFzc3dvcmQ= Content-Type: application/json Host: api.github.com { "description": "Flat HTTP API Documentation", "has_issues": true, "has_projects": false, "has_wiki": false, "homepage": "https://hilton.org.uk/fhad/", "name": "FHAD", "private": false }

Slide 27

Slide 27 text

Type Name Purpose Value header Authorization HTTP Basic Authentication GitHub username and personal access token or OAuth token instead of your password Request POST /user/repos HTTP/1.1 Authorization: Basic aGlsdG9uOm5vdWFscGFzc3dvcmQ= Content-Type: application/json Host: api.github.com { "description": "Flat HTTP API Documentation", "has_issues": true, "has_projects": false, "has_wiki": false, "homepage": "https://hilton.org.uk/fhad/", "name": "FHAD", "private": false } Request parameters

Slide 28

Slide 28 text

Random GIF Returns a random GIF, limited by tag. Excluding the tag parameter will return a random GIF from the GIPHY catalog. Request GET /v1/gifs/random?tag=cat&rating=g&api_key=Zz39DYlRU00XtxoCg HTTP/1.1 Accept: application/json Host: api.giphy.com Request parameters Type Name Purpose Values/example query tag Filter results by tag cat query rating MPAA rating filter y, g, pg, pg-13, r, unrated, nsfw query api_key GIPHY API Key Zz39DYlRU00XtxoCgCVGhMIT6yQSBPeG

Slide 29

Slide 29 text

GIF by ID Returns a GIF given that GIF’s unique ID. Request GET /v1/gifs/heIX5HfWgEYlW?api_key=Zz39DYlRU00XtxoCg HTTP/1.1 Accept: application/json Host: api.giphy.com Request parameters Type Name Purpose Value/example path ??????? GIF ID heIX5HfWgEYlW query api_key GIPHY API Key Zz39DYlRU00XtxoCg

Slide 30

Slide 30 text

GIF by ID Returns a GIF given that GIF’s unique ID. Request GET /v1/gifs/heIX5HfWgEYlW?api_key=Zz39DYlRU00XtxoCg HTTP/1.1 Accept: application/json Host: api.giphy.com Request parameters Value/example Purpose heIX5HfWgEYlW GIF ID Zz39DYlRU00XtxoCg GIPHY API Key

Slide 31

Slide 31 text

GIPHY API Key GIF ID HTTP/1.1 200 OK Content-Type: application/json Date: Fri, 28 Sep 2018 13:48:08 GMT X-RateLimit-Limit-day: 1000 X-RateLimit-Limit-hour: 42 X-RateLimit-Remaining-day: 999 X-RateLimit-Remaining-hour: 41 { "data": { "bitly_gif_url": "https://gph.is/1TV6U7O", "bitly_url": "https://gph.is/1TV6U7O", GIF by ID Returns a GIF given that GIF’s unique ID. GET /v1/gifs/heIX5HfWgEYlW?api_key=Zz39DYlRU00XtxoCg HTTP/1.1 Accept: application/json Host: api.giphy.com

Slide 32

Slide 32 text

HTTP/1.1 200 OK Content-Type: application/json Date: Fri, 28 Sep 2018 13:48:08 GMT X-RateLimit-Limit-day: 1000 X-RateLimit-Limit-hour: 42 X-RateLimit-Remaining-day: 999 X-RateLimit-Remaining-hour: 41 { "data": { "bitly_gif_url": "https://gph.is/1TV6U7O", "bitly_url": "https://gph.is/1TV6U7O", "caption": "Cat writing API documentation", "content_url": "", "embed_url": "https://giphy.com/embed/heIX5HfWgEYlW", "fixed_height_downsampled_height": "200", "fixed_height_downsampled_url": "https://media1.giphy.com/media/heIX5HfWgEYlW "fixed_height_downsampled_width": "200", "fixed_height_small_height": "100", "fixed_height_small_still_url": "https://media1.giphy.com/media/heIX5HfWgEYlW "fixed_height_small_url": "https://media1.giphy.com/media/heIX5HfWgEYlW/100.g "fixed_height_small_width": "100", "fixed_width_downsampled_height": "200",

Slide 33

Slide 33 text

GIPHY API Key Excluding this parameter will return a random GIF from the GIPHY catalog. MPAA rating filter Filter results by tag Random GIF Returns a random GIF, limited by tag. GET /v1/gifs/random?tag=cat&rating=g&api_key=Zz39DYlRU00XtxoCg HTTP/1.1 Accept: application/json Host: api.giphy.com

Slide 34

Slide 34 text

GIPHY API Key Excluding this parameter will return a random GIF from the GIPHY catalog. MPAA rating filter Filter results by tag Random GIF Returns a random GIF, limited by tag. GET /v1/gifs/random?tag=cat&rating=g&api_key=Zz39DYlRU00XtxoCg HTTP/1.1 Accept: application/json Host: api.giphy.com

Slide 35

Slide 35 text

Authentication Basic Authentication using your GitHub username and personal access token or OAuth token instead of your password. Create Create a new repository for the authenticated user. POST /user/repos HTTP/1.1 Authorization: Basic aGlsdG9uOm5vdWFscGFzc3dvcmQ= Content-Type: application/json Host: api.github.com { "description": "Flat HTTP API Documentation", "has_issues": true, "has_projects": false, "has_wiki": false, "homepage": "https://hilton.org.uk/fhad/", "name": "FHAD", "private": false }

Slide 36

Slide 36 text

Image width Image height Cat API by Sara Vieira for ReactJSGirls Photo by Jari Hytönen on Unsplash Placeholder cats Returns an image with optional width and height. GET /placeholder/800/400 HTTP/1.1 Connection: keep-alive Host: www.catis.life HTTP/1.1 200 OK Content-Type: image/jpeg Date: Sun, 30 Sep 2018 14:32:40 GMT

Slide 37

Slide 37 text

Flat HTTP API documentation 1. Literal request and response 2. Parameter annotations 3. Proper layout and typography 4. API design warnings 5. Less effort to write 6. Probably easier to consume 7. Good HTTP APIs by example !38 @PeterHilton •

Slide 38

Slide 38 text

API design checklist (honest API docs)

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

When you show the 
 raw HTTP session, API ugliness has nowhere to hide… !41 @PeterHilton •

Slide 41

Slide 41 text

⚠ If the request URL is not valid, the response status is 500 Server Error instead of 404 Not Found. Retrieve evil plan Fetches the specified world domination plan. GET /plans/world-domination/42 HTTP/1.1 Accept: text/xml HTTP/1.1 500 Internal Server Error Content-Type: text/xml 666_INVALID_EVIL_PLAN_ID

Slide 42

Slide 42 text

⚠ A valid request URL with the wrong HTTP method returns a 500 Server Error status instead of 405 Method Not Allowed. Delete evil plan Discontinue a world domination plan. DELETE /plans/world-domination/001 HTTP/1.1 Accept: text/xml HTTP/1.1 500 Internal Server Error Content-Type: text/xml 666_DELETE_EVIL_PLANS_WITH_POST

Slide 43

Slide 43 text

Execute evil plan Fetches the specified world domination plan. POST /plans/world-domination/01 HTTP/1.1 Accept: text/xml HTTP/1.1 200 OK Content-Type: text/xml 666_ERROR_ERROR_ERROR ⚠ Don’t expect to receive a 400 Bad Request status for an invalid request. You’re probably going to get 500 Server Error, or maybe 200 OK.

Slide 44

Slide 44 text

API design warnings checklist - client errors 1. 404 Not Found for URL paths that don’t match a back-end 2. 404 Not Found for URL paths that include an invalid ID 3. 405 Method Not Allowed for a valid URL but an unsupported method, e.g. GET /p/login 4. 400 Bad Request for other request errors, e.g. the wrong request Content-type or a missing required parameter !45 @PeterHilton •

Slide 45

Slide 45 text

" Creating a new widget returns a 200 OK status instead of 201 Created. The URL is in the response body instead of a Location response header. Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: text/xml Accept: text/xml World domination HTTP/1.1 200 OK Content-Type: text/xml

Slide 46

Slide 46 text

# Our back-end framework is rubbish, but at least the right status is in the JSON Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: text/xml Accept: application/json World domination HTTP/1.1 200 OK Content-Type: application/json { "status":201, "message":"Created", "location":"world-domination/02" }

Slide 47

Slide 47 text

API design warnings checklist - more status codes 5. 201 Created after creating a new object, with a Location response header that contains the new object’s URL 6. Don’t return 200 OK when the client sent a bad request, 
 or when there was a server-side error 7. If the back-end framework is rubbish, at least include the expected status text, e.g. Created, in the response !48 @PeterHilton •

Slide 48

Slide 48 text

$ Let’s be realistic - the API is just going to ignore your Accept request header, and always give you XML (or worse). Want 406 Not Acceptable instead? Dream on! Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: text/xml Accept: application/json World domination HTTP/1.1 200 OK Content-Type: text/xml

Slide 49

Slide 49 text

% The response body is a JSON array… in random order. List evil plans Lists all published plans. GET /plans HTTP/1.1 Accept: application/json HTTP/1.1 200 OK Content-Type: application/json [ {"id":"01","title":"World domination"} {"id":"07","title":"Kill Bond"} {"id":"03","title":"Make SOAP great again"} {"id":"04","title":"Teach UML in schools"} {"id":"02","title":"Defend the patriarchy"} {"id":"06","title":"Roll out SAFe"} {"id":"08","title":"Mandate open plan offices"} {"id":"05","title":"Something with blockchain"} ]

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

& The request requires JSON embedded in form encoding. (and we’ve fixed most of the encoding bugs) Create evil plan Publishes a new world domination plan. POST /plans HTTP/1.1 Content-type: text/xml Accept: application/json title=World%20domination&
 plan=%7B%22summar%22%3A%22world+domination%22%2C %22scope%22%3A%22WORLD%22%2C%22owner%22%3A%22Dr+ Evil%22%7D HTTP/1.1 201 Created Location: /plans/world-domination/02

Slide 52

Slide 52 text

API design warnings checklist - JSON 8. Always send a JSON response body when the request included an Accept: application/json header 9. Always sort JSON arrays in responses rather than using random order 10. Don’t mix data formats (e.g. XML and JSON) in the same HTTP resource representation !53 @PeterHilton •

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

The future of software documentation

Slide 55

Slide 55 text

@PeterHilton • Back in 2003… !56

Slide 56

Slide 56 text

Head First Java

Slide 57

Slide 57 text

The future of documentation Markdown, reStructuredText and AsciiDoc remove barriers to actually writing the words and spaces Documentation generation pipelines help reduce the cost of layout and production … but there’s more to docs than all that. !58 @PeterHilton •

Slide 58

Slide 58 text

@PeterHilton http://hilton.org.uk/ How to write 
 maintainable code (company training)