Rocky Mountain Ruby 2011

API Design Matters

API Design Matters

by Anthony Eden

In this engaging presentation titled "API Design Matters" at the Rocky Mountain Ruby 2011 conference, speaker Anthony Eden explores the critical aspects and principles of API design, emphasizing its long-term impact on software projects. Eden highlights that all developers inherently design APIs, whether internal or external, and argues that there is a surprising lack of attention given to effective API design despite its importance.

Key points discussed include:
- Importance of API Design: APIs outlive their implementations, making it essential for developers to design them thoughtfully to maximize utility.
- Core Principles of Good API Design:
- APIs should be easy to learn and follow the principle of least astonishment.
- A well-designed API leads to more readable code, helping developers understand and use the API effectively.
- Extensibility is crucial; APIs must accommodate future growth and complexity without breaking changes.
- APIs should minimize misuse, creating barriers against poor coding practices.
- Avoiding reliance on side effects and execution order is vital to maintain API reliability.
- APIs need to strike a balance between power and simplicity, including only necessary functionalities.
- The 'Five C's of API Design: Eden categorizes essential principles into five C's:
- Consistency: APIs should align with existing designs.
- Clarity: Signatures and methods should be unambiguous.
- Convenience: APIs should enhance user experience and ease coding tasks.
- Conciseness: APIs should be compact yet thorough.
- Completeness: APIs should fulfill their intended scope without unnecessary elements.
- Examples in Ruby: Eden critiques the Ruby standard library (net/http) for its flawed design, illustrating issues with clarity and user expectations. In contrast, he praises libraries like Typhus and Faraday for their adherence to the five C's, showcasing user-friendly and effective API designs.
- Best Practices for API Development: Engaging with potential users during the design process and crafting sample client code helps refine API usability. Effective documentation and sensible defaults are key to balancing clarity and functionality.

Eden concludes with a call to action for developers to recognize their role in API design and the importance of reflective practices in improving API quality over time. His discussion not only reinforces API design fundamentals but also encourages ongoing dialogue and learning in software development.

