Zen and the Art of API Maintenance

54b75f6fbf4434162bfcda6b0cb9b86b?s=47 John Sheehan
November 22, 2013

Zen and the Art of API Maintenance

Presented at Ultimate Developer's Conference #UDE2013 on November 22, 2013

54b75f6fbf4434162bfcda6b0cb9b86b?s=128

John Sheehan

November 22, 2013
Tweet

Transcript

  1. John Sheehan CEO, Runscope Building API integrations you can live

    with. Friday, November 22, 13
  2. “There's just no getting around it: you're building a distributed

    system. -- Mark Cavage, ACM Queue Friday, November 22, 13
  3. Mobile & Desktop Apps Web Sites & Applications Internal APIs

    3rd-party Service APIs Public APIs Friday, November 22, 13
  4. Mobile & Desktop Apps Web Sites & Applications Internal APIs

    3rd-party Service APIs Public APIs fn(){} fn(){} fn(){} fn(){} fn(){} Friday, November 22, 13
  5. fn(){} fn(){} fn(){} fn(){} fn(){} YOUR APPLICATION Friday, November 22,

    13
  6. What are the challenges for running an API-driven application? Friday,

    November 22, 13
  7. CHALLENGE #1 Getting a complete picture of your app. Friday,

    November 22, 13
  8. Keep an eye on everything. Friday, November 22, 13

  9. CHALLENGE #2 Managing change. Friday, November 22, 13

  10. Prefer lighter abstractions. Friday, November 22, 13

  11. Think twice about taking a dependency on an SDK. Friday,

    November 22, 13
  12. Your Code The API API SDK v1 v2 Friday, November

    22, 13
  13. Your Code The API API SDK ??? ??? Friday, November

    22, 13
  14. SDKs OK: Prototyping No good HTTP Client Building Clients Complex

    APIs Uses Native APIs Friday, November 22, 13
  15. SDK DANGER: Using more than one Community-built Many dependencies Inactive

    Friday, November 22, 13
  16. HOW WE DO Smarter HTTP client Thin wrappers Friday, November

    22, 13
  17. url = "https://ec2-23-212-199-23.us-west-2.amazo resp = requests.get(url) if resp.ok: return resp.json()

    else: # retry? return None def get_user(id): def main(): user = get_user(id); print user["name"] Friday, November 22, 13
  18. class Identity def get_user(id): url = "service://identity/users/" + id resp

    = smart_client.get(url) if resp.ok: return User(resp.json()) return AnonymousUser() from Identity import get_user def main(): user = get_user(id); print user.name Auto-locate service Smart retries Friday, November 22, 13
  19. CHALLENGE #3 High-fidelity testing. Friday, November 22, 13

  20. Let’s look at some API testing frameworks. Friday, November 22,

    13
  21. require  'rubygems' require  'test/unit' require  'vcr' VCR.configure  do  |c|  

     c.cassette_library_dir  =  'fixtures/vcr_cassettes'    c.hook_into  :webmock  #  or  :fakeweb end class  VCRTest  <  Test::Unit::TestCase    def  test_example_dot_com        VCR.use_cassette('synopsis')  do              url  =  'http://yourapihere.com'            response  =  Net::HTTP.get_response(URI(url))            assert_match  /Example  domains/,  response.body        end    end end VCR github.com/vcr/vcr Friday, November 22, 13
  22. mocky.io Friday, November 22, 13

  23. frisby.js Friday, November 22, 13

  24. aspec github.com/songkick/aspec # no users have pending notifications GET /users/with-pending-notifications

    200 application/json [] # users with events on their calendar have pending notifications POST /users/764/metro-areas/999 204 POST /users/764/artists/123 204 POST /events/5?artist_ids=123&metro_area_id=999 204 POST /events/5/enqueue-notifications 204 GET /users/with-pending-notifications 200 application/json [[764, "ep"]] # users are unique in the response POST /users/764/artists/123 204 POST /users/764/artists/456 204 POST /users/764/metro-areas/999 204 POST /events/5?artist_ids=123,456&metro_area_id=999 204 POST /events/5/enqueue-notifications 204 GET /users/with-pending-notifications 200 application/json [[764, "ep"]] Friday, November 22, 13
  25. service  "http://localhost:4567"  do    def  responds_with_json_where        JSON.parse(response.body)

       end    resource  "/lolz"  do        get  do            it  {  responds_with.status  :ok  }            it  {  responds_with_json_where['lolz'].must_be_instance_o            with_query("q=monorail")  do                it  "only  lists  lolz  that  match  the  query"  do                    responds_with_json_where['lolz'].wont_be_empty                    responds_with_json_where['lolz'].each  do  |lol|                        lol['title'].must_match  /monorail/                    end                end            end HyperSpec Friday, November 22, 13
  26. Friday, November 22, 13

  27. Friday, November 22, 13

  28. Friday, November 22, 13

  29. Friday, November 22, 13

  30. Free sign up, free t-shirt: runscope.com/ts Friday, November 22, 13