Why developer-friendliness is central to API design
August 23, 2011 1 Comment
Today, APIs play a bigger role in software development than ever before. The evolution of computing has been dominated by ever-increasing levels of abstraction; the use of higher-level languages, of course, but also the development of platforms, libraries, and frameworks. Professor Douglass C. Smith claims the progression of this second category far outpaced the development of programming languages. Developers are also noticing that difficulty has shifted from designing algorithms and data structures to choosing, learning, and using an ever-growing set of APIs. Yet, many developers may be surprised to learn just how much effort goes into designing these APIs.
Microsoft took API design very seriously since the beginning of Windows in the early 80s, understanding that the success of a platform depends on the number of developers it attracts (watch Steve Ballmer yelling “developers, developers, developers” on stage). A more recent example of Microsoft’s API design efforts is showcased in the extensive guidelines developed for the design of the huge .Net Framework API. And Microsoft is not alone in these efforts. Sun (now Oracle) has very similar guidelines for the APIs that form the Java SDK. In a 2002 interview, Joshua Bloch, former architect in the Core Java Platform Group (currently with Google), talks about Sun’s approach to API design. In yet another example, SAP AG hired a team of researchers from Carnegie Mellon University to help design a set of new web services interfaces.
Taking API design seriously isn’t restricted to the large software vendors. Volunteers working on the Eclipse open-source project are asked to follow the guidelines published to the Eclipse API Central. The Qt Application Development Framework has The Little Manual of API Design. And the examples could continue… But if it looks like we are going to use the “everybody else is doing it” fallacy here, there is much more to it. We believe you can derive very concrete and palpable benefits from taking a disciplined approach towards API design, and we will discuss these next.
If books are judged by their cover, then software platforms, services, frameworks, and libraries are judged by their APIs. When developers evaluate the APIs, they are likely passing judgment on the entire service or component. Paying special attention to API design is a smart investment towards making a good impression, especially since publicly visible APIs are typically only a tiny fraction of the entire code base.
Developers and organizations are increasingly realizing that API quality directly impacts the quality of their own code. It might sound weird to hold API authors responsible for the quality of someone else’s code, but this happens more and more. Copy-and-paste style programming, boilerplate code, and similar questionable practices used to be blamed on the lack of programming skills until it was noticed that sometimes even the smartest, most careful developers can only avoid them by painfully wrapping APIs with their own classes and methods. It does not take a genius to figure out what these wrappers are: efforts to eliminate or isolate the effects of API deficiencies. Since APIs are written once, but used many times, calls to fix such APIs are getting louder. With thousands of public web services and free open source components to choose from, the days when developers silently put up with whatever APIs we gave them are long gone.
Any effort put into making APIs easier to learn, easier to use, and easier to troubleshoot directly translates into considerable programmer productivity gains. Let’s return to the main premise of the opening paragraph: today developers spend most of their time learning APIs, using APIs, and debugging code that is nothing more than moderately complex business logic wrapped around multiple complex API calls. It should come as no surprise that better APIs make developers more productive.
Concrete data supports the above claim. Studies conducted in 2007 by a team of researchers lead by Brad Myers and Jeff Stylos determined that even minor changes to APIs, for example replacing factory methods with regular constructors, can lead to significant programmer productivity gains. Moreover, the difference in productivity was measured regardless of the experience level of the developers. While the measured times varied little among participants using constructors, it showed differences up to a factor of 10 between participants using the factory method, a clear sign that they were struggling with the API. The same surprisingly large difference in productivity, up to a factor of 10 times, was measured when a method was moved to a different class within the API, or even after the same team redesigned the API of a big and complex SAP Business Rules Framework. Precise productivity measurements like these are difficult, and published results are still rare. While most claims about big increases in programmer productivity are only supported by anecdotal evidence, we have little reason to doubt their trustworthiness.
While numerous studies show that API quality is the main driving factor of software adoption, many developers and quite a few organizations are still not convinced. Perhaps they did not read Brian Foote’s The Selfish Class, which claims that “Software artifacts that cannot attract programmers are not reused, and fade into oblivion.” When adoption doesn’t happen, it is still common to blame the programmer’s monumental ego, FUD spread by the competition, or even the mystifying Not Invented Here Syndrome, rather than poor API quality. But often you don’t even know where to start because many APIs lack useful documentation. In addition, some APIs are so closely tailored for a specific usage scenario that they are tedious or even impossible to use in a different context. Others come with so many dependencies that it is indeed easier to re-implement the functionality from scratch. As long as API quality remains low, software adoption rates remain low.
What are good APIs
But what are good APIs? What appeals to programmers? What qualities should an API possess to attract them? Where should we direct our efforts?
We are repeatedly told to design intuitive APIs, although practical advice on how to do it remains in short supply. Wikipedia defines intuition as “understanding without apparent effort”, while Merriam Webster describes it as “the power or faculty of attaining to direct knowledge or cognition without evident rational thought and inference”. The truth is that the intuitive thought process is so quick and so closely associated with words like natural, straightforward, expected or obvious that nobody completely understands how intuition really works. Most contrast intuition with complex logical reasoning or learning by trial-and-error. Often, it is easier to identify counter-intuitive APIs from a sense of deep frustration we experience while using them. While it is hard to pinpoint precisely what makes APIs intuitive, some API design best practices, such as considering the perspective of the caller or striving for consistency are known to turn out intuitive APIs. We will discuss these practices in future installments.
We are also constantly reminded to keep APIs simple. We are even encouraged to leave functionality out to prevent them from getting complex. While the desire to satisfy 100% of all imaginable use cases unquestionably leads to overly-complex APIs, one cannot always simplify APIs by leaving out features. Obviously, there is a limit beyond which crippled APIs are no longer useful. We will discuss how to tell if APIs are getting complex and techniques for keeping it simple in a separate installment. Simplicity is an important requirement, but it needs to be carefully balanced against many other requirements.
Self-proclaimed experts are quick to point out that APIs should be easy to understand, remember, and use, but most of them fail to follow up with practical advice. Let’s start with the observation that understanding programs is quite different from understanding colloquial English. It is more like understanding a text from an exotic field of science.
“In this paper we study top quark production from string balls at LHC and compare with the parton fusion results at NNLO using pQCD. We find significant top quark production from string balls at LHC which is comparable to standard model pQCD results. We also find that ds/dpT of top quarks from string balls does not decrease significantly with increase in pT, whereas it decreases sharply in case of standard model pQCD scenario.”
A particle physicist familiar with the terms used can easily decipher the meaning of the above text. Good APIs strive to maintain programmer familiarity by minimizing the number of new terms and concepts and reusing existing programming vocabulary wherever possible. When naming new concepts they stay as close as possible to the spoken language and respect the established conventions of the programming environment. APIs that ignore this requirement are just as difficult to understand as the text above. Choosing memorable names is important enough to have an entire installment on this topic alone.
Speaking about ease of use… With so many APIs to work with, developers don’t have the time to thoroughly evaluate them. Instead, they just give them a quick test drive. If an API doesn’t allow for this kind of experimentation, or if the first impressions are not positive, it will not be given further consideration. We should encourage experimentation by making core scenarios very easy to implement and by providing code samples developers can play with. It helps with experimentation if APIs are safe to play with. Safety also plays a crucial role in the later phases of development, reducing the number of issues we need to debug and fix. API safety is often equated with good error detection and reporting but it is more than that: the API should make it impossible or hard for the developer to get it into a bad situation. Rico Marini calls this concept the “Pit of Success”. We will talk more about making APIs safe in a future installment.
Because developers like to experiment with APIs, they rarely read the documentation up-front. This raises the question of whether APIs need to be documented at all. To answer it, let’s remember that in everyday speech we can understand a message even if parts of it have been lost; for example we can read an article from a ragged, dirty newspaper picked up from the subway floor. The same is not true for short, terse messages like Tweets or SMS. A single missing word can make such messages incomprehensible. We say that such messages lack redundancy. APIs also lack redundancy and the main purpose of documentation is to provide it. Redundancy should not be confused with repetition; it is the same information but conveyed in a different form, increasing the chances that it will be understood. Simply repeating the information found in method signatures in the documentation does nothing to help. We will discuss writing helpful documentation in a future installment. Because specifying behavior accurately is critically important and challenging at the same time, we will discuss this topic separately.
It is important to realize that the above-discussed API qualities do not appear spontaneously and that API design requires special attention. This is a crucial point. Many developers still doubt the need for a separate API design step because they see software interfaces spontaneously emerging at module boundaries during implementation. But experience shows that such spontaneous software interfaces have serious shortcomings when used as APIs, not because of developer negligence or incompetence, but because the design trade-offs we routinely make during implementation simply do not result in good APIs. To make matters worse, fixing such APIs after they are published is extremely difficult. As Joshua Bloch says, “Public APIs, like diamonds, are forever. You have one chance to get it right so give it your best”. This thought alone should be enough to question the practice of letting APIs emerge spontaneously. APIs won’t improve until API design is separated from the implementation effort and it is treated as a separate task. We will call such APIs purpose-made APIs.
Purpose-made APIs are much more likely to contain intuitive, meaningfully named methods and types, more likely to be carefully documented, and more likely to come with helpful samples, tutorials and unit tests. Such APIs are often simple, yet complete, and can evolve over time without breaking their clients; they have few dependencies and pack a lot of functionality. They are safer to use, encourage experimentation and support writing fast and efficient client code. The list isn’t complete. It just sketches the background against which Erich Gamma’s statement should be understood: “A key lesson here is that API is not just a documented class. And, APIs don’t just happen; they are a big investment.”
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 Canada License.