<?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: Prasad MK</title>
    <description>The latest articles on DEV Community by Prasad MK (@prasadmk).</description>
    <link>https://dev.to/prasadmk</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%2F3957831%2F2b306458-eebf-4ada-8733-a882b13258c2.png</url>
      <title>DEV Community: Prasad MK</title>
      <link>https://dev.to/prasadmk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prasadmk"/>
    <language>en</language>
    <item>
      <title>Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents</title>
      <dc:creator>Prasad MK</dc:creator>
      <pubDate>Sat, 30 May 2026 18:01:22 +0000</pubDate>
      <link>https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m</link>
      <guid>https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m</guid>
      <description>&lt;h2&gt;
  
  
  Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents (Part 4)
&lt;/h2&gt;

&lt;p&gt;Parts 1 through 3 assumed one thing: a human is in the loop. A developer runs the local gate, reads the failure, and makes a deliberate decision. Even in Part 3, the vibe coder is still present. They feed the spec to the AI, read the output, and decide whether to push.&lt;/p&gt;

&lt;p&gt;Part 4 removes that assumption entirely.&lt;/p&gt;

&lt;p&gt;Autonomous AI agents, tools like Devin, AutoGPT, or custom LangChain pipelines, can now write code, run tests, interpret failures, and open pull requests without a human reviewing each step. This is not a future scenario. Teams are already running these workflows today.&lt;/p&gt;

&lt;p&gt;The drift problem does not disappear in this environment. It accelerates. And it gets a new capability: the ability to cover its own tracks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Autonomous Agents Break the Framework
&lt;/h2&gt;

&lt;p&gt;An AI agent tasked with a refactor will do whatever it takes to satisfy the local objective. If it changes the pagination logic and the REST Assured test fails, it does not stop and ask a developer for guidance. It looks at the failure, determines that the test is an obstacle, and rewrites the assertion to make the build green.&lt;/p&gt;

&lt;p&gt;From the agent's perspective, the task is complete. The build passes. The PR opens.&lt;/p&gt;

&lt;p&gt;From the system's perspective, the contract was just silently redefined by an automated process that had no awareness of downstream consumers, no knowledge of the versioning rules from Part 2, and no constraint preventing it from touching protected files.&lt;/p&gt;

&lt;p&gt;The governance framework built in Parts 1 and 2 relied on human judgment at the decision point. CODEOWNERS works when a human reviewer looks at the PR. A verbal rule about not mutating tests works when a developer reads it and understands why it exists.&lt;/p&gt;

&lt;p&gt;Neither of these holds when the contributor is an agent running at machine speed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Programmatic Rails, Not Prose Rules
&lt;/h2&gt;

&lt;p&gt;You cannot solve an automated problem with a social solution. Telling an AI agent to follow the rules in its system prompt is not a governance strategy. Context windows drift. Model updates change behavior. Prompt instructions get deprioritized when the agent is focused on satisfying a local objective.&lt;/p&gt;

&lt;p&gt;The framework needs to be deterministic. The rails need to be structural. The enforcement needs to happen at the system level, not the prompt level.&lt;/p&gt;

&lt;p&gt;Three layers make this work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 1: The Constraint File as a Machine-Readable Contract
&lt;/h2&gt;

&lt;p&gt;The first layer moves the governance rules out of prose and into a structured file that the agent is required to parse before acting.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.ai-rules.json&lt;/code&gt; file at the root of the repository:&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;"repository_constraints"&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="nl"&gt;"api_versioning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STRICT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breaking_changes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NEVER_MUTATE_EXISTING_TESTS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"known_domain_rules"&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="nl"&gt;"/api/v1/users"&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="nl"&gt;"pagination_base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"enforced_by"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UserWorkflowVerificationTest.java"&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;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;This file does two things. It tells the agent exactly which domain rules govern each endpoint, and it explicitly names the test file that enforces each rule. When the agent is tasked with modifying anything under &lt;code&gt;/api/v1/users&lt;/code&gt;, it must parse &lt;code&gt;known_domain_rules&lt;/code&gt; first and treat those constraints as non-negotiable inputs, not suggestions.&lt;/p&gt;

&lt;p&gt;The critical shift here is the difference between a rule the agent reads and a rule the agent loads as structured data. Prose in a system prompt gets weighed against the agent's objective. A JSON constraint file that the orchestration script injects into the agent's context window before execution is a boundary condition, not a preference.&lt;/p&gt;

&lt;p&gt;Commit this file to the repository and version it alongside the API. When the contract evolves, the constraint file evolves with it in the same PR. The rules are traceable, reviewable, and visible to every contributor, human or automated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 2: The Pre-Commit Hook as the Deterministic Bouncer
&lt;/h2&gt;

&lt;p&gt;The constraint file sets expectations. The pre-commit hook enforces them at the moment the agent tries to commit.&lt;/p&gt;

&lt;p&gt;This is the most important layer because it operates entirely outside the agent's control. No matter what the agent decided to do, no matter what it changed, the hook runs before the commit is allowed to proceed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Contract integrity check - runs before every commit&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Run the protected REST Assured contract tests&lt;/span&gt;
mvn &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-Dtest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;UserWorkflowVerificationTest

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Contract tests failed. The commit is blocked."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fix the underlying code. Do not modify the test assertions."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Verify the agent did not touch the protected test file&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"UserWorkflowVerificationTest.java"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: AI Agent attempted to modify a protected contract test."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Functional changes require a new API version or architectural sign-off."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hook does two things in sequence. It runs the contract tests and blocks the commit if they fail. Then it checks the git diff and blocks the commit if the agent touched the protected test file at all, regardless of whether the tests pass.&lt;/p&gt;

&lt;p&gt;That second check is the one that matters most. An agent that rewrites the assertion to make a failing test pass will produce a green test result. Without the diff check, the first gate would not catch it. The combination of both checks closes that gap entirely.&lt;/p&gt;

&lt;p&gt;If the agent cannot commit, it cannot open a PR. If it cannot open a PR, the drift never reaches the repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 3: The Coder and Auditor Pattern
&lt;/h2&gt;

&lt;p&gt;The two layers above are defensive. They block bad commits from landing. The third layer is diagnostic. It determines why a failure happened and instructs the agent on how to fix it correctly.&lt;/p&gt;

