A Quick Brain-Dump on API Desgin
I’ll keep this one pretty brief as I haven’t totally nailed down my thoughts on this. I still thought it was worth a quick little post:
When you’re creating a brand new API to expose some functionality of a system, should you design it with a strong focus on how the internals work? Should you ignore how internals work and make it as easy to consume as possible? Or is there an obvious balance?
I find myself trying to answer this question without ever explicitly asking it. Any time I’m looking to extend or connect systems, this is likely to come up.
Most recently I started trying to look at creating an API over AMQP to connect my game back-end to a Unity 3D front-end. I had been developing the back-end for a little while now, so I had a pretty good idea for how things needed to work from that perspective. The front-end? Not so much really. I knew some basic actions that I was considering, so I tried coding up an early API for them.
A lot of my focus was around how I was going to implement the code on the back-end to make this API work. It resulted in some of the API calls looking a little bit gross. But the idea was that if I settled on an API that would make the back-end easy to implement, I could get it up and running faster.
After a little while, I feel like my API isn’t getting any cleaner from a consumer perspective, and funny enough, it’s not actually as easy to implement as I was hoping on the back-end. Which had me reflect on a work example…
Once Upon a Time at Work…
Well, it was only a couple of months ago, really. I was working with a colleague on integrating a new system into an existing code base. We decided we wanted to approach the API problem from a consumer perspective. We said “let’s make this as easy to call and use as possible so that people ACTUALLY want to use it”.
We set out with this mission, and created a pretty simplistic API. The challenge? There was a lot of heavy lifting and a bit of voodoo going on behind the scenes. But you know what? We hid the magic in one spot of the code (instead of having ugly stuff scattered all over the code base) and it ended up being a very usable API.
So does this consumer-first, top-down approach to API design always work? I’m not sure. Some similarities/differences in the scenarios:
- In my current situation, I have a back-end implemented and very minimal code implemented on the caller side. In the work example, we had nothing implemented on the back-end, and a ton of code implemented where the caller side would be.
- In both examples, at least one of the caller side or back-end side was reasonably well understood. For my game, the back-end was pretty well understood. For work, the caller side was pretty well understood and we had experience with what we’d call a “failed’ back-end implementation (that we were actually setting out to redesign).
- The work example was a relatively small subset for an API, but the game example was about to be a very specific implementation that I’d need to adapt into a pattern for all messaging in my AMQP system
So there’s a few things to consider there. I think I’m at the point in my game where I’d like to revisit how I’m forming this API and try it from a client-first perspective. Now that I know some of the catches, maybe I’ll shed some new light!
How do you approach API design?