Writing usable APIs in practice
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Asproni, Giovanni
Programmers, explicitly or implicitly, when working on complex systems, end up designing some APIs to accomplish their tasks, either because the product itself is some kind of general purpose library or because they need to write some libraries and packages to put some common code of their applications. There is plenty of information available about how to write clean and maintainable code, but not a lot about writing usable APIs. The two things are related, but they are not the same. In fact, clean code is code that is clean from the point of view of its maintainers, usable APIs, on the other hand, refer to code that programmers (other than the original author) find easy to use. We’ll see how usable APIs help in writing clean code (and vice-versa). In this session I will introduce the concept of API usability, explain its importance – e.g., impact on productivity and defects – and show its relation with clean code, as well as some (sometimes surprising) research results from literature. I will also give some practical advice on how to start writing more usable APIs.
 Okay, can you hear me? It's like I don't have any answer, so I suppose nobody hears me. Okay. So writing usable APIs in practice. Now I have to tell you that if you have Googled about this, there are some other versions, of this talk I gave at other conferences, however, they are not identical. I always modify the material a bit, update stuff a bit, and generally speaking, I update it with the stuff that at that point in time annoys me the most. Okay. Basically, my talks are born out of frustration of what I see in my daily job. I work as a freelance consultant and contractor, and I write code. And I land on legacy code bases where some interesting decisions were taken. Okay. As far as APIs go, in this case, now we'll see it, but this talk is not only about APIs as when you download, I don't know, Spring API or Hibernator or something like this, but it's also about the stuff you do at work. Your internal project, if you actually are careful and disciplined in the factoring your code, you'll end up with plenty of packages or libraries or the things that represent some sort of APIs, subsystems you use in the rest of the system. Okay. So the idea is that I'll talk about things that are relevant both for the public, if you publish APIs for third parties or use them in your own internal projects. Okay. I want to start with the definition first. In this case, when I'm talking about APIs, I'm not talking about the Amazon APIs or stuff like this, but it's more like, and we'll be using this definition, any well-defined interface that defines the service that one component, module or application provides to other software elements. As a practical example, Java IO package, Java 7, this is, this provides an API. Okay. I use it as an example because then I have a running example with this thing. So basically, this is all the interfaces, classes, the exceptions are part of the API, the errors, but also you don't see them here, but also the public methods, stuff that the users of this package use are part of the API in this context. Okay. By the way, if at any point in time you have questions, observations, and stuff like this, please raise your hand. And then talk about usability. So what is usability about in this context? It's about efficiency. Something is usable for if it allows you to be productive in what you do. You want to do something without wasting time. It's about effectiveness. So we want to be able to actually solve the problem you are trying to solve in a proper way. It's about error prevention. A good software API, a well-written one, will help you to avoid some stupid mistakes. And this is also about ease of learning. How many of you have come across APIs that actually you can guess what method to call in which class, stuff like this, or the order of parameters? I've seen some very, very few. But it's about this. It's like the ease of learning is about the fact that you can navigate it in simple ways without too much effort. And then since I've seen Uncle Bob with questions, yesterday I saw Jeff Watts with the questions. I said, okay, I want my own question in my talk now. I'm jealous. There it is. This is a usability question. This is the big O notation. So it means that the upper bound for this is one on B. I don't know exactly which kind of function is. But that's an upper bound. And B is the brain power necessary to achieve your goals. Sorry, I thought I deleted this one. The question is made up in totally arbitrary. I was just jealous on my side for the other guys. But the serious message is that actually if you think as a rule of thumb that you don't want to overload the brain of people doing their work. Why do we want to bother with this ability anyway? Is from a company perspective actually, APIs can be quite a great asset, but also a huge liability. For example, I work a lot with investment banks. And typically I work on the big systems they use in the back end, pricing, risk in this kind of stuff. And they use the APIs written by the quant that are the mathematicians that come up with their own algorithms to calculate risk or price of an auction or some other things. And those APIs are actually company secret. To see them, to be allowed to see them as a developer, here to a special organization, most of the time. Because the algorithms are secret. So they are actually one of the main assets of the company. The problem is that usually there are also huge liabilities. Let's say, as Kevlin would say, the code is usually written in good ways. Is often written badly with memory leaks, is a mess, difficult to change, not tests. Many wonder why you have problems in the markets, but really sometimes some important functions have no automated tests whatsoever around those. And so when they want to implement new models or change something in the technology, they may have huge problems. So a project, for example, to go from, I work in a bank that was a project to go from Visual C++6 to, I think, Visual 2008, probably, took ages, can take years, simply because the code was written in horrible ways. Live alone, the visual C++6 is not really C++ in many ways, but it was mostly because of the way the things were written. But also there is the other perspective, it is how many of you here are programmers, write code every day, so most of them. Any managers, architects? Out. So from a programmer's perspective, having an API that is actually usable means that they have fewer bugs to take care of. Since, for example, I can guess how to use the classes and the methods and the data, it's easier for me to write code that makes sense. Of course, my code, as a consequence, will have higher quality, which will be so easy to maintain. And I'll be also more productive. It'll do what I'm supposed to do in less time. Really, a few days ago, I was working with a C API to do the tenet using some C++ code. I actually spent two days to understand how a particular bit of functionality worked to achieve my goals. Because of the patterns they followed, inviting it because of the documentation around it, but you see two days. So let's say that I didn't really feel very productive. So I had an impact, and I worked as a contractor, and I'm paid by the day, which means has an impact of my customers as well. And also, here I distinguish between two types of APIs, just as I mentioned before. So there are the public APIs, the ones that are given to third parties. You may work for a company that produces some libraries or packages to give outside, or the typical open source software that we download and the libraries we use. The private ones, in this case, are the ones for internal users. As I said before, if you refactor your application, modularize it properly, you'll end up with many several APIs to achieve different goals in the different parts of your application. As we see, there is one main difference between these two that is basically the private ones. You can do whatever you like with them. They are internal of your project, so if you make a mess, you will make a cause and mistake. You have a bad decision, you can always change it. With the public ones, you can't. Or, well, better, you can, but with great, great effort, because they are used by other people, other teams, and sometimes they even rely on your bugs. So in fact, I claim that any non-trivial software application involves writing one or more APIs. And also, I think that when we talk about good code, you know, as developers, what we mean in a way is also code that is usable from a developer's perspective. When you read a good fragment of code, it's like you think it's good because you know what you can do with it. You know how you can possibly modify it, you understand what it does. So it's usable from a programmer's perspective. Any questions so far? Okay. Now, I will introduce some concepts, usability concepts. The idea here is that I want to give you some information that you might not have come across, but I hope it will be useful for your work today, you know, to help you think in slightly different ways when you write some classes, packages, functions. After these, I'll talk also about some simple techniques you can use directly. You know, go back to work, you can do that. The simple techniques are chosen not because they are the only ones, it's just a small subset and these are things that are, as we see later, actually not difficult at all. Somehow we always forget about that stuff. All the code I see keeps having the same issues. So first concept, affordances. An affordance is a quality of an object or an environment that allows an individual to perform an action. A door affords opening and closing. This thing has a laser pointer, you can see it very well, but affords pointing. We can apply exactly the same concept to APIs. Let's think this is Java 4 reading a file line by line. Was kind of interesting. You can see that the affordance is there. You can read the file line by line. But also you can also see that sometimes the affordance is not that visible. It's not immediate. So if you had like media experience at that time of like, I need to do this, how do I do that? Then the first point of call is let's look at the class file. Nothing in there to help you. And then start to read all the classes and methods in the Java U and at some point you arrive at this thing. So the affordances there may not be that visible. The second point is that part of the affordances are also the errors. What can you do in case of errors? What does the API tell you? Something happens, what can you do about that? So the exceptions are part of their own affordances as well. And as you can see this example, there is plenty of stuff going on just to read the file line by line processing. This is an example with Java 7. They introduced a new package. Still doing the same thing. Here is a bit clearer in the code what you can do. Come out because there is a concept of file path. There is a concept of char set which you may or may not care about. Sometimes we just want to read the file line by line like this. And then there is something, this read all lines method. Still there are some things like files is a static class as a similar name to the other one but then I'll talk about this a bit more in that later on. And somebody that looked at one version of this talk on InfoQ actually criticized me for saying, well, the example with the file is not relevant. You should have used the Java class, Java Util Scanner. Admittedly this code is cleaner than the first example. Fewer things going on. But if you have a few minutes of patience, I will explain why I don't think it's still a good API kind of thing. This is what you do in Python. Which I put this because what you do in Python or Ruby, this kind of languages is usually kind of obvious. This is why the duck typing languages have the property that usually somehow are written in a way that the obvious stuff you want to do the 80% of the time is just easy to find. And then there are some cognitive dimensions. By the way, I put at the end of the slides, I'll make them available. Somehow there are references, books and link references also. Here you see where I put the material as well in terms of things. So you can check the sources. So the first one is the abstraction level. Minimum and maximum levels of abstraction exposed by the API. Think about the file example. We see it again. The first example, there was the concept of file, but then there was the concept of reader and buffer reader and things that are actually at different level of abstraction in that context. Working framework. How much stuff have you got to know to be able to achieve your goal? How many classes, methods, packages, how many of these things are involved? Progressive evaluation. To what extent you can execute partially completed code? How many of you, when you try some new library, you want to achieve something, how many of you actually copy and paste some example code and then start tweaking it and maybe removing some and start to execute bit by bit and add more? How many of you do that? So an API that is amenable to this, that you can do that, allows you some progressive evaluation, basically helps you in understanding how it works. And penetrability, that is the extent to which you must understand the underlying implementation details. For example, things like sorting lists. For that one, it's very important to understand if the implementation is N log N or N square or whatever else. You can't see it from the surface, from reading the API. You have to understand, in a way, it's internal. Somebody has to tell you, but in a way, it's internal if you don't know. Or is it thread safe or not? Sometimes people don't tell you these things, but maybe you need to know that because the context in which you are using it, if it is not thread safe, you may have a problem. And then you don't know if it is thread safe and to be sure you have the log around it and then maybe you discover that it was thread safe and you have plenty of far too many logs and you start to have other kind of things. So how much you need to know about the details so you can be effective in using it. And then consistency, how much can you infer of the API once you know part of it? And here is the realm of naming conventions or the central decor patterns of your design of the API. It's the kind of the map of the system. How easy it is for people to actually understand it. Let's look at the examples again with the cognitive dimensions on the side so we have a reference point. So abstraction level, in this example, there is quite a lot of stuff going on because I want to read the file line by line. For me, the concept here is that I have a file and it has got lines. But here you have to know about file readers, buffered readers. And stuff like this. It's like there is more than abstraction level. There are many things, different levels you have to know to be effective with it. If I'm not clear, raise your hand and ask questions. Then think about the working framework. Again, you need to know about several classes and exceptions and things. Progressive evaluation, well, in a way it allows you to that because you can always create a reader and file reader and compose them one at a time and get there. So yeah, there is some. Penetrability, well, in this case, it's like you don't really need to know much of the internals. So it's okay. But at least it's all public stuff in this package. We are not thinking about any trading issues here or stuff like this. So kind of okay. Consistency, well, I'd say that Java is consistent in making your life difficult sometimes. So in a way, it's consistent to an extent. But let's look also here. This is an interesting point that will come up in a minute. So everything is in the same package. So I'm looking just at one of the Java IO API. I think, yeah. I need to just browsing that package. I'm able to do this. The example with Java 7. So this is a different one here. The abstraction level is actually much better because I've got the concept, well, of file and file system path and chart set that is, well, depending on what you read is probably interesting, even if probably most of the time you will end up with the default chart set anyway. So the abstraction level from this point of view is much better. Working framework, again, doesn't really require to know. A lot is fine. Progressive evaluation, you can do that. Penetrability, just like before. Consistency, again, same Java stuff. However, and also everything comes from the same package. So if you know about this package, you can browse it. But now I want to attract the rotation to one thing. This thing is in addition to the Java IO package. So basically now in Java 7, you have different ways of achieving the same goal using different subsystems, different APIs, which have things with similar names. I actually checked the files class. There's also some methods that are like the file class on the other one, except the file is not static. Files is mostly static methods. The other is not. So it's kind of, if you know about both, you may end up actually being confused. So living in the same system, these things actually may make your life not easier. And then the example with the scanner. The code is cleaner, I agree on that. And probably it's abstraction level. You have this concept of scanner. Scanner of what? What does a scanner scan? You can scan many things in many ways. What does it mean? And then if you look also, the scanner does basically scan stacks to give you some and chops it in various ways. But then also the scanner takes a file. So it's kind of odd. I don't think the abstraction level is quite much up. The code is cleaner. But look at this. It's two packages, two different ones. So you have to know about the Java.io file and the Java.util scanner. Live along the file that calling something utility is always a bit, you know, it's like the article. You don't know where to put stuff. You put it in utility. So the guy that criticized me on the web was like, you just said to use the scanner, your example is not relevant. Well, you know, the example was not about writing the code test. It was about talking the usability of the APIs here. So I maintain that even if the code here is cleaner, actually, it's not more usable. I would contend that actually using the first example is probably easier for you to find. Because you have to browse one place that is the obvious one, deals with files, yeah, and then you end up digging there. But it never comes to my mind. I say, ah, it's not here. Let's look at util. And then of course, the same example with the Python just to hear the abstraction level is exactly what you want, basically. You have a file, you have your lines, working framework requires one class and the red lines method on the class, which is pretty obvious. And progressive evaluation is pretty easy. Actually with Python, it's interesting. The first time I used it, I needed to solve a problem at work. So, but I didn't know the language at all. I got a Python in a nutshell book on the weekend, but basically ended up on the Monday having production ready code. But most of the code I wrote, I could kind of guess. You know, it's like, well, this class can be. And that was an interesting experiment because basically you can see that the APIs there are written in a way that address the 80% case. Yeah. Maybe they are not just as flexible and the Java, but I don't care. You know, most of the time you don't care about these things. Any questions so far? Okay. Now some techniques. As I said, these are techniques that are not new. It's nothing I've invented. It's nothing I've shatting or anything with this. But it's something that I see. I see again and again the same problems. Yeah. Basically, to actually improve our code, sometimes we just need a bit of attention and just apply very simple techniques that we all should know. Yeah. And I like this quote from Alan Perlis about this. You know, none of the ideas presented here are new. They are just forgotten from time to time. It just describes my feelings about these kind of things. So I talk about things like user perspective, naming, explicit context, error reporting, and also incremental design. For who in this room these things are completely unknowns, you cannot even guess what this stuff is. Yeah. So as you can see, it's nothing particularly new. But first thing, we developers are in the habit of doing our own stuff. Then somebody cannot use our code, our APIs, comes to us and we say, oh, how is it possible? Look, it's simple. You just do da, da, da, da, da, da, da, da. Yeah. It's obvious. The problem is that you really need to ask what would the user do? Yeah. And you are not the user. You are not the user. You are not the user. Okay. So you really need to do something to make sure that whatever you are doing makes sense to the user. First thing, these should be trivial. Yeah. Use language constructs to make intent clear. Make TV. If you read something like this on the code, you say, well, maybe you know that this code is about some TV-related stuff, television, that stuff. Yeah, it's creating a TV instance. What the hell is true and false? Yeah. I see code like this all the time. Then you go and dig into the implementation and you say, ah, it's color and then I guess it's color. The other one must be black and white and then it's safety or flat screen or something like this. Yeah. What if we had just an enum? As I say, this, how many of you have never seen code with a Boolean like this? How many of you always write code in ways similar to this? Okay. Well, I include the, you know, I'm just like you. It's not that I'm perfect. Just to be clear. And then you can end up with this. So basically, if the API for making TV had exposed this kind of enum, also this ability would be much, much easier because from the person having to call them, it would be much easier to guess what to do. And also reading the code, so now understand what it does. But sometimes when we develop our own stuff, we lose focus on this perspective because we are too engrossed into the ideas we have in our heads. Yeah. And forget about these aspects because after all, if you are doing it, we are the experts. Yeah. So we are not too tough with the complexity. And with the code I'm dealing with right now, it's even worse because I don't have Boolean. I don't have Boolean. I have int and hash define instead of constants, you know, to define the various ints, which is even worse because you look at the, you know, Boolean at least has two values, so it's like, OK. But then an int, you know, times 32 or something values, which ones are actually allowed and which are not? Yeah. Questions so far? These other things give control to the code. Now who can answer this? What's wrong with this? Have a look at the code and tell me. By the way, this comes from real production code I've seen. OK. So I didn't make it up. So if you find it strange, it's not because I wanted to be, you know, to make a point. It just comes from real production code. Maybe we need to tell me what's wrong with this. OK. Let's have a look. The first one is startable. So as a metal start, that returns a startable. But if you call the start again, we'll throw an exception saying, oh, it is already started. Sorry. Yeah. So if you, in your code, if you come up with a startable, you can start it, but then it's like, you can't do much about it. Then there is the stopable one that does work basically the same pattern. It turns to a stopable. But if it is already stopped, you cannot stop it. So users of this API are actually forced. Well, first of all, it's obvious that these things go together. So they are actually forced if they get a startable to downcast to a stopable. So basically they have to know an implementation detail to be able to do something with their own active object. Yeah. Is it clear? You get a startable somewhere, you start it, but at some point you need to stop it. You know that, you know, has to be a stopable, so you have to cast it to the stopable things. These kind of things can make your code incredibly brittle because then you change something, the casts don't work anymore, you have all sorts of problems. This had been written by someone that a very interesting idea about single responsibility principle. After a while we reflected the whole thing to this, which was, you know, is a service has a start, has a stop, and you know if it is started or not. So you know what to do with it. But this kind of code actually happens again. I see this again and again of variations on the team. It's like you provide an API, but then to do something you have to downcast and have to know the implementation class underneath. You have the interface, but you have to know the implementation, downcast it to do something interesting to you. As I said, these are very simple things to do. Unfortunately code like this is still out there. Who does TDD in this room? At least write unit testing even if not TDD. The same people. A few more. Now if you actually write your API using TDD, put it in the shoes of a user. Because if you write the test first, they think you don't want to be in your test. It's like I need to read the file line by line, but of course now let's start with a file reader, then a buffer reader, blah, blah, blah, blah, blah, blah. At the end you have the file. If you use TDD, usually you tend to say, you know what, I want to have a file in which I can read lines. Because in that point in time you become the user and these kind of tedious to do the long winded way. And also helps you because it helps you with outside in development. Usually you look at what you are trying to achieve exactly from the user perspective first, not from the internals, not from the other side. If writing a test is painful, the design may be wrong. Now this is an important thing because I work with teams that use TDD and they couldn't read their tests. So they were confused by their tests. So that was a very strong sign that something was wrong somewhere. And this is something that is very easy to spot because you see as soon as you get confused something is wrong. And that is actually a strong signal that maybe you should change something in your design. Not only this, the test will provide up to the documentation and examples of use. How many of you, before using a new API, the very first thing to do is less download the PDF manual and read about it? One. That's the first one. How many of you, instead, download snippets of code, copy, paste, try to compile and see what it does? If you use TDD, providing the tests actually also to the user, this provides the examples of use. The snippets of code to get them started. And of course, with TDD helps with various things, the abstraction level. As I said, it helps you to limit the number of abstractions in the mainline scenario simply because you're writing the test first, you probably are lazy, just like I am, and you don't want to write a blot of stuff just to get to the main point. The tendency is to get to the main point first and then work around it. Helps keep the working framework smaller pretty much for the same reason. You don't want to instantiate a zillion classes or dependencies first. And in fact, if you have to mock, to instantiate 50 mocks before actually being able to test your class, your design might be slightly wrong. Helps with the progressive evaluation as well, because the tests themselves are written in a progressive way. So somehow naturally, your code follows the same kind of pattern. Provides examples on how the components interact with each other. So helps with the penetrability as well. So people can see there in the test that passing what they can do. And consistency, if you are doing TDD properly, you refactor. I don't know how many of you were there and talked this morning about TDD, what went wrong. The refactoring bit is always the bit that everybody seems to forget. Well, he mentioned that. And this will help you to actually maintain that consistency as well, because if you refactor, you'll end up actually taking care of these things. So if you think about the first example, reading a file, probably if it was like if the read line was considered an important function there in Java and was developed TDD, this is an hypothesis. A potential test would have been something like this. You get some expected lines. You instantiate the file read lines and check that they are exactly what you expect. So probably you would end up with code more similar to this. I pretty sure you wouldn't end up with this thing. Yeah? Anybody thinking that I'm talking rubbish here? Questions? OK. Naming. We talk always about naming. We have also big heated conversations usually in the team about these things. But the rules are actually simple rules, not necessarily simple to implement. So first one is reserve the simplest and most intuitive names for the entities using the most common scenarios. Yeah? So if the scenarios that are the main line was, reserve the best names for those things. Pick one word per concept. This particular point is advanced stuff in investment banking. I've seen applications where in the same application, the same concept was called trade, deal, contract and transaction. The same code base. OK. So this seems to be an easy thing to do. Well it's easy to say but requires some discipline. Yeah? And use easy to remember conventions. So with the maybe get set is not a great convention. But for example in Java is an important one simply because also some libraries rely on it anyway. Yeah? So you can use that or whatever it is you choose, make sure you follow it. So this will help people to guess. The names will make the API actually easier and more consistent. Yeah? I've done this in several projects and it's actually, you know, a bit hard to do at the beginning but then if the conventions are clear and not complicated, you know, without need picking on tiny details, just you need a few simple ones, you'll end up with a much, much better thing at the end. Don't be cute. Don't use funny names. Don't be funny. Don't do like one guy, I work in a code base, he wrote things like there was something to make a difference between transaction, banking transactions and he called transaction if guru. That's not fun. That's a problem because if I'm new to the project, yeah, I look for something for the difference and then I read guru is like what the heck is this thing? Yeah? And this is an example of not to do taken from Java again. I'm not trying to pick on Java. It's just that it's full of good examples for this kind of talks. Now something called Java your object string constants. Obviously, it's obvious what it does, yeah? And also is some documentation. This is a common constants written into the object serialization stream. Anybody can guess what this thing does? Then I got inside, say, ah, satikint and then it is beautiful also that the constants have the comments like base, wire handle, first wire handle to be assigned. Protocol version one, a string protocol version. What the hell is going on here? Then if you think about it actually, this is a kind of DSL for the serialization. They use these constants to mark various points in the serialization stream. So it's actually DSL, yeah? It's not expressed with constants but it's a DSL, but the naming of the class, it tells you this is implemented using constants. It's not telling you what it does, what it's supposed to do. And the comments here, they just is the kind of comments that is basically copy the code, paste the code and put spaces in between the uppercase thing. But this is a very common thing. I see again and again. Explicit context, what I mean with this? So we have some assumptions when you produce an API. We have assumptions about the external environment, yeah? And also there are some kind of context when interested in it. There is a deployment context, yeah, and the runtime one. So assumptions about the external environment is also things about who controls the application, things like this. Typically if you have GUI APIs, they want, often they want the main thread, they own the main thread, main loop, stuff like this. And also the context of deployment runtime, the deployment one, dependencies on other APIs, which version should you have of this other thing for this one to work? Dependence on deployment part. Sometimes you can deploy some things only some parts of your system for whatever reason, yeah? Or I work with APIs that I think with user permissions, licensing things or stuff like this. So things you have to keep in mind and have to be very clear, yeah? Example of interesting deployment context in a bank, I was working in a system that used an internally developed login framework. Well, the login framework was in the application, so I joined the team, something was not quite working and then I said, okay, look at the logs. I couldn't see any logs. After a couple of hours of confusion because I knew that the application was going through those parts, I asked a colleague and said, oh yeah, I forgot to tell you, you know, it depends on this setting on the Windows registry file. If you don't set this in the registry, you can't actually, it won't log anything in your application, okay, I use some colorful Italian expressions at that point. This is an assumption on the deployment context and was not even made public, yeah? Besides what you may think or not about this kind of choices, yeah, I don't have a positive opinion on this, but still, yeah, was an important one. And then there is the runtime context, initialization and finalization steps. Depending on the API, sometimes you need some method called at the beginning, initialize and finalize at the end. I don't know, maybe if you have, for example, middleware, typically if you have used Core, Binder, PaaST or ISO, some middleware systems, sometimes you have to initialize the thing that will connect you to the external world, yeah? Stuff like this in the main. Preconditions for calling methods or functions or instantiating classes, yeah? How can I use this? As a user of the API, what am I supposed to do to be able to use the functionality, yeah? Assumptions about global application state, this is my preferred one. Because the global state, now, global is not actually cool anymore. In recent years, we haven't done with global, you know, we have done with global, no more global, we use singletons. Two things that a singleton is not a global variable. Because my description of a singleton is that it's a global variable with a raincoated and dark glasses, yeah? Trying to look inconspicuous, you know, and I noticed. The thing is that if you have global state in your API, you can actually cause trouble to the users. Because, sorry, they can be very difficult to use in a concurrent environment, yeah? If you went to the closure talks and things, they might, I wasn't there, but they might mention these kind of things, yeah? The functional languages are big on this. You don't want any global stuff. Not even global, not even changing stuff most of the time, so even stronger than that. They can make test setup extremely hard. Now, I interviewed quite a few times people that said they knew design patterns. Typically, this is a hint that I have to ask them some interesting questions. That is, okay. What have you read? That is always the gango for book, nothing else. Which pattern have you used? The singleton. Ha, ha. Have you had any problems with that? No. Do you test your code? Of course. How did you do that with the singleton? Ah, yeah, we had to do, you know, this trick in the linking so we could have the singleton for testing purposes. I was like, ah, that was actually not a problem. I know that I think of it. Yeah, it was a bit of, you know. I can make penetability really hard by having dependencies. If you use singletons, yeah, I now use the word singleton for the global state just for the sake of argument. It's just you have a call onto the singleton inside your function but it's invisible from outside. Yeah, so as a user of the API, I don't know that you call something there that is global state. So I may have horrible surprises. And I've had quite a few headaches because of this kind of stuff. Yeah. As I said, all these stuff so far sounds obvious, yeah? Yet how, raise your hand if you have seen applications where there were singletons that gave you problems with testing or concurrency or whatever, yeah? There is plenty of that. So reporting, the next one. This is extremely important for visibility because if things go wrong, I need to know what I can do about that. Yeah? You just need to know how errors are reported, what is reported when, oh, sorry, I clicked one more, and what they can do about them. The code base I'm working right now, the people since they are using C++ and C++, they are using C++. When you throw, you can throw everything, even the kitchen sink. You know, you can throw, in the throw clothes, you can put strings in whatever type you like. They throw char star, which means that if something happens, I have no way to decide programmatically what kind of error was, yeah? Java code dealt with in a previous contract, the guys that work on this framework, wonderful persistency framework for the bank, so they gave out the API that had only one exception that was thrown in all cases, you know, wrong connection error, data error, whatever, the only way to decide what the problem was was parsing the message string. Yeah? So these are actually awful things that can cause incredible trouble, and you can't recover from error easily if you have that, yeah? So actually, with the error reporting, you need to make recovery easy to do. So you can use error codes, possibly, or exception classes, or often a mix of the above, yeah? Sometimes I see exception classes with error codes inside, because, you know, depending what you do, having a forest of exceptions is just as bad as having none, yeah? So there is a matter of balance. But whatever it is has to be easy from a programmatic point of view, yeah? So the user has to be the person, the developer that is writing code with your API has to have a way to take action in case something bad really happens. Yes? These errors, do you throw them in the face of the user? The user in this talk is the programmer using the API, okay? The user in this context is the user of the API, okay? Yeah, you throw them in their face. As I said, text messages are not good enough, yeah? And something that is not clear to many people is that error management and reporting need careful design from the very beginning. The problems I've seen are due to the fact that very often error management is an afterthought. Most of the time I have the same experience. And this is incredibly bad because it can cause trouble in the code using your API. They have to make all sorts of convoluted mess just to understand what is going on there, yeah? Possibly knows implementation details so they know what kind of thing to expect, yeah? And the other thing that is sometimes not clear to many people is that what is an error at one level may not be an error at another one. This is something that confuses the hell out of many people. So if you read the file and the file class tells you throws an exception, say file not found exception at that file class level, that is obviously an error, yeah? So says, well, you told me to read something that doesn't exist. I'm telling you, I cannot do anything, sorry. From the perspective of the caller, might not be an error, might be something that just happens, yeah? We take data from some call that comes from the user, yeah? That made a typo there, yeah? And then it is not really that you report back, look, that you gave me the wrong, the name of something doesn't exist, yeah? If it is a typo, you can't consider that like as an exception at that level. Something that happens here to deal with it, yeah? It's not exceptional in any way, yeah? So the error at that level has a different connotation, okay? Because sometimes people think that whatever exception is at the bottom level has to be, you know, major error that has to be reported all the way through. No, you don't want that. You have to keep in consideration that keeping into account that from the caller point of view may not be an error. So you have to be clear, yeah, about your design there. And then the final thing, incremental design. I put this quote from Fred Brooks. Every who of you knows about Fred Brooks and read this book, The Mythical Manmunt. Okay, so he is often quoted for, you know, saying, talking about clean architecture design. The interesting bit is that this quote is not about the internals of the system, it's about the APIs exported, yeah? I will contain that conceptual integrity is the most important consideration in system design. It is better to have a system omit certain anomalous features and improvements, but to reflect one set of design ideas than to have one that contains many good, but independent and uncoordinated ideas. So we often use the conceptual integrity to talk about the internal architecture of our systems, yeah? But here, what Fred Brooks referred to is like from the outside point of view. He was the project lead of the IBM 360 operating system, yeah? So basically his users were programmers, yeah? Now when we talk about API design, we always talk about flexibility. We need to be able to do everything, yeah? You need to do this, ha, my API can do that. You need to do that, completely different. We can do that as well. Yeah, my suggestion is start specific and small and be focused, yeah? Start with the 80% case first. And also I would say, remember that when you talk about API software and they're associated the word framework, framework means that there are frames around it. Frames put some constraints, yeah, to what you do. A framework is made to limit your choices, not to extend them, yeah? Quite a counterintuitive thing, yeah? But if you do that, actually, you'll be in a much better position because it's actually easier to extend your API later on. Because if you start big, we need to solve this problem and that problem, then you think oh, okay, then we make it flexible, but flexibility is not truly flexible. It's flexible in a specific direction. If the changes go into a different one, you have a problem. How many of you have dealt with the flexible frameworks, possibly written by you or by teammates or your project that the flexibility ended up being something that was just dead code to carry on and maintain? Yeah? So I'm suggesting it's always start specific and small, you have more options later on. When you know more, you know in which direction to actually add stuff, yeah? It's easier to add than to remove always. And you are not going to need it anyway, you're going to need it anyway. You are very bad at predicting what you are going to need later on. Of course, sometimes we know that something is absolutely necessary. I'm not saying that is always the case. So if we know that, we do that, yeah? But most of, very often we just don't, we guess. Then there is the caveat, I mentioned before, sorry, that public APIs are more difficult to reflect on. In fact, some errors may actually become features. Think about Excel. I don't know if any of you have done any programming with Excel. Because an interesting concept of date, so the dates are integer from, I think it's the 31st of December, 1899. But then there is a bug in Excel that they thought that 1900, I think, was a leap here. And basically this screws up the whole thing. So everybody knows about the mistake. So all the code is written around that bug when they have to deal with dates in Excel. That's a feature. If they fix that, they will break tons of code. Yeah? And you can change them. But the techniques to reflect are usually involved some form of deprecation and versioning. So it can be quite a complicated affair. It's not really a quick feedback loop of a few hours. It can be months, years. It can be a very, very long thing. Yeah? Or you can choose the C++ way that you just pile up craft on top of craft and keep everything. Yeah? And now that you have some time, six minutes, so I wasn't sure if I could get to this point or not. But yeah, I want to show a couple of studies that are, you know, they also, they have quite something that surprised me in a way. The first one is the factory pattern. How many of you do Java in this room? C-sharp? How many of the APIs use involve some form of factory to instantiate stuff? Now, I've seen, I have a more limited experience in C-sharp than in Java. And Java certainly, it seems that is, if you have an API without a factory, you know, you are not cool. But according to this study, the factories are detrimental to usability in several situations. I put a reference to the study also in the slides. You can download it from the web. And actually, the constructors are much better. Now I have to say that personally, my personal experience, I agree with this. I didn't do the study, but I always find it awkward to work with factories also because often they involve factories or factories. You know, you go to the meta, meta level is like what the hell, I just want one of these. Sometimes even if they reduce usability, you still need them for various reasons. You know, you don't want to expose some things. But often you can do without. So it's basically, if you are writing something that involves factories, take a step back and see if there are other ways to get around the problem. And then there is another one that is even more surprising for me, the constructors. Apparently, according to this study, programmers strongly prefer and were more effective with APIs that did not require constructor parameters. So basically construct without parameters and then set properties later. Which I found a bit odd. Yeah? I did the study, so maybe I need to read it again to see if there are any caveats in the way they did the study. But I find this very interesting. In this case, in my opinion, it doesn't really matter what they say, I would still go with a constructor and the parameters because it's easy to forget setting stuff. And then if you find yourself, you know, construct, set, set, set, construct, set, set, you say, you know what, I'll put a factory in. And I say, now I have two problems. But, you know, these are kind of surprising things when I was researching from material. I wasn't really expecting this stuff. I don't know, especially in this one, why, but apparently it's like that. And nowadays also with the ideas I don't think is actually that problematic anyway. You can only see the parameters there with the autocompletion. That said, I'm done. Any questions? Questions, remarks, these are links to all the resources there and then there are also some books I suggest. Yeah? But have you got any questions or remarks or even something that contradicts whatever I've said? Yes. Yeah. But still, yeah, you can do that. Yeah? So we said the example is a startable and stopable thing. Yeah? Said instead of throwing, you just ignore it if you're already started. That solves the problem of the exception, but introduce anyone. How do you know that it started? You know, basically what you end up doing is any part of the code where you need the thing to be started, you will start it all over the place. Because it's like, I'm not quite sure here if it is in the state in which I want. So you know what? I make sure it is. Which will make your successive refactoring actually quite hard. Sorry? Restart method. Yeah. Well, I have seen in the refactor example. So basically the refactor example is a potential. Yeah, is what we did. Yeah? It was like, it was a better solution. I'm not saying that there is a best solution. But the whole point was that if you have something like this, you want the programmers, the fuselage API to be able to do something with it, to take decisions. If I start it, I want to be able to say, well, is it started? Can I, I want to be able to stop it? Yeah? Without recurring to strange tricks. Yeah? This was the point there. Then you can solve the problem, maybe in many other ways. Yeah? That my solution was an example of a solution. Okay? Any other? Okay. Thank you very much. Time for coffee. No problem. Perfect.
 Okay, can you hear me?  It's like I don't have any answer, so I suppose nobody hears me.  Okay.  So writing usable APIs in practice.  Now I have to tell you that if you have Googled about this, there are some other versions,  of this talk I gave at other conferences, however, they are not identical.  I always modify the material a bit, update stuff a bit, and generally speaking, I update  it with the stuff that at that point in time annoys me the most.  Okay.  Basically, my talks are born out of frustration of what I see in my daily job.  I work as a freelance consultant and contractor, and I write code.  And I land on legacy code bases where some interesting decisions were taken.  Okay.  As far as APIs go, in this case, now we'll see it, but this talk is not only about APIs  as when you download, I don't know, Spring API or Hibernator or something like this,  but it's also about the stuff you do at work.  Your internal project, if you actually are careful and disciplined in the factoring your  code, you'll end up with plenty of packages or libraries or the things that represent  some sort of APIs, subsystems you use in the rest of the system.  Okay.  So the idea is that I'll talk about things that are relevant both for the public, if  you publish APIs for third parties or use them in your own internal projects.  Okay.  I want to start with the definition first.  In this case, when I'm talking about APIs, I'm not talking about the Amazon APIs or stuff  like this, but it's more like, and we'll be using this definition, any well-defined interface  that defines the service that one component, module or application provides to other software  elements.  As a practical example, Java IO package, Java 7, this is, this provides an API.  Okay.  I use it as an example because then I have a running example with this thing.  So basically, this is all the interfaces, classes, the exceptions are part of the API,  the errors, but also you don't see them here, but also the public methods, stuff that the  users of this package use are part of the API in this context.  Okay.  By the way, if at any point in time you have questions, observations, and stuff like this,  please raise your hand.  And then talk about usability.  So what is usability about in this context?  It's about efficiency.  Something is usable for if it allows you to be productive in what you do.  You want to do something without wasting time.  It's about effectiveness.  So we want to be able to actually solve the problem you are trying to solve in a proper  way.  It's about error prevention.  A good software API, a well-written one, will help you to avoid some stupid mistakes.  And this is also about ease of learning.  How many of you have come across APIs that actually you can guess what method to call  in which class, stuff like this, or the order of parameters?  I've seen some very, very few.  But it's about this.  It's like the ease of learning is about the fact that you can navigate it in simple ways  without too much effort.  And then since I've seen Uncle Bob with questions, yesterday I saw Jeff Watts with the questions.  I said, okay, I want my own question in my talk now.  I'm jealous.  There it is.  This is a usability question.  This is the big O notation.  So it means that the upper bound for this is one on B.  I don't know exactly which kind of function is.  But that's an upper bound.  And B is the brain power necessary to achieve your goals.  Sorry, I thought I deleted this one.  The question is made up in totally arbitrary.  I was just jealous on my side for the other guys.  But the serious message is that actually if you think as a rule of thumb that you don't  want to overload the brain of people doing their work.  Why do we want to bother with this ability anyway?  Is from a company perspective actually, APIs can be quite a great asset, but also a huge  liability.  For example, I work a lot with investment banks.  And typically I work on the big systems they use in the back end, pricing, risk in this  kind of stuff.  And they use the APIs written by the quant that are the mathematicians that come up with  their own algorithms to calculate risk or price of an auction or some other things.  And those APIs are actually company secret.  To see them, to be allowed to see them as a developer, here to a special organization,  most of the time.  Because the algorithms are secret.  So they are actually one of the main assets of the company.  The problem is that usually there are also huge liabilities.  Let's say, as Kevlin would say, the code is usually written in good ways.  Is often written badly with memory leaks, is a mess, difficult to change, not tests.  Many wonder why you have problems in the markets, but really sometimes some important functions  have no automated tests whatsoever around those.  And so when they want to implement new models or change something in the technology, they  may have huge problems.  So a project, for example, to go from, I work in a bank that was a project to go from  Visual C++6 to, I think, Visual 2008, probably, took ages, can take years, simply because  the code was written in horrible ways.  Live alone, the visual C++6 is not really C++ in many ways, but it was mostly because  of the way the things were written.  But also there is the other perspective, it is how many of you here are programmers, write  code every day, so most of them.  Any managers, architects?  Out.  So from a programmer's perspective, having an API that is actually usable means that  they have fewer bugs to take care of.  Since, for example, I can guess how to use the classes and the methods and the data,  it's easier for me to write code that makes sense.  Of course, my code, as a consequence, will have higher quality, which will be so easy  to maintain.  And I'll be also more productive.  It'll do what I'm supposed to do in less time.  Really, a few days ago, I was working with a C API to do the tenet using some C++ code.  I actually spent two days to understand how a particular bit of functionality worked to  achieve my goals.  Because of the patterns they followed, inviting it because of the documentation around it,  but you see two days.  So let's say that I didn't really feel very productive.  So I had an impact, and I worked as a contractor, and I'm paid by the day, which means has an  impact of my customers as well.  And also, here I distinguish between two types of APIs, just as I mentioned before.  So there are the public APIs, the ones that are given to third parties.  You may work for a company that produces some libraries or packages to give outside, or  the typical open source software that we download and the libraries we use.  The private ones, in this case, are the ones for internal users.  As I said before, if you refactor your application, modularize it properly, you'll end up with  many several APIs to achieve different goals in the different parts of your application.  As we see, there is one main difference between these two that is basically the private ones.  You can do whatever you like with them.  They are internal of your project, so if you make a mess, you will make a cause and mistake.  You have a bad decision, you can always change it.  With the public ones, you can't.  Or, well, better, you can, but with great, great effort, because they are used by other  people, other teams, and sometimes they even rely on your bugs.  So in fact, I claim that any non-trivial software application involves writing one or  more APIs.  And also, I think that when we talk about good code, you know, as developers, what  we mean in a way is also code that is usable from a developer's perspective.  When you read a good fragment of code, it's like you think it's good because you know  what you can do with it.  You know how you can possibly modify it, you understand what it does.  So it's usable from a programmer's perspective.  Any questions so far?  Okay.  Now, I will introduce some concepts, usability concepts.  The idea here is that I want to give you some information that you might not have come across,  but I hope it will be useful for your work today, you know, to help you think in slightly  different ways when you write some classes, packages, functions.  After these, I'll talk also about some simple techniques you can use directly.  You know, go back to work, you can do that.  The simple techniques are chosen not because they are the only ones, it's just a small  subset and these are things that are, as we see later, actually not difficult at all.  Somehow we always forget about that stuff.  All the code I see keeps having the same issues.  So first concept, affordances.  An affordance is a quality of an object or an environment that allows an individual to  perform an action.  A door affords opening and closing.  This thing has a laser pointer, you can see it very well, but affords pointing.  We can apply exactly the same concept to APIs.  Let's think this is Java 4 reading a file line by line.  Was kind of interesting.  You can see that the affordance is there.  You can read the file line by line.  But also you can also see that sometimes the affordance is not that visible.  It's not immediate.  So if you had like media experience at that time of like, I need to do this, how do I  do that?  Then the first point of call is let's look at the class file.  Nothing in there to help you.  And then start to read all the classes and methods in the Java U and at some point you  arrive at this thing.  So the affordances there may not be that visible.  The second point is that part of the affordances are also the errors.  What can you do in case of errors?  What does the API tell you?  Something happens, what can you do about that?  So the exceptions are part of their own affordances as well.  And as you can see this example, there is plenty of stuff going on just to read the  file line by line processing.  This is an example with Java 7.  They introduced a new package.  Still doing the same thing.  Here is a bit clearer in the code what you can do.  Come out because there is a concept of file path.  There is a concept of char set which you may or may not care about.  Sometimes we just want to read the file line by line like this.  And then there is something, this read all lines method.  Still there are some things like files is a static class as a similar name to the other  one but then I'll talk about this a bit more in that later on.  And somebody that looked at one version of this talk on InfoQ actually criticized me  for saying, well, the example with the file is not relevant.  You should have used the Java class, Java Util Scanner.  Admittedly this code is cleaner than the first example.  Fewer things going on.  But if you have a few minutes of patience, I will explain why I don't think it's still  a good API kind of thing.  This is what you do in Python.  Which I put this because what you do in Python or Ruby, this kind of languages is usually  kind of obvious.  This is why the duck typing languages have the property that usually somehow are written  in a way that the obvious stuff you want to do the 80% of the time is just easy to find.  And then there are some cognitive dimensions.  By the way, I put at the end of the slides, I'll make them available.  Somehow there are references, books and link references also.  Here you see where I put the material as well in terms of things.  So you can check the sources.  So the first one is the abstraction level.  Minimum and maximum levels of abstraction exposed by the API.  Think about the file example.  We see it again.  The first example, there was the concept of file, but then there was the concept of reader  and buffer reader and things that are actually at different level of abstraction in that  context.  Working framework.  How much stuff have you got to know to be able to achieve your goal?  How many classes, methods, packages, how many of these things are involved?  Progressive evaluation.  To what extent you can execute partially completed code?  How many of you, when you try some new library, you want to achieve something, how many of  you actually copy and paste some example code and then start tweaking it and maybe removing  some and start to execute bit by bit and add more?  How many of you do that?  So an API that is amenable to this, that you can do that, allows you some progressive  evaluation, basically helps you in understanding how it works.  And penetrability, that is the extent to which you must understand the underlying implementation  details.  For example, things like sorting lists.  For that one, it's very important to understand if the implementation is N log N or N square  or whatever else.  You can't see it from the surface, from reading the API.  You have to understand, in a way, it's internal.  Somebody has to tell you, but in a way, it's internal if you don't know.  Or is it thread safe or not?  Sometimes people don't tell you these things, but maybe you need to know that because the  context in which you are using it, if it is not thread safe, you may have a problem.  And then you don't know if it is thread safe and to be sure you have the log around it  and then maybe you discover that it was thread safe and you have plenty of far too many logs  and you start to have other kind of things.  So how much you need to know about the details so you can be effective in using it.  And then consistency, how much can you infer of the API once you know part of it?  And here is the realm of naming conventions or the central decor patterns of your design  of the API.  It's the kind of the map of the system.  How easy it is for people to actually understand it.  Let's look at the examples again with the cognitive dimensions on the side so we have  a reference point.  So abstraction level, in this example, there is quite a lot of stuff going on because I  want to read the file line by line.  For me, the concept here is that I have a file and it has got lines.  But here you have to know about file readers, buffered readers.  And stuff like this.  It's like there is more than abstraction level.  There are many things, different levels you have to know to be effective with it.  If I'm not clear, raise your hand and ask questions.  Then think about the working framework.  Again, you need to know about several classes and exceptions and things.  Progressive evaluation, well, in a way it allows you to that because you can always create  a reader and file reader and compose them one at a time and get there.  So yeah, there is some.  Penetrability, well, in this case, it's like you don't really need to know much of the  internals.  So it's okay.  But at least it's all public stuff in this package.  We are not thinking about any trading issues here or stuff like this.  So kind of okay.  Consistency, well, I'd say that Java is consistent in making your life difficult sometimes.  So in a way, it's consistent to an extent.  But let's look also here.  This is an interesting point that will come up in a minute.  So everything is in the same package.  So I'm looking just at one of the Java IO API.  I think, yeah.  I need to just browsing that package.  I'm able to do this.  The example with Java 7.  So this is a different one here.  The abstraction level is actually much better because I've got the concept, well, of file  and file system path and chart set that is, well, depending on what you read is probably  interesting, even if probably most of the time you will end up with the default chart  set anyway.  So the abstraction level from this point of view is much better.  Working framework, again, doesn't really require to know.  A lot is fine.  Progressive evaluation, you can do that.  Penetrability, just like before.  Consistency, again, same Java stuff.  However, and also everything comes from the same package.  So if you know about this package, you can browse it.  But now I want to attract the rotation to one thing.  This thing is in addition to the Java IO package.  So basically now in Java 7, you have different ways of achieving the same goal using different  subsystems, different APIs, which have things with similar names.  I actually checked the files class.  There's also some methods that are like the file class on the other one, except the file  is not static.  Files is mostly static methods.  The other is not.  So it's kind of, if you know about both, you may end up actually being confused.  So living in the same system, these things actually may make your life not easier.  And then the example with the scanner.  The code is cleaner, I agree on that.  And probably it's abstraction level.  You have this concept of scanner.  Scanner of what?  What does a scanner scan?  You can scan many things in many ways.  What does it mean?  And then if you look also, the scanner does basically scan stacks to give you some and  chops it in various ways.  But then also the scanner takes a file.  So it's kind of odd.  I don't think the abstraction level is quite much up.  The code is cleaner.  But look at this.  It's two packages, two different ones.  So you have to know about the Java.io file and the Java.util scanner.  Live along the file that calling something utility is always a bit, you know, it's like  the article.  You don't know where to put stuff.  You put it in utility.  So the guy that criticized me on the web was like, you just said to use the scanner,  your example is not relevant.  Well, you know, the example was not about writing the code test.  It was about talking the usability of the APIs here.  So I maintain that even if the code here is cleaner, actually, it's not more usable.  I would contend that actually using the first example is probably easier for you to find.  Because you have to browse one place that is the obvious one, deals with files, yeah,  and then you end up digging there.  But it never comes to my mind.  I say, ah, it's not here.  Let's look at util.  And then of course, the same example with the Python just to hear the abstraction level  is exactly what you want, basically.  You have a file, you have your lines, working framework requires one class and the red lines  method on the class, which is pretty obvious.  And progressive evaluation is pretty easy.  Actually with Python, it's interesting.  The first time I used it, I needed to solve a problem at work.  So, but I didn't know the language at all.  I got a Python in a nutshell book on the weekend, but basically ended up on the Monday having  production ready code.  But most of the code I wrote, I could kind of guess.  You know, it's like, well, this class can be.  And that was an interesting experiment because basically you can see that the APIs there are  written in a way that address the 80% case.  Yeah.  Maybe they are not just as flexible and the Java, but I don't care.  You know, most of the time you don't care about these things.  Any questions so far?  Okay.  Now some techniques.  As I said, these are techniques that are not new.  It's nothing I've invented.  It's nothing I've shatting or anything with this.  But it's something that I see.  I see again and again the same problems.  Yeah.  Basically, to actually improve our code, sometimes we just need a bit of attention and just apply  very simple techniques that we all should know.  Yeah.  And I like this quote from Alan Perlis about this.  You know, none of the ideas presented here are new.  They are just forgotten from time to time.  It just describes my feelings about these kind of things.  So I talk about things like user perspective, naming, explicit context, error reporting,  and also incremental design.  For who in this room these things are completely unknowns, you cannot even guess what this  stuff is.  Yeah.  So as you can see, it's nothing particularly new.  But first thing, we developers are in the habit of doing our own stuff.  Then somebody cannot use our code, our APIs, comes to us and we say, oh, how is it possible?  Look, it's simple.  You just do da, da, da, da, da, da, da, da.  Yeah.  It's obvious.  The problem is that you really need to ask what would the user do?  Yeah.  And you are not the user.  You are not the user.  You are not the user.  Okay.  So you really need to do something to make sure that whatever you are doing makes sense  to the user.  First thing, these should be trivial.  Yeah.  Use language constructs to make intent clear.  Make TV.  If you read something like this on the code, you say, well, maybe you know that this code  is about some TV-related stuff, television, that stuff.  Yeah, it's creating a TV instance.  What the hell is true and false?  Yeah.  I see code like this all the time.  Then you go and dig into the implementation and you say, ah, it's color and then I guess  it's color.  The other one must be black and white and then it's safety or flat screen or something  like this.  Yeah.  What if we had just an enum?  As I say, this, how many of you have never seen code with a Boolean like this?  How many of you always write code in ways similar to this?  Okay.  Well, I include the, you know, I'm just like you.  It's not that I'm perfect.  Just to be clear.  And then you can end up with this.  So basically, if the API for making TV had exposed this kind of enum, also this ability  would be much, much easier because from the person having to call them, it would be much  easier to guess what to do.  And also reading the code, so now understand what it does.  But sometimes when we develop our own stuff, we lose focus on this perspective because  we are too engrossed into the ideas we have in our heads.  Yeah.  And forget about these aspects because after all, if you are doing it, we are the experts.  Yeah.  So we are not too tough with the complexity.  And with the code I'm dealing with right now, it's even worse because I don't have Boolean.  I don't have Boolean.  I have int and hash define instead of constants, you know, to define the various ints, which  is even worse because you look at the, you know, Boolean at least has two values, so  it's like, OK.  But then an int, you know, times 32 or something values, which ones are actually allowed and  which are not?  Yeah.  Questions so far?  These other things give control to the code.  Now who can answer this?  What's wrong with this?  Have a look at the code and tell me.  By the way, this comes from real production code I've seen.  OK.  So I didn't make it up.  So if you find it strange, it's not because I wanted to be, you know, to make a point.  It just comes from real production code.  Maybe we need to tell me what's wrong with this.  OK.  Let's have a look.  The first one is startable.  So as a metal start, that returns a startable.  But if you call the start again, we'll throw an exception saying, oh, it is already started.  Sorry.  Yeah.  So if you, in your code, if you come up with a startable, you can start it, but then it's  like, you can't do much about it.  Then there is the stopable one that does work basically the same pattern.  It turns to a stopable.  But if it is already stopped, you cannot stop it.  So users of this API are actually forced.  Well, first of all, it's obvious that these things go together.  So they are actually forced if they get a startable to downcast to a stopable.  So basically they have to know an implementation detail to be able to do something with their  own active object.  Yeah.  Is it clear?  You get a startable somewhere, you start it, but at some point you need to stop it.  You know that, you know, has to be a stopable, so you have to cast it to the stopable things.  These kind of things can make your code incredibly brittle because then you change something,  the casts don't work anymore, you have all sorts of problems.  This had been written by someone that a very interesting idea about single responsibility  principle.  After a while we reflected the whole thing to this, which was, you know, is a service  has a start, has a stop, and you know if it is started or not.  So you know what to do with it.  But this kind of code actually happens again.  I see this again and again of variations on the team.  It's like you provide an API, but then to do something you have to downcast and have  to know the implementation class underneath.  You have the interface, but you have to know the implementation, downcast it to do something  interesting to you.  As I said, these are very simple things to do.  Unfortunately code like this is still out there.  Who does TDD in this room?  At least write unit testing even if not TDD.  The same people.  A few more.  Now if you actually write your API using TDD, put it in the shoes of a user.  Because if you write the test first, they think you don't want to be in your test.  It's like I need to read the file line by line, but of course now let's start with a file  reader, then a buffer reader, blah, blah, blah, blah, blah, blah.  At the end you have the file.  If you use TDD, usually you tend to say, you know what, I want to have a file in which  I can read lines.  Because in that point in time you become the user and these kind of tedious to do the long  winded way.  And also helps you because it helps you with outside in development.  Usually you look at what you are trying to achieve exactly from the user perspective first,  not from the internals, not from the other side.  If writing a test is painful, the design may be wrong.  Now this is an important thing because I work with teams that use TDD and they couldn't  read their tests.  So they were confused by their tests.  So that was a very strong sign that something was wrong somewhere.  And this is something that is very easy to spot because you see as soon as you get confused  something is wrong.  And that is actually a strong signal that maybe you should change something in your design.  Not only this, the test will provide up to the documentation and examples of use.  How many of you, before using a new API, the very first thing to do is less download the  PDF manual and read about it?  One.  That's the first one.  How many of you, instead, download snippets of code, copy, paste, try to compile and see  what it does?  If you use TDD, providing the tests actually also to the user, this provides the examples  of use.  The snippets of code to get them started.  And of course, with TDD helps with various things, the abstraction level.  As I said, it helps you to limit the number of abstractions in the mainline scenario simply  because you're writing the test first, you probably are lazy, just like I am, and you  don't want to write a blot of stuff just to get to the main point.  The tendency is to get to the main point first and then work around it.  Helps keep the working framework smaller pretty much for the same reason.  You don't want to instantiate a zillion classes or dependencies first.  And in fact, if you have to mock, to instantiate 50 mocks before actually being able to test  your class, your design might be slightly wrong.  Helps with the progressive evaluation as well, because the tests themselves are written  in a progressive way.  So somehow naturally, your code follows the same kind of pattern.  Provides examples on how the components interact with each other.  So helps with the penetrability as well.  So people can see there in the test that passing what they can do.  And consistency, if you are doing TDD properly, you refactor.  I don't know how many of you were there and talked this morning about TDD, what went wrong.  The refactoring bit is always the bit that everybody seems to forget.  Well, he mentioned that.  And this will help you to actually maintain that consistency as well, because if you refactor,  you'll end up actually taking care of these things.  So if you think about the first example, reading a file, probably if it was like if the read  line was considered an important function there in Java and was developed TDD, this  is an hypothesis.  A potential test would have been something like this.  You get some expected lines.  You instantiate the file read lines and check that they are exactly what you expect.  So probably you would end up with code more similar to this.  I pretty sure you wouldn't end up with this thing.  Yeah?  Anybody thinking that I'm talking rubbish here?  Questions?  OK.  Naming.  We talk always about naming.  We have also big heated conversations usually in the team about these things.  But the rules are actually simple rules, not necessarily simple to implement.  So first one is reserve the simplest and most intuitive names for the entities using the  most common scenarios.  Yeah?  So if the scenarios that are the main line was, reserve the best names for those things.  Pick one word per concept.  This particular point is advanced stuff in investment banking.  I've seen applications where in the same application, the same concept was called trade, deal, contract  and transaction.  The same code base.  OK.  So this seems to be an easy thing to do.  Well it's easy to say but requires some discipline.  Yeah?  And use easy to remember conventions.  So with the maybe get set is not a great convention.  But for example in Java is an important one simply because also some libraries rely on  it anyway.  Yeah?  So you can use that or whatever it is you choose, make sure you follow it.  So this will help people to guess.  The names will make the API actually easier and more consistent.  Yeah?  I've done this in several projects and it's actually, you know, a bit hard to do at the  beginning but then if the conventions are clear and not complicated, you know, without  need picking on tiny details, just you need a few simple ones, you'll end up with a much,  much better thing at the end.  Don't be cute.  Don't use funny names.  Don't be funny.  Don't do like one guy, I work in a code base, he wrote things like there was something  to make a difference between transaction, banking transactions and he called transaction  if guru.  That's not fun.  That's a problem because if I'm new to the project, yeah, I look for something for the  difference and then I read guru is like what the heck is this thing?  Yeah?  And this is an example of not to do taken from Java again.  I'm not trying to pick on Java.  It's just that it's full of good examples for this kind of talks.  Now something called Java your object string constants.  Obviously, it's obvious what it does, yeah?  And also is some documentation.  This is a common constants written into the object serialization stream.  Anybody can guess what this thing does?  Then I got inside, say, ah, satikint and then it is beautiful also that the constants have  the comments like base, wire handle, first wire handle to be assigned.  Protocol version one, a string protocol version.  What the hell is going on here?  Then if you think about it actually, this is a kind of DSL for the serialization.  They use these constants to mark various points in the serialization stream.  So it's actually DSL, yeah?  It's not expressed with constants but it's a DSL, but the naming of the class, it tells  you this is implemented using constants.  It's not telling you what it does, what it's supposed to do.  And the comments here, they just is the kind of comments that is basically copy the code,  paste the code and put spaces in between the uppercase thing.  But this is a very common thing.  I see again and again.  Explicit context, what I mean with this?  So we have some assumptions when you produce an API.  We have assumptions about the external environment, yeah?  And also there are some kind of context when interested in it.  There is a deployment context, yeah, and the runtime one.  So assumptions about the external environment is also things about who controls the application,  things like this.  Typically if you have GUI APIs, they want, often they want the main thread, they own  the main thread, main loop, stuff like this.  And also the context of deployment runtime, the deployment one, dependencies on other  APIs, which version should you have of this other thing for this one to work?  Dependence on deployment part.  Sometimes you can deploy some things only some parts of your system for whatever reason,  yeah?  Or I work with APIs that I think with user permissions, licensing things or stuff like  this.  So things you have to keep in mind and have to be very clear, yeah?  Example of interesting deployment context in a bank, I was working in a system that  used an internally developed login framework.  Well, the login framework was in the application, so I joined the team, something was not quite  working and then I said, okay, look at the logs.  I couldn't see any logs.  After a couple of hours of confusion because I knew that the application was going through  those parts, I asked a colleague and said, oh yeah, I forgot to tell you, you know, it  depends on this setting on the Windows registry file.  If you don't set this in the registry, you can't actually, it won't log anything in  your application, okay, I use some colorful Italian expressions at that point.  This is an assumption on the deployment context and was not even made public, yeah?  Besides what you may think or not about this kind of choices, yeah, I don't have a positive  opinion on this, but still, yeah, was an important one.  And then there is the runtime context, initialization and finalization steps.  Depending on the API, sometimes you need some method called at the beginning, initialize  and finalize at the end.  I don't know, maybe if you have, for example, middleware, typically if you have used Core,  Binder, PaaST or ISO, some middleware systems, sometimes you have to initialize the thing  that will connect you to the external world, yeah?  Stuff like this in the main.  Preconditions for calling methods or functions or instantiating classes, yeah?  How can I use this?  As a user of the API, what am I supposed to do to be able to use the functionality, yeah?  Assumptions about global application state, this is my preferred one.  Because the global state, now, global is not actually cool anymore.  In recent years, we haven't done with global, you know, we have done with global, no more  global, we use singletons.  Two things that a singleton is not a global variable.  Because my description of a singleton is that it's a global variable with a raincoated  and dark glasses, yeah?  Trying to look inconspicuous, you know, and I noticed.  The thing is that if you have global state in your API, you can actually cause trouble  to the users.  Because, sorry, they can be very difficult to use in a concurrent environment, yeah?  If you went to the closure talks and things, they might, I wasn't there, but they might  mention these kind of things, yeah?  The functional languages are big on this.  You don't want any global stuff.  Not even global, not even changing stuff most of the time, so even stronger than that.  They can make test setup extremely hard.  Now, I interviewed quite a few times people that said they knew design patterns.  Typically, this is a hint that I have to ask them some interesting questions.  That is, okay.  What have you read?  That is always the gango for book, nothing else.  Which pattern have you used?  The singleton.  Ha, ha.  Have you had any problems with that?  No.  Do you test your code?  Of course.  How did you do that with the singleton?  Ah, yeah, we had to do, you know, this trick in the linking so we could have the singleton  for testing purposes.  I was like, ah, that was actually not a problem.  I know that I think of it.  Yeah, it was a bit of, you know.  I can make penetability really hard by having dependencies.  If you use singletons, yeah, I now use the word singleton for the global state just for  the sake of argument.  It's just you have a call onto the singleton inside your function but it's invisible from  outside.  Yeah, so as a user of the API, I don't know that you call something there that is global  state.  So I may have horrible surprises.  And I've had quite a few headaches because of this kind of stuff.  Yeah.  As I said, all these stuff so far sounds obvious, yeah?  Yet how, raise your hand if you have seen applications where there were singletons that  gave you problems with testing or concurrency or whatever, yeah?  There is plenty of that.  So reporting, the next one.  This is extremely important for visibility because if things go wrong, I need to know  what I can do about that.  Yeah?  You just need to know how errors are reported, what is reported when, oh, sorry, I clicked  one more, and what they can do about them.  The code base I'm working right now, the people since they are using C++ and C++, they are  using C++.  When you throw, you can throw everything, even the kitchen sink.  You know, you can throw, in the throw clothes, you can put strings in whatever type you like.  They throw char star, which means that if something happens, I have no way to decide  programmatically what kind of error was, yeah?  Java code dealt with in a previous contract, the guys that work on this framework, wonderful  persistency framework for the bank, so they gave out the API that had only one exception  that was thrown in all cases, you know, wrong connection error, data error, whatever, the  only way to decide what the problem was was parsing the message string.  Yeah?  So these are actually awful things that can cause incredible trouble, and you can't recover  from error easily if you have that, yeah?  So actually, with the error reporting, you need to make recovery easy to do.  So you can use error codes, possibly, or exception classes, or often a mix of the above, yeah?  Sometimes I see exception classes with error codes inside, because, you know, depending  what you do, having a forest of exceptions is just as bad as having none, yeah?  So there is a matter of balance.  But whatever it is has to be easy from a programmatic point of view, yeah?  So the user has to be the person, the developer that is writing code with your API has to  have a way to take action in case something bad really happens.  Yes?  These errors, do you throw them in the face of the user?  The user in this talk is the programmer using the API, okay?  The user in this context is the user of the API, okay?  Yeah, you throw them in their face.  As I said, text messages are not good enough, yeah?  And something that is not clear to many people is that error management and reporting need  careful design from the very beginning.  The problems I've seen are due to the fact that very often error management is an afterthought.  Most of the time I have the same experience.  And this is incredibly bad because it can cause trouble in the code using your API.  They have to make all sorts of convoluted mess just to understand what is going on there,  yeah?  Possibly knows implementation details so they know what kind of thing to expect, yeah?  And the other thing that is sometimes not clear to many people is that what is an error  at one level may not be an error at another one.  This is something that confuses the hell out of many people.  So if you read the file and the file class tells you throws an exception, say file not  found exception at that file class level, that is obviously an error, yeah?  So says, well, you told me to read something that doesn't exist.  I'm telling you, I cannot do anything, sorry.  From the perspective of the caller, might not be an error, might be something that just  happens, yeah?  We take data from some call that comes from the user, yeah?  That made a typo there, yeah?  And then it is not really that you report back, look, that you gave me the wrong, the  name of something doesn't exist, yeah?  If it is a typo, you can't consider that like as an exception at that level.  Something that happens here to deal with it, yeah?  It's not exceptional in any way, yeah?  So the error at that level has a different connotation, okay?  Because sometimes people think that whatever exception is at the bottom level has to be,  you know, major error that has to be reported all the way through.  No, you don't want that.  You have to keep in consideration that keeping into account that from the caller point of  view may not be an error.  So you have to be clear, yeah, about your design there.  And then the final thing, incremental design.  I put this quote from Fred Brooks.  Every who of you knows about Fred Brooks and read this book, The Mythical Manmunt.  Okay, so he is often quoted for, you know, saying, talking about clean architecture design.  The interesting bit is that this quote is not about the internals of the system, it's  about the APIs exported, yeah?  I will contain that conceptual integrity is the most important consideration in system  design.  It is better to have a system omit certain anomalous features and improvements, but to  reflect one set of design ideas than to have one that contains many good, but independent  and uncoordinated ideas.  So we often use the conceptual integrity to talk about the internal architecture of our  systems, yeah?  But here, what Fred Brooks referred to is like from the outside point of view.  He was the project lead of the IBM 360 operating system, yeah?  So basically his users were programmers, yeah?  Now when we talk about API design, we always talk about flexibility.  We need to be able to do everything, yeah?  You need to do this, ha, my API can do that.  You need to do that, completely different.  We can do that as well.  Yeah, my suggestion is start specific and small and be focused, yeah?  Start with the 80% case first.  And also I would say, remember that when you talk about API software and they're associated  the word framework, framework means that there are frames around it.  Frames put some constraints, yeah, to what you do.  A framework is made to limit your choices, not to extend them, yeah?  Quite a counterintuitive thing, yeah?  But if you do that, actually, you'll be in a much better position because it's actually  easier to extend your API later on.  Because if you start big, we need to solve this problem and that problem, then you think  oh, okay, then we make it flexible, but flexibility is not truly flexible.  It's flexible in a specific direction.  If the changes go into a different one, you have a problem.  How many of you have dealt with the flexible frameworks, possibly written by you or by  teammates or your project that the flexibility ended up being something that was just dead  code to carry on and maintain?  Yeah?  So I'm suggesting it's always start specific and small, you have more options later on.  When you know more, you know in which direction to actually add stuff, yeah?  It's easier to add than to remove always.  And you are not going to need it anyway, you're going to need it anyway.  You are very bad at predicting what you are going to need later on.  Of course, sometimes we know that something is absolutely necessary.  I'm not saying that is always the case.  So if we know that, we do that, yeah?  But most of, very often we just don't, we guess.  Then there is the caveat, I mentioned before, sorry, that public APIs are more difficult  to reflect on.  In fact, some errors may actually become features.  Think about Excel.  I don't know if any of you have done any programming with Excel.  Because an interesting concept of date, so the dates are integer from, I think it's the  31st of December, 1899.  But then there is a bug in Excel that they thought that 1900, I think, was a leap here.  And basically this screws up the whole thing.  So everybody knows about the mistake.  So all the code is written around that bug when they have to deal with dates in Excel.  That's a feature.  If they fix that, they will break tons of code.  Yeah?  And you can change them.  But the techniques to reflect are usually involved some form of deprecation and versioning.  So it can be quite a complicated affair.  It's not really a quick feedback loop of a few hours.  It can be months, years.  It can be a very, very long thing.  Yeah?  Or you can choose the C++ way that you just pile up craft on top of craft and keep everything.  Yeah?  And now that you have some time, six minutes, so I wasn't sure if I could get to this point  or not.  But yeah, I want to show a couple of studies that are, you know, they also, they have quite  something that surprised me in a way.  The first one is the factory pattern.  How many of you do Java in this room?  C-sharp?  How many of the APIs use involve some form of factory to instantiate stuff?  Now, I've seen, I have a more limited experience in C-sharp than in Java.  And Java certainly, it seems that is, if you have an API without a factory, you know, you  are not cool.  But according to this study, the factories are detrimental to usability in several situations.  I put a reference to the study also in the slides.  You can download it from the web.  And actually, the constructors are much better.  Now I have to say that personally, my personal experience, I agree with this.  I didn't do the study, but I always find it awkward to work with factories also because  often they involve factories or factories.  You know, you go to the meta, meta level is like what the hell, I just want one of these.  Sometimes even if they reduce usability, you still need them for various reasons.  You know, you don't want to expose some things.  But often you can do without.  So it's basically, if you are writing something that involves factories, take a step back  and see if there are other ways to get around the problem.  And then there is another one that is even more surprising for me, the constructors.  Apparently, according to this study, programmers strongly prefer and were more effective with  APIs that did not require constructor parameters.  So basically construct without parameters and then set properties later.  Which I found a bit odd.  Yeah?  I did the study, so maybe I need to read it again to see if there are any caveats in the  way they did the study.  But I find this very interesting.  In this case, in my opinion, it doesn't really matter what they say, I would still go with  a constructor and the parameters because it's easy to forget setting stuff.  And then if you find yourself, you know, construct, set, set, set, construct, set, set, you say,  you know what, I'll put a factory in.  And I say, now I have two problems.  But, you know, these are kind of surprising things when I was researching from material.  I wasn't really expecting this stuff.  I don't know, especially in this one, why, but apparently it's like that.  And nowadays also with the ideas I don't think is actually that problematic anyway.  You can only see the parameters there with the autocompletion.  That said, I'm done.  Any questions?  Questions, remarks, these are links to all the resources there and then there are also  some books I suggest.  Yeah?  But have you got any questions or remarks or even something that contradicts whatever  I've said?  Yes.  Yeah.  But still, yeah, you can do that.  Yeah?  So we said the example is a startable and stopable thing.  Yeah?  Said instead of throwing, you just ignore it if you're already started.  That solves the problem of the exception, but introduce anyone.  How do you know that it started?  You know, basically what you end up doing is any part of the code where you need the  thing to be started, you will start it all over the place.  Because it's like, I'm not quite sure here if it is in the state in which I want.  So you know what?  I make sure it is.  Which will make your successive refactoring actually quite hard.  Sorry?  Restart method.  Yeah.  Well, I have seen in the refactor example.  So basically the refactor example is a potential.  Yeah, is what we did.  Yeah?  It was like, it was a better solution.  I'm not saying that there is a best solution.  But the whole point was that if you have something like this, you want the programmers,  the fuselage API to be able to do something with it, to take decisions.  If I start it, I want to be able to say, well, is it started?  Can I, I want to be able to stop it?  Yeah?  Without recurring to strange tricks.  Yeah?  This was the point there.  Then you can solve the problem, maybe in many other ways.  Yeah?  That my solution was an example of a solution.  Okay?  Any other?  Okay.  Thank you very much.  Time for coffee.  No problem.  Perfect.
Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_0_107.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_108_2783.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_2784_3251.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_3252_4211.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_4212_6659.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_6660_6803.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_6804_7187.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_7188_10115.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_10116_12311.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_12312_14099.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_14100_14411.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_14412_15335.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_15336_15887.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_15888_16343.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_16344_16583.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_16584_19787.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_19788_20519.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_20520_20807.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_20808_20879.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_20880_21143.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_21144_22139.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_22140_25631.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_25632_28451.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_28452_33059.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_33060_34967.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_34968_35951.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_35952_37379.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_37380_37499.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_37500_37571.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_37572_37643.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_37644_37823.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_37824_37871.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_37872_38027.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_38028_38171.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_38172_38315.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_38316_38363.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_38364_38543.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_38544_38723.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_38724_39071.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_39072_39599.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_39600_39647.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_39648_39815.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_39816_39947.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_39948_39995.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_39996_40043.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40044_40139.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40140_40271.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40272_40343.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40344_40439.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40440_40511.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40512_40643.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40644_40943.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_40944_41195.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_41196_41399.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_41400_41531.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_41532_44567.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_44568_44891.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_44892_45059.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_45060_45671.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_45672_49343.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_49344_51839.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_51840_52571.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_52572_56387.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_56388_57143.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_57144_57995.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_57996_58067.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_58068_58895.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_58896_59411.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_59412_59483.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_59484_59891.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_59892_64163.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_64164_65807.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_65808_67067.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_67068_67175.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_67176_69191.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_69192_71903.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_71904_73991.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_73992_75779.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_75780_80843.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_80844_80975.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_80976_81047.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_81048_81191.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_81192_85691.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_85692_88919.png Giovanni_Asproni_-_Writing_Usable_APIs_in_Practice-68383287_88920_89011.png