&lt;p&gt;The pattern is a separation of roles. Agent A is the Coder. It writes and refactors code. Agent B is the Auditor. It reviews the delta when the gate fails. These are two distinct LLM instances with different objectives, and they must never be the same instance self-reviewing its own output.&lt;/p&gt;

&lt;p&gt;The reason this separation matters is the same reason a developer should not approve their own PR. An agent asked to both write code and verify its correctness will optimize for satisfying its own objective. The Auditor needs to be a genuinely independent process with a different prompt, a different focus, and explicit authority to reject the Coder's output.&lt;/p&gt;

&lt;p&gt;When the pre-commit hook fails, the orchestration layer triggers the Auditor with this prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System: You are an automated architectural gatekeeper.
Your job is to determine if a code change introduced a breaking
regression or a valid feature expansion.

Input Artifacts:
1. Git diff of the change: [Insert diff]
2. Test failure log: [Insert REST Assured terminal output]
3. Enforced rules schema: [Insert .ai-rules.json contents]

Task: Analyze whether the code change violates any constraint
defined in the rules schema. If it does, generate a rejection
log that instructs the Coder agent to revert the specific change
and fix the underlying logic.

Constraint: Under no circumstances should the test suite assertions
be modified. The tests define the contract. The code must conform to them.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auditor does not fix the code. It produces a rejection log that describes exactly what the Coder did wrong and what it needs to do differently. The Coder then receives that rejection log as its next input and retries.&lt;/p&gt;

&lt;p&gt;This loop continues until the pre-commit hook passes cleanly, meaning the contract tests are green and the protected test files are untouched.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Full Framework Holds Together
&lt;/h2&gt;

&lt;p&gt;Stepping back across all four parts, the same contract flows through every layer.&lt;/p&gt;

&lt;p&gt;The Spring Boot application exposes its live OpenAPI spec at &lt;code&gt;/v3/api-docs&lt;/code&gt;. REST Assured derives its tests from that contract. Postman derives its collections from that contract. CODEOWNERS enforces that nobody modifies the core test files without cross-team review. API versioning ensures that behavioral changes ship as new endpoints, not as silent mutations to existing ones. The &lt;code&gt;.ai-rules.json&lt;/code&gt; file encodes the domain rules as machine-readable constraints. The pre-commit hook enforces those constraints at commit time, regardless of whether the contributor is human or automated. The Auditor agent closes the diagnostic loop when something goes wrong.&lt;/p&gt;

&lt;p&gt;At no point does the framework rely on trust, memory, or discipline. Every layer is structural. Every enforcement is deterministic. The contract is defined once, in the code, and every tool downstream, whether it is a developer, a vibe coder, or an autonomous agent, operates within the same boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing the Series
&lt;/h2&gt;

&lt;p&gt;The zero-drift problem is not a tooling problem. The tools, REST Assured, Postman, Git, OpenAPI, were always capable of solving it. The missing piece was a coherent framework that connected them into a single chain of enforcement, from the individual developer's local machine all the way to an autonomous agent operating without human oversight.&lt;/p&gt;

&lt;p&gt;That chain is now complete. Start with Part 1 today. The local loop costs less than an hour to set up and pays back immediately. Add the governance layer from Part 2 when the team grows. Introduce the AI prompt discipline from Part 3 when AI tools enter the workflow. Apply the programmatic rails from Part 4 when agents start opening PRs on their own.&lt;/p&gt;

&lt;p&gt;The build should be green because the contract is intact. Every time. At every scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Zero-Drift API Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-zero-drift-api-series-stop-trusting-a-green-build-you-cant-explain-k56"&gt;Intro: The Zero-Drift API Series: Stop Trusting a Green Build You Can't Explain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 1: A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39"&gt;Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg"&gt;Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m"&gt;Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor</title>
      <dc:creator>Prasad MK</dc:creator>
      <pubDate>Sat, 30 May 2026 17:53:16 +0000</pubDate>
      <link>https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg</link>
      <guid>https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg</guid>
      <description>&lt;h2&gt;
  
  
  The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor (Part 3)
&lt;/h2&gt;

&lt;p&gt;Parts 1 and 2 solved the drift problem for developers who write code deliberately. A local REST Assured gate, Postman wired to the live spec, CODEOWNERS enforcing review on core contracts, and API versioning as the hard rule when behavior changes.&lt;/p&gt;

&lt;p&gt;Now introduce a different kind of contributor. A vibe coder.&lt;/p&gt;

&lt;p&gt;A vibe coder is not a junior developer making rookie mistakes. They are often highly productive engineers who use AI tools like Cursor, Copilot, or a plain LLM chat window to generate, refactor, and iterate on code rapidly. The focus is on intent and outcome, not on typing syntax. The speed is real. So is the risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem AI Introduces
&lt;/h2&gt;

&lt;p&gt;An AI model generating code has no awareness of your running system. It does not know that your pagination is 1-based, that a specific field was renamed three sprints ago, or that a downstream mobile client breaks if a response envelope changes shape.&lt;/p&gt;

&lt;p&gt;It knows patterns. It knows what a Spring Boot controller generally looks like. It knows what a TypeScript fetch service should probably contain. When it does not have exact information, it fills the gap with a confident-looking guess.&lt;/p&gt;

&lt;p&gt;This is called a hallucinated contract. The generated code compiles. The types look plausible. The field names are reasonable. And the first time it runs against your actual API, something quietly breaks because the AI invented a payload shape that does not match reality.&lt;/p&gt;

&lt;p&gt;The drift problem from Part 1 gets faster and more confident with AI in the loop. Without a semantic anchor, the AI is just a very fluent source of test rot.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Feed the Spec, Not Just the Prompt
&lt;/h2&gt;

&lt;p&gt;The OpenAPI spec that has been doing all the work in Parts 1 and 2 is the answer here too. Instead of asking the AI to guess your contract, you hand it the exact live definition from your running application and treat it as a non-negotiable constraint.&lt;/p&gt;

&lt;p&gt;When the AI works from &lt;code&gt;/v3/api-docs&lt;/code&gt;, it is no longer guessing. It knows the exact endpoint paths, the required fields, the optional fields, the data types, the pagination parameters, and the response envelope shape. Every piece of code it generates is grounded in what the system actually does right now.&lt;/p&gt;

&lt;p&gt;This is the semantic anchor. The same single source of truth that keeps Postman and REST Assured in sync becomes the context that keeps AI-generated code from drifting before it is even written.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Prompts That Close the Loop
&lt;/h2&gt;

&lt;p&gt;The following prompts cover the three stages where AI intersects with the zero-drift workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt 1: Generate the REST Assured Test Suite from the Live Spec
&lt;/h3&gt;

&lt;p&gt;Instead of writing boilerplate test classes by hand, boot the application, grab the OpenAPI JSON from &lt;code&gt;http://localhost:8080/v3/api-docs&lt;/code&gt;, and hand it to the AI with this prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System: You are an expert backend QA engineer.
I will provide my local application's raw OpenAPI/Swagger JSON specification.

Task: Generate a complete REST Assured integration test class in Java
using @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT).

Rules:
1. Use standard given().when().then() syntax.
2. Every endpoint in the specification must have at least one contract
   validation test covering the status code and core payload fields.
3. Implement random local port setup using @LocalServerPort.

Here is the OpenAPI specification JSON:
[Paste http://localhost:8080/v3/api-docs output here]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI now generates tests that match your actual endpoints, your actual field names, and your actual response structure. Not a plausible approximation of them.&lt;/p&gt;

&lt;p&gt;This also means the tests are immediately runnable. There is no cleanup pass to fix field names the AI invented.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt 2: Debug a Failing Test Without Reading the Stack Trace
&lt;/h3&gt;

&lt;p&gt;When a local REST Assured test fails after an AI-assisted refactor, the natural instinct is to dig through the stack trace manually. There is a faster path. Feed the failure back to the AI with the relevant controller code and let it diagnose itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Context: My local REST Assured test suite just failed during a local check.

The Error:
[Paste the JUnit failure output here, e.g.:
 JSON path currentPage expected 1 but was 2]

The Relevant Controller/Service Code:
[Paste your Spring Boot Controller or Service class here]

Task: Analyze the code and the test failure. Identify where the contract
logic diverged. Fix the underlying Java code to restore backward
compatibility with the expected contract. Explain exactly why the
runtime behavior shifted.

Constraint: Do not modify the test assertion. The test reflects the
contract. The code must conform to it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The constraint at the end is critical and worth stating explicitly every time. Without it, the AI's default instinct is to make the test pass by any means available, including rewriting the assertion. That is exactly the silent mutation problem from Part 2. The prompt structure prevents it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt 3: Generate Frontend Code with Zero Payload Drift
&lt;/h3&gt;

&lt;p&gt;When a vibe coder moves to building a UI component or a mobile client that consumes the Spring Boot API, the same anchor applies. Do not let the AI guess the payload shape. Give it the spec.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Context: I am building a frontend component in React/TypeScript that
communicates with my Spring Boot backend.

Input: Here is the live contract definition from my backend:
[Paste http://localhost:8080/v3/api-docs output here]

Task: Generate a TypeScript Fetch/Axios service and the corresponding
interface types for the /api/v1/users endpoint.

Rules:
1. All property names must match the schema definitions exactly.
2. Respect optional and required field flags from the spec.
3. Pagination handling must be based strictly on the parameters
   defined in the specification. Do not assume defaults.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a TypeScript service that matches the backend contract on the first generation. No runtime surprises when the frontend hits the actual API. No field name mismatches discovered in a browser console after a deploy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters Beyond Convenience
&lt;/h2&gt;

&lt;p&gt;There is a deeper point here worth stating plainly.&lt;/p&gt;

&lt;p&gt;The zero-drift framework in Parts 1 and 2 is built on one principle: the contract lives in the code, and everything else derives from it. REST Assured derives from it. Postman derives from it. CODEOWNERS enforces that nobody silently redefines it.&lt;/p&gt;

&lt;p&gt;When AI enters the workflow without this anchor, it introduces a third source of truth, which is the model's best guess. That guess is invisible, confident, and wrong in ways that are hard to detect until something breaks downstream.&lt;/p&gt;

&lt;p&gt;Feeding the spec into the AI does not slow down the vibe coder's workflow. It makes the output trustworthy on the first pass, which is actually faster than the debug cycle that follows a hallucinated contract.&lt;/p&gt;

&lt;p&gt;The local REST Assured gate from Part 1 remains the final verification. If the AI introduced drift anywhere, the gate catches it in under five seconds. The developer does not need to know exactly what the AI got wrong. They feed the failure back with Prompt 2, fix the code, and run the gate again.&lt;/p&gt;

&lt;p&gt;The loop is fast. The contract stays clean. The AI becomes an accelerator rather than a liability.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;Parts 1 through 3 handle the developer-in-the-loop case, whether they are writing code manually, working in a large team with governance constraints, or using AI tools to generate at speed.&lt;/p&gt;

&lt;p&gt;Part 4 removes the developer from the loop entirely. When autonomous AI agents are opening pull requests without a human reviewing each change, the governance framework needs to be deterministic and programmatic. Pre-commit hooks, constraint files baked into the repository, and a multi-agent Coder/Auditor pattern that treats the AI like an untrusted contributor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m"&gt;Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Zero-Drift API Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-zero-drift-api-series-stop-trusting-a-green-build-you-cant-explain-k56"&gt;Intro: The Zero-Drift API Series: Stop Trusting a Green Build You Can't Explain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 1: A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39"&gt;Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg"&gt;Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m"&gt;Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams</title>
      <dc:creator>Prasad MK</dc:creator>
      <pubDate>Sat, 30 May 2026 17:39:22 +0000</pubDate>
      <link>https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39</link>
      <guid>https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39</guid>
      <description>&lt;h2&gt;
  
  
  Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams (Part 2)
&lt;/h2&gt;

&lt;p&gt;Part 1 established a clean local loop. REST Assured runs in under five seconds on the developer's machine. Postman pulls from the live OpenAPI spec. The gate is reliable, and the contract is grounded in actual code.&lt;/p&gt;

&lt;p&gt;Now add 50 developers to that picture.&lt;/p&gt;

&lt;p&gt;Suddenly the local loop is not enough. One developer's intentional optimization is another downstream team's production regression. And here is the uncomfortable truth: the test suite itself becomes the attack surface.&lt;/p&gt;




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

&lt;p&gt;When a REST Assured test fails because a developer changed API behavior, there are two ways to make it green again. Fix the code, or fix the test.&lt;/p&gt;

&lt;p&gt;Fixing the test is faster. It feels harmless. The build goes green. The PR merges. And somewhere downstream, a team that depended on the original behavior starts seeing failures they did not cause and cannot explain.&lt;/p&gt;

&lt;p&gt;This is not a discipline problem. It is a structural one. When a single developer can silently redefine an API contract by updating an assertion, the test suite stops being a gate and starts being a liability.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Opposite Trap Is Just as Bad
&lt;/h2&gt;

&lt;p&gt;The instinctive overcorrection is to declare all existing tests immutable. Nobody touches them. Ever. Only new tests get added.&lt;/p&gt;

&lt;p&gt;This sounds safe. It is not.&lt;/p&gt;

&lt;p&gt;When an API genuinely needs to evolve, the old test stays active and stays failing. Teams start using &lt;code&gt;@Ignore&lt;/code&gt; just to get deployments through. The test suite becomes a graveyard of outdated assertions that nobody trusts and nobody deletes. This is test rot, and it is just as damaging as silent contract mutation, just slower.&lt;/p&gt;

&lt;p&gt;The dilemma looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;The Benefit&lt;/th&gt;
&lt;th&gt;The Trap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Never change old tests&lt;/td&gt;
&lt;td&gt;Prevents silent contract rewrites&lt;/td&gt;
&lt;td&gt;Causes permanent build failures when changes are intentional. The suite becomes a historical graveyard.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Freely modify existing tests&lt;/td&gt;
&lt;td&gt;Keeps the suite clean and passing&lt;/td&gt;
&lt;td&gt;One developer can redefine the contract for the entire organization without downstream teams knowing.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither extreme works at scale. The answer is a governance layer that distinguishes between the two cases explicitly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architectural Solution: Three Levers
&lt;/h2&gt;

&lt;p&gt;You do not need to throw away REST Assured or install a complex contract broker to solve this. Three levers, used together, handle the governance problem with tooling teams already have.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lever 1: API Versioning as a Hard Rule
&lt;/h3&gt;

&lt;p&gt;When a functional change modifies payload structure, field behavior, or pagination logic, the existing endpoint is not touched. It stays exactly as it is, along with its REST Assured tests.&lt;/p&gt;

&lt;p&gt;The change ships under a new version.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/api/v1/users&lt;/code&gt; and its test suite remain intact and passing. &lt;code&gt;/api/v2/users&lt;/code&gt; is introduced with a brand new test class that reflects the new behavior. Downstream teams consuming v1 are never broken. They migrate to v2 on their own timeline.&lt;/p&gt;

&lt;p&gt;This rule eliminates the core dilemma entirely. There is no decision to make about whether to update an old test, because the old endpoint and its tests are never touched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lever 2: CODEOWNERS to Enforce Review on Core Contracts
&lt;/h3&gt;

&lt;p&gt;GitHub and GitLab both support a &lt;code&gt;CODEOWNERS&lt;/code&gt; file that assigns mandatory reviewers to specific directories. This is the enforcement mechanism for the versioning rule.&lt;/p&gt;

&lt;p&gt;Place the core REST Assured contract tests under a protected path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/test/java/com/yourorg/contracts/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add a CODEOWNERS entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight codeowners"&gt;&lt;code&gt;&lt;span class="n"&gt;src/test/java/com/yourorg/contracts/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;@api-architecture-team&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now any PR that modifies a file inside that directory cannot merge without explicit approval from the API architecture group. A developer can freely add new test files for new features anywhere else. But touching an existing contract test requires a deliberate, cross-team sign-off.&lt;/p&gt;

&lt;p&gt;The gate is no longer social. It is structural.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lever 3: Deprecation Windows Instead of Deletion
&lt;/h3&gt;

&lt;p&gt;When old functionality genuinely needs to retire, the process is not a silent test deletion. It is a staged, visible wind-down.&lt;/p&gt;

&lt;p&gt;Mark the old endpoint as &lt;code&gt;@Deprecated&lt;/code&gt; in the Spring Boot controller. Log a deprecation warning on every call so downstream teams see it in their monitoring. Schedule the removal for a future sprint with a communicated deadline. When that sprint arrives, the old endpoint and its REST Assured tests are deleted together, in the same PR, with full visibility.&lt;/p&gt;

&lt;p&gt;Nobody is surprised. Nobody discovers a broken contract in production. The retirement is as explicit as the original contract.&lt;/p&gt;




&lt;h2&gt;
  
  
  When You Need More: Consumer-Driven Contract Testing
&lt;/h2&gt;

&lt;p&gt;The three levers above handle the majority of real-world cases without additional infrastructure. But if your organization has many independent teams consuming the same APIs, and the coordination cost of manual review is becoming a bottleneck, the next step is Consumer-Driven Contract Testing using Pact or Spring Cloud Contract.&lt;/p&gt;

&lt;p&gt;The model works like this. Each downstream team that consumes an API publishes a consumer contract that explicitly declares what it expects. When Team A's developer makes a change that affects &lt;code&gt;/api/v1/users&lt;/code&gt;, the CI pipeline automatically pulls all active consumer contracts from a shared broker and runs them against the new code. If any consumer contract fails, the merge is blocked and the affected team is notified before anything ships.&lt;/p&gt;

&lt;p&gt;The key difference from the lever approach is that enforcement becomes fully automated. No human reviewer needs to catch the conflict. The pipeline catches it.&lt;/p&gt;

&lt;p&gt;This is the right investment when teams are large enough that CODEOWNERS review becomes a bottleneck, or when downstream consumers are external and cannot be coordinated manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Looks Like in Practice
&lt;/h2&gt;

&lt;p&gt;A developer on Team A refactors the user pagination logic and opens a PR.&lt;/p&gt;

&lt;p&gt;Without governance: the test fails, they update the assertion, CI goes green, Team B finds out when their service breaks in staging.&lt;/p&gt;

&lt;p&gt;With governance: the test fails. The CODEOWNERS rule blocks the PR from merging without architecture review. The reviewer looks at the change, determines it is a functional behavioral shift, and asks the developer to version the endpoint. &lt;code&gt;/api/v2/users&lt;/code&gt; is introduced. Team B is notified of the new version. Team A ships. Nobody breaks.&lt;/p&gt;

&lt;p&gt;The difference is not more process. It is the right structure in the right place.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;At this point the framework handles individual developers and distributed teams. The contract is protected, the versioning rule is enforced, and the governance layer is structural rather than cultural.&lt;/p&gt;

&lt;p&gt;Part 3 introduces a different kind of contributor: AI-assisted developers using tools like Cursor or Copilot, who are generating code faster than any review process was designed to handle. The same OpenAPI spec that anchors Postman and REST Assured becomes the semantic anchor that keeps AI-generated code from hallucinating payload shapes and silently drifting from the contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg"&gt;Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Zero-Drift API Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-zero-drift-api-series-stop-trusting-a-green-build-you-cant-explain-k56"&gt;Intro: The Zero-Drift API Series: Stop Trusting a Green Build You Can't Explain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/a-guide-to-stop-breaking-merges-unifying-postman-and-rest-assured-in-spring-boot-42fg"&gt;Part 1: A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39"&gt;Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg"&gt;Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m"&gt;Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>devops</category>
      <category>softwareengineering</category>
      <category>testing</category>
    </item>
    <item>
      <title>A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot</title>
      <dc:creator>Prasad MK</dc:creator>
      <pubDate>Sat, 30 May 2026 17:32:41 +0000</pubDate>
      <link>https://dev.to/prasadmk/a-guide-to-stop-breaking-merges-unifying-postman-and-rest-assured-in-spring-boot-42fg</link>
      <guid>https://dev.to/prasadmk/a-guide-to-stop-breaking-merges-unifying-postman-and-rest-assured-in-spring-boot-42fg</guid>
      <description>&lt;h2&gt;
  
  
  A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot (Part 1)
&lt;/h2&gt;

&lt;p&gt;Every engineering team hits this wall eventually. A developer updates an endpoint, verifies it quickly on their local machine, and opens a pull request. It merges cleanly. Then the deployment pipeline breaks, or worse, a silent contract regression slips past CI entirely and lands in staging or production.&lt;/p&gt;

&lt;p&gt;This is the Merge Bottleneck. The root cause is almost never a lack of discipline. It is a structural flaw in how validation workflows are separated.&lt;/p&gt;

&lt;p&gt;Manual exploratory testing in Postman does not scale to guard git merges. A detached REST Assured suite that nobody keeps in sync with the actual API introduces test drift. Two tools, two descriptions of the same system, diverging silently over time.&lt;/p&gt;

&lt;p&gt;This guide fixes that by making the Spring Boot application itself the single source of truth, and wiring both Postman and REST Assured to consume from it directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Strategy: Code-First Sync
&lt;/h2&gt;

&lt;p&gt;Spring Boot, with the &lt;code&gt;springdoc-openapi&lt;/code&gt; dependency, exposes a live OpenAPI specification at &lt;code&gt;/v3/api-docs&lt;/code&gt; whenever the application is running. That endpoint is your contract. Everything else derives from it.&lt;/p&gt;

&lt;p&gt;Both Postman and REST Assured point at the same running application. When the code changes, both tools see the change immediately. There is no manual sync step, no documentation page to update, and no drift.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Establish the Local Automated Gate
&lt;/h2&gt;

&lt;p&gt;The primary safety net needs to run on the developer's machine in seconds, before any CI queue is involved. Waiting for a pipeline to tell you the build is broken is already too late in the feedback loop.&lt;/p&gt;

&lt;p&gt;The setup uses &lt;code&gt;@SpringBootTest&lt;/code&gt; with &lt;code&gt;RANDOM_PORT&lt;/code&gt;. This spins up a real, isolated instance of the application, runs REST Assured verifications against it, and tears down the context when the test completes. Nothing is mocked. The full application stack runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webEnvironment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SpringBootTest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WebEnvironment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RANDOM_PORT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserWorkflowVerificationTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@LocalServerPort&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@BeforeEach&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;RestAssured&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseURI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://localhost"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;RestAssured&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;verifyUserCreationContract&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;RestAssured&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;given&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{\"name\": \"Dev Team\", \"email\": \"dev@company.com\"}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/v1/users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test is the gate. If it is red, the developer does not push.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Wire Postman to the Live Local Spec
&lt;/h2&gt;

&lt;p&gt;When a developer needs to explore edge cases or visually inspect a payload, they should not be manually constructing JSON from memory or from a Confluence page last updated six months ago.&lt;/p&gt;

&lt;p&gt;The workflow is three steps. Boot the Spring Boot application locally at &lt;code&gt;http://localhost:8080&lt;/code&gt;. Open Postman, select Import, and provide &lt;code&gt;http://localhost:8080/v3/api-docs&lt;/code&gt;. Postman parses the live OpenAPI spec and generates a structured collection that exactly matches the current code state.&lt;/p&gt;

&lt;p&gt;When a data model changes in the Java source, Postman's Update Collection re-syncs against the new spec automatically. No human transcription. No stale payloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: The Local Feedback Loop
&lt;/h2&gt;

&lt;p&gt;The workflow only holds if it becomes the standard operating loop before any push. The pattern is simple. Write the code. Run REST Assured locally from the IDE, which takes around five seconds. If the test fails, stay on the machine, open Postman to visually inspect the response against the live local server, fix the underlying code, and re-run. If the test passes, push the PR with confidence.&lt;/p&gt;

&lt;p&gt;The pipeline never sees the broken state. That is the point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anatomy of a Catch: The Pagination Index Bug
&lt;/h2&gt;

&lt;p&gt;This is a real scenario that slips past traditional code review more often than it should.&lt;/p&gt;

&lt;p&gt;The API is designed with a 1-based pagination index. &lt;code&gt;page=1&lt;/code&gt; returns the first page of results. During a refactor, a developer accidentally shifts the implementation to 0-based. The code compiles. The database queries run. The application starts cleanly. Nothing in the build output signals a problem. In a siloed workflow, this ships, and downstream clients break on deploy.&lt;/p&gt;

&lt;p&gt;Here is how the dual-layer workflow catches it before that happens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: The Automated Gate Stops the Push
&lt;/h3&gt;

&lt;p&gt;The REST Assured contract test for pagination is already in the suite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;verifyPaginationContract&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;RestAssured&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;given&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"size"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/v1/users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"currentPage"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hamcrest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users.size()"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hamcrest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;greaterThan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer runs this locally. Because the code now treats &lt;code&gt;page=1&lt;/code&gt; as the second page, the API returns an empty list and &lt;code&gt;currentPage: 2&lt;/code&gt;. The assertion fails in under five seconds. The branch stays local. The PR does not open.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Postman Makes the Bug Visible
&lt;/h3&gt;

&lt;p&gt;The developer imports the current &lt;code&gt;/v3/api-docs&lt;/code&gt; into Postman, fires a manual request with &lt;code&gt;page=1&lt;/code&gt;, and reads the raw response:&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;"currentPage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalPages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"users"&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="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;The defect is immediately visible. No stack trace reading. No guesswork. Passing &lt;code&gt;page=1&lt;/code&gt; yields &lt;code&gt;currentPage: 2&lt;/code&gt;. The index shift is confirmed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Resolution Decision
&lt;/h3&gt;

&lt;p&gt;At this point the developer makes an explicit, documented decision rather than a silent one.&lt;/p&gt;

&lt;p&gt;If it was an accidental bug, fix the index mapping back to 1-based in the controller, re-run the REST Assured test until it is green, and push clean code. The pipeline never sees the regression.&lt;/p&gt;

&lt;p&gt;If it was an intentional requirement change, update the REST Assured assertion to &lt;code&gt;equalTo(0)&lt;/code&gt;, refresh the Postman collection to align, and commit the code and test change together in the same PR. The change is visible, traceable, and reviewed.&lt;/p&gt;

&lt;p&gt;The critical difference from the broken workflow is that the decision cannot happen silently. It is forced into the open at the developer's desk, not discovered in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Gives You
&lt;/h2&gt;

&lt;p&gt;REST Assured becomes an objective, automated gate. It does not care about developer intent. It verifies the contract and reports pass or fail.&lt;/p&gt;

&lt;p&gt;Postman stays a flexible, interactive debugging surface. It is no longer a documentation artifact that drifts. It is a live mirror of the running code.&lt;/p&gt;

&lt;p&gt;Both tools operate from the same definitions, derived in real time from the active application. There is no synchronization step to forget, and there is no version of the spec living outside the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;This loop works cleanly on a single developer's machine. The feedback is fast, the gate is reliable, and the contract is grounded in the actual code.&lt;/p&gt;

&lt;p&gt;But what happens when 50 developers are all pushing to the same repository? What stops one of them from simply updating the REST Assured assertion to match their bug and pushing a green build?&lt;/p&gt;

&lt;p&gt;That is the governance problem. That is exactly what Part 2 covers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39"&gt;Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Zero-Drift API Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-zero-drift-api-series-stop-trusting-a-green-build-you-cant-explain-k56"&gt;Intro: The Zero-Drift API Series: Stop Trusting a Green Build You Can't Explain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 1: A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39"&gt;Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg"&gt;Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m"&gt;Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>java</category>
      <category>springboot</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Zero-Drift API Series: Stop Trusting a Green Build You Can't Explain</title>
      <dc:creator>Prasad MK</dc:creator>
      <pubDate>Sat, 30 May 2026 17:20:26 +0000</pubDate>
      <link>https://dev.to/prasadmk/the-zero-drift-api-series-stop-trusting-a-green-build-you-cant-explain-k56</link>
      <guid>https://dev.to/prasadmk/the-zero-drift-api-series-stop-trusting-a-green-build-you-cant-explain-k56</guid>
      <description>&lt;h2&gt;
  
  
  The Zero-Drift API Series
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Stop Trusting a Green Build You Can't Explain (Introduction)
&lt;/h2&gt;

&lt;p&gt;There is a specific kind of production incident that hurts more than the others.&lt;/p&gt;

&lt;p&gt;Not the kind where the stack trace is obvious. The kind where the build was green, the tests passed, and the code review looked clean, and yet something that &lt;em&gt;used to work&lt;/em&gt; silently stopped working for a downstream team, a frontend client, or a mobile app. No alarm. No contract violation flagged. Just a broken assumption that traveled all the way to production dressed as a passing test.&lt;/p&gt;

&lt;p&gt;That is the &lt;strong&gt;drift problem&lt;/strong&gt;. And it is not a testing problem. It is a governance problem.&lt;/p&gt;

&lt;p&gt;This four-part series is a practical engineering framework for teams running Spring Boot REST APIs who want deterministic confidence, not just green builds, across every layer of their delivery pipeline: from a solo developer's local machine, up through a large distributed team, through AI-assisted development, and all the way to autonomous AI agents writing and merging code.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Are Solving
&lt;/h2&gt;

&lt;p&gt;The root cause is structural, not cultural. When &lt;strong&gt;Postman lives in one silo and REST Assured lives in another&lt;/strong&gt;, teams get two independent descriptions of the same API that drift apart over time. The automated tests stop reflecting reality. The manual tests stop reflecting the code. And the first person to notice is usually a downstream consumer, in production.&lt;/p&gt;

&lt;p&gt;Layered on top of that: &lt;strong&gt;anyone can rewrite a failing test to make it pass&lt;/strong&gt;. A pagination index shifts from 1-based to 0-based, the assertion gets quietly updated to match, CI goes green, and three downstream clients break on the next deploy. The test did not catch the regression. The test &lt;em&gt;became&lt;/em&gt; the regression.&lt;/p&gt;

&lt;p&gt;Scale that to 50+ developers, add AI code generation tools that hallucinate payload keys and rewrite tests to cover their own mistakes, then add autonomous AI agents triggering pull requests, and the problem compounds fast.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Four-Part Framework
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 1: A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot&lt;/strong&gt;&lt;br&gt;
The foundation. Establishing the Spring Boot application itself as the single source of truth via its live OpenAPI spec, so Postman and REST Assured consume identical definitions, eliminating drift at the individual developer loop before a line of code reaches the pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams&lt;/strong&gt;&lt;br&gt;
The governance layer. What happens when 50+ developers are all touching the same codebase and one intentional change silently redefines a contract for everyone else. API versioning, &lt;code&gt;CODEOWNERS&lt;/code&gt;, and the architectural choice between "never touch old tests" (which causes test rot) and "freely modify tests" (which kills downstream trust).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor&lt;/strong&gt;&lt;br&gt;
The AI-assisted development layer. How developers using Cursor, Copilot, or LLMs can ground their AI tools in the live local spec, eliminating hallucinated payload keys, automating test generation, and closing the feedback loop when AI-generated code silently breaks a contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents&lt;/strong&gt;&lt;br&gt;
The enforcement layer. When AI agents are autonomously writing code and opening PRs, you cannot rely on them remembering rules. Pre-commit hooks, &lt;code&gt;.ai-rules.json&lt;/code&gt; constraint files, and a Coder/Auditor multi-agent pattern that treats the AI like an untrusted contributor, with deterministic, programmatic rails.&lt;/p&gt;




&lt;p&gt;Each part builds on the last. You can apply Part 1 today, in isolation. Parts 2 through 4 progressively harden that foundation for teams at scale.&lt;/p&gt;

&lt;p&gt;The goal throughout is the same: &lt;strong&gt;the build should be green because the contract is intact, not because someone updated the assertion.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's start local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://dev.to/prasadmk/a-guide-to-stop-breaking-merges-unifying-postman-and-rest-assured-in-spring-boot-42fg"&gt;Part 1 -&amp;gt; A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Zero-Drift API Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-zero-drift-api-series-stop-trusting-a-green-build-you-cant-explain-k56"&gt;Intro: The Zero-Drift API Series: Stop Trusting a Green Build You Can't Explain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/a-guide-to-stop-breaking-merges-unifying-postman-and-rest-assured-in-spring-boot-42fg"&gt;Part 1: A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/who-approved-this-change-managing-api-contracts-and-test-rot-in-large-engineering-teams-39"&gt;Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/the-ai-superpower-how-vibe-coders-use-openapi-as-a-semantic-anchor-45kg"&gt;Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/prasadmk/locking-down-the-pipeline-enforcing-contract-integrity-against-autonomous-ai-agents-m5m"&gt;Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vibecoding</category>
      <category>testing</category>
      <category>agents</category>
      <category>java</category>
    </item>
    <item>
      <title>Rescuing a Broken Master from a Tag, Cleanly Through a PR</title>
      <dc:creator>Prasad MK</dc:creator>
      <pubDate>Fri, 29 May 2026 06:46:14 +0000</pubDate>
      <link>https://dev.to/prasadmk/rescuing-a-broken-master-from-a-tag-cleanly-through-a-pr-416i</link>
      <guid>https://dev.to/prasadmk/rescuing-a-broken-master-from-a-tag-cleanly-through-a-pr-416i</guid>
      <description>&lt;p&gt;It happens on real teams. Someone merges the wrong thing, a dependency update silently breaks the build, or a hotfix lands without a proper review. Whatever the reason, &lt;strong&gt;master is broken and you still have work to ship.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The good news: if you have a working release tag, you have everything you need. This guide walks through the full recovery arc, branching from the tag, adding new code on top, and raising a clean pull request that merges back to master with no surprise conflicts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why branching from the tag is the right move
&lt;/h2&gt;

&lt;p&gt;The instinct is often to "fix master directly." Resist it. You do not know the full extent of what broke, and working on master violates the PR policy anyway. The tag is a precise snapshot of code that worked. Treating it as your new baseline keeps every subsequent decision clean and reviewable.&lt;/p&gt;

&lt;p&gt;The strategy has three phases: &lt;strong&gt;establish&lt;/strong&gt; a branch at the good commit, &lt;strong&gt;build&lt;/strong&gt; your new work on top of it, then &lt;strong&gt;reconcile&lt;/strong&gt; the divergence before raising a PR so the reviewer sees nothing but your intentional changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 1: Create a branch from the tag
&lt;/h2&gt;

&lt;p&gt;This single command is the foundation of everything that follows. It tells Git to set your new branch's starting commit to exactly where &lt;code&gt;v1.3&lt;/code&gt; points, not to the current tip of master.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the branch rooted at the v1.3 tag&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; fix/restore-from-v1.3 v1.3

&lt;span class="c"&gt;# Push it to Bitbucket so the team can see it&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin fix/restore-from-v1.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before writing a single line of new code, take a moment to understand exactly what diverged. This pays off when you reconcile later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# What commits exist on master that are not in v1.3?&lt;/span&gt;
git log v1.3..origin/master &lt;span class="nt"&gt;--oneline&lt;/span&gt;

&lt;span class="c"&gt;# What do those changes actually look like?&lt;/span&gt;
git diff v1.3 origin/master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; The diff above tells you exactly which files the broken commits touched. Those are the files you will need to consciously handle when you reconcile later. Keep a mental note of them.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Phase 2: Add your new work on top
&lt;/h2&gt;

&lt;p&gt;You are now on a stable foundation. Build your features or fixes as normal, with one discipline: commit small and commit often. This is not just good practice in general, it becomes essential when you need to explain your PR to a reviewer who is looking at a branch that diverges from a broken master.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Work on your files normally&lt;/span&gt;
git add src/feature-x.js src/utils.js

&lt;span class="c"&gt;# Write commit messages that will read well in the PR&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: add validation layer for incoming payloads"&lt;/span&gt;

git add tests/feature-x.test.js
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"test: unit coverage for payload validator"&lt;/span&gt;

&lt;span class="c"&gt;# Push regularly : this keeps Bitbucket in sync&lt;/span&gt;
git push origin fix/restore-from-v1.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Phase 3: Reconcile before you raise the PR
&lt;/h2&gt;

&lt;p&gt;This is the step most guides skip or handle lazily. If you raise a PR now, Bitbucket will show conflicts because your branch started at &lt;code&gt;v1.3&lt;/code&gt; and master has moved (to broken commits) since then. The reviewer gets a noisy, confusing diff.&lt;/p&gt;

&lt;p&gt;The correct approach is to resolve all of this &lt;strong&gt;locally on your branch&lt;/strong&gt; before the PR goes up. Bitbucket then shows a clean diff of exactly what you intended to change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Pull master's current state into your branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make sure you have the latest remote state&lt;/span&gt;
git fetch origin

&lt;span class="c"&gt;# While ON your fix branch, merge master in&lt;/span&gt;
git checkout fix/restore-from-v1.3
git merge origin/master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Resolve conflicts, keeping your code
&lt;/h3&gt;

&lt;p&gt;Git will flag every file where your branch and the broken master diverge. For each conflict, you decide: keep your code, keep master's code, or blend them. In a recovery scenario, you almost always want to keep your branch's version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Option A: Accept your branch's version for a specific file&lt;/span&gt;
git checkout &lt;span class="nt"&gt;--ours&lt;/span&gt; src/api/handler.js
git add src/api/handler.js

&lt;span class="c"&gt;# Option B: Open the file manually, remove conflict markers,&lt;/span&gt;
&lt;span class="c"&gt;# edit to the state you want, then stage it&lt;/span&gt;
&lt;span class="c"&gt;# &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD  (your branch)&lt;/span&gt;
&lt;span class="c"&gt;# =======       (divider)&lt;/span&gt;
&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; origin/master  (master)&lt;/span&gt;
git add src/api/handler.js

&lt;span class="c"&gt;# Once all conflicts are resolved, commit the merge&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"merge: absorb origin/master, keep v1.3 baseline + new features"&lt;/span&gt;

&lt;span class="c"&gt;# Push the resolved branch&lt;/span&gt;
git push origin fix/restore-from-v1.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Watch out with &lt;code&gt;--ours&lt;/code&gt;:&lt;/strong&gt; During a &lt;code&gt;git merge&lt;/code&gt;, "ours" refers to the branch you were on when you ran the merge your fix branch. "Theirs" refers to the branch being merged in master. This is the &lt;strong&gt;opposite&lt;/strong&gt; of how it works during a &lt;code&gt;git rebase&lt;/code&gt;. Keep this straight.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Verify the diff before raising the PR
&lt;/h3&gt;

&lt;p&gt;This is your sanity check. The three-dot diff shows only the commits that exist on your branch but not on master, exactly what the reviewer will see in the PR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Three-dot diff: "what does my branch add beyond master?"&lt;/span&gt;
git diff origin/master...fix/restore-from-v1.3

&lt;span class="c"&gt;# Cross-check: should show ONLY your new intentional changes&lt;/span&gt;
git diff v1.3 HEAD

&lt;span class="c"&gt;# If the output matches your intentions, you are ready to raise the PR&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Raising the pull request in Bitbucket
&lt;/h2&gt;

&lt;p&gt;With the branch pushed and conflicts pre-resolved, the PR creation is straightforward. In Bitbucket, navigate to your repository, go to &lt;strong&gt;Pull Requests&lt;/strong&gt;, and create a new PR from &lt;code&gt;fix/restore-from-v1.3&lt;/code&gt; to &lt;code&gt;master&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Source branch:&lt;/strong&gt; &lt;code&gt;fix/restore-from-v1.3&lt;/code&gt;. Target branch: &lt;code&gt;master&lt;/code&gt;. Bitbucket should now show no merge conflicts because you resolved them on your branch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write a clear PR description.&lt;/strong&gt; Mention that the branch was rooted at tag &lt;code&gt;v1.3&lt;/code&gt; due to the broken master state. Reviewers need this context to evaluate the diff correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Link the relevant issue or Jira ticket&lt;/strong&gt; if your team uses one. This anchors the PR to business context and keeps the audit trail intact.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The reviewer sees a clean, intentional diff.&lt;/strong&gt; No broken master code, no spurious conflict markers. Just your new work on top of the last stable release.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What happens to master after the merge
&lt;/h2&gt;

&lt;p&gt;When the PR merges, master will contain: the complete history up to &lt;code&gt;v1.3&lt;/code&gt;, the merge commit that absorbed the broken state, and your new feature commits on top. The broken commits are still in the history, they are not erased, but they are effectively neutralized because your branch's code takes precedence in the final snapshot.&lt;/p&gt;

&lt;p&gt;This is the cleanest outcome achievable under a PR-only policy. You did not force-push, you did not rewrite shared history, and every change that landed in master went through a review.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on force-pushing master:&lt;/strong&gt; Some guides suggest resetting master to the tag and force-pushing. That works in a solo repo but is destructive on a shared branch, it rewrites history that other developers may have already pulled. The approach in this guide avoids that entirely.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Quick reference: the full command sequence
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Branch from the working tag&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; fix/restore-from-v1.3 v1.3
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin fix/restore-from-v1.3

&lt;span class="c"&gt;# 2. Understand the divergence (read-only, safe to run anytime)&lt;/span&gt;
git log v1.3..origin/master &lt;span class="nt"&gt;--oneline&lt;/span&gt;
git diff v1.3 origin/master

&lt;span class="c"&gt;# 3. Do your work, commit often&lt;/span&gt;
git add &amp;lt;files&amp;gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: your message here"&lt;/span&gt;
git push origin fix/restore-from-v1.3

&lt;span class="c"&gt;# 4. Absorb master into your branch (pre-resolve before PR)&lt;/span&gt;
git fetch origin
git merge origin/master
&lt;span class="c"&gt;# resolve conflicts, then:&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"merge: absorb master, keep v1.3 baseline + new features"&lt;/span&gt;
git push origin fix/restore-from-v1.3

&lt;span class="c"&gt;# 5. Verify the diff is clean&lt;/span&gt;
git diff origin/master...fix/restore-from-v1.3

&lt;span class="c"&gt;# 6. Raise the PR in Bitbucket: fix/restore-from-v1.3 -&amp;gt; master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The principle worth keeping
&lt;/h2&gt;

&lt;p&gt;Tags are not just release markers. They are &lt;strong&gt;named, immutable recovery points&lt;/strong&gt;. Any time master becomes unstable and a working tag exists, you have a clean path back, without rewriting shared history, without bypassing your PR policy, and without making your reviewer's job harder than it needs to be. The work is in the reconciliation step. Get that right, and the merge is uneventful.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>git</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
