🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build an AI YAML Diff Explainer with Claude API

A 200-line Kubernetes manifest diff in a PR review is easy to skim past and miss what actually changed. Build a tool that uses Claude API to explain YAML diffs in plain English before anyone approves the PR.

DevOpsBoysJun 18, 20264 min read
Share:Tweet

Reviewing a raw YAML diff in a pull request is genuinely hard — whitespace changes look identical to meaningful ones at a glance, a single changed value buried in 150 unchanged lines is easy to miss, and "what does this change actually do to the running system" requires mentally simulating the deployment, not just reading text.

Let's build a tool that takes a YAML diff and explains it the way a senior engineer would in a review comment — what changed, why it matters, and what could break.

Step 1: Get a Clean Diff

python
import subprocess
 
def get_yaml_diff(base_ref: str, head_ref: str, file_path: str) -> str:
    result = subprocess.run(
        ["git", "diff", f"{base_ref}...{head_ref}", "--", file_path],
        capture_output=True, text=True
    )
    return result.stdout

Step 2: Build the Explanation Prompt

python
from anthropic import Anthropic
 
client = Anthropic()
 
def explain_yaml_diff(file_path: str, diff: str) -> dict:
    prompt = f"""You are a senior platform engineer reviewing a Kubernetes/Helm 
manifest change in a pull request: {file_path}
 
DIFF:
{diff}
 
Explain this change for a PR reviewer who needs to approve it quickly but 
correctly. Cover:
 
1. SUMMARY: One sentence — what does this change actually do to the running system?
2. SPECIFIC CHANGES: List each meaningful change (ignore pure formatting/whitespace) 
   with the old value, new value, and what field it affects.
3. RISK ASSESSMENT: Could this change cause downtime, a resource crunch, a security 
   gap, or unexpected behavior? Be specific about WHY, not generic.
4. WHAT TO VERIFY: What should the reviewer specifically check before approving 
   (e.g. "confirm the new memory limit is enough based on actual usage metrics")?
 
If the diff is purely cosmetic (reordering, whitespace, comments) with no 
functional change, say so clearly and skip the risk section.
 
Return as JSON with keys: summary, changes (list), risk_level (none/low/medium/high), 
risk_explanation, verification_steps (list)."""
 
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2048,
        messages=[{"role": "user", "content": prompt}]
    )
    
    import json
    return json.loads(response.content[0].text)

Step 3: Post as a PR Comment

python
import requests
import os
 
def post_pr_comment(repo: str, pr_number: int, explanation: dict, file_path: str):
    risk_emoji = {"none": "✅", "low": "🟢", "medium": "🟡", "high": "🔴"}
    
    changes_md = "\n".join(f"- {c}" for c in explanation["changes"])
    verify_md = "\n".join(f"- [ ] {v}" for v in explanation["verification_steps"])
    
    body = f"""## 🤖 AI Diff Explanation: `{file_path}`
 
**Summary:** {explanation['summary']}
 
**Changes:**
{changes_md}
 
**Risk Level:** {risk_emoji[explanation['risk_level']]} {explanation['risk_level'].upper()}
 
{explanation['risk_explanation']}
 
**Before approving, verify:**
{verify_md}
"""
    
    requests.post(
        f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments",
        headers={"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}"},
        json={"body": body}
    )

Step 4: Wire Into CI for Every Manifest Change

yaml
# .github/workflows/explain-yaml-diff.yml
name: Explain YAML Diffs
on:
  pull_request:
    paths:
      - '**/*.yaml'
      - '**/*.yml'
 
jobs:
  explain:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Get changed YAML files
        id: changed
        run: |
          echo "files=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- '*.yaml' '*.yml' | tr '\n' ' ')" >> $GITHUB_OUTPUT
      - name: Explain each changed file
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          for f in ${{ steps.changed.outputs.files }}; do
            python scripts/explain_diff.py \
              --file "$f" \
              --base origin/${{ github.base_ref }} \
              --head HEAD \
              --repo ${{ github.repository }} \
              --pr ${{ github.event.pull_request.number }}
          done

Example Output

For a diff that changes a Deployment's replica count, removes a resource limit, and adds a new env var:

markdown
## 🤖 AI Diff Explanation: deployment.yaml
 
**Summary:** This change reduces replica count from 5 to 2, removes the memory 
limit entirely, and adds a new DATABASE_POOL_SIZE environment variable.
 
**Changes:**
- replicas: 5 → 2 (reduces redundancy and total capacity by 60%)
- resources.limits.memory: "1Gi" → REMOVED (pod can now consume unlimited 
  memory on the node, risking node-level resource starvation for other pods)
- env: added DATABASE_POOL_SIZE=50 (new variable, not previously configured — 
  verify the application actually reads this and that 50 connections is 
  appropriate for the database's max_connections setting)
 
**Risk Level:** 🔴 HIGH
 
Removing the memory limit is the most concerning change here — without a limit, 
a memory leak in this application could consume all available memory on its node, 
affecting other pods scheduled there, not just this deployment. The replica 
reduction from 5 to 2 also removes meaningful redundancy if this is a 
customer-facing service.
 
**Before approving, verify:**
- [ ] Confirm the memory limit removal is intentional, not an accidental deletion
- [ ] If intentional, add a limit back based on actual observed memory usage 
      rather than running unbounded
- [ ] Confirm replica count of 2 still meets availability requirements during 
      a single pod restart or node drain
- [ ] Verify DATABASE_POOL_SIZE=50 doesn't exceed the database's max_connections 
      when multiplied across all replicas and other services sharing that database

This catches exactly the kind of change that's easy to approve quickly because the diff "looks small" — three line changes, but one of them (removing a resource limit) is genuinely risky in a way that's easy to miss when skimming a diff visually rather than reasoning about its operational effect.

Review the manifests themselves with AI too: Build an AI Kubernetes Manifest Code Reviewer

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments