API Primer — Versioning APIs
Welcome to the second set of in depth details about the art of building APIs
Here’s a link to the first part related to API Naming Conventions
If these are useful or need improvement or you find something which does not look right, please do share your valuable feedback.
A live project is constantly evolving with new functionality being added daily resulting in constant changes at the API layer
The below aspects of an API can change over time
- Path parameter
- Query parameter
- Headers
- Payload: Request/Response
- Database schema changes
- Change in business logic like new integration or new validations
- External dependencies (APIs, etc.) change
- Endpoint to be changed (extremely rare)
Why versioning
- Time to integrate
External consumers of your API need time to adapt to the changes
- Deployment dependencies
Even if your APIs are used within your team and you can coordinate releases of say UI and API, deployment delays between microservices, high availability, etc. mean that we have to support backward compatibility in most cases
Approaches
Database
Here’s a ready reckoner
Code
- Feature Flags
Passed via environment variables to the application to enable / disable functionality as needed. Much needed when all features / changes are pushed to master
- New versions via version in the path parameter — “v1”, “v2” so on
Here’s a Decision Matrix that will be helpful
Impact on application code
Typically, application code consists of following packages
- com.company.api.v1.controller
- com.company.api.v1.dto
- com.company.api.v1.dto.mapper [ DTO to Model mapping. Idea being service layer only deals with Models ]
- com.company.api.v1.dto.validator [ input sanitization and validation ]
- com.company.service [ business rules/validations and persistence logic ]
- com.company.dao
- com.company.model
Now let’s break up the changes to try and decipher how to walk the talk i.e. code
For any changes to Path/Query Parameters, Headers and Request/Response Payloads it’s best to add new packages per version
- com.company.api.v[n].controller
- com.company.api.v[n].dto
- com.company.api.v[n].dto.mapper
- com.company.api.v1.dto.validator
If these changes trigger changes into model, then we can choose to manage models
- add to current model
- extend current model for the next version
- create a new package and model
For changes to business logic, depending on the change we can
- add to same function: especially applicable if we have feature flag protected changes
- create a new function in same Service class
- create a new package/class to map to v2 packages in other layers
Overall, its best to create separate packages while minimizing code duplication and maximizing code reuse.
Clean up
Ensure that old un-used code from past versions are periodically cleaned up to avoid lots of unused code flows.
Conclusion
Hope the details were helpful and meaningful to help you derive a strategy that works for your projects.