Now that we have a solid understanding of the fundamentals, I would like to draw your attention to a few principles that you will see echoed across the series. Principles are distinct from practices in that practices specify an implementation while principles are more like philosophies. Consequently, understanding these core principles will help you make decisions that are at a minimum directionally correct when there are no scale tested practices available for you to examine.
Make no assumptions
The operation you expect to never fail will most certainly fail. Expect network connections to fail. The thing you expect your users to never do will happen. All observable behaviors of your system will be depended on by somebody. In other words, make no assumptions.
Make things as simple as possible
The simplest approach is often the best. Use the fewest number of concepts necessary to solve the problem.
"Perfection is achieved not when there is nothing more to add, but when there is nothing more to take away." - Antoine de Saint-Exupéry
Separate concerns
If you look at the companies that make up the API economy... They took problems that were a real pain to solve, spent a few years coding up solutions, and now provide that service to others. For many, Twilio included, the service itself is a black box. Customers don't know or care how it works. They just plug our code into theirs, and off they go.1
This quote captures the fundamental goal of API design: eliminate complexity for customers and instead provide a clean interface that they can use to solve their problems. The mechanism that makes this possible is abstraction. Abstraction is the practice of hiding complex implementation details behind a simple, intuitive interface.
Separation of concerns refers to the practice of organizing your code into distinct sections, each addressing a single concern. A "concern" is a distinct area of responsibility, logic, or functionality. One concern might be authentication, while another might be data access.2 At its core, separation of concerns is a form of abstraction. We can abstract away complexity by separating concerns into smaller, more manageable pieces. This allows us to focus on larger and harder problems, while simultaneously making our code easier to understand, test, and maintain.3
Separating concerns results in a layered system where each component encapsulates it's own concerns and dependencies. Each layer will provide a clean data model and interface to the layer above it (the consumer).4 The benefits of this approach are significant.
- Maintainability: Each layer focuses on a single responsibility, making the code easier to understand and modify.
- Testability: Layers can be easily mocked for testing since consumers only depend on the interface, not implementation details.
- Loose Coupling: Changes in one layer have minimal impact on others, enabling independent development and evolution.5
There are some symptoms of poor abstraction.6
- Hidden relationships and dependencies
- Overly simplistic modeling
- Unnecessary or over restrictive coupling
APIs are for humans
At the end of the day, the people who will be using your API are humans. And if they aren't, they were trained on human data. Your job is to make the developer using your API as successful as possible. In my experience, optimal human design is very difficult to develop in a vacuum. This means that your first design will likely not be optimal and that you should seek out feedback from your users. Notice that I said "seek out" and not "wait for". Many people are too polite and kind to tell you what is not working well or what is confusing. You should observe how things are used. Find those points of frustration and smooth them wherever possible. You must empathize with your users and solve even the problems they do not verbalize.