Earlier this year, we introduced Component Libraries, a new tool for creating reusable code shared across APIs in your Stoplight Workspace. They’re a great way to improve governance, especially when used alongside automated linting tools such as Stoplight Style Guides. Most Stoplight users who are currently using shared Components have 20, 50, or more APIs in their Workspaces, and ensuring that all of them are using the same format for common data is important for the longevity of these API programs.
Component Libraries can also be a secret ingredient for developing cleaner, more durable, resilient codebases from the start. At some point in your career, you’ve doubtless encountered the idea of DRY code – the acronym stands for “Don’t Repeat Yourself.” Below, we’ll explore some of the principles of clean code, and why they’re so important for APIs. We’ll also look at two common development workflows and the pros and cons of each. Finally, we’ll explain how Stoplight Component Libraries might be the tool you need for getting the best of all worlds: clean, DRY code that will serve you and your API consumers well over the long haul.
Prevent Soggy Services With a DRY, SOLID Foundation
A mentor once told me, “All code is legacy code after 15 minutes.” The best reason to write clean code is to leave a legacy that will be useful to others after you’ve moved on – even if it’s just while you’ve moved on to lunch. For API developers, that means writing code that is easy to use for other developers working on both sides of the API – producers and consumers.
An emphasis on clean code is a good match for a practical API-as-a-Product mindset; if you think of it as only the province of geeky performance optimization gurus, you are overlooking some crucial benefits of the approach. Treating your API program as a first-class product means constantly monitoring usage, integrating new technologies, and pivoting to meet new user needs. Frequent iteration requires code that is efficient enough to support observability and resilient enough to adapt to change.
So what is clean code? The DRY acronym is a common shorthand, but it’s only a starting place. Another acronym that may give you some clearer direction is SOLID. It’s also an approach that, as you’ll see below, works especially well with Stoplight Component Libraries:
- Single Responsibility Principle: Every class should have a single responsibility and a single reason to change. This is an object-oriented version of the functional programming principle that functions should have “no side effects.”
- Open-Closed Principle: Once an entity is created, tested, and deployed, new code can extend it but not modify it. Using interfaces to create variations on the original class helps prevent breaking changes.
- Liskov Substitution Principle: Substituting an instance of a child class should never break functionality for a parent class. Child classes must inherit all the functionality of parent classes. This is similar to the Open-Closed Principle but is concerned with inheritance rather than changes over time.
- Interface Segregation Principle: Adhering to this principle helps make compliance with the principles above easier. Essentially, it’s preferable to have more simple interface classes for a parent class than it is to have one complex, over-specified class. This allows combinations of functionality that are more precisely tailored to client needs, keeping client applications lighter and more loosely coupled.
- Dependency Inversion Principle: High-level modules shouldn’t depend on low-level modules; most dependencies should be on abstractions. If one class will depend on another, the dependency should be on a simple, abstract interface that can be extended as needed in the future.
Whether you strictly adhere to the SOLID principles or just make an effort to follow general practices for cleaner code, the purpose is to build an API program that will hold up over time. It’s certainly possible to go too far — critics caution that an unquestioning use of ‘clean code’ practices can slow down performance — which is why we emphasize the need to keep principles in perspective. Enduring products need to meet real user needs but also need to be engineered to withstand changes in those needs. The goal of any code standards approach should be resilience and flexibility, positioning future developers to continue serving your API consumers effectively.
Focus On Real Users Over Ideal Standards
There’s no absolute best practice when it comes to designing a new API program. As we said above, principles and practices should serve your product goals, not dictate your approach. So where should you begin your API design process? There are two main options, each representing “opposite” ends of your API:
- API Design-First approaches begin with defining end-user needs, and designing application architecture to most effectively meet those needs. In this approach, API endpoints are optimized for specific use cases based on your knowledge of how developers will interact with your product.
- Domain-Driven Design begins with your source data, often called your domain. That data represents the expertise you provide to your API users, and beginning your design process here can allow you to shape your data into models that most accurately and efficiently reflect your domain knowledge.
Some purists insist that Domain-Driven approaches are the only way to ensure your application architecture is truly clean, free of potentially problematic dependencies, and stable over time.
As you may have guessed, we’re going to push back on that a bit. Some of the reasoning is sound – establishing shared terminology and definitions at the inception of a project is a crucial step, and creating an accurate, flexible data model is obviously important, no matter where you begin. But realizing those benefits doesn’t require a domain-driven approach, and your API users may have valuable information you’ll miss if you wait too long to evaluate their needs.
An Imperative for API User Needs
API-Design First is a development approach that’s closely related to the idea of an API-first product strategy. API-first begins with treating your API as a first-class product, focusing on defining and meeting consumer needs. Design-first turns the principle into practice, by iteratively defining the outcomes your API program must achieve and carefully describing the details of how it will deliver those outcomes. The development process starts with determining the data users need and the optimal shape for that data and works down from there.
Design Thinking for APIs Sows Seeds of Success
It’s no secret that we’re believers in API-first development. The approach can create much greater efficiency throughout your design and development process and can lead to more enduring integrations with less trial and error. Because your teams start by thinking about real-world use cases, real-word security concerns can get attention from the start. You can consider factors like mobile usage and metrics requirements early on, ensuring that your API design meets the technical specifications you’ll need when it’s released. APIs are products for consumers who happen to be developers; designing your APIs with empathy for developer needs is a well-tested business strategy.
Don’t Let Designs Get Ahead of Reality
Although designing based on consumer needs is important, it’s worth noting that an API-first approach may not always result in a well-organized architecture. API-first approaches can lead to muddled, messy code, as well as anemic domain models. In part, that’s because design processes that start at the consumer end tend to focus on endpoints and methods, with a lot of pieced-together transaction scripts. Developers build controller methods that manipulate data into the shape needed for the promised endpoints. This process can generate a significant amount of imperative code with unnecessary complexity and repetition. While API-first design doesn’t necessarily lead to a mess, preventing it requires careful planning and coordination.
Declare Your Domain Expertise
In contrast to API-first design, Domain-Driven Design prioritizes the knowledge and functions of your internal resources. The premise is that by starting with a data architecture that accurately and efficiently represents core business concepts, while securing the support of internal stakeholders, you can reduce the overall complexity of your API.
Capture the Benefits of Domain-Driven Design
In theory, DDD will lead to more declarative code that is easier to read and thus easier to maintain. Defining the shape, function, and relationship of your data models in the initial stages of development can increase the chance of producing code that is clean and DRY. When you eventually get to the stage of creating controller methods and API endpoints, they will be dictated by the shape of the models, and will fall naturally into place in a way that reflects the real-world entities represented in the data.
DDD Has Drawbacks, Too
In practice, a narrow focus on domain expertise can lead to models that aren’t flexible enough to serve real-world use cases, especially when the focus is exclusively on internal contexts. The adage “Don’t ship the org chart” serves as a cautionary tale about the tendency of domain-focused teams to create products that reflect their biases rather than the needs of product consumers. This is formally known as “Conway’s Law”, and it can have serious consequences for product quality. Avoiding the pitfalls takes conscious effort throughout your API design process.
DDD can also be slow and leaves you with little to test against your API consumers’ expectations until quite late in development. If you’re trying to get a new service or product to market quickly, or if your technical challenges exceed your data challenges, a strict Domain-Driven approach may be more of a hindrance than a help.
So where does this leave us when it comes to choosing a design process for a new API program? It seems that while neither approach is perfect, both have a lot of potential value.
Component Libraries For Fresh Front-Ends and DRY Back-Ends
Stoplight’s Component Libraries are a tool that can help API developers balance these two design approaches. Component Libraries are a powerful part of the Stoplight toolkit. Currently available to Pro and Enterprise accounts in a beta release, they allow teams to share JSON schema models across all their APIs.
Build Your Back-End Better
Component Libraries allow you to create a single standard data model and share it across multiple APIs, so you can employ key concepts from Domain-Driven Design:
- Treat shared components as a domain model that represents your most important ideas, knowledge, and data for your business requirements. Your Component Library can be your single source of truth.
- Use masking to more flexibly reuse models for different APIs, representing different subdomains. Masking your base class works much like extending an interface, a key practice of SOLID design.
- Visualize and share your design patterns to others in your Workspace, increasing code reuse and making your API DRYer.
- Create clear separations between bounded contexts by creating separate APIs within a Stoplight Workspace, keeping crucial shared components consistent within the Workspace.
- Easily communicate with technical and non-technical stakeholders about domain requirements, using naming conventions in your Component Library to establish ubiquitous language.
Using Component Libraries can help you realize the advantages of DDD: simpler communication, a solid foundation that allows greater flexibility, and clarity about where the shape of your data dictates other design choices.
Keep Your API Consumers In Focus
- Carefully designed, consistent data models, codified in Component Libraries help create an improved Developer Experience for your API consumers.
- Development teams have straightforward access to shared resources and tools to promote collaboration and governance, increasing engineering efficiency and cost savings.
- Standardizing your data models helps ensure consistent data segregation and reduces possible points of exposure, leading to improved API security.
- Component Libraries are a single tool that provides a simple visual interface for sharing your models with technical and non-technical stakeholders, leading to better coordination between teams, especially if you’re already using Stoplight tools for other parts of your API development workflow.
- Combining a product mindset with tools that support your API teams’ efficiency and your code quality sets you up for increased innovation and growth.
Component Libraries support high-quality API programs throughout the product life cycle. They serve as a repository for your domain knowledge, allowing you to document the expertise of your internal resources and business functions in a readily usable, shareable format. When you use an API design-first strategy, it becomes easy to transition data from your designs through mocking and into development since Component Libraries integrate with other Stoplight and OpenAPI tools. Regardless of where you begin, you can build APIs with DRY, SOLID code, and top-notch developer experience.
Any decisions about your API design process and tooling should be driven by your unique needs and practical realities. There’s no single paradigm that will serve every API program or development team. The advantage of Component Libraries is that they don’t force you into a dogmatic approach – whatever your strategy, this is a tool that can help you realize the benefits of both a carefully structured domain layer and a user-focused design process.
If you’re currently on a Pro or Enterprise account, you can get started with it today. Look for announcements about more features coming soon!