<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: API Contract Guard</title>
    <description>The latest articles on DEV Community by API Contract Guard (@apicontractguard).</description>
    <link>https://dev.to/apicontractguard</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4033203%2F240f102f-0ba0-493a-862c-46a80f4667e2.png</url>
      <title>DEV Community: API Contract Guard</title>
      <link>https://dev.to/apicontractguard</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/apicontractguard"/>
    <language>en</language>
    <item>
      <title>How to catch breaking OpenAPI changes before they reach production</title>
      <dc:creator>API Contract Guard</dc:creator>
      <pubDate>Fri, 17 Jul 2026 06:12:24 +0000</pubDate>
      <link>https://dev.to/apicontractguard/how-to-catch-breaking-openapi-changes-before-they-reach-production-44jj</link>
      <guid>https://dev.to/apicontractguard/how-to-catch-breaking-openapi-changes-before-they-reach-production-44jj</guid>
      <description>&lt;p&gt;An API can stay online, return 200s, and still break every client that depends on it.&lt;/p&gt;

&lt;p&gt;That is the uncomfortable part of API compatibility: the dangerous change often looks completely reasonable inside the implementation pull request. A response field is renamed. A previously optional parameter becomes required. An old status code disappears.&lt;/p&gt;

&lt;p&gt;The service still builds. Its unit tests may still pass. Existing consumers fail later.&lt;/p&gt;

&lt;p&gt;This guide shows what to check in an OpenAPI contract before merging a change.&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as a breaking OpenAPI change?
&lt;/h2&gt;

&lt;p&gt;A change is breaking when a client that was valid against the previous contract may no longer work against the new one.&lt;/p&gt;

&lt;p&gt;The most common examples are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;removing a path or HTTP method&lt;/li&gt;
&lt;li&gt;removing a documented response status&lt;/li&gt;
&lt;li&gt;adding a required request parameter&lt;/li&gt;
&lt;li&gt;making an optional request property required&lt;/li&gt;
&lt;li&gt;removing a response property&lt;/li&gt;
&lt;li&gt;changing a schema type incompatibly&lt;/li&gt;
&lt;li&gt;narrowing accepted enum values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additive changes are usually safer. Adding a new endpoint, a new optional request field, or a new response property normally preserves backward compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small change with a large blast radius
&lt;/h2&gt;

&lt;p&gt;Imagine the current contract contains this operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/health&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service is healthy&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;503"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service is unavailable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A pull request removes the &lt;code&gt;200&lt;/code&gt; response because the implementation now returns a richer status object elsewhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/health&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;503"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service is unavailable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The YAML is valid. The server may be correct according to the new design. But any monitor, generated SDK, or integration expecting the documented success response now has an incompatible contract.&lt;/p&gt;

&lt;p&gt;That should be a visible pull-request failure, not a production discovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare the base contract with the candidate
&lt;/h2&gt;

&lt;p&gt;The useful comparison is not simply whether the new OpenAPI file is valid. Validation answers: “Is this a legal OpenAPI document?”&lt;/p&gt;

&lt;p&gt;Compatibility answers: “Will clients built against the old document still work?”&lt;/p&gt;

&lt;p&gt;For every pull request:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the OpenAPI document from the base branch.&lt;/li&gt;
&lt;li&gt;Read the proposed document from the pull-request branch.&lt;/li&gt;
&lt;li&gt;Compare paths, operations, parameters, responses, and schemas.&lt;/li&gt;
&lt;li&gt;Classify each difference as compatible or breaking.&lt;/li&gt;
&lt;li&gt;Report the exact path and reason in the pull-request check.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The base branch represents the promise already made to consumers. The candidate represents the promise the team wants to make next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make compatibility a required GitHub check
&lt;/h2&gt;

&lt;p&gt;A report that developers must remember to run will eventually be skipped. Put the comparison in the same loop as tests, linting, and security checks.&lt;/p&gt;

&lt;p&gt;A practical workflow is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;install the checker on selected repositories&lt;/li&gt;
&lt;li&gt;open a pull request that changes an OpenAPI file&lt;/li&gt;
&lt;li&gt;receive a pass or fail check on the commit&lt;/li&gt;
&lt;li&gt;require that check in branch protection&lt;/li&gt;
&lt;li&gt;version or explicitly migrate intentional breaking changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important outcome is not “never make a breaking change.” Sometimes an API genuinely needs one. The outcome is that the break becomes a deliberate engineering decision with a migration path instead of an accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  A review checklist
&lt;/h2&gt;

&lt;p&gt;Before approving an API contract change, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Were any paths or methods removed?&lt;/li&gt;
&lt;li&gt;Did any input become required?&lt;/li&gt;
&lt;li&gt;Were response codes or response fields removed?&lt;/li&gt;
&lt;li&gt;Did a property change type?&lt;/li&gt;
&lt;li&gt;Did an enum lose a value?&lt;/li&gt;
&lt;li&gt;Is an intentional break accompanied by versioning or migration notes?&lt;/li&gt;
&lt;li&gt;Is the compatibility check required before merge?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try the comparison
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apicontractguard.com/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=openapi_breaking_changes#checker" rel="noopener noreferrer"&gt;API Contract Guard's free checker&lt;/a&gt; compares baseline and candidate OpenAPI documents in YAML or JSON. The browser tool runs without an account.&lt;/p&gt;

&lt;p&gt;For automated checks, the GitHub App runs the same comparison on pull requests. You can also &lt;a href="https://github.com/ptekspy/contract-guard-live-demo/pull/1/checks" rel="noopener noreferrer"&gt;see a real failed GitHub check&lt;/a&gt; before installing anything.&lt;/p&gt;

&lt;p&gt;The goal is simple: find the contract break while it is still a small diff, not after it becomes a customer incident.&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>testing</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
