API Design Anti-patterns: How to identify & avoid them

Presenter: Hari Krishnan
Event: Agile India 2025
Location: Bengaluru, India

Presentation summary

We all want to build great APIs—but what exactly makes an API great? Is being RESTful enough? Is it always necessary to achieve Level 3 of the Richardson Maturity Model? What about principles like the Robustness Principle (Postel’s Law) or Hyrum’s Law, which expose the deeper challenges of designing APIs that are both resilient and predictable? How do these principles align with modern practices like the API Design First approach, where good API design is at the core of building adaptable systems? If these questions have been keeping you awake at night, this workshop is for you.

In this session, we’ll explore the principles and values that guide the design and development of great APIs. We’ll tackle the deeper challenges of creating APIs that are resilient and predictable by avoiding key anti-patterns—such as misused HTTP methods and hidden inter-parameter dependencies—that can make APIs harder to use, scale, and maintain. Furthermore, we’ll discuss how to shift-left the identification of breaking changes to the API design phase itself.

Through hands-on exercises and real-world examples, participants will learn to diagnose API design smells and apply practical strategies to fix them.

Conference website: https://2025.agileindia.org

Transcript

 

Mastering API Design: Identifying and Avoiding Anti-patterns

In today’s digital landscape, crafting effective APIs is paramount for seamless integration and user experience. Join me as we delve into the principles of API design, explore common anti-patterns, and equip ourselves with strategies to enhance our API governance.

Table of Contents

Introduction to API Design

API design is the backbone of modern software development. It sets the stage for how applications communicate with one another. A well-designed API not only enhances functionality but also ensures a smooth user experience. Understanding the principles behind effective API design is crucial for developers, architects, and businesses alike. In this section, we will explore the foundational concepts that guide API design, helping you to avoid common pitfalls and embrace best practices.

Key Principles of API Design

  • Simplicity: APIs should be easy to understand and use. A simple interface reduces the learning curve and encourages adoption.
  • Consistency: Consistency in naming conventions, response structures, and error handling fosters predictability, making APIs easier to work with.
  • Versioning: APIs should support versioning to allow for changes and improvements without breaking existing implementations.
  • Documentation: Comprehensive documentation is essential. It acts as a guide for users, helping them understand how to interact with the API effectively.
  • Security: Security measures should be integrated from the start, ensuring that user data and system integrity are protected.

Understanding REST and Its Origin

Representational State Transfer, or REST, is an architectural style that has become the standard for designing networked applications. It was introduced by Roy Fielding in his doctoral dissertation in 2000. RESTful APIs are characterized by their use of standard HTTP methods and stateless interactions, which makes them scalable and efficient.

The Core Principles of REST

  • Resources: Everything in a RESTful architecture is treated as a resource, identified by URIs.
  • Statelessness: Each API call from a client must contain all the information needed to understand and process the request.
  • Standardized Methods: REST uses standard HTTP methods like GET, POST, PUT, and DELETE to perform operations on resources.
  • Representation: Clients interact with resources through representations, typically in formats like JSON or XML.

 

The Richardson Maturity Model

The Richardson Maturity Model is a framework that helps assess the level of RESTfulness of an API. Proposed by Leonard Richardson, this model outlines four levels of maturity, each building on the previous. Understanding these levels allows developers to identify how well their APIs adhere to REST principles.

Levels of the Richardson Maturity Model

  1. Level 0: The Swamp of POXThis level represents the most basic form of an API, often using a single endpoint for all operations. It lacks the structure and clarity needed for effective API design.
  2. Level 1: Resources and Their SeparationAt this level, APIs start to separate resources into distinct endpoints. This is a significant improvement over Level 0, allowing for better organization and clarity.
  3. Level 2: Embracing HTTP VerbsAPIs at this level fully utilize HTTP methods, allowing for more efficient interactions. The use of status codes further enhances communication between clients and servers.
  4. Level 3: HATEOAS ExplainedThe highest level introduces Hypermedia as the Engine of Application State (HATEOAS), where clients can navigate the API dynamically using links provided by the server.

Level 0: The Swamp of POX

At Level 0, APIs resemble Remote Procedure Calls (RPC) over HTTP. They typically expose a single endpoint, leading to a lack of clarity and organization. All operations—creating, updating, and deleting resources—are handled through this one endpoint using POST requests.

Challenges of Level 0

  • No Resource Separation: All operations are lumped together, making it difficult to manage and scale.
  • Inconsistent Responses: Without standardized status codes, developers may end up inventing their own error codes, leading to confusion.
  • Lack of Middleware Efficiency: The absence of HTTP verbs means the API misses out on caching and other efficiencies.

 

Level 1: Resources and Their Separation

Level 1 marks a significant advancement in API design. Here, developers separate resources into distinct endpoints. This separation allows for clearer interactions and lays the groundwork for better API management.

Benefits of Level 1

  • Improved Organization: Each resource has its own endpoint, making it easier to understand and manage.
  • Potential for Scalability: This level sets the stage for future enhancements, allowing APIs to evolve into more sophisticated designs.
  • Better Client Experience: Clients can interact with specific resources, streamlining their experience.

 

Level 2: Embracing HTTP Verbs

At Level 2, APIs fully embrace HTTP methods—GET, POST, PUT, DELETE—allowing for a more RESTful design. This level improves communication and efficiency significantly.

Advantages of Level 2

  • Cacheability: By utilizing GET requests, responses can be cached, improving performance.
  • Standardized Status Codes: Developers can rely on standard HTTP status codes, simplifying error handling and improving clarity.
  • Predictable Interactions: Users can anticipate how the API will behave, making integration smoother.

 

Level 3: HATEOAS Explained

Level 3 introduces HATEOAS, allowing APIs to guide clients through available actions dynamically. The server provides links to possible actions, enhancing the client experience and reducing hard-coded URLs.

Benefits of HATEOAS

  • Dynamic Navigation: Clients can discover available actions without needing to know the API structure beforehand.
  • Reduced Client Code Changes: Changes to the API don’t require clients to be updated, fostering flexibility.
  • Enhanced Workflow Management: Servers can dictate the flow of interaction, optimizing user experience based on context.

 

Exploring Postel’s Robustness Principle

Postel’s Robustness Principle is a guiding philosophy for API designers, encapsulated in the phrase: “Be conservative in what you do, and liberal in what you accept from others.” This principle emphasizes the importance of flexibility in API design. It encourages developers to create APIs that can tolerate extra fields and variations in input while maintaining a strong internal structure.

For instance, when creating an order, an API might only require a few essential fields like product list, delivery address, and payment method. However, if a client sends additional fields, a robust API should ideally accept them without throwing errors. This tolerance can significantly reduce friction for users and enhance adoption rates.

 

Benefits of Postel’s Principle

  • Higher Adoption Rates: By being lenient with input, developers can attract more users who may have varying needs.
  • Resilience Against Minor Deviations: A robust API can handle unexpected variations in requests without breaking.
  • Improved User Experience: Clients can interact with the API more freely, leading to a smoother experience.

Challenges of Implementation

While Postel’s principle encourages flexibility, it can also lead to implementation complexity. For example, if an API receives date formats in multiple styles, it may require additional logic to interpret and standardize these inputs. This adds overhead to development and maintenance.

Moreover, the principle can create challenges in defining clear API contracts. If users come to depend on undocumented behaviors, any changes in the API can lead to unexpected issues.

 

Understanding Hyrum’s Law

Hyrum’s Law states that “with a sufficient number of users of an API, it does not matter what you promise in the contract; all observable behaviors of your system will be depended on by somebody.” This insight highlights the importance of consistency in API design.

Even if an API does not explicitly state certain behaviors, users will come to rely on them. For instance, if an API consistently returns data in a specific order, users might implement code based on that order, leading to issues if the order changes.

 

Implications of Hyrum’s Law

  • Backward Compatibility: APIs must be cautious about changing observable behaviors, as it can break existing implementations.
  • User Expectations: Even undocumented behaviors can create expectations among users, leading to potential friction if those behaviors change.
  • API Evolution Challenges: Balancing the desire to evolve an API while maintaining backward compatibility can be a difficult task.

Building Resilient APIs

Resiliency in API design is crucial for maintaining seamless operations, especially when dealing with external dependencies. A resilient API can handle failures gracefully, ensuring that users can still interact with it effectively.

For example, consider an order management service that relies on inventory and fulfillment systems. If the fulfillment service is down, the API should still be able to accept orders without completely halting operations. It might return a response indicating that the order has been received, but the fulfillment status is pending.

API resilience

 

Strategies for Achieving Resiliency

  • Retries: Implementing automatic retries for failed requests can help recover from transient issues.
  • Circuit Breakers: These can prevent the system from attempting to call a service that is known to be down, conserving resources.
  • Graceful Degradation: If a service is unavailable, provide alternative responses or partial functionality to maintain user experience.
  • Clear Communication: Use standard HTTP status codes to inform users of the current state of their requests (e.g., 202 Accepted, 200 OK).

Defining Idempotency in API Design

Idempotency is a key concept in API design, referring to the property that multiple identical requests will have the same effect as a single request. This is particularly important for ensuring fault tolerance and simplifying client-side logic.

For instance, a PUT request that updates a resource should be idempotent. If the client sends the same request multiple times, the resource’s state should remain unchanged after the first request.

 

Benefits of Idempotency

  • Fault Tolerance: Clients can safely retry requests without worrying about unintended side effects.
  • Consistency: The API maintains predictable behavior, simplifying client implementations.
  • Improved Caching: Idempotent methods can be cached effectively, enhancing performance.

API Design Anti-patterns: Common Mistakes

Understanding common anti-patterns in API design can save developers from significant headaches down the line. These mistakes can lead to poor user experiences and decreased adoption rates.

One common anti-pattern is using a single endpoint for multiple operations, which can confuse users and complicate the API’s structure. Each operation should ideally have a dedicated endpoint to ensure clarity.

Examples of API Design Anti-patterns

  • Inconsistent Naming Conventions: Using different cases (camelCase, snake_case) for resources can confuse users and make documentation unclear.
  • Overloading HTTP Methods: Using POST for operations that should be GET can lead to issues with caching and idempotency.
  • Ignoring Versioning: Failing to implement versioning can lead to breaking changes that affect users unexpectedly.

Using Linters for Better API Design

Linters are invaluable tools for maintaining code quality, including API specifications. They can help identify inconsistencies and enforce best practices, ensuring that APIs adhere to established standards.

Tools like Vacuum and Spectral allow developers to define rules for their APIs, catching issues such as inconsistent naming conventions or missing documentation. By integrating linters into the development workflow, teams can catch errors early and improve the overall quality of their APIs.

Benefits of API Linters

  • Consistency: Enforce uniform naming conventions and styles across the API.
  • Early Detection of Issues: Catch potential problems before they reach production, reducing the cost of fixing them later.
  • Improved Documentation: Ensure that all necessary elements are documented, enhancing the user experience.

The Importance of API Specifications

API specifications serve as the blueprint for API design, providing a clear guide for developers and consumers alike. They outline the expected behavior, endpoints, and data structures, ensuring everyone is on the same page.

Well-defined API specifications can help prevent misunderstandings and reduce the likelihood of errors during implementation. They also act as a living document that evolves with the API over time.

Importance of API Specifications

Key Components of Effective API Specifications

  • Clear Endpoint Definitions: Specify the purpose and functionality of each endpoint.
  • Data Models: Clearly outline the expected request and response formats.
  • Error Handling Guidelines: Define how errors will be communicated to users, including status codes and messages.
  • Versioning Information: Include details on how versioning will be managed as the API evolves.

Handling Complex Queries in APIs

When it comes to complex queries, the challenge lies in effectively managing multiple parameters without overwhelming the API or the end user. The conventional approach of cramming numerous parameters into query strings can lead to issues, especially regarding browser limitations on URL length.

One solution is to differentiate between query parameters and headers. While query parameters are ideal for filtering and sorting, headers can be used for metadata that doesn’t directly affect the resource being fetched. Understanding when to use each can streamline your API interactions.

Alternative Methods for Complex Searches

  • Introducing the Query Method: A new HTTP method, ‘QUERY’, is being proposed to allow for idempotent requests with a request body. This could facilitate complex payloads and enhance the handling of intricate search criteria.
  • Utilizing POST for Search Creation: Another approach is to create a search resource using POST, which returns a search ID. Subsequent GET requests can then leverage this ID to fetch results, allowing for caching and better performance.

Using POST to create search resource

Best Practices for API Design

Effective API design is not just about functionality; it’s about creating a smooth experience for developers and users alike. Here are some best practices to keep in mind:

  • Make Parameters Optional: While it can be tempting to require numerous parameters, consider making as many as possible optional. This approach allows consumers the flexibility to send only what they need.
  • Implement Conditional Parameters: For scenarios where certain parameters depend on others, consider using conditional logic. For instance, if a user specifies a price, ensure that the relevant price range parameters are also provided.

Managing Complexity on the Server Side

As APIs grow more complex, server-side implementation can become a challenge. It’s essential to keep validation straightforward. Instead of accepting a wide array of optional parameters, focus on defining clear, specific endpoints for distinct functionalities.

By splitting responsibilities across multiple endpoints, you can maintain clarity and enhance the API’s usability. This approach not only simplifies the server’s task but also reduces the potential for errors in handling requests.

Server-side complexity management

Avoiding Nulls and Optional Fields

Null values can introduce unnecessary complexity into API design. When dealing with Boolean or enum types, the presence of a nullable option can create confusion for both providers and consumers. Instead of allowing nulls, consider making fields optional.

For instance, if a user doesn’t need to specify a value, they can simply omit the field. This approach clarifies intent and reduces ambiguity.

 

Benefits of Using Optional Fields

  • Clearer Communication: Optional fields make it evident that the absence of a value is acceptable.
  • Reduced Complexity: By avoiding nulls, you simplify the logic required to handle incoming data.
  • Improved User Experience: Consumers can interact with the API without the added burden of interpreting null values.

 

Conclusion and Future Directions

As we look to the future of API design, the focus should remain on creating robust, flexible, and user-friendly interfaces. Embracing new methodologies, such as the proposed QUERY method and leveraging platforms like GraphQL, can provide innovative solutions to complex API challenges.

Continuous improvement is key. Regularly revisiting API specifications and involving all stakeholders in the design process ensures that we stay aligned with the needs of users while maintaining technical integrity.

 

FAQ: Common Queries on API Design

1. Can query parameters be mandatory?

While technically possible, making query parameters mandatory can lead to validation complexities. It’s often better to use headers for mandatory fields, allowing for clearer communication of intent.

2. How do I handle complex queries without overwhelming the API?

Consider breaking down complex queries into simpler, focused endpoints. Alternatively, utilize the POST method to create a search resource and use a GET request for subsequent retrievals.

3. What should I do about null values in my API?

Avoid nullable fields where possible. Instead, use optional parameters to clarify intent and reduce complexity in handling incoming requests.

4. How can I ensure backward compatibility in my API?

Implement a governance model that includes regular checks against previous API specifications. Use automated testing to verify that new changes do not break existing functionality.

More to explore