<?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: Aakash Sangwan</title>
    <description>The latest articles on DEV Community by Aakash Sangwan (@aakash2408).</description>
    <link>https://dev.to/aakash2408</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%2F396334%2Fcb162348-f534-4982-8733-e70dc783f65d.jpeg</url>
      <title>DEV Community: Aakash Sangwan</title>
      <link>https://dev.to/aakash2408</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aakash2408"/>
    <language>en</language>
    <item>
      <title>I Built a Tool That Auto-Fixes Downstream Code When You Change an API</title>
      <dc:creator>Aakash Sangwan</dc:creator>
      <pubDate>Wed, 05 Aug 2026 07:02:15 +0000</pubDate>
      <link>https://dev.to/aakash2408/i-built-a-tool-that-auto-fixes-downstream-code-when-you-change-an-api-25e8</link>
      <guid>https://dev.to/aakash2408/i-built-a-tool-that-auto-fixes-downstream-code-when-you-change-an-api-25e8</guid>
      <description>&lt;p&gt;You change a field in your proto file. You push it. Then you spend the next two days pinging 4 teams on Slack asking them to update their consumers.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Ripple&lt;/strong&gt; to eliminate that entire workflow. Push a breaking API change → fix PRs appear in every consumer repo. In 15 seconds. No manual coordination.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;There are great tools for &lt;em&gt;detecting&lt;/em&gt; API breaking changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;buf&lt;/strong&gt; catches proto incompatibilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;oasdiff&lt;/strong&gt; diffs OpenAPI specs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL Inspector&lt;/strong&gt; flags schema changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But detection is only step 1. The real pain is &lt;strong&gt;propagation&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You know &lt;code&gt;user.proto&lt;/code&gt; removed &lt;code&gt;phone_number&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;But WHO uses &lt;code&gt;phone_number&lt;/code&gt;? Which repos? Which files?&lt;/li&gt;
&lt;li&gt;And what's the correct fix in each consumer?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That coordination — finding consumers, understanding their usage, writing the fix, opening PRs — takes &lt;strong&gt;2-3 days per breaking change&lt;/strong&gt; at most orgs I've observed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Ripple Does
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You push: removed `phone_number` from user.proto

Ripple:
  ✅ Detects: field 3 removed (breaking)
  ✅ Finds: python-sdk/client.py, node-api/handlers/user.ts, java-gateway/UserService.java
  ✅ Generates: correct fix for each file (removes the dead field reference)
  ✅ Opens: 3 PRs with explanation of what changed and why

Time: ~15 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works Under the Hood
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Diff Engines (one per contract type)
&lt;/h3&gt;

&lt;p&gt;Ripple has custom parsers for 10 contract types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAPI / Swagger&lt;/li&gt;
&lt;li&gt;Protobuf / gRPC&lt;/li&gt;
&lt;li&gt;GraphQL&lt;/li&gt;
&lt;li&gt;Database (SQL + Prisma)&lt;/li&gt;
&lt;li&gt;AsyncAPI (Kafka, SNS, MQTT)&lt;/li&gt;
&lt;li&gt;Avro (Confluent Schema Registry)&lt;/li&gt;
&lt;li&gt;tRPC (TypeScript)&lt;/li&gt;
&lt;li&gt;Thrift (Apache)&lt;/li&gt;
&lt;li&gt;JSON Schema&lt;/li&gt;
&lt;li&gt;Smithy (AWS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each engine understands the semantics of its format. Removing an optional field is fine. Removing a required field is breaking. Changing a type is breaking. Adding a required field without a default is breaking.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Consumer Finding (the hard part)
&lt;/h3&gt;

&lt;p&gt;This is where most tools stop. Finding consumers is genuinely difficult because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consumers might be in different repos&lt;/li&gt;
&lt;li&gt;They might reference the spec indirectly (through generated code)&lt;/li&gt;
&lt;li&gt;Naming conventions vary wildly between codebases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ripple uses an &lt;strong&gt;ensemble approach&lt;/strong&gt; combining 5 strategies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simplified version of the ensemble
&lt;/span&gt;&lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nf"&gt;grep_for_field_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;removed_field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# Basic but fast
&lt;/span&gt;&lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nf"&gt;check_import_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;              &lt;span class="c1"&gt;# Who imports this?
&lt;/span&gt;&lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nf"&gt;query_git_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="c1"&gt;# Who changed when this changed?
&lt;/span&gt;&lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nf"&gt;check_playbooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="c1"&gt;# Custom rules
&lt;/span&gt;&lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nf"&gt;multi_invoker_detection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# Same spec, multiple callers
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The git history approach is the most interesting — if &lt;code&gt;user.proto&lt;/code&gt; and &lt;code&gt;python-sdk/client.py&lt;/code&gt; always change together in commits, they're probably coupled. This is based on research from &lt;strong&gt;PropBench&lt;/strong&gt;, a benchmark I built for measuring engineering judgment in change propagation (268 real scenarios, 1,223 consequence files analyzed).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fix Generation
&lt;/h3&gt;

&lt;p&gt;For each consumer file, Ripple generates the fix using:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Template-based fixes&lt;/strong&gt; for common patterns (field removal → remove reference)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM-powered fixes&lt;/strong&gt; for complex cases (Claude generates the correct code)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt; — the fix must pass basic syntax checks before opening a PR&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. PR/MR Creation
&lt;/h3&gt;

&lt;p&gt;Opens a pull request (GitHub), merge request (GitLab), or PR (Bitbucket) with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear title: "fix: Remove &lt;code&gt;phone_number&lt;/code&gt; reference (field removed in user.proto)"&lt;/li&gt;
&lt;li&gt;Explanation of what changed upstream&lt;/li&gt;
&lt;li&gt;The minimal diff to fix the consumer&lt;/li&gt;
&lt;li&gt;Link back to the original commit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation (One Click)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/apps/ripple-api" rel="noopener noreferrer"&gt;Install the Ripple GitHub App&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitLab:&lt;/strong&gt; Visit &lt;code&gt;your-ripple-server/auth/gitlab&lt;/code&gt; → Click Authorize → Done&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bitbucket:&lt;/strong&gt; Visit &lt;code&gt;your-ripple-server/auth/bitbucket&lt;/code&gt; → Click Authorize → Done&lt;/p&gt;

&lt;p&gt;That's it. Webhooks are auto-installed on all your repos. Push a breaking change and watch the fix PRs appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes This Different From Dependabot/Renovate?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Dependabot&lt;/th&gt;
&lt;th&gt;Ripple&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Library versions&lt;/td&gt;
&lt;td&gt;API consumer code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;New version published&lt;/td&gt;
&lt;td&gt;Breaking spec change pushed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fix type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bump version number&lt;/td&gt;
&lt;td&gt;Modify actual code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Knowledge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Package registry&lt;/td&gt;
&lt;td&gt;Your repo's git history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single repo&lt;/td&gt;
&lt;td&gt;Cross-repo propagation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dependabot bumps &lt;code&gt;protobuf&lt;/code&gt; from 4.0 to 4.1 in your &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ripple rewrites your &lt;code&gt;user_service.py&lt;/code&gt; to handle the fact that &lt;code&gt;user.proto&lt;/code&gt; no longer has a &lt;code&gt;phone_number&lt;/code&gt; field.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Research Behind It
&lt;/h2&gt;

&lt;p&gt;Ripple is backed by &lt;strong&gt;PropBench&lt;/strong&gt; — a benchmark of 268 real engineering changes I analyzed to understand WHY developers miss downstream impacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;34%&lt;/strong&gt; of misses: test files with non-obvious naming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;26%&lt;/strong&gt; of misses: same-package files with no naming relationship&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;16%&lt;/strong&gt; of misses: config/YAML/JSON requiring domain knowledge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;39%&lt;/strong&gt; of consequences are &lt;strong&gt;cross-package&lt;/strong&gt; (invisible to single-repo tools)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple grep finds 7% of affected files. Adding co-change history from git bumps that to 17-38%. The ensemble approach reaches 82% at the package level.&lt;/p&gt;

&lt;p&gt;The takeaway: most of what we call "senior engineering judgment" in change propagation is actually &lt;strong&gt;learnable patterns&lt;/strong&gt; — naming conventions + git history + domain rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Landing page:&lt;/strong&gt; &lt;a href="https://aakash2408.github.io/ripple" rel="noopener noreferrer"&gt;aakash2408.github.io/ripple&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/Aakash2408/ripple" rel="noopener noreferrer"&gt;github.com/Aakash2408/ripple&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub App:&lt;/strong&gt; &lt;a href="https://github.com/apps/ripple-api" rel="noopener noreferrer"&gt;github.com/apps/ripple-api&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free. Open source. Looking for 10 teams to try it and give feedback.&lt;/p&gt;

&lt;p&gt;If you've ever spent a day fixing downstream code after an API change, I'd love to hear about your workflow. What contract types do you use? How do you find consumers today? How long does propagation take at your org?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built in ~6 days as a side project. Currently a solo founder applying to YC. If this resonates, star the repo or install the app — it helps more than you'd think.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>opensource</category>
      <category>devtools</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
