<?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: Jaroslav Suva</title>
    <description>The latest articles on DEV Community by Jaroslav Suva (@jsuvic).</description>
    <link>https://dev.to/jsuvic</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3843639%2F2ece3173-e866-4b96-a530-464ab74026e9.jpeg</url>
      <title>DEV Community: Jaroslav Suva</title>
      <link>https://dev.to/jsuvic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jsuvic"/>
    <language>en</language>
    <item>
      <title>How to Detect Breaking API Changes Before They Hit Production</title>
      <dc:creator>Jaroslav Suva</dc:creator>
      <pubDate>Mon, 13 Apr 2026 12:14:05 +0000</pubDate>
      <link>https://dev.to/diffmon/how-to-detect-breaking-api-changes-before-they-hit-production-3cmh</link>
      <guid>https://dev.to/diffmon/how-to-detect-breaking-api-changes-before-they-hit-production-3cmh</guid>
      <description>&lt;p&gt;Third-party APIs drift without warning. If your integration logic assumes yesterday's payload shape, today's upstream rollout can become your production incident.&lt;/p&gt;

&lt;p&gt;This guide shows how to monitor API contracts as operational dependencies, separate schema changes from value changes, keep diffs deterministic, and route incidents to the right owner before customer flows break.&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as breaking drift
&lt;/h2&gt;

&lt;p&gt;Treat these as contract-risk events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed field used by mapping or business logic.&lt;/li&gt;
&lt;li&gt;Renamed field with no backwards-compatible alias.&lt;/li&gt;
&lt;li&gt;Type change (&lt;code&gt;string&lt;/code&gt; to &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt; to &lt;code&gt;string&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Requiredness change (nullable to required, optional to required).&lt;/li&gt;
&lt;li&gt;Nested shape change in arrays/objects consumed by downstream code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple rule: if your parser, validator, or transform would fail or silently mis-handle data, treat it as breaking drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate schema changes from value changes
&lt;/h2&gt;

&lt;p&gt;API monitoring gets noisy when every payload difference is treated the same. A useful distinction is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema change:&lt;/strong&gt; the response contract changed. A field was removed, added, renamed, made required, or changed type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value change:&lt;/strong&gt; the contract stayed the same, but a value at a known path changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Schema changes should usually win triage priority because they can break parsing, validation, ETL jobs, typed clients, and integration tests. Value changes matter too, but they are easier to reason about once the response shape is known to be stable.&lt;/p&gt;

&lt;p&gt;This separation also prevents double counting. If &lt;code&gt;customer.address&lt;/code&gt; changes from an object to a string, value differences below &lt;code&gt;customer.address.*&lt;/code&gt; are symptoms of the schema change, not separate incidents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not collapse null, absent, and empty
&lt;/h2&gt;

&lt;p&gt;Many production API failures start with a subtle assumption about "missing" data. Treat these as different states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;null&lt;/code&gt;: the field exists and explicitly carries no value.&lt;/li&gt;
&lt;li&gt;Absent: the field is missing from the response entirely.&lt;/li&gt;
&lt;li&gt;Empty string or empty array: the field exists and carries an empty value of the expected type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those states have different meanings for serializers, validators, UI fallbacks, and database writes. A third-party API changing &lt;code&gt;phone: null&lt;/code&gt; to an absent &lt;code&gt;phone&lt;/code&gt; field can be a breaking API change even if both look like "no phone number" in a dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stabilize signal (select + ignore + canonicalization)
&lt;/h2&gt;

&lt;p&gt;High-signal monitoring starts with selecting only contract-critical JSON paths and ignoring volatile metadata.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"selectJsonPaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"data.customer.id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"data.customer.status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"data.subscription.plan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"errors[*].code"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignoreJsonPaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"meta.requestId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"meta.traceId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"meta.generatedAt"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then canonicalize before diffing/hashing so field order and non-semantic formatting do not create false positives. Deterministic input gives deterministic diffs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triage workflow and baseline acceptance
&lt;/h2&gt;

&lt;p&gt;When meaningful drift is detected:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Incident opens automatically with structured diff context.&lt;/li&gt;
&lt;li&gt;On-call or service owner acknowledges to claim triage.&lt;/li&gt;
&lt;li&gt;Resolve if drift is unexpected and remediated.&lt;/li&gt;
&lt;li&gt;Accept baseline when rollout is expected and now represents the new contract.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Baseline acceptance should be an explicit operational decision, not an accidental side effect of "latest snapshot wins". The point is to stop repeated alerts for expected drift while preserving the audit trail that the contract changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use request-linked debugging
&lt;/h2&gt;

&lt;p&gt;Every alert should give engineers enough context to reproduce and explain the change. At minimum, preserve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request ID&lt;/li&gt;
&lt;li&gt;status code&lt;/li&gt;
&lt;li&gt;observed URL&lt;/li&gt;
&lt;li&gt;monitor configuration version&lt;/li&gt;
&lt;li&gt;diff fingerprint&lt;/li&gt;
&lt;li&gt;links to the before and after evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without request-linked debugging, an API breaking change turns into archaeology. With it, you can tell whether the change came from an upstream rollout, a different auth context, a redirect, a throttled response, or your own monitor configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation and idempotency mindset
&lt;/h2&gt;

&lt;p&gt;Route &lt;code&gt;diff.detected&lt;/code&gt; events to incident tooling and chatops using webhooks. Treat webhook consumers as idempotent by keying on event identity/fingerprint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schemaVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;diff.detected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;evt_01JX...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;occurredAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-02-11T12:41:00.000Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;monitorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mon_123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;diff_fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256:abc...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;diff_summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;added&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="cm"&gt;/* structured change records */&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use request IDs and delivery logs to correlate downstream processing failures and retries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Define Tier 1 API dependencies and assign response owners.&lt;/li&gt;
&lt;li&gt;Create one monitor per external contract boundary, not per endpoint variant.&lt;/li&gt;
&lt;li&gt;Start with narrow &lt;code&gt;selectJsonPaths&lt;/code&gt;, then expand only when needed.&lt;/li&gt;
&lt;li&gt;Wire webhooks into your incident system before increasing check frequency.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://diffmon.dev/guides/detect-api-breaking-changes?utm_source=devto&amp;amp;utm_medium=republish&amp;amp;utm_campaign=launch_2026_q1&amp;amp;utm_content=devto_detect_api_breaking_changes_v1" rel="noopener noreferrer"&gt;Start monitoring your API contracts with Diffmon →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>devops</category>
      <category>monitoring</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
