Swagger, or OpenAPI Specification, doesn’t inherently track changes; it describes APIs. The changelog you’re looking for is an organizational artifact that uses the OpenAPI spec to communicate evolution.

Consider this: you have a running API, and you’ve just updated its OpenAPI definition. If you want to know what changed, you’d typically compare two versions of the OpenAPI file.

Let’s say we have two versions of an OpenAPI spec, openapi-v1.yaml and openapi-v2.yaml. We can use a diff tool to highlight the differences.

# openapi-v1.yaml
openapi: 3.0.0
info:
  title: My Awesome API
  version: 1.0.0
paths:
  /users:
    get:
      summary: Get a list of users
      responses:
        '200':
          description: A list of users
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                    name:
                      type: string
# openapi-v2.yaml
openapi: 3.0.0
info:
  title: My Awesome API
  version: 1.1.0
paths:
  /users:
    get:
      summary: Get a list of users
      responses:
        '200':
          description: A list of users
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                    name:
                      type: string
                    email: # New field
                      type: string
                      format: email
  /users/{userId}: # New endpoint
    get:
      summary: Get a specific user by ID
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: A single user
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  name:
                    type: string
                  email:
                    type: string

Running diff openapi-v1.yaml openapi-v2.yaml would show us these changes:

--- openapi-v1.yaml
+++ openapi-v2.yaml
@@ -11,3 +11,27 @@
                 properties:
                   id:
                     type: integer
                   name:
                     type: string
+                  email: # New field
+                    type: string
+                    format: email
+  /users/{userId}: # New endpoint
+    get:
+      summary: Get a specific user by ID
+      parameters:
+        - name: userId
+          in: path
+          required: true
+          schema:
+            type: integer
+      responses:
+        '200':
+          description: A single user
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  id:
+                    type: integer
+                  name:
+                    type: string
+                  email:
+                    type: string

The core problem this solves is communicating API evolution to consumers without breaking their integrations. A breaking change is one that, if adopted by a client, would cause their existing code to fail. Non-breaking changes can be adopted without immediate breakage, but might pave the way for future breakage if not handled carefully.

Internally, the OpenAPI specification is a JSON or YAML document that describes an API’s endpoints, operations, parameters, request/response bodies, and schemas. When you change this document, you’re essentially updating the "contract" of your API.

The key levers you control are the fields within the OpenAPI document:

  • paths: Adding a new path (/users/{userId}) is non-breaking. Removing an existing path is breaking.
  • operation (e.g., get, post on a path): Adding a new HTTP method to an existing path is non-breaking. Removing an existing method is breaking.
  • parameters:
    • Adding a new optional parameter is non-breaking.
    • Adding a new required parameter is breaking.
    • Changing a parameter’s type (e.g., integer to string) is breaking.
    • Removing a parameter is breaking.
  • requestBody / responses content schema:
    • Adding a new optional field to an object schema is non-breaking.
    • Adding a new required field to an object schema is breaking.
    • Removing a field from an object schema is breaking.
    • Changing a field’s type is breaking.
    • Adding a new possible value to an enum is non-breaking.
    • Removing a value from an enum is breaking.

The most surprising true thing about tracking API changes via OpenAPI is that the version number in info.version is entirely semantic. The spec itself doesn’t enforce or interpret it; it’s up to you and your consumers to decide if a change warrants a 1.0.0 to 1.1.0 (minor, potentially non-breaking) or 1.0.0 to 2.0.0 (major, likely breaking) increment. Many teams use automated tools that analyze the diff between two OpenAPI specs and suggest whether changes are breaking or non-breaking based on predefined rules.

The next concept you’ll want to explore is how to version your API across multiple OpenAPI specifications simultaneously.

Want structured learning?

Take the full Swagger course →