Michael Sullivan
Mar 12, 2018 · 4 min read

When I started my career, it was easy to test a new endpoint: you pointed your browser at the URL for that specific page in the app. Then, inevitably, when your code did something unexpected, you had the output right there.

Later on, it became more a matter of using the javascript console in your web browser to pull the data, as we moved from a “generate HTML” model to a “javascript client pulls from an API” model. But still, you were the developer of both the front end and the back end, and testing your code was relatively easy.

When I jumped to mobile, things got messier. Not only do most engineers specialize in only server-side or client-side code, smartphones are inherently more locked down than browsers. There’s no equivalent of a javascript console in a smartphone app. This has resulted in situations where the API engineer has said to the mobile engineer, “Okay, I think that the endpoint is ready for you” without anyone having ever actually hit the endpoint. Tests are great, but they only go so far.

How I feel every time an endpoint has a stupid error

Some people handle this problem with curl, but I’ve always found that cumbersome. Just keeping an auth token, not to mention a handful of UUIDs for a database, or other state, gets complicated and time-consuming to handle manually. Some developers of my acquaintance save their state in environment variables, but I find bash scripting annoying.

That’s why, in my last two jobs, I’ve created simple API consumers inside of repls: one in ruby, the other in python. Both languages (and most of their peers) give me access to a simple, powerful library for making HTTP requests with a concise syntax, and I use those to build a function that requests every endpoint that I want, with parameters for the inputs that I want to provide. So instead of doing:

curl --compress -X PUT -H 'Accept-Encoding: gzip, deflate' -H 'Authorization: Bearer XXJDNETHISISFAKEJDNXXX' -D '{"name":"newname"}' 'https://your.url.here/v1/group/my-group-id/person/my-person-id.json'

I can write something like this:

> client.update_person(name="newname")

Which I personally like a lot better. I write module variables into my client that hold auth tokens and other session state, and create defaults for any parameter that I reasonably can.

Because I use the clients in a repl, I can manually walk through a workflow very simply and iteratively, simply reading the JSON responses, or if I need to do something more complicated, it’s simple to parse data out of the response I get or build simple automation scripts (useful for demos!). For example, here’s a lightly fictionalized workflow for the Life360 API:

> import simple_client as client
> client.fast_setup(username="myemail@life360.com")
> client.index_groups()
{u'groups': [{u'name': u'Regress', u'id': u'a9646aff-27ad-4d24-b607-4430f7436bbd'},{u'name': u'Test2', u'id': u'a8bbfa26-6051-40c6-8aa8-9113ef2e41e4'},{u'name': u'Foobar', u'id': u'92d4e058-d398-4a75-8938-fa063453e0ee'}]}
> client.index_members(group_id='a9646aff-27ad-4d24-b607-4430f7436bbd')
{u'members': [{u'name': u'Joe', u'id': u'db1a8427-4b05-44d8-b2cd-bb454390dc10'},{u'name': u'Jane', u'id': u'd97350db-7f03-414e-9c94-b63c4b51fedf'},{u'name': u'Emilio', u'id': u'447f746a-9731-4e36-8052-cbcc0b21f634'}]}
> members = _['members']
> for m in members:
client.remove_member(member_id=m['id'])

And voila, I’ve tested the case when you remove all the members of a group through a real exercise of the API.

And it’s trivial for me to hit my new endpoint and make sure that it provides basically correct data. This is huge in a variety of situations:

  • When developing a new feature, doing my own end-to-end testing before handing it off to my mobile developer partner makes them a lot happier with me.
  • And then, when a mobile developer does come to me and say, “There’s a problem with your endpoint,” I can quickly bring it up and say, “This is the data that it’s providing, what don’t you like?”
  • Finally, in emergency situations, when a bad bug is coming down the line and minutes matter, the most important aspect of triaging is narrowing down the general code area that you should be looking at. Making it a matter of a few seconds to say, “I hit the production API, it’s returning data” vs “I hit the production API, it’s erroring” is priceless.

Doing all this with just a few dozen lines of code in my “simple client” library is a major win, and allows me as an engineering leader to make this same kind of certainty about our endpoint mandatory. Some of the other developers at Life360 use simple client, some have other solutions that serve them as well, but having the option to use simple client means that we can always say, “You must have the ability to rapidly hit your endpoints on real environments and provide assurances about them.”


Working with Us

Life360 is creating the largest membership service for families by developing technology that helps managing family life easier and safer. There is so much more to do as we get there and we’re looking for talented people to join the team: check out our jobs page.

Life360 Engineering

Musings of Life360's talented engineers

Thanks to Josh Wickham

Michael Sullivan

Written by

Life360 Engineering

Musings of Life360's talented engineers

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade