# Evolution space

This page list all the manner in which a RESTful API can evolve. It includes interface and behavioral changes.&#x20;

Behavioral changes relate to how operations should be composed to execute business processes, access rights and resource state related pre-conditions.

Two scientific contributions detail how RESTful APIs evolve. These are reported here. In last section I add elements I consider to be missing from the literature.

This enumeration aims at listing all possible HTTP API evolutions. It is not limited to breaking changes.

## Changes from [How Does Web Service API Evolution Affect Clients?](https://xiongyingfei.github.io/papers/icws13.pdf)

First 10 changes are described as compile-time breaking changes. The  last 4 are listed as run-time breaking changes.

Examples describing APIs follow the OpenApi specification in a lighter version to omit useless details.

Grey block representing quotes contains the text describing the change from the paper.

### **1 - Add or remove parameter**

The below example shows an example of adding and removing a parameter.

{% hint style="warning" %}
**Missing point:** parameter is optional or required ? optional parameters are not breaking changes.
{% endhint %}

{% tabs %}
{% tab title="v1" %}

```yaml
/greeting:
  get:
    parameters:
      - name: string
      - font: string
```

{% endtab %}

{% tab title="v2" %}

```yaml
/greeting:
  get:
    parameters:
      - font: string
      - age: number
      # age has been added
      # name has been removed
```

{% endtab %}
{% endtabs %}

### 2 - Change type of parameter

Description from authors:

> This kind of change only appears in Amazon MWS API evolution, such as merging several independent simple parameters into one complex composite parameter. For changes in this category, developers can easily migrate the client application in most cases. Usually, the new parameter can be synthesized by old parameters directly, and this process can be done automatically if developers know the synthesis rule from old parameters to new parameters.

{% tabs %}
{% tab title="v1" %}

```javascript
// request body, not response 
// because parameters are considered here
{
  id: 1234,
  title: "Buy groceries",
  due_date: "2020-01-01",
  due_time: "8:00"
}
```

{% endtab %}

{% tab title="v2" %}

```javascript
// request body, not response
// because parameters are considered here
{
  id: 1234,
  title: "Buy groceries",
  due_details: {
    date: "2020-01-01",
    time: "8:00"
  }
}
```

{% endtab %}
{% endtabs %}

### 3 - Change type of return value

> This pattern is similar to ***Change Type of Parameter*** and appears in Google API and Amazon API.

{% hint style="info" %}
See example above, this time the JSON would be the response body and not the request body.
{% endhint %}

### 4 - Delete method

> For most cases, methods are deleted because their functionality is subsumed by other methods. However, there are cases where a method is removed and no replacement can be found. In the latter case, the migration of clients relying on the method becomes a big problem. This is also quite different from local API migration, as clients of local API could run with a copy of the old library and call the deleted method in the old library.

### 5 - Rename method & 6 - Rename parameter

> These two patterns are quite common in all APIs we surveyed. The main reason is to give self-explanatory names to methods and parameters. The migration for such changes is relatively easy comparing with other change patterns, and it can be fully automated with existing tools.

{% tabs %}
{% tab title="v1" %}

```yaml
/projects/{id}?token={token}:
  get:
    parameters:
      - id: string
      - token: bearer_token
```

{% endtab %}

{% tab title="v2 (rename parameter)" %}

```yaml
# rename parameter token ➜ user
/projects/{id}?user={user}:
  get:
    parameters:
      - id: string
      - user: bearer_token
```

{% endtab %}

{% tab title="v3 (rename method)" %}

```yaml
# rename method /projects ➜ /project
/project/{id}?user={user}:
  get:
    parameters:
      - id: string
      - user: bearer_token
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Parameters can be path parameters, query, headers or body parameters. No kind of parameter is excluded.
{% endhint %}

### 7 - Change format of parameter & 8 - Change format of return value

> These two patterns mean the type of a parameter or a return value does not change, but the format of them changes. For example, in Google Calendar API, a method accepts a string type parameter that needs to be encoded using URLEncode in the version 2.0. But in version 3.0, developers need only pass a string type parameter without encoding. These patterns are discovered in four APIs except Twitter API. In most cases, the goal of the changes is to facilitate the development of new client applications.

{% tabs %}
{% tab title="v1" %}

```yaml
createdBefore:
  schema:
    type: string
    format: date
```

{% endtab %}

{% tab title="v2" %}

```yaml
createdBefore:
  schema:
    type: string
    format: datetime # <- was date in v1
```

{% endtab %}
{% endtabs %}

### 9 - Change XML tag

> This pattern only occurs in Google Calendar API, where the value name used in the new JSON format differ from the original tags used in the XML messages. This pattern can be solved automatically.

{% tabs %}
{% tab title="v1 (xml)" %}

```markup
<?xml version="1.0" encoding="UTF-8" ?>
<project id="1234">
    <name>The 0 project</name>
</project>
```

{% endtab %}

{% tab title="v2 (json)" %}

```javascript
{
  projectId: "1234" // projectId replaces id
  title: "The 0 project" // title replaces name
}
```

{% endtab %}
{% endtabs %}

### 10 - Combine methods

> This pattern means several methods are combined into one method. We find this pattern in the evolution of Amazon API. Amazon MWS API is a data-intensive API, there are a large amount of data need to be exchanged. In the old version of this API, developers need to interact with the server more than one times to get required data. But after evolution, developers can acquire the data from only one invocation. This decreases the latency in communication. However, the migration for this change can hardly be automated, since we need to find out possible consecutive invocations to the involved methods and group them into one.

{% tabs %}
{% tab title="Case 1: v1" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - title: string
      - collaborators: userId[]
/analytics/{project_id}:
  get:
    responseBody:
      - project_id: string
      - creation_date: datetime
      - updates_count: number
      - last_update: datetime
```

{% endtab %}

{% tab title="Case 1: v2" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - title: string
      - collaborators: userId[]
      - project_id: string
      - creation_date: datetime
      - updates_count: number
      - last_update: datetime
```

{% endtab %}

{% tab title="Case 2: v1" %}

```yaml
/projects/{project_id}/archive:
  put:
    description: Archive the project
    responseCode: 204
/projects/{project_id}/unarchive:
  put:
    description: Unarchive the project
    responseCode: 204
```

{% endtab %}

{% tab title="Case 2: v2" %}

```yaml
/projects/{project_id}/archive:
  post:
    description: Switch the archived status of the project
    responseCode: 200
    responseBody:
      - status: boolean
```

{% endtab %}
{% endtabs %}

### 11 - Split method

> We find this pattern in Amazon API evolution. It does not mean that one method split into several methods, but that one method was replaced by two different methods in different conditions. In Amazon API, the functionality of a method named 'PutInboundShipment' is to create or update an item about shipment in the database. If the item already exists, its record is updated; otherwise is created. In the new version, if the user wants to create an item, they should use the method named 'CreateInboundShipment', otherwise they should use 'UpdateInboundShipment' for updating the record of that item.

The second case of the above example, for change n°8 illustrate this if we go from v2 to v1.

### 12 - Expose data

{% hint style="danger" %}
Could not understand the point
{% endhint %}

This pattern appears in Google Calendar API, which provides data service. Data can be placed in a deep hierarchy, or can be organized in a flatter hierarchy. For ex- ample, in version 2.0 of Google Calendar API the resource path of ‘originalEventId’ and ‘originalStartTime’ are represented in the Atom format as follows:

```markup
<atom:entry>
    <gd:originalEvent href="originalEventAtomId" id="originalEventId">
      <gd:when startTime="originalStartTime"/>
    </gd:originalEvent>
</atom:entry>
```

And in version 3.0 the resource path of these two methods represented as JSON format as follows:

```javascript
{
  "recurringEventId": originalEventId,
  "originalStartTime": originalStartTime
}
```

### 13 - Unsupport request method

{% hint style="danger" %}
Not relevant to me. More information below.
{% endhint %}

> HTTP request methods include get, post, put and delete. In Sina Weibo API and Twitter API evolution, some methods in these APIs change from supporting two request methods in old version to supporting only one request method.If client applications just use a request mode that is supported by new version of an API, we need to do nothing. But if we use other unsupported request methods, we should modify the request method in our applications.

Removing a request method (GET, POST, PUT, DELETE, etc.) implies removing a method (also named operation) from the API, because one operation is identified by the couple `<verb; url>`. Consequently, from my understanding of this change, it is the same as 4.&#x20;

### 14 - Change default value of parameter

> This pattern appears in Sina Weibo API evolution. In an HTTP request, parameters may have default values. Most of these parameters are about quantity, such as how many tweets can be displayed in one page. When the default values of such parameters change, we classify these changes in this category. The reason is that changes of default values may break the (potentially well-designed) layouts of a page, and such problems can only be captured at runtime.

**Example:**

{% tabs %}
{% tab title="v1" %}

```yaml
parameters:
  - name: limit
    description: Page size of this collection
    schema:
      type: integer
      minimum: 1
      maximum: 40
      default: 20 # default page size
```

{% endtab %}

{% tab title="v2" %}

```yaml
parameters:
  - name: limit
    description: Page size of this collection
    schema:
      type: integer
      minimum: 1
      maximum: 40
      default: 10 # updated default page size, was 20
```

{% endtab %}
{% endtabs %}

### 15 - Change upper bound of parameter

> This pattern only appears in Sina Weibo API evolution. Some particular parameters have upper bounds, e.g., a parameter may indicate how many tweets should be returned in one method invocation, and the upper bound is set for the maximum number of tweets that can be returned. When an upper bound becomes smaller, we classify this change as this pattern. Note that when an upper bound becomes larger, it is not a breaking change. Whether this pattern will cause problem in migration depends on the argument passed to the corresponding methods. If the argument is always smaller than the new upper bounds, nothing needs to be done for the migration. Otherwise, several invocations may be needed to retrieve all the needed data.

{% tabs %}
{% tab title="v1" %}

```yaml
tags:
  type: array
  items:
    type: string
  maxItems: 40
```

{% endtab %}

{% tab title="v2" %}

```yaml
tags:
  type: array
  items:
    type: string
  maxItems: 20 # lower than 40 previously
```

{% endtab %}
{% endtabs %}

### 16 - Restrict access to API

> This pattern appears in Sina Weibo API evolution. Some methods are sensitive to information such as a method acquiring the private message of a person. So in new version of Sina Weibo API, the API providers improve the access authority of these methods. If developers want to access these methods, they should apply the authorization from API providers.
>
> In this case, automatically migrating client applications is almost impossible, but a good migrating tool could provide useful help for developers.

{% tabs %}
{% tab title="v1" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - title: string
      - collaborators: userId[]
```

{% endtab %}

{% tab title="v2" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - title: string
      - collaborators: userId[]
    security:
      - bearerToken: [] # adds JWT based auth
```

{% endtab %}
{% endtabs %}

## Changes from "A Case Study of Web API Evolution"

Examples describing APIs follow the OpenApi specification in a lighter version to omit useless details.

### 17 - Move API elements

> We found 114 occurrences of element moves, where API elements are moved under new hierarchy or renamed. For example, Stripe API moved two objects under a new API element as found in the following change log from version 2013-08-13:
>
> “Remove fee and fee details properties on charge and transfer objects. Instead, fee information is now stored on the corresponding balance transaction. ”

This point is relatively similar to "10 - Combine methods". However, the data is moved to a third, already existing, resource instead of one of the two already existing mentioned.

{% tabs %}
{% tab title="v1" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - title: string
      - creation_date: datetime
      - tasks_completed_count: number
/tasks?project_id={project_id}
  get:
    parameters:
      - project_id: string
    responseBody:
      - last_task_creation_date: datetime
      - points_count: number
      - tasks: 
          type: Task[]
```

{% endtab %}

{% tab title="v2" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - title: string
/tasks?project_id={project_id}
  get:
    parameters:
      - project_id: string
    responseBody:
      - tasks: 
          type: Task[]
/analytics/{project_id}:
  get:
    responseBody:
      - project_id: string
      - creation_date: datetime # from project
      - tasks_completed_count: number # from project
      - last_task_creation_date: datetime # from tasks
      - points_count: number # from tasks
```

{% endtab %}
{% endtabs %}

### 18 - Rename API elements

> We found that 31 API elements were renamed for consistency. The following example from the Facebook API shows a rename 3:
>
> “Facebook will be renaming the adgroup status value AD- GROUP PAUSED TO PAUSED to be consistent with the rest of the object’s APIs. ”

{% tabs %}
{% tab title="v1" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - title: string
      - collaborators: userId[]
```

{% endtab %}

{% tab title="v2" %}

```yaml
/projects/{project_id}:
  get:
    responseBody:
      - project_id: string
      - name: string # <- was title in v1
      - collaborators: userId[]
```

{% endtab %}
{% endtabs %}

### 19 - Behavior change

> We have found 247 examples where Web APIs changed the resultant data while keeping the API interfaces intact. These are commonly triggered by bug fixes. For example, Wordpress REST API 1.1 changed as follows 4:
>
> “Correct password-protected post handling. Password-protected posts could previously be exposed to all users, however could also have broken behavior with excerpts. Password-protected posts are now hidden to unauthenticated users.”
>
> Another example that demonstrates a behavior change can be found from the 2013-10-29 version of Stripe API 5:
>
> “When we apply a $Y coupon to a $X dollar invoice, we are no longer applying the remainder of the coupon to the account balance if Y > X. Applications of coupons to $0 invoices will no longer count as a redemption of the coupon.”

### 20 - Post condition change

> In the studied Web APIs we found examples where the immediate result of an API call remained unchanged but new post conditions are imposed requiring additional work for the API users. The following change on Facebook platform API illustrates this:
>
> “Starting May 13th, 2014, developers that accept payments will be required to subscribe to and honor Realtime Updates to ensure order fulfillment and appropriate handling of disputes from all payers. If you do not subscribe to and honor these updates, Facebook reserves the right, under our Developer Payment Terms to withhold payouts and/or stop your app from accepting payments. ”

### 21 - HTTP header change

> Web APIs also change the custom HTTP headers that are used as meta data. For example, the following shows a change when the WordPress Web API introduced two new headers to describe the pagination status of an API call:
>
> “Send X-WP-Total and X-WP-TotalPages headers for information on post/pagination counts”
>
> Similarly, the Github API changed their custom headers about pagination in the following example:
>
> “No longer using the X-Next or X-Last headers. Pagination info is returned in the Link header instead.”

To me, this may be considered the same as "3 - Change type of return value".&#x20;

### 22 - Error condition change

> Web APIs change the error conditions that may require the users to adapt their integration. For example, in Salesforce API Version 29 the error codes were changed :
>
> “...different ExceptionCode and StatusCode values are now re- turned for some error conditions when saving user records. We strongly recommend that you test your code and modify it accordingly if your clients rely on specific ExceptionCode and StatusCode values being returned... ”

➜ more information [here](https://resources.docs.salesforce.com/186/latest/en-us/sfdc/pdf/salesforce_winter14_release_notes.pdf) page 309

## Additions with Fabernovel

From my experience as a software developer I noticed some kinds of changes that are not listed above. These are listed below.

### 23 - Change request method

Request methods change to denote a change of the idempotent property of the operation. Inverting POST and PUT is the most likely change because their difference is not evident for most developers.

{% tabs %}
{% tab title="v1" %}

```yaml
/projects/{project_id}/archive:
  put:
    description: Archive the project
    parameters:
      - archive: boolean
    responseCode: 204
```

{% endtab %}

{% tab title="v2" %}

```yaml
/projects/{project_id}/archive:
  post: # put is changed to post here
    description: Archive the project
    parameters:
      - archive: boolean
    responseCode: 204
```

{% endtab %}
{% endtabs %}

### 24 - Pre condition change

Preconditions are the conditions determining if the operation targeted by the request can be executed.&#x20;

To resolve these conditions, the user context and access rights, along with the state of the resource, and more globally the system, are taken into account. From these contextual information business rules are applied to determine if the operation can be executed.

Such a change does not lead a design-time break or run-time break but creates a trap for the user who could delete projects and cannot anymore.

{% tabs %}
{% tab title="v1" %}
**Operation**: delete a project

**Precondition**: the user is a `collaborator` of the project
{% endtab %}

{% tab title="v2" %}
**Operation**: delete a project

**Precondition**: the user is a `collaborator` of the project, has the `ADMIN` role and the project state is `archived`
{% endtab %}
{% endtabs %}

### 25 - The order in which a set of operations must be played to achieve a business process changed

Considering the business process `placing an order`, the order in which operations must be played to achieve this may change while operations interface do or do not evolve. For example:

{% tabs %}
{% tab title="v1" %}

1. Validate cart
2. Input delivery information
3. Input payment information
4. Select delivery option
5. Confirm order
   {% endtab %}

{% tab title="v2" %}

1. Validate cart
2. Select delivery option *(was 4 in v1)*
3. Input delivery information
4. Input payment information
5. Confirm order
   {% endtab %}
   {% endtabs %}

### 26 - The set of operations to execute to achieve a business process changed

Considering the same business process as previous change, `placing an order`, the set of operations to achieve this may change while operations interface do or do not evolve. For example:

{% tabs %}
{% tab title="v1" %}

1. Validate cart
2. Input delivery information
3. Input payment information
4. Select delivery option
5. Confirm order
   {% endtab %}

{% tab title="v2" %}

1. Validate cart
2. Input delivery information and select delivery option
3. Input payment information
4. Confirm order
   {% endtab %}
   {% endtabs %}

### 27 - Add method

{% hint style="warning" %}
THIS IS NOT A BREAKING CHANGE
{% endhint %}

The title is self-explanatory, no example provided.

### 28 - Change input parameter constraints

The business constraints applied on a model changed. It does not cause any change of the API interface but requests which where accepted before will be refused after the change takes place.

In the OpenApi specification, JSON Schema is used to describe the constraints to apply on the data models.

{% tabs %}
{% tab title="v1" %}

```yaml
/projects:
  post:
    requestBody:
      - title: 
          - type : string
          - minLength: 4
          - maxLength: 140
    responseStatus: 201
    responseBody: empty
```

{% endtab %}

{% tab title="v2" %}

```yaml
/projects:
  post:
    requestBody:
      - title: 
          - type : string
          - minLength: 8
          - maxLength: 40
          - pattern: ^[a-zA-Z0-9_]*$
    responseStatus: 201
    responseBody: empty
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cheronantoine.gitbook.io/ph-d/api-client-evolution/evolution-space.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
