Backward Compatibility
- Why Backward Compatibility Matters
- What’s New
- How it works
- Command Essentials
- Common Use Cases
- Pratical Examples (Try it Yourself)
- Handling Contracts In Progress
- Backward Compatibility Rules
- Comparing Two Contracts (Deprecated)
- Validating Changes In Git On Your Laptop (Deprecated)
- Validating Changes In CI (Deprecated)
- Troubleshooting
Why Backward Compatibility Matters
Backward compatibility ensures that updates to your API specifications don’t accidently break compatibility. and prevents rework. Perform automated backward compatibility checks to ensure we are not accidentally breaking compatibility.
Specmatic offers this powerful feature to check for backward compatibility between different versions of your API specifications.
- Catch issues early: Detect compatibility problems before any code is written
- Specification-Based Analysis: By comparing the old and new versions of your API specification, Specmatic provides instant feedback on potential breaking changes.
- Shift-Left Testing: Detect breaking changes during API design phase, reduce costly downstream fixes and client disruptions.
In the following sections, we’ll show you how to use Specmatic to maintain backward compatibility in your microservices & microfrontend development process.
What’s New
The new command has been overhauled to be more intuitive and supports a wider range of specifications. It now includes:
- Comprehensive support for OpenAPI, gRPC, and GraphQL.
- Simplified usage with smarter messages to help.
Note: Older commands for backward compatibility will be deprecated soon. We recommend switching to the new command to make the most of its improved capabilities.
How it works
Specmatic integrates backward compatibility checks into both:
- Local changes, to catch issues as you update the specifications.
- CI pipelines, ensuring every change is validated before it reaches production.
Here’s a high-level overview of the workflow:
graph TD;
A[Specification Changes] -->|Analyzed by| B{Backward Compatibility Check}
B -->|Compatible| C[Green Light for Deployment]
B -->|Incompatible| D[Refinement Needed]
C -->|Seamless Integration| E[Enhanced User Trust]
D -->|Iterative Improvement| A
Using Backward Compatibility
Command Essentials
To check for backward compatibility of your API specifications, use:
specmatic backward-compatibility-check [options]
Key Options
--target-path
: Focus your analysis on specific file or folder. Default is all files and folder.--base-branch
: Select your comparison base. This defaults to head of the current branch.
Common Use Cases
1. Validating Work in Progress (local development)
For immediate feedback on your uncommitted changes:
specmatic backward-compatibility-check
sequenceDiagram
participant Developer
participant Specmatic
participant LocalBranch
Developer->>Specmatic: Execute backward-compatibility-check
Specmatic->>LocalBranch: Retrieve current state
Specmatic->>Specmatic: Analyze changed files
Specmatic->>Specmatic: Perform compatibility analysis
Specmatic->>Developer: Deliver compatibility assessment
2. As a pre-commit hook
Add to .git/hooks/pre-commit. This makes sure backward compatibility is always checked before commit, even if you don’t do it manually.
#!/bin/sh
specmatic backward-compatibility-check
graph TD
A[Developer initiates commit] -->|Triggers| B[Pre-commit hook]
B -->|Runs| C[Specmatic backward-compatibility-check]
C -->|Compatible| D[Commit proceeds]
C -->|Incompatible| E[Commit halted]
E -->|Developer notified| F[Changes reviewed]
F --> A
3. Pre-Merge Validation (in your CI pipeline)
Ensure your changes align with the main project direction:
specmatic backward-compatibility-check --base-branch origin/main
sequenceDiagram
participant CI
participant Specmatic
participant FeatureBranch
participant MainBranch
CI->>Specmatic: Run check with --base-branch origin/main
Specmatic->>FeatureBranch: Retrieve current state
Specmatic->>MainBranch: Fetch main branch state
Specmatic->>Specmatic: Perform compatibility analysis
Specmatic->>CI: Present comprehensive results
4. Analyzing specific files
specmatic backward-compatibility-check --target-path ./api/products.yaml
5. Comparing with a different branch
specmatic backward-compatibility-check --base-branch origin/feature_v2
Practical Examples (Try it yourself)
Orders API Evolution
Consider this initial Orders API specification:
# filename: api_products_v1.yaml
openapi: 3.0.0
info:
title: Sample Product API
version: 0.1.9
servers:
- url: http://localhost:8080
description: Local
- url: http://localhost:9000
description: Specmatic Stub Server
paths:
/products/{id}:
get:
summary: Get Products
description: Fetch product details
parameters:
- in: path
name: id
schema:
type: number
required: true
description: Product ID
responses:
'200':
description: Returns product details
content:
application/json:
schema:
type: object
properties:
name:
type: string
sku:
type: string
Now, let’s evolve this API by adding a new category
field:
# Updated version of api_products_v1.yaml
openapi: 3.0.0
info:
title: Sample Product API
version: 0.2.0
paths:
/products/{id}:
get:
summary: Get Products
description: Fetch product details
parameters:
- in: path
name: id
schema:
type: number
required: true
responses:
'200':
description: Returns product details
content:
application/json:
schema:
type: object
properties:
name:
type: string
sku:
type: string
category:
type: string
Run the backward compatibility check:
specmatic backward-compatibility-check --target-path ./api_products_v1.yaml
Specmatic will approve this change, as adding an optional field maintains backward compatibility. Here is the sample output of the command
Breaking Change
However, let’s change the data type of name from ‘string’ to ‘number’:
# Breaking change in api_products_v1.yaml
openapi: 3.0.0
info:
title: Sample Product API
version: 0.2.0
paths:
/products/{id}:
get:
summary: Get Products
description: Fetch product details
parameters:
- in: path
name: id
schema:
type: number
required: true
responses:
'200':
description: Returns product details
content:
application/json:
schema:
type: object
properties:
name:
type: number
sku:
type: string
category:
type: string
Running the check now:
specmatic backward-compatibility-check --target-path ./api_products_v1.yaml
Specmatic will flag this as incompatible, protecting your API consumers from unexpected changes. Following is the output of the command.
Handling Changes In Progress
APIs whose design is still in progress can be tagged WIP in the OpenAPI Specifications. Specmatic will not break builds or return failure on when it see backward incompatible changes to WIP APIs. It will still print the error feedback.
# filename api_products_v1.yaml
openapi: 3.0.0
info:
title: Sample Product API
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.1.9
servers:
- url: http://localhost:8080
description: Local
- url: http://localhost:9000
description: Specmatic Stub Server
paths:
/products/{id}:
get:
summary: Get Products
description: Get Products
tags:
- "WIP"
parameters:
- in: path
name: id
schema:
type: number
required: true
description: Numerical Product Id
responses:
'200':
description: Returns Product With Id
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
sku:
type: string
/products:
post:
summary: Add Product
description: Add Product
requestBody:
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
sku:
type: string
nullable: true
responses:
'200':
description: Returns Product With Id
content:
application/json:
schema:
type: object
required:
- id
properties:
id:
type: integer
Once the specification is complete you can remove the WIP tag.
Backward Compatibility Rules
Maintaining backward compatibility is about changing the API provider WITHOUT breaking any existing consumer. Consumers should just continue working as-is, without needing to “keep up”.
[!IMPORTANT] All the existing commands for backward compatibility (listed in the following sections) will be phased out in our next major release. We highly recommend transitioning to the more robust
backward-compatibility-check
command to future-proof your development process.
Backward Compatibility Commands (Deprecated)
Comparing Two Contracts (Deprecated)
Note: The following command will be deprecated soon. We recommend switching to the new command to make the most of its improved capabilities.
Create a file named api_products_v1.yaml.
# filename api_products_v1.yaml
openapi: 3.0.0
info:
title: Sample Product API
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.1.9
servers:
- url: http://localhost:8080
description: Local
- url: http://localhost:9000
description: Specmatic Stub Server
paths:
/products/{id}:
get:
summary: Get Products
description: Get Products
parameters:
- in: path
name: id
schema:
type: number
required: true
description: Numerical Product Id
responses:
'200':
description: Returns Product With Id
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
sku:
type: string
This contract contains an API for fetching the details of a product.
Let’s add a new api to create a product record:
# filename api_products_v2.yaml
openapi: 3.0.0
info:
title: Sample Product API
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.1.9
servers:
- url: http://localhost:8080
description: Local
- url: http://localhost:9000
description: Specmatic Stub Server
paths:
/products/{id}:
get:
summary: Get Products
description: Get Products
parameters:
- in: path
name: id
schema:
type: number
required: true
description: Numerical Product Id
responses:
'200':
description: Returns Product With Id
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
sku:
type: string
/products:
post:
summary: Add Product
description: Add Product
requestBody:
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
sku:
type: string
nullable: true
responses:
'200':
description: Returns Product With Id
content:
application/json:
schema:
type: object
required:
- id
properties:
id:
type: integer
The old /products/{id} API remains intact, and the new /products API is added on.
The newer contract is backward compatible with the older, as existing consumers are only using the old API, which remains unchanged.
Run the specmatic compare command to confirm this, and see the result:
-
java -jar specmatic.jar compare api_products_v1.yaml api_products_v2.yaml
-
npx specmaitc compare api_products_v1.yaml api_products_v2.yaml
-
docker run -v "/local-directory:/specs" znsio/specmatic compare "/specs/api_products_v1.yaml" "/specs/api_products_v2.yaml"
You should now see an output as shown below.
The newer contract is backward compatible
Let’s change the original contract of square to return sku
as a num integer
instead of string
in the response:
# filename api_products_v2.yaml
openapi: 3.0.0
info:
title: Sample Product API
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.1.9
servers:
- url: http://localhost:8080
description: Local
- url: http://localhost:9000
description: Specmatic Stub Server
paths:
/products/{id}:
get:
summary: Get Products
description: Get Products
parameters:
- in: path
name: id
schema:
type: number
required: true
description: Numerical Product Id
responses:
'200':
description: Returns Product With Id
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
sku: #this has changed from string to integer
type: integer
Now try it again:
-
java -jar specmatic.jar compare api_products_v1.yaml api_products_v2.yaml
-
npx specmaitc compare api_products_v1.yaml api_products_v2.yaml
-
docker run -v "/local-directory:/specs" znsio/specmatic compare "/specs/api_products_v1.yaml" "/specs/api_products_v2.yaml"
Specmatic will show you an error message, saying that the change is not backward compatible. The reason for this is that existing consumers are expecting a string “sku”, but will get an “integer” instead.
In scenario "Get Products. Response: Returns Product With Id"
API: GET /products/(id:number) -> 200
>> RESPONSE.BODY.sku
This is number in the new contract response but string in the old contract
The newer contract is not backward compatible.
If the change is not backward compatible, the compare command exits with exit code 1. You can use this in a script.
Validating Changes In Git On Your Laptop (Deprecated)
Note: The following command will be deprecated soon. We recommend switching to the new command to make the most of its improved capabilities.
If api_products_v1.yaml
is part of a git repository, changes can be made directly to this file instead of creating a new one.
Then to confirm that it is a backward compatible change, before committing the change, run this command:
-
java -jar specmatic.jar compatible git file ./run/specmatic/examples/api_products_v1.yaml
-
npx specmatic compatible git file api_products_v1.yaml
-
docker run -v "/git-repo:/git-repo" znsio/specmatic compatible git file "/git-repo/api_products_v1.yaml"
This command exits with exit code 1 if the change is backward incompatible. It can be configured as a git pre-commit hook.
The newer contract is backward compatible
Validating Changes In CI (Deprecated)
Note: The following command will be deprecated soon. We recommend switching to the new command to make the most of its improved capabilities.
In CI, you will need to compare the changes in a contract from one commit to the next.
You can do this with the following command:
-
java -jar specmatic.jar compatible git commits api_products_v1.yaml HEAD HEAD^1
-
npx specmatic compatible git commits api_products_v1.yaml HEAD HEAD^1
-
docker run -v "/git-repo:/git-repo" znsio/specmatic compatible git commits "/git-repo/api_products_v1.yaml" HEAD HEAD^1
You can even use commit hashes here if you wish to compare any other pair of commits.
The newer contract is backward compatible
This command exits with exit code 1 if the change is backward incompatible.
Troubleshooting
- Command Not Recognized:
- Verify Specimatic is properly configured for your current directory (either Docker or Node package or Python module or Jar file)
- Ensure you are using the latest version of Specmatic.
- Unexpected Outcomes:
- Confirm the accuracy of file paths and branch names.
- Review your recent changes for unintended modifications.
- Too many changes:
- For large-scale projects, utilize the
--target-path
to focus on specific components.
- For large-scale projects, utilize the
- CI Pipeline Issues:
- Ensure your CI environment has the necessary permissions to access all required branches
- Verify that your CI configuration correctly sets up Specmatic before running the compatibility check