00:00:09.280 All right, so I'm here to talk to you today about API design. So, why am I here to talk about API design? This is an easy question, so I'm going to throw some softballs at you here. How many of you write software for a living? Okay, I'm hoping quite a few of you. Great! And how many of you design APIs for a living? Okay, see? All of your hands should be going up right now! If you write software, you design APIs for a living; you just may not know it yet. That's one of the reasons why I think it's important to talk about it, and it's actually not discussed very often.
00:00:19.480 I don't know; I mean, it may be discussed, but there's very little written about good API design. I suppose we're supposed to just know good API design after we've been developers for a long time, but I think we can do better than that. So my talk today is to get us not only thinking about API design but hopefully to actually inspire some of you to start writing about it and delve a little deeper into it.
00:00:46.879 As software developers, APIs are your product, both internally and externally. For example, anyone who's developing web apps these days probably either already has an external-facing API or will eventually build one. But there are also internal APIs within your applications where different components communicate with each other, or even where classes interact. So there are APIs all over the place, and those are ultimately the products you're developing as you write software.
00:01:10.840 Another reason why it’s important to talk about APIs is because they outlive implementations. Anyone who's used a library that has evolved over time has noticed that while the API is often improved, the implementation behind it can change constantly. Getting the APIs right is a crucial part of making a library really useful; thus, discussing API design is important.
00:01:39.600 Good API design can lead to many other things, like influencing client-level implementation. So, that's another reason it’s important to have this conversation. Now, here are a few principles—some things that come from what's already been written about API design. I'll touch on some presentations and papers that address it. There's really not a lot, but I will share what I can.
00:02:03.840 The first principle is that any API should be easy to learn. An API should be designed around the principle of least astonishment. When you think something should work a certain way, and it does, you feel a sense of satisfaction. The principle of least astonishment is very important in API design. Additionally, what makes something easy to learn often has to do with naming conventions. It's essential to think about naming in the context of your audience—who is going to use and talk about the API.
00:02:29.800 Another principle that contributes to good API design is that it should produce readable code. From a client usage perspective, if you have a class that is communicating with another class within your application and they have a well-designed API, then the client code is going to be straightforward to understand and probably not too difficult to write. I’m not saying it will write itself, but it might get pretty close.
00:02:56.560 Good APIs are also extensible; they should grow with changing requirements. Additionally, they should be designed with future growth in mind. APIs will live a long time; they won’t be something you can just change a couple of weeks after releasing them, as this would make it impossible for anyone to write client code. Therefore, it's vital to design APIs with the potential for growth in mind.
00:03:12.720 Moreover, APIs should be easily splittable into smaller APIs when needed. As APIs grow, they become more complex; this is just a fact of software development. Thus, the better the API design, the easier it will be to take one API and break it into multiple APIs. This could involve breaking it into two or three distinct components as ideas evolve and start to amalgamate into something too complicated.
00:03:58.560 Another vital aspect of good API design is to minimize the risks of misuse. A well-designed API should make it hard to write bad code. This principle ties back into the concept of writing good code. You shouldn't be able to abuse a well-designed API; instead, it should facilitate the writing of correct code. This means that your client code shouldn’t be complex.
00:04:23.000 Good API design avoids depending on side effects and order of execution. For instance, a poor API design might rely on the order of some states you've set up in advance. If that order changes, the API's behavior will change as well, making it a bad design. A well-designed API minimizes side effects and doesn't depend heavily on aspects like order of execution.
00:04:51.839 The final principle from this initial set is that an API should be sufficiently powerful while being as small as possible. This principle suggests that APIs should implement only the necessary functionality required to complete the job without excessive features. You shouldn't add functionalities just because you might need them in the future or because it seems like a fun challenge; adding to an API is much easier than removing functionality later since the latter could break existing dependencies.
00:05:26.560 Now, those are the fundamental principles I want to share—principles inspired by works like those of Joshua Bloch, who discussed good API design, and numerous papers addressing this topic. I'd like to distill these principles into a more memorable set, so I’m going to categorize them under the letter 'C.' The first principle is consistency in APIs. Good APIs should remain consistent with existing APIs. If you follow a particular style for writing your methods or structuring your APIs, related APIs should ideally follow suit to prevent confusion when different APIs behave inconsistently.
00:06:01.520 In addition to consistency, another critical aspect is clarity. Your code and method signatures should provide clear indications of their purpose. This means you want to avoid sending mixed signals that could confuse users. Convenience is another principle; your API should allow users to write code more easily than having to implement everything themselves. The result should be a user-friendly experience that emphasizes usability. Simplifying the user experience leads to better integration of your libraries.
00:07:10.240 Next is conciseness; it’s important for APIs to be brief while still being comprehensive enough to meet user needs. Finally, completeness ensures that the API does what is necessary without introducing superfluous elements.
00:07:40.720 Those are the five 'C's: be consistent, be clear, be convenient, be concise, and be complete. Now, let’s look at some specific examples, specifically in Ruby, which I know is surprising. I'll be using the standard library net/http, which many in the Ruby community often criticize for its design flaws.
00:08:15.360 As I go through this example, I want you to consider the five 'C's: is this library consistent, clear, convenient, concise, and complete? Let's examine several method calls and their behaviors. You’ll notice how the methods can seem confusing, especially when comparing one method over another.
00:08:49.920 For instance, we have multiple variations of the 'get' method that differ only slightly in their parameters, yet it remains unclear how these methods should ideally be used. The signature of these methods often lacks clarity, eliciting questions about what port is being used and how to define options. The library design complicates what could otherwise be straightforward requests.
00:09:40.480 Here's another example of questionable design in the library. Take a look at one particular 'get' method that has three arguments where two are optional. However, it's unclear what these optional parameters should be. The headers are labeled 'init_header' rather than simply 'headers,' which generates confusion. The documentation indicates that the second argument is obsolete, yet it still functions. This leads to ambiguity in how to use the method correctly.
00:10:25.760 The documentation also states that if this method is called with a block, it will yield each fragment of the response body in turn as a string as it is read from the socket, but leaves much to be deciphered about the structure of the response. Moreover, the documentation indicates a significant divergence in behavior depending on the version of the protocol being used, which only adds to the confusion surrounding its usage.
00:11:15.280 As we continue observing the structure of 'get' methods, we can identify many programming smells, such as inconsistencies in expectations from the user and unnecessary complexity. Furthermore, side effects become apparent in certain methods, which must separate concerns more effectively to avoid overwhelming the user with nuances.
00:11:48.400 While this library has managed to exist for some time, admittedly getting the job done, the convoluted design leads to excessive complexity. In comparison, let’s look at some examples of libraries that have better design principles and adhere to the five 'C's. One such example is Typhus, an HTTP library that permits a clean and straightforward construction of requests.
00:12:32.320 Typhus effectively manages parallel requests, requiring clear operations. The API allows straightforward responses from its requests. Additionally, another library worth mentioning is Faraday, which adopts a different yet intuitive approach to conducting HTTP operations, simplifying method calling and embracing clarity in its design.
00:13:32.360 Faraday reduces complication by letting users create a class-level GET method that encapsulates all necessary details, making the API design more user-friendly. Both Typhus and Faraday illustrate the value of easy-to-understand APIs that deliver comprehensive and effective functionality. They demonstrate consistency, clarity, convenience, conciseness, and completeness, thereby providing good examples to aspire to.
00:14:12.600 Turning to more design principles, let’s consider some techniques for developing better APIs. One essential step is to consult with those who will use your API. Understand their needs, and become the client yourself during the design process. This client-first perspective should guide the API design with user interactions driving the final implementation.
00:15:02.679 To ensure your API meets requirements, write sample client code and run through it against your API regularly to identify any issues. The key is to tease out anything that doesn’t fit well from a client perspective and maintain clarity in design as it relates to user experience, even when contemplating implementation details.
00:15:46.480 Make sure to define your APIs with a suitable level of jargon, using specific terms only if your audience is familiar with them. Additionally, choose sensible defaults and ensure to document those conventions. In doing so, balance clarity for novice users against the needs of more experienced users who may expect deeper functionality.
00:16:28.000 Remember that the principles outlined earlier—being consistent, clear, convenient, concise, and complete—should act as foundational guidelines while your API evolves. Leveraging existing patterns to implement an API can often lead to success, making integration smoother and less cumbersome.
00:17:09.480 As I wrap up my talk, I invite software developers everywhere to recognize the inherent API design responsibilities we all share. Designing APIs with purpose and intent is crucial; simply going through the motions isn’t sufficient. I encourage you all to reflect on good examples of APIs around you and continue learning from best practices in API design.
00:17:51.920 When examining your own APIs, consider how you might refactor them for improvement, keeping potential challenges for existing users in mind. Good API design allows for agility and adaptability, and refining that design should be an ongoing process. Insisting on a user-centered design approach while avoiding unnecessary complexity is vital in crafting APIs.
00:18:33.320 Lastly, I want to express gratitude to everyone for attending my talk. The slides I used for this presentation borrowed from various resources, and I have chocolate to share with all of you, which will be available at the front of the stage! Feel free to grab some!
00:19:15.280 With that, I would like to open the floor for questions. If you have any questions, please feel free to ask. My aim is to provide clarity on API design and any challenges you may face throughout implementation.
00:19:40.560 Okay, so the consensus around API design and implementation is significant. One of the important aspects is understanding that all code essentially functions as its own set of APIs. If you're writing methods in a class, those public methods are part of an API. However, private methods serve more as implementations that internally drive the behaviors of your reactions.
00:20:19.200 The question arises often regarding standard libraries; while they provide a foundation, I believe in keeping them lightweight and avoiding unnecessary bulk. My stance is that a smaller set of well-designed libraries is superior to an expansive collection with excessive incongruities.
00:20:50.480 Regarding API versioning, it's a nuanced topic that deserves thorough attention. Others might adopt semantic versioning and facilitate adjustments for API designs over time without harming the user experience. Some might argue in favor of maintaining flexibility for users while ensuring any transformative changes continue to push the API forward.
00:21:35.120 Every API should reflect intended use in ways that allow for progressive enhancements. Users should never feel locked into a particular version, and the best APIs empower their users to evolve alongside them without encountering undue friction.
00:22:05.440 When we talk about Service Provider Interfaces (SPIs), we’re referring to the foundational designs that allow for connections to multiple underlying services while also maintaining a public-facing API. This encapsulated design allows for flexibility while keeping user interactions seamless, which is essential for overall good API design.
00:22:39.440 Are there any final thoughts or questions? I want to ensure everyone feels equipped to engage with API design topics and tackle any obstacles to achieving good practice. If not, I appreciate your participation, and remind you again, feel free to enjoy the chocolates I brought!
00:23:16.640 Thank you! Please enjoy the conference and engage in meaningful dialogues about enhancing our experiences with software development and API design.