 So, this hour we're going to talk about APIs, APIs. Who here is an API producer? Who is created and released an API? Okay, maybe about a quarter to a third. All right, who here is a web developer? Who has released a website? Okay, that's about at least half, half to two-thirds. You too are API producers. You're writing code that's designed to be called by other codes, like a web browser. So, you also have produced an API. Now, who here has written the class? A Ruby class, Java class, C++ class. Most hands should be up, okay. All right, you also are API producers. You are writing code that is designed to be called by other code. Now, in this hour, we're going to focus on network APIs, not class APIs. So, some of this will apply to classes and other parts of it won't. Some of the underlying principles will be the same though. Okay, last question. Who here speaks a language? English, Japanese, Spanish, Hindi? Everyone's hands should be up. All right, you are not necessarily API producers, but you're doing many of the same things. You're communicating, you're interacting with something else. Some other person, you're communicating. So, this is something that we all do. It can be useful to think of APIs in terms of communication. So, we'll come back to that. But in this hour, we're going to talk about APIs. We're going to talk about APIs. In particular, we're going to focus on client libraries, API client libraries. Now, what is a client library? It's a Ruby gem, a Ruby library that provides a nice Ruby interface for calling an API, a network API. So, suppose you've built an API, maybe it uses HTTP, JSON. Users can call your API using a standard HTTP client, for example. So, here's a simple example that might use net HTTP as part of the Ruby standard library to call an API. So, you construct an HTTP request, maybe marshal some JSON data, and parse a JSON response. There was a lot of text up there. But what if you could make that same API call by just invoking a method? Something like this. This is clearly a lot shorter, a lot easier than constructing that HTTP request. We could say this is a better kind of user experience for your users, or since your users are developers, this is a better, happier developer experience. Of course, this means that you have to write that class. You have to write that example client class that will provide those methods. You have to build it into a library, and then you have to maintain it. You have to keep it up to date. You have to document it and all that baggage. So, how do you do all that? What are some techniques that you can use that go into a good client library? And is that engineering effort worth it? It's all the effort needed to maintain that worth it. Those are some of the questions that we're going to go over in this session. We'll think about why an API client library might be useful. We'll talk about how it can provide better, more happy developer experience for your users. We'll see some of the common features of client libraries, some of the techniques and the practices that go into writing them. Then we'll talk about tools that might help you generate client libraries for your APIs. So before we get started, just a bit about your speaker. My name is Daniel Azuma. I've been part of the Ruby community for a little while. I've been developing in Ruby for about 13 years. I did Java and C++ and other things before that. I've kind of done a variety of things. Currently, I work at this place where I serve as the Ruby engineering lead for Google Cloud. My job here is to make the cloud better for Ruby developers, for people like you. And one of the major areas that my team works on at Google is looking after the Ruby client libraries for Google's APIs. Google, of course, is a large company with lots of products, lots of APIs. We have several hundred public APIs right now. That doesn't include all of our internal services, of course. And so we provide client libraries for all of those APIs. And that means several hundred services. And if you multiply that by the number of languages we support, programming languages, that's a lot of code to maintain. So when you get to that point, you start to wonder, is it worth it? Is all that effort worth it? Is all that engineering effort to maintain a client library worth it? When you could just have your users just construct HTTP requests, is it worth it? Most of you probably don't have to deal with the kind of scale, of course, that we have at Google, but it's still a good question. And it's a question that I once faced early in my career, long before I joined Google. So imagine me early in my career, maybe not that early. It was a number of years ago, but there, that's better. I was at a startup. And we were a Ruby shop, and we were writing a back-end API for a mobile app. Now my team, we were all back-end developers. We were Ruby devs. And we didn't know very much about mobile languages and mobile frameworks at that time. It was pretty early. So we hired an external engineering team, it was actually an offshore team, to do our mobile development for us. So we, my back-end team, we deployed our API to staging and we documented it very carefully. We documented it, because we didn't know what kind of tools and what kind of libraries that the mobile engineering team had at their disposal. We didn't want to take the time to learn those mobile languages and mobile frameworks ourselves. So we documented this, we created this spec, and we sent it to our mobile team. And about a week passed, and we started getting emails from them, saying, hey, you know, we don't understand this. We don't understand this part, or we can't get this or that to work. First it was the auth. Then it was the security, the request signing. Then it was the session handling. Then it was the instrumentation. Then it was back to the auth and how it interacted with the session handling. Back and forth, around and around, I had thought that we had documented this pretty well. But apparently I was wrong. Soon we started having late night meetings with them. This mobile team was offshore. So they were in an opposite time zone. I had to stay up in the office until about 10 or 11 every night. And we had these late night meetings with them. Then late night remote pairing sessions with them. I ended up learning a lot about mobile languages and mobile frameworks. But this was not a great developer experience. Not for me, not for our mobile team. All in all, we lost three, maybe four weeks to this. Just getting the two sides of the API to talk to each other before the team even got started on the mobile interface itself. Some of you are on startups today. Three or four weeks, that can be an eternity when you're bootstrapping a startup. So later on, after we finally shipped, we got together and we talked about this. What should we have done? Should we have written the mobile API code ourselves? Would that have helped? I'm actually not sure. Maybe it's a tough question. I don't know. What I do know, though, is that this experience opened my eyes to the fact that APIs are hard. APIs are really hard. They're hard in many ways. They're hard in resource design. If you have a restful API, mapping your problem domain into resources is hard. Naming is hard. Versioning is hard. Future-proofing your API is hard. But also using an API, calling an API, can also be hard. For example, auth. Enough said. Who here has implemented an auth flow? Who here has gotten it right the first time? There we go. Here's another one. Retry. When can you retry safely? How many times do you retry? How long do you wait between retries? All that exponential backoff. What if you get quota errors? Do you still retry? Do you retry differently? It's complicated. Here's another one. Caching. Oh my gosh, I don't even want to talk about caching. As an API user, there are lots of issues that you have to deal with, that you have to keep track of beyond just the request and the response. You have error handling. You have instrumentation. You have long-running operations, long list of things. In general, real-world APIs are much more than just HTTP requests. This morning, Matz shared with us that he sees programming languages as a form of expression of ideas. You remember that? I think it's a very apt description. And I think it very much applies to APIs as well. They are a form of expression of ideas. So for example, consider what we're doing here in this room. I'm standing up here and we're sharing ideas. I'm giving you content. I'm giving you words. But sharing ideas, communication, is much more than just words. There's a lot of side channel information going on here, being passed back and forth. So for example, I might emphasize certain things because I want you to understand that they're important. I might emphasize certain things. I might gloss over something else because, yeah, whatever. Emphasis, tone of voice, facial expressions, a lot of side channel information. It goes both ways. I'm standing up here talking, but I'm also observing. Who's listening and who's sitting there on your laptops writing email? What kind of how are you all reacting to things? There's also context, the order in which things are presented and discussed. That matters. Setting matters, the fact that we're at a Ruby conference in this beautiful space. It shapes our conversation. Identity matters, the fact that I'm a Ruby developer and a Google engineer. You hear me differently than if I were a US senator or if I were a barista or if I were Mott's or if I were Aaron Patterson, then you would definitely hear me differently. All of these things, not just the content, but also the context and the side channel is all part of this sharing of ideas. And of course, you know this already. We all do this. We all communicate. We all understand, to some extent, what these things mean, what all the side channel means, what the context means. There's a protocol to human communication. There's a protocol to the sharing of ideas. And you are familiar with that protocol. And that's what I would say is the role of the client library. It's to know the protocol. It's to know the protocol. Not just the HTTP or JSON protocol, but also the auth, also the retry, the sessions, the caching, all of those side concerns that are not handled by your HTTP library. Now you provide a client library to handle all that protocol so that your users don't have to deal with it. All right? So that's all nice, that's all hand wavy, that's great. What does this mean? What does this mean really in practice? So let's take a look at a really simple example. This is a simple example of an interaction with a client library. This happens to be for Google's Translate API, but it's pretty typical of many client libraries. You notice that once we create an object, the object represents the service. It's sometimes called a stub object because it represents the service, but it doesn't implement the service. So we have an object. Typically the stub object encapsulates some of the basic concerns of communicating with that service. A lot of that side channel information, such as identity, auth, timeouts, and so forth, is encapsulated here in this object. And then actual API calls can be made by calling methods, okay? Very straightforward. And typically, of course, results come back as objects, and then you can pull out information from them, okay? Now notice what we don't see here. Notice what we don't see. We don't see URLs. We don't see JSON or HTTP. This might be an HTTP JSON API, but that's just the protocol. It's not part of the functionality of the API, so it's not what your users care about. And we also try to hide other difficult issues, like retry, like caching. This is all very important, hiding certain things that the users don't care about for creating a happy developer experience. Developer experience, we're gonna talk a lot about developer experience because it really is at the core of what drives a client library. And again, I'm really grateful to Matz this morning for reminding us that developer experience is really at the heart of our values as Rubyist. Things like happiness, niceness, productivity, community, the experience of being a developer, having that be happy, having that be enjoyable. So let's talk about a few pro tips. Let's talk about what sort of things go into an effective client library and can give your users a happy developer experience. First, as much as possible, use the Ruby abstractions. Use the abstractions that are provided by Ruby, Ruby idioms. So make the usage of your library familiar to Ruby developers. This may seem obvious, but let's talk about what that means. It means on the one hand, using standard Ruby classes for data. For example, if you need to express a timestamp, Ruby has a time class for that. It means integrating with the standard Ruby libraries for things that's appropriate for. So if you're writing a client, for example, for a logging API, Ruby has a logger class. That's how Rubyists expect to interact with a logging system. So use that. Here's a good example from ActiveRecord. Who here uses ActiveRecord? You have Rails developers, lots of Rails developers in the house. That's great, you're welcome here too. This is a Rails conference as much as a Ruby conference in reality. Now this is how to iterate over all the records in your database. ActiveRecord provides a method findEach that returns a Ruby enumerator. You can also pass a block to it. But enumerator is the standard Ruby abstraction for kind of a stream of data, a stream of objects. Under the hood here, ActiveRecord is actually doing something that you might not expect. It's actually making multiple database calls. Getting a batch of records at a time because you might have a large database table and you don't wanna load that entire thing into memory at once. But since it gives you an enumerator, a standard Ruby abstraction, you don't have to worry about all that. You just make one call, you get an enumerator and now you can do all sorts of things. You can transform the data, you can iterate over it, you can make it lazy, all these things as an enumerator. You have all that power of Ruby available to you. So use Ruby abstractions to help your users and give a happy developer experience. Second thing, errors will happen in your API. As much as possible, handle errors in your client library. Handle errors for your users as much as possible. This is because error handling is really annoying. Error handling is really annoying. A lot of different things can go wrong and each problem demands a different response. You might have server problems, you might have network glitches, your all off tokens might expire, you might have quota errors. And who has time to figure out how to handle every single one of those cases properly? So as much as possible, your client library should handle its own errors. That might mean handling retry for your users. Handling quota so your users don't have to do it. Anything that you actually have to report back to the user then, translate it into an exception, for example. So your users don't have to look up error codes and then have a big case statement. Again, very important for a happy developer experience. All right, let's talk about security. Security, this is another meticulous, difficult, detailed concern that's really hard to get right. So help your users out in the client library. Do things like check inputs. Obviously that means check types, check ranges, but also do escaping for your users. Sanitize all the inputs. ActiveRecord does this for you again. Don't make your users do this themselves. Help your users prevent those SQL injection attacks. Very, very important. Also, if there's a security or some kind of encryption protocol that has to be implemented, build that into the client library. Just last week, I read a blog article from someone who was really struggling to implement the request signing that was required for this API that they were calling. Request signing is a really, really enormous pain in the ass. Don't make your users go through that. So give them a client that handles that. Now, one objection that I sometimes hear regarding client libraries is, oh, it's an extra layer of abstraction, so it's just gonna hurt performance, right? There's this fear that there's gonna be a performance impact. Let me tell you a secret. This is nonsense. Generally speaking, in the network API, the network latency, database latency in the backend, something else is going to dominate your performance. So a client library generally is not gonna make it materially worse. In fact, oftentimes, the reverse is true. A client library, a good client library can often improve performance. How does it do this? Because it gives you the chance to help your users do the right thing when it comes to performance. Your client library can help with performance optimizations that only you know about your API. It can help with best practices, such as proper caching, batching calls, and so forth, so your users don't have to do it themselves. So let's think about, again, a remote logging API. Typically, in a logging API, users write a log entry, it gets transmitted to a backend, indexed, stored, and so forth. And if your user has had to send an API call for each log entry, and if that's an HTTP request, you're waiting for an HTTP request in response. Don't do that. That takes time, and that's not cool. Logging's supposed to be fast. Even if you do that kind of asynchronously, you send a request and don't wait for the response, and you have other problems, right? You have to deal with asynchronous behavior. You have to deal with what happens if now you can't retry. So instead, a good logging API will typically provide a batch send call, right? So it's a call that will send a whole bunch of log entries at once. And then in your client library, whenever the user writes a log entry, just queue it up in the client library. So you keep a queue of these log entries. Then after the queue gets to a certain size or a certain amount of time is elapsed, then use that send batch call to transmit all those log entries back to the backend. Better yet, do so asynchronously. Use a background thread, use a background agent process. Do something like this. A system like this is critical for acceptable performance in certain APIs like a remote logging API. But again, something that can be really hard to do for your users, especially if you're dealing with asynchronous behavior. So don't make your users do it. Provide it in the client library. Again, very, very important for this happy developer experience. Finally, client libraries should provide instrumentation. Instrumentation. This means gathering information about usage. Gathering information includes things like logging calls, logging events, log all the errors that you encounter when calling an API. And that includes the ones that you retried and succeeded. Don't throw any errors away. Those errors are important. Users need to know about them. All very valuable information for both for your users as well as for yourself as an API provider. Logs, traces, latency tracing, very useful information, statistics. Your client library can, to do this sort of thing, it can integrate with a system like OpenCensus to collect statistics, to collect trace information. If you expect your users to be Rails apps, you can use active support notifications, lots of different libraries that you can use to help you with this sort of thing. However you do it, it's very important to instrument your API usage and to do that in the client library so that you and your users can have visibility into what's going on. So we have a list of very useful, very important services that a client library can provide. Sounds great, very nice. Very nice for our users, but it does seem like a lot of work, right? Especially if you have Google's problem. So here's what we do at Google and what many organizations, both large and small do. And it's a technique that you can use as well. Use what's called an interface description language, or IDL, interface description language. This is a machine-readable specification of your API. It's a description of your API that can be parsed and understood by a program. An IDL description describes things like what calls are available in the API, what parameters need to be passed and what return values are generated, any data types that are used by the API, other properties of API calls, such as whether it's idempotent, what kind of authentication is needed, and so forth, and also oftentimes documentation for each API call is represented in an IDL. Now, when I talk about IDLs, I can sometimes tell who out there are the old timers who have been doing enterprise development for some time because they start cringing and start showing some signs of PTSD. They might be thinking about something like this that brings up memories of all sorts of terrible things. IDLs have been around for quite a while and some of them are not pretty. Enormous complicated XML files that are just no fun to deal with at all. But I'm here to tell you that it's not that bad anymore. So here's an example of what a modern IDL might look like. This one is in an open format. It's called OpenAPI. Some of you may know it as Swagger, which is an earlier name for it. The open version now is called OpenAPI. You can see some endpoints information, basic endpoints and metadata, as well as a description of one call, a user's call along with a description of what its responses look like. You can see it's YAML, so it's very readable, still very expressible, expressive, flexible, but not too scary. Now there are other IDLs, Apache Thrift is another common one, came out of Facebook. Google uses protocol buffers, GRPC. One thing that all these formats have in common, of course, is that they are machine readable. That means once you have a specification, you can do all sorts of cool things like generating client libraries. So what if you could have a program read the specification and automatically spit out client libraries for you? That would be awesome. So in Ruby, we have a couple of general approaches that we can do to do something like this, because Ruby is a very dynamic, very powerful language, so one of those is metaprogramming. Ruby has a number of very powerful metaprogramming features. You can dynamically create classes, create methods at runtime. So you could build a client library that looks kind of like this, reads an API description from an IDL file, and then metaprograms your library. So you can imagine having a single Ruby gem that can handle pretty much any API just by reading an IDL and generating classes for you. This is actually how some of the early versions of Google's API client libraries worked. They called a Google service called the Discovery Service that served these IDL files and then used that with metaprogramming to build clients, client classes dynamically. So we had a small library that did this, that pretty much we covered all of Google's APIs with a small library. It was really cool. It was also really annoying for our users because metaprogramming makes for very obscure code. If you wanted to understand how the client worked internally or if you got an exception or something and you had to dig through a stack trace, you ended up wading through these layers and layers of metaprogramming. It also meant that we didn't have very good or sometimes any documentation for the library because our documentation tools like our doc, like Yard, they have a hard time figuring out what to do with metaprogrammed code. So again, developer experience, not so great, not so great. So most modern client libraries use a different approach. Code generation, code generation. And after a short while, we switched Google's client libraries to use this approach. So what does that look like? The idea here is to take the generator that reads the IDL and generates classes and pull that out of the library. So instead of doing metaprogramming inside the library, we pulled that out and we just had its write Ruby files, write.rb files. We had the generator generate the library itself as static Ruby classes. And because it generates static Ruby classes, they can be used also to generate other things like associated documentation. You generate the comments that go with those classes that get turned into our doc or your doc. You can do that straight from the descriptions or the comments that you have in the IDL file. So now you not only have automatic code, automatic client libraries, but also automatic documentation. That's pretty cool. And in fact, you can get automatic of all sorts of things. Skeleton code for the server side, even tests. I would love to have a computer write my tests for me. Be awesome. Google does all of this kind of internally for our client libraries. If you go to almost any organization that generates lots of APIs, they're doing similar things. They're generating clients, generating documentation. Code generation in general is a really fun topic. If you wanna learn more about it, my colleague Alex is up here. We'll be speaking on it tomorrow afternoon. Raise your hand, Alex. I highly recommend his talk. But you don't have to be Google to get started. Client libraries are important no matter how big you are. And you can use these kind of generation tools, IDLs, even if you're just getting started. They're still useful. So here's how you can get started. First, choose a specification format. Again, there are small handful of choices out there. Being a Googler, I might plug Google's formats, which are protobuf and gRPC. And if you're looking for a really high performance kind of ultra scalable system, that might be the way to go. But honestly, in order to get started, especially if you already have a REST API that you want to work with, then consider OpenAPI. It's open source, has wide adoption, and very good tooling. So once you've chosen the standard, then write a description of your API. So write it in that IDL format. This will be the input for your code generation. I've also found that writing this is really useful for kind of honing the design of your API, kind of cleaning that up. Because you can see all of it in one place. And it's really not that bad. If you use OpenAPI, there are also online tools that you can use to help you write it. Third, once you've got a description, then you can start playing with a client library generator. The OpenAPI generator supports lots of languages, including Ruby, so you can generate just kind of a starting point library. Then once you get a basic client going, you might have special requirements for your API. And so it's really easy to customize those generators just by editing templates. So just a few steps, and you'll be well on your way to creating your own client libraries for your APIs and generating them automatically. So just to recap here is what we've talked about. We talked about why you should consider providing an API client library for your API. We talked about what a client library should do, what API concerns it should handle, things like retry, things like security, so your users don't have to deal with them. We talked about how to generate client libraries. Even if you're supporting hundreds and hundreds of APIs across lots of languages, you can use cogeneration to maintain clients and maintain those documentation, even using a pretty small team. We have a pretty small team at Google that does this. However you go about doing this, a client library can be very, very important, a key part of creating that happy developer experience. And your API is worth a happy developer experience. And we as a community, we're about a happy developer experience. So yes, you should provide a client library for your API. So thank you for coming. I've put together a set of getting started resources. It will be at this URL, so if you need to photo your phone, that's the URL to capture. It's not up yet. I will have it up by the end of the day. But the slides, when they're posted will be there. The video, when it's posted will be there along with a bunch of links, articles and stuff that I've collected. So feel free to look at that. I think we have a few minutes. Do we have a few minutes for questions? See ya. Okay, we have a few minutes back there, yes. Yeah, so the question is, how do you handle versioning of APIs? When you make breaking changes and your API contract changes, what do you do in your client APIs? It's a hard problem. At Google we actually do two different things. Number one, we do express the version explicitly on our API clients, so you can choose when you create that stub object, you can choose version one or version two of the stub object. You know, it's just a parameter in the constructor. You can also do things, and we also do this in a few of our client libraries, create like a veneer over the generated code that tries to smooth out some of the differences in the API specification. So some higher level interface that for commonly used operations that would call down to those lower level generated code. So a couple of techniques that you could use, but yes, it is a difficult situation. GraphQL, I think people are using that as well for some of those issues. Yeah, anything else? Okay, I don't see any other hands, so thank you for coming. Have a great day.