 Hello everyone, my name is Elat Tabak and I'm a Principal Software Engineer in TeamLib at Red Hat. I work on a product called OpenShift Caster Manager, or OCM for short. And some of the examples I'll be showing you in this presentation are taken from that product. Today I'd like to talk to you about something called Developer Experience. At first let me share this. A few years ago I went online on social network and I saw this picture. And it really made me laugh. At the time I was doing a transition from being a backend developer to doing some more front-end work. I've learned JavaScript and Angular, and I actually became a full spec developer. And when I saw this picture I really understood what it means from trying to take a backend perspective in IDs, which may involve their own functionality and try to implement the front-end using these principles. The end result may be not as friendly or usable as you may want it to be. Let's look at this picture for example. It has all the functionality that you want it to have, but the experience that it may leave with the users of it, it's not really usable or friendly as you'd like it to be. In many cases when you're developing a backend or an API, you mostly thinking about the functionality, having all the main functions that you'd like the API to support, but you will always think about the experience of someone that integrates with your API, which is mostly the front-end developers, but basically anyone that integrates with the API. So let me share some points and tips about how you can make the backend development better and having an API that has a better developer experience for anyone that integrates with your API can eventually create better products and make product development faster. It's just a friction between the team and eventually deliver a better experience for the customer. So the first item I'd like to talk to you about is embedding. So how I'd like to call it, how to avoid the whole of the API. So this is an example of cluster list. A cluster is basically a Kubernetes cluster or an OpenGIF cluster in this case, which is based on Kubernetes and it has a few fields through it. In this table, it's showing the cluster name on the left, the version, which is basically the OpenGIF version that this cluster is installed with and the region on the right, which is the cloud provider region that this cluster is installed in. Now, at some point, the UX team came up with this idea, showing a small icon next to the version indicating that this version has an available upgrade to it. And if you hover your mouse over that version, you will get a list of available upgrades. And if you click on it, you will get to the upgrade settings page. Now let's look at the backend and see how the API is architecture. So on the left side, there's the cluster object. It has several fields, for example, the name and the version and the region, of course, the version is an object that contains the ID and an address, which is a link. And if you get that link, you will get the version object on the right, which also contains the available upgrade list. So from the backend perspective, in order to support this new icon showing the available upgrades, all the client needs to do is for each cluster take the version of that cluster, get that specific version, and then extract the available upgrades from it. And the problem is that with a large set of clusters, let's say you have a list of 100 clusters, and potentially each one of them can be on a different opening version. That means the UI or the client needs to traverse each cluster to get the available upgrades for that. And that eventually slows down the application, creates a network overload. It also overloads the backend because potentially every API called that the client is doing will require authorization and maybe a transaction to get the specific information from the database. So it's very slow and there is the possibility of creating a non-responsive client if it's taking too long. So how can we solve it? So first the definition. A chatty API is one that requires a consumer to make it the tremendous amount of distinct API calls to get the needed information about the resource. In this example, it's the available upgrades included in the version and the client needs to get each version to get the list of available upgrades. So by using embedding, you can take the necessary fields, in this case, the available upgrades list and embed them in the parent or the host object. In this case, it's the cluster. So by taking the available upgrades and provide them inside the version object, which included in the cluster, you can have the available upgrades immediately when you get a cluster. But not only for a single cluster, but also for a list of clusters. In traditional relational database, that usually means some kind of a join between the cluster stable and the version table. So it may be very efficient to do that this way instead of getting each version one at a time, even if you're doing it concurrently, it will still take a time and create a load. So in this way, we're avoiding it. I can also tell you that Netflix, for example, did a full redesign of their API just to avoid chatting it. Searchable API is another item on the list. In many cases, the front end will like to present searching capabilities over a list of resources, which usually means filtering these objects. And filtering is very efficient on the backend side if you can filter specific resources that you like to see. But it's not only filtering, it's also sorting, because if you're searching for something, you'd like the results to be in the same order every time you search this cluster and not show different ordering or sorting every time you search something. That may be very confusing for the client if you see different result orders every time you search. But generation is also important usually because the client doesn't have anything to do with or doesn't like to get a large or too large amount of results. It may slow down the application again and blow out the memory eventually. So pagination is also important. So you have to have all three in order to create a good experience on the front end side when searching items. A fast API, in many cases, the backend system tries to do some operations that take longer than a few seconds and sometimes even longer than a few minutes. And that slows down the response to the client side which eventually creates a bad experience to the end user and the front end developers really need to mitigate that which creates another load on the front end developer as well. By introducing some kind of an operation idea that the server or the backend can return, the client can pull that information and the server can make the long operation asynchronous from the API call that can help reduce the amount of time that the client is waiting just to submit a single request. Error templates or how to avoid text parsing. So let's take this example. In this example, the server is returning an HTTP 400 error message which says you do not have enough quota to scale up this faster name as the end to end one cluster. You'll require 10 nodes of five and five extra types and five nodes of five to extra types. However, however your current quota allows only and there's a full specification of what's the missing quota here. Now, if you look at the backend side, this is perfect. This has all the information that tells you exactly what went wrong with the request and it tells you or hints you how to resolve it. Let's look at the UI design for that. So you have this design which is a window or a model showing a title, the missing quota. On the left side, you can see a red icon and a red error icon and a message that says you do not have enough quota to scale up the SD end to end one cluster. You can see the SD end to end one name is also a link and if you click on it, you should be going to the cluster details page. On the bottom part, there's the exceeded resources part showing M5XLH4 and M5XLH3. These are the exceeded resources for the quota. Now, if I'll try to create that design of an error message out of this screen, I would have to parse this screen, extract the necessary information out of it and then present that to the user. And of course, how do I create a link to that cluster specifically? Well, it's complicated and if I'll try to do that, it's not only complicated but also very fragile. If anything will change in that error message, it will most likely break this design trying to parse that. So how can we solve it using error templates? The idea of error templates is taking the interesting parts of the error message and make them feel. So for example, introducing a type of an error, like a quota error, can help the front end identify this specific error with about quota, which means the UI can do things like decide on a specific design for it, maybe some special icons that are related to quota errors. And there's the title, which is missing quota, which can be used for the type to be the title of that error message. And the message saying you do not have enough quota to scale up and then an argument list with an index of zero, which points you to the bottom part of this error message when that value is a name but also an address for a cluster, which means the front end can use that and present the name of the cluster but also generate a link to that, which will take the client to get to the cluster details page. Also, there's a details part, which is included as part of the quota error, which includes the exceeded resources part. So that can be used to create the bottom part of this error message. Okay, let's move on. Validation. In many cases, when the client tries to submit a large amount of fields, which contain some object to the backend to the API, there's a lot of validation going on in backend and in many cases, if there's, let's say, a string that shouldn't have a value it's maybe too long or too short, maybe has to be a number no bigger than something or not negative. So there's a lot of validation going on in the backend and if I'm trying to submit something and it fails on validation, I can fix that and try again but then if I have a lot of fields and there's a lot of validation going on, it can take a while until I can get it right. So that can create a very frustrating process for whoever integrates with the API. Including all validations in one response can help me by getting the entire set of errors that failed on validation. I can fix that in one bulk and resend the request, which makes the development cycle a lot easier and faster. Using sample codes is also important. You obviously are familiar with the 200 code and 400, 500 around RESTful API, of course, but nothing or not everything should be 200 or 400, for example. Let me give you an example. So let's say I'm trying to create a cluster and I'm posting to the cluster's endpoint. I'm providing a name and a version with ID 100 which doesn't really exist. So what happens if the backend returns an HTTP 404, which means no found, not found? You may argue, well, the version doesn't really exist. I couldn't find it, so that's a reasonable status code. But according to the REST, the URL itself that I'm trying to reach, in this case, the cluster's endpoint, when getting back at 404, it usually means for me that I'm trying to contact an endpoint that doesn't really exist. Or maybe I don't have enough permissions to get it or to use it. So in this case, using or returning from the API and HTTP 400 is much more easier or much more extraordinary than a 404. All right, moving on. So with API users to learn and use, but this is the interesting part, it's also hard to miss use. And let me give you a short example. So let's say I'm trying to list clusters and doing a GET request on the cluster's endpoint and say I have over 3 million clusters and this is a real number from our system. That's a lot. That potentially can create a load on the client side and, of course, on the network, but also on the backend side because it needs to serialize all this information and send it back to the client. By introducing a default, you're helping the client from using the API. So that can really help and boost the performance application and avoid misuse of the API. Including, you've seen it in the examples before, but I like to emphasize it. So let's say I'm trying to get specific clusters and I'm doing a GET on the cluster's endpoint with an ID of the clusters and I'm getting back a name and a region, but the region itself contains minimal set of information. As a developer, if I'm getting an address, I immediately understand how can I get more information about that specific region? So this really helps whoever integrates with your API how to navigate through and get more information it's required. Consistency. So please use the same casing and convention throughout the API. So no mixing of ID and or uppercase, lowercase, small, uppercase, for example, just use the same convention throughout the API. Time to time. Use ISO 8601 and accept any time zone but store it and return it in UTC. Units. To use the same throughout the API and no mixing of, for example, if your API is using a storage system, don't try to mix bytes with gigabytes but just use the same units for all the values. If you'd like to learn more, I suggest to this website 100 days of the X which is shortfordevelopersperience.com and I'll take any questions now.