Slide 1

Slide 1 text

BROKEN APIS BREAK TRUST TIPS FOR CREATING AND UPDATING APIS

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Instagram: @pug_katsu

Slide 4

Slide 4 text

WHO AM I? ▸ AWS SDK for Ruby ▸ Twitter: @alexwwood ▸ Please do tweet your comments and questions about the talk! ▸ Bad Jokes

Slide 5

Slide 5 text

AGENDA ▸ Defining APIs and Backwards Compatibility ▸ Safe API Changes ▸ Constraints and Validation ▸ Exceptions ▸ Undocumented API Contracts ▸ Conclusion: Backwards Compatibility Rules

Slide 6

Slide 6 text

DEFINITIONS

Slide 7

Slide 7 text

"THERE IS A THEORY WHICH STATES THAT IF EVER ANYONE DISCOVERS EXACTLY WHAT THE UNIVERSE IS FOR AND WHY IT IS HERE, IT WILL INSTANTLY DISAPPEAR AND BE REPLACED BY SOMETHING EVEN MORE BIZARRE AND INEXPLICABLE. THERE IS ANOTHER THEORY WHICH STATES THAT THIS HAS ALREADY HAPPENED." Douglas Adams, "Hitchhiker's Guide to the Galaxy"

Slide 8

Slide 8 text

ANATOMY OF A WEB API ▸ Resources: Things that an API manages. ▸ Shape: A structure with members. ▸ Input Shape: A top-level request shape passed to an operation. ▸ Output Shape: A top-level response shape returned from an operation. ▸ Error Shape: A structure that represents an error response from an operation. ▸ Member: Belongs to a shape, represents a property of the shape. ▸ Operation: Something exposed by the service that can be invoked. ▸ NOTE: Can all of this apply to libraries which expose an API but do not make calls to the internet? Absolutely.

Slide 9

Slide 9 text

EVOLUTION OF CLIENTS RELATIVE TO APIS CLIENT API

Slide 10

Slide 10 text

EVOLUTION OF CLIENTS RELATIVE TO APIS CLIENT V2 API V2 CLIENT V1 API V1

Slide 11

Slide 11 text

EVOLUTION OF CLIENTS RELATIVE TO APIS CLIENT V2 API V2 CLIENT V1 API V1

Slide 12

Slide 12 text

EVOLUTION OF CLIENTS RELATIVE TO APIS CLIENT V3 API V3 CLIENT V2 API V2 CLIENT V1 API V1

Slide 13

Slide 13 text

EVOLUTION OF CLIENTS RELATIVE TO APIS CLIENT V3 API V3 CLIENT V2 API V2 CLIENT V1 API V1 CLIENT V4 API V4

Slide 14

Slide 14 text

EVOLUTION OF CLIENTS RELATIVE TO APIS CLIENT V3 API V3 CLIENT V2 API V2 CLIENT V1 API V1 CLIENT V4 API V4 CLIENT ∞ API ∞

Slide 15

Slide 15 text

EVOLUTION OF CLIENTS RELATIVE TO APIS API' CLIENT CLIENT'

Slide 16

Slide 16 text

API Backward Compatibility + Client Forward Compatibility =

Slide 17

Slide 17 text

ANATOMY OF A WEB API Trip travelers [string array] description [string] flight [Flight] ...

Slide 18

Slide 18 text

ANATOMY OF A WEB API Trip travelers [string array] description [string] flight [Flight] ... SHAPE MEMBERS

Slide 19

Slide 19 text

ANATOMY OF A WEB API Trip travelers [string array] description [string] flight [Flight] ... SHAPE MEMBERS SPECIAL MEMBER

Slide 20

Slide 20 text

ANATOMY OF A WEB API Trip travelers [string array] description [string] flight [Flight] ... Flight flight_number [string] airline [string] status [string] ...

Slide 21

Slide 21 text

ANATOMY OF A WEB API ▸ GetTrip ▸ CreateTrip ▸ UpdateTrip ▸ ListTrips ▸ DeleteTrip

Slide 22

Slide 22 text

ANATOMY OF A WEB API ▸ GetTrip ▸ CreateTrip ▸ UpdateTrip ▸ ListTrips ▸ DeleteTrip ▸ GET /trips/:id ▸ POST /trips ▸ PATCH /trips/:id ▸ GET /trips ▸ DELETE /trips/:id

Slide 23

Slide 23 text

ANATOMY OF A WEB API ▸ GetTrip ▸ CreateTrip ▸ UpdateTrip ▸ ListTrips ▸ DeleteTrip ▸ GET /trips/:id ▸ POST /trips ▸ PATCH /trips/:id ▸ GET /trips ▸ DELETE /trips/:id ▸ 'trips#show' ▸ 'trips#create' ▸ 'trips#update' ▸ 'trips#index' ▸ 'trips#destroy'

Slide 24

Slide 24 text

ANATOMY OF A WEB API trip_client.get_trip(id: 123) GET /trips/123 { trip: { travelers: ["Alex"], description: "RailsConf 2018", flight: { flight_number: "DL3378", airline: "Delta", status: "Landed" } } }

Slide 25

Slide 25 text

ANATOMY OF A WEB API trip_client.get_trip(id: 123) GET /trips/123 { trip: { travelers: ["Alex"], description: "RailsConf 2018", flight: { flight_number: "DL3378", airline: "Delta", status: "Landed" } } }

Slide 26

Slide 26 text

ANATOMY OF A WEB API trip_client.get_trip(id: 123) GET /trips/123 { trip: { travelers: ["Alex"], description: "RailsConf 2018", flight: { flight_number: "DL3378", airline: "Delta", status: "Delayed" # Because of course it was. } } }

Slide 27

Slide 27 text

ANATOMY OF A WEB API trip_client.get_trip(id: 321) GET /trips/321 ResourceNotFound

Slide 28

Slide 28 text

ANATOMY OF A WEB API ▸ Resources: Things that an API manages. ▸ Shape: A structure with members. ▸ Input Shape: A top-level request shape passed to an operation. ▸ Output Shape: A top-level response shape returned from an operation. ▸ Error Shape: A structure that represents an error response from an operation. ▸ Member: Belongs to a shape, represents a property of the shape. ▸ Operation: Something exposed by the service that can be invoked. ▸ NOTE: Can all of this apply to libraries which expose an API but do not make calls to the internet? Absolutely.

Slide 29

Slide 29 text

SAFE API CHANGES

Slide 30

Slide 30 text

I MIGHT DROP THE WORLD ...IF I CHANGE HANDS. Lil' Wayne

Slide 31

Slide 31 text

ADDING & REMOVING Trip travelers [string array] description [string] flight [Flight] ...

Slide 32

Slide 32 text

ADDING & REMOVING Trip travelers [string array] description [string] flight [Flight] confirmed [boolean]

Slide 33

Slide 33 text

ADDING & REMOVING Trip travelers [string array] description [string] flight [Flight] confirmed [boolean]

Slide 34

Slide 34 text

ADDING & REMOVING Trip travelers [string array] description [string] flight [Flight] confirmed [boolean]

Slide 35

Slide 35 text

ADDING & REMOVING Trip travelers [string array] description [string] flight [Flight] confirmed [boolean] WRONG

Slide 36

Slide 36 text

ADDING & REMOVING Trip travelers [string array] description [string] flight [Flight] confirmed [booleanstring]

Slide 37

Slide 37 text

ADDING & REMOVING Trip travelers [string array] description [string] flight [Flight] confirmed [booleanstring] WRONG

Slide 38

Slide 38 text

ADDING MEMBERS Trip travelers [string array] description [string] flight [Flight] confirmed [boolean]

Slide 39

Slide 39 text

ADDING MEMBERS - NEW CLIENT trip_client = TripClient.new resp = trip_client.get_trip(id: 321) resp.travelers # => ["Alex"] resp.confirmed # => true

Slide 40

Slide 40 text

ADDING MEMBERS - OLD CLIENT trip_client = TripClient.new resp = trip_client.get_trip(id: 321) resp.travelers # => ["Alex"]

Slide 41

Slide 41 text

INPUT SHAPES - ADDING AND REMOVING ListTripsRequest next_token [string] max_results [integer] ... ...

Slide 42

Slide 42 text

INPUT SHAPES - ADDING AND REMOVING ListTripsRequest next_token [string] max_results [integer] confirmed [boolean] ...

Slide 43

Slide 43 text

INPUT SHAPES - ADDING AND REMOVING ListTripsRequest next_token [string] max_results [integer] confirmed [boolean] ...

Slide 44

Slide 44 text

INPUT SHAPES - ADDING AND REMOVING ListTripsRequest next_token [string] max_results [integer] confirmed [boolean] (Required) ...

Slide 45

Slide 45 text

INPUT SHAPES - ADDING AND REMOVING ListTripsRequest next_token [string] max_results [integer] confirmed [boolean] (Required) ... WRONG

Slide 46

Slide 46 text

INPUT SHAPES - ADDING AND REMOVING trip_client = TripClient.new resp = trip_client.list_trips

Slide 47

Slide 47 text

INPUT SHAPES - ADDING AND REMOVING trip_client = TripClient.new resp = trip_client.list_trips # TripClient::Errors::MissingRequiredParameter

Slide 48

Slide 48 text

INPUT SHAPES - ADDING AND REMOVING trip_client = TripClient.new resp = trip_client.list_trips # TripClient::Errors::MissingRequiredParameter WRONG

Slide 49

Slide 49 text

INPUT SHAPES - ADDING AND REMOVING trip_client = TripClient.new resp = trip_client.list_trips

Slide 50

Slide 50 text

INPUT SHAPES - ADDING AND REMOVING trip_client = TripClient.new resp = trip_client.list_trips # ArgumentError: missing required parameter # :confirmed

Slide 51

Slide 51 text

INPUT SHAPES - ADDING AND REMOVING trip_client = TripClient.new resp = trip_client.list_trips # ArgumentError: missing required parameter # :confirmed WRONG

Slide 52

Slide 52 text

KEY TAKEAWAYS ‣ Having your code break on you is a jarring, painful experience. Make serious efforts to avoid doing this to your users. ‣ Since some changes are harder than others, think ahead when initially launching your API to avoid the likelihood of needing to make "bad changes". ‣ For Web APIs, there is no complete way around this no matter how clever you are with clients. ‣ Community clients will be built. ‣ People will make raw, well-formed HTTP requests outside of your client libraries. ‣ Ruby does NOT get a free lunch.

Slide 53

Slide 53 text

CONSTRAINTS, VALIDATION, AND EXCEPTIONS

Slide 54

Slide 54 text

DID YOU HAVE TO DO THIS? I WAS THINKING THAT YOU COULD BE TRUSTED. Taylor Swift

Slide 55

Slide 55 text

AGAIN: NO ADDING NEW REQUIRED PARAMETERS ▸ Breaks existing code AND old clients. ▸ Removing "required" attribute from parameters is okay.

Slide 56

Slide 56 text

CONSTRAINT VALIDATION ListTripsRequest next_token [string] max_results [integer] (Maximum: 25) confirmed [boolean] ...

Slide 57

Slide 57 text

CONSTRAINT VALIDATION ListTripsRequest next_token [string] max_results [integer] (Maximum: 20) confirmed [boolean] ...

Slide 58

Slide 58 text

CONSTRAINT VALIDATION ListTripsRequest next_token [string] max_results [integer] (Maximum: 20) confirmed [boolean] ... WRONG

Slide 59

Slide 59 text

CONSTRAINT VALIDATION ListTripsRequest next_token [string] max_results [integer] (Maximum: 50) confirmed [boolean] ...

Slide 60

Slide 60 text

TERMINAL STATES Flight flight_number [string] airline [string] status [string] ...

Slide 61

Slide 61 text

TERMINAL STATES ▸ Flight.status ▸ Interim: OnTime, Delayed, Boarding, InFlight ▸ Terminal: Landed, Cancelled

Slide 62

Slide 62 text

TERMINAL STATES ▸ Flight.status ▸ Interim: OnTime, Delayed, Boarding, InFlight ▸ Terminal: Landed, LandedOnTime, LandedLate, Cancelled

Slide 63

Slide 63 text

TERMINAL STATES ▸ Flight.status ▸ Interim: OnTime, Delayed, Boarding, InFlight ▸ Terminal: Landed, LandedLate, Cancelled

Slide 64

Slide 64 text

TERMINAL STATES ▸ Flight.status ▸ Interim: OnTime, Delayed, Boarding, InFlight ▸ Terminal: Landed, LandedLate, Cancelled WRONG

Slide 65

Slide 65 text

TERMINAL STATES ▸ Flight.status ▸ Interim: OnTime, Delayed, Boarding, InFlight ▸ Terminal: Landed, Cancelled

Slide 66

Slide 66 text

TERMINAL STATES ▸ Flight.status ▸ Interim: OnTime, Delayed, Boarding, InFlight, OnApproach ▸ Terminal: Landed, Cancelled

Slide 67

Slide 67 text

EXCEPTIONS trip_client.get_trip(id: 321) GET /trips/321 ResourceNotFound

Slide 68

Slide 68 text

EXCEPTIONS begin resp = trip_client.get_trip(id: id) rescue ResourceNotFound => e logger.info("Trip #{id} does not exist.") nil end

Slide 69

Slide 69 text

EXCEPTIONS trip_client.get_trip(id: 321) GET /trips/321 ResourceNotFound || ResourceDeleted

Slide 70

Slide 70 text

EXCEPTIONS begin resp = trip_client.get_trip(id: id) rescue ResourceNotFound => e logger.info("Trip #{id} does not exist.") nil end # Suddenly this raises ResourceDeleted...

Slide 71

Slide 71 text

EXCEPTIONS begin resp = trip_client.get_trip(id: id) rescue ResourceNotFound => e logger.info("Trip #{id} does not exist.") nil end # Suddenly this raises ResourceDeleted... WRONG

Slide 72

Slide 72 text

EXCEPTIONS begin resp = trip_client.get_trip(id: id) rescue ResourceNotFound => e if e.previously_existed? logger.info("Trip #{id} previously deleted.") else logger.info("Trip #{id} does not exist.") end nil end

Slide 73

Slide 73 text

UNDOCUMENTED API CONTRACTS

Slide 74

Slide 74 text

WHEN GIVEN A CHOICE BETWEEN TWO OPTIONS, CHOOSE ALL THREE. John Carlyle

Slide 75

Slide 75 text

WITH A SUFFICIENT NUMBER OF USERS OF AN API, IT DOES NOT MATTER WHAT YOU PROMISE IN THE CONTRACT, ALL OBSERVABLE BEHAVIORS OF YOUR SYSTEM WILL BE DEPENDED ON BY SOMEBODY. Hyrum's Law

Slide 76

Slide 76 text

CONCLUSION API DESIGN RULES YOU CAN USE!

Slide 77

Slide 77 text

RELATED TALKS ▸ AWS re:Invent 2017: Embracing Change without Breaking the World (DEV319) ▸ AWS re:Invent 2017: Deploying and Managing Ruby Applications on AWS (DEV207) ▸ RailsConf 2018: The GraphQL Way: A new path for JSON APIs. ▸ Note: This talk already happened, but you can view later on YouTube for a different POV on some of these topics.

Slide 78

Slide 78 text

FOUR OUT OF FIVE DENTISTS RECOMMEND SAVING THIS SLIDE FOR REFERENCE DO ▸ APIs ▸ Add members/shapes ▸ Add intermediate workflow states ▸ Add detail to exceptions ▸ Add new opt-in exceptions ▸ Loosening constraints ▸ Clients ▸ Forward compatible (support all of the above) ▸ Focus on discoverability ▸ APIs ▸ Remove/rename members and shapes. ▸ Change member types ▸ Add new terminal workflow states ▸ Add new exceptions without opt-in ▸ Tighten constraints ▸ Clients ▸ Validate API constraints DO NOT