Skip to content

Commit 34e220c

Browse files
authored
improve documentation-13 (#319)
1 parent 47f73d6 commit 34e220c

File tree

4 files changed

+30
-31
lines changed

4 files changed

+30
-31
lines changed

docs/stylesheets/extra.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.md-header__button.md-logo {
2-
margin-top: 0
2+
margin-top: 0;
33
margin-bottom: 0;
44
padding-top: 0;
55
padding-bottom: 0;
Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
# How to build a versioning framework
22

3-
## Questions to ask yourself when rating your framework
3+
## Questions to consider when evaluating your framework
44

5-
### How easy is it to create a version?
5+
### How simple is it to create a new API version?
66

7-
If it is too easy, it is probably a trap. The framework is probably hiding too much complexity from you and will shoot you in the back later. For example, early on we tried a simple "copy the entire business logic into a separate directory" approach which made it so simple to add new versions. We added too many of them in the end, thus it got hellishly hard to maintain or get rid of these versions.
7+
If it is too simple, it is probably a trap. The framework is probably hiding too much complexity from you. For example, early on the Cadwyn dev team tried a straightforward "copy the entire business logic into a separate directory" approach which made it so easy to add new versions. We added too many of them in the end, thus it got hellishly hard to maintain or get rid of those versions.
88

9-
### How easy is it to delete an old version?
9+
### How simple is it to delete an old API version?
1010

11-
Your framework must be able to let you clean up versions as simply as possible and cheaply whenever you need to. For example, if your framework tries to minimize the amount of code duplication in your repository by having new routes include old routes within them and new business logic inherit from classes from old business logic, then deleting an old version is going to be painful; often even dangerous as versions can quickly start interacting with each other in all sorts of ways, turning a single small application into a set of interconnected applications.
11+
Your framework should allow you to clean up versions as simply and cheaply as possible. For example, if your framework tries to minimize the amount of code duplication in your repository by new routes including old routes and by new business logic inheriting from classes from old business logic, then deleting an old version is going to be painful (often even dangerous) as versions can quickly start interacting with each other turning a single small application into a set of interconnected applications.
1212

13-
### How easy is it to see the differences between versions?
13+
### How simple is it to see the differences between API versions?
1414

15-
The easier it is, the better off our users are.
15+
The simpler it is, the better off your users are.
1616

17-
### What exactly do you need to duplicate to create a new version?
17+
### What exactly do you need to duplicate in order to create a new API version?
1818

19-
The less we duplicate and maintain manually, the easier it is to support. However, the less we duplicate, the higher the risk of breaking old versions with new releases.
19+
The less you duplicate and maintain manually, the simpler it is to support. However, the less you duplicate, the greater the risk of breaking older versions in subsequent releases.
2020

21-
### How easy is it to notice accidental data versioning?
21+
### How simple is it to detect accidental data versioning?
2222

23-
Data versioning is an incredibly big issue when versioning your API.
24-
So if your framework makes it hard to version data -- it's really good!
23+
Data versioning is an important issue when versioning your API. If your framework makes it hard to version data -- it's really good!

docs/theory/how_we_got_here.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,54 @@
11
# How we got here
22

3-
Over the years we have seen so many ways to do API Versioning. In fact, the majority of these ways can be put into an elegant evolution. Let's go through it from the largest level of duplication to the smallest.
3+
Over the years the Cadwyn dev team have come across a number of approaches to do API versioning. There are three major approaches described below, ordered by decreasing level of duplication.
44

5-
## Types of API versioning
5+
## Approaches to API versioning
66

7-
There are three ([\[1\]](https://smartlogic.io/blog/2012-12-12-developing-an-api/), [\[2\]](https://thenewstack.io/tricks-api-versioning/)) main ways to version an API, each consequent being less safe but more convenient to both the API clients and maintainers. Essentially they can be classified by which layers of [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) they version.
7+
There are three main approaches to version an API, each one less safe than the last but more convenient to both the API clients and maintainers. ([source 1](https://smartlogic.io/blog/2012-12-12-developing-an-api/), [source 2](https://thenewstack.io/tricks-api-versioning/)) They can be classified by which layers of [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) they version.
88

9-
### 1. Versioning proxy, which points requests to versioned apps
9+
### 1. Versioning a proxy, which routes requests to versioned apps
1010

11-
This approach versions all three layers: separate data, separate business logic, separate representation. Essentially you create a completely different app for each version. Your versions are independent and cannot in any way affect each other. You can make any sorts of changes in future versions without worrying about breaking the old ones.
11+
This approach versions all three layers: separate data, separate business logic, separate representation. Actually a completely different app is created for each version. The versions are independent and cannot affect each other. Any changes can be made in future versions without worrying about breaking the old ones.
1212

13-
This approach is the most expensive to support but if breaking old functionality is unacceptable and if you need to support a small number of versions (1-3), then this option is viable.
13+
This approach is the most expensive to support but if breaking the old functionality is unacceptable and if a small number of versions (1-3) needs to be supported, then this option is relevant.
1414

15-
Note that this is essentially **data** or **application** versioning, not **API** versioning anymore. If it is impossible for your user to freely move between API versions (back and forth), then you are probably doing a bit of **data versioning** yourself. It can simplify your app's logic but will significantly inconvenience your users because they will not be able to easily switch API versions without waiting for your team to help. Additionally, a single client will never be able to use two versions at the same time. At least not easily.
15+
Note that this approach is essentially **data** or **application** versioning, not **API** versioning. If it is impossible for your user to freely move between API versions (back and forth), then you are probably doing a bit of **data versioning** yourself. It can simplify your app's logic but will significantly inconvenience your users because they will not be able to easily switch API versions without waiting for your team to help. Additionally, a single client will never be able to use two versions at the same time. At least not easily.
1616

1717
*Mostly used in older-style apps or in critical infrastructure where no mistakes are permitted*
1818

19-
### 2. One router, which points requests to versioned controllers
19+
### 2. One router, which routes requests to versioned controllers
2020

2121
This approach versions business logic and representation layers while leaving data layer the same. You still have to duplicate all of your business logic but now your clients will be able to migrate between versions easily and you will be able to share some of the code between versions, thus lowering the amount of things you would need to duplicate.
2222

23-
The problem with this method is that any refactoring will most likely have to happen in all versions at once. Any changes in the libraries they depend on will also require a change in all versions. When the number of versions starts to rise (>2), this becomes a significant problem for the performance and morale of API maintainers.
23+
The problem with this approach is that any refactoring will most likely have to happen in all versions at once. Any changes in the libraries they depend on will also require a change in all versions. When the number of versions starts to rise (>2), this becomes a significant problem for the performance and morale of API maintainers.
2424

25-
This is also the approach we have originally started with. It is likely the worst one out there due to its fake simplicity and actual complexity. In the long run, this approach is one of the hardest to support but most importantly: it's probably the **hardest to migrate from**.
25+
This is also the approach the Cadwyn dev team have originally started with. It is likely the worst one out there due to its fake simplicity and actual complexity. In the long run, this approach is one of the hardest to support but most importantly: it is probably the **hardest to migrate from**.
2626

2727
*Popular in [.NET environment](https://github.com/dotnet/aspnet-api-versioning) and is likely the first choice of any API due to the simplicity of its implementation*
2828

2929
### 3. One router, shared controllers, which respond with versioned representations
3030

31-
This approach versions only the API itself. The business logic and data below the API is the same for all versions (with rare exceptions) so API maintainers have the pleasure of maintaining only one API version while users have the added benefit that non-breaking featurees and bugfixes will automatically be ported to their version. This is the only method that allows you to support a large number of versions because it has the least amount of duplication of all methods. This is usually accomplished by adding a separate layer that builds responses out of the data that your service returns. It can be a separate service, a framework, or just a set of functions.
31+
This approach versions only the API itself. The business logic and data structures are the same for all API versions (with rare exceptions) so API maintainers have the pleasure of maintaining only one API version while users have the added benefit that non-breaking featurees and bugfixes will automatically be ported to their version. This is the only approach that allows you to support a large number of versions because it has the least amount of duplication of all approaches. This is usually accomplished by adding a separate layer that builds responses out of the data that your service returns. It can be a separate service, a framework, or just a set of functions.
3232

33-
Note that in this method, the usage of **data versioning** now becomes an inconvenience to **both** API users and maintainers. See, when you have a single business logic for all versions, you might need additional conditionals and checks for versions where data structure or data itself has changed. That is **in addition** to pre-existing inconveniences for the users. However, sometimes it might still happen so our goal is to minimize the frequency and impact of data versioning.
33+
Note that in this approach, the usage of **data versioning** now becomes an inconvenience to **both** API users and maintainers. See, when you have a single business logic for all versions, you might need additional conditionals and checks for versions where data structure or data itself has changed. That is **in addition** to pre-existing inconveniences for the users. However, sometimes it might still happen so our goal is to minimize the frequency and impact of data versioning.
3434

3535
*Popular in API-First tech giants that have to support backwards compatibility for a long time for a large number of clients*
3636

3737
Note that this approach actually has two important subtypes:
3838

3939
#### i. Duplication-based response building
4040

41-
The simplest possible builder: for each API version, we define a new request/response builder that builds the full response for the altered API routes or migrates the user request to the latest version. It is incredibly simple to implement but is not scalable at all. Adding values to all builders will require going through all of them with the hope of not making mistakes or typos. Trying to support more than 8-12 versions with this approach will still be challenging.
41+
The simplest possible builder: for each API version a new request/response builder is defined that builds the full response for the altered API routes or migrates the user request to the latest version. It is incredibly simple to implement but is not scalable at all. Adding values to all builders will require going through all of them with the hope of not making mistakes or typos. Trying to support more than 8-12 versions with this approach will be challenging.
4242

43-
We might think of smart ways of automating this approach to support a larger number of versions. For example, to avoid duplicating the entire builder logic every time, we can pick a template builder and only define differences in child builders. Let's pick the latest-version builder as template because it will never be deprecated deleted and our developers will have the most familiarity with it. Then we need to figure out a format to define changes between builders. We can remove a field from response, add a field, change the value of a field somehow, and/or change the type of a field. We'll need some DSL to describe all possible changes.
43+
One might think of smart ways of automating this approach to support a larger number of versions. For example, to avoid duplicating the entire builder logic every time, one can pick a template builder and only define differences in child builders. You can pick the latest-version builder as template because it will never be deprecated or deleted and your developers will have the most familiarity with it. Then you need to figure out a format to define changes between builders. You can remove a field from response, add a field, change the value of a field somehow, and/or change the type of a field. You will need some DSL to describe all possible changes.
4444

45-
Then we start thinking about API route differences. How do we describe them? Or do we just duplicate all routes? Do we maybe use inheritance? No matter what we do, we'll eventually also come to a DSL, which is why some tech giants have chosen [approach ii](#ii-migration-based-response-building).
45+
Then it is time to start thinking about API route differences. How to describe them? Maybe just duplicate all routes? Maybe use inheritance? No matter what, one will eventually come to a DSL, which is why some tech giants have chosen [approach ii](#ii-migration-based-response-building).
4646

4747
A code generation yaml-based version of this approach [was used at SuperJob](https://habr.com/ru/companies/superjob/articles/577650/).
4848

4949
#### ii. Migration-based response building
5050

51-
This is effectively an automated version of [approach i](#i-duplication-based-response-building). It has the minimal possible amount of duplication compared to all other approaches. Using a specialized DSL, we define schema migrations for changes in our request and response schemas, we define compatibility gates to migrate our data in accordance with schema changes, and we define route migrations to change/delete/add any routes.
51+
This is an automated version of [approach i](#i-duplication-based-response-building). It has the minimal possible amount of duplication compared to all other approaches. Using a specialized DSL, schema migrations are defined for changes in request and response schemas, compatibility gates are defined to migrate data in accordance with schema changes, and route migrations are defined to change/delete/add any routes.
5252

53-
This is the method that [Stripe](https://stripe.com/blog/api-versioning), [Linkedin](https://engineering.linkedin.com/blog/2022/-under-the-hood--how-we-built-api-versioning-for-linkedin-market), and [Intercom](
54-
https://www.intercom.com/blog/api-versioning/) have picked and this is the method that **Cadwyn** implements for you.
53+
This is the approach that [Stripe](https://stripe.com/blog/api-versioning), [Linkedin](https://engineering.linkedin.com/blog/2022/-under-the-hood--how-we-built-api-versioning-for-linkedin-market), and [Intercom](
54+
https://www.intercom.com/blog/api-versioning/) have chosen and this is the approach that **Cadwyn** implements for you.
File renamed without changes.

0 commit comments

Comments
 (0)