· 7 min read

Building ChartImpact: A Kubernetes-Aware Helm Chart Diff Tool


When releasing a new version of a Helm chart, the most critical question isn’t “what changed?” it’s “will this break services running the previous version?” After watching teams hesitate to upgrade because they couldn’t confidently answer that question, I built a tool to solve it.

The Problem #

You’re maintaining a Helm chart used by multiple teams across different environments. You’re about to release v2.0, but some services are still on v1.8. Before releasing, you need to understand: what’s the actual impact? Will upgrading require configuration changes? Will it trigger pod restarts? Are there breaking changes hiding in the diff?

Standard diff tools show you something like this:

  spec:
+   selector:
+     app: api
+     version: v2
-   selector:
-     app: api
    ports:
-   - port: 8080
+   - port: 8000
---
  ingress:
    paths:
-   - path: /api/v1
+   - path: /api/v2
      backend:
        service:
-         port: 8080
+         port: 8000
  labels:
-   version: "1.8"
+   version: "2.0"

Sure, there are changes but which ones will cause an outage if you miss them? The Service selector now includes version: v2 pods running v1.8 won’t match and will lose all traffic immediately. The port change from 8080 to 8000 means existing services trying to connect will fail. The ingress path change from /api/v1 to /api/v2 breaks every client using the old endpoint. These are deployment-breaking changes, buried among cosmetic label updates. Tools like dyff give you prettier output, but they still treat everything as text. They don’t understand that a selector change will instantly disconnect your pods from the Service.

When you’re releasing a new chart version, you need to know: Which changes are breaking? Which ones will cause immediate outages? Which are safe to roll out gradually? You need a tool that understands Kubernetes semantics and tells you: “This selector change will break traffic routing” or “This port change requires coordinated deployment across all services.”

What I Built #

ChartImpact is a Helm chart diff tool designed to answer the backward compatibility question. It compares any two chart versions (tags, branches, commits) and tells you what’s changing in terms that matter for your deployment decisions:

  1. Classifies changes semantically - Identifies changes as container.image, workload.replicas, resources.cpu, etc., not just YAML diffs
  2. Assesses compatibility impact - Categorizes changes by availability risk, security implications, and rollout requirements
  3. Surfaces breaking changes - High-impact changes (images, replicas, security contexts) are highlighted; safe changes (labels, annotations) are de-emphasized

It’s built with Go for the backend (using the official Helm SDK) and Next.js for the frontend. The core innovation is the internal diff engine that understands Kubernetes resource semantics and helps teams make confident upgrade decisions.

The Semantic Classification System #

The heart of ChartImpact is its ability to understand what is changing, not just that something changed. Here’s how it works:

// classifySemanticType determines the semantic type based on the path
func classifySemanticType(path string) string {
    if strings.Contains(path, ".image") {
        return "container.image"
    }
    if strings.Contains(path, ".replicas") {
        return "workload.replicas"
    }
    if strings.Contains(path, ".resources.limits.cpu") || 
       strings.Contains(path, ".resources.requests.cpu") {
        return "resources.cpu"
    }
    // ... more classifications
}

// determineImportance assigns importance level
func determineImportance(path string, semanticType string) string {
    switch semanticType {
    case "container.image", "workload.replicas", "security.context":
        return "high"  // Critical for availability/security
    case "resources.cpu", "resources.memory", "container.env":
        return "medium"  // Important but not critical
    case "metadata.label", "metadata.annotation":
        return "low"  // Cosmetic changes
    }
}

This classification system assigns every change a semantic type, importance level, and category (workload, networking, security, resources, etc.). Changes also get flags like "runtime-impact", "rollout-trigger", or "scaling-change" to indicate their operational implications.

How the Diff Engine Works #

ChartImpact’s diff engine operates at the field level, not the line level:

func (e *Engine) Compare(manifest1, manifest2 string) (*DiffResult, error) {
    // 1. Parse YAML manifests into structured Resources
    resources1, _ := ParseManifests(manifest1)
    resources2, _ := ParseManifests(manifest2)
    
    // 2. Match resources by identity (apiVersion, kind, name, namespace)
    map1 := GetResourcesByKey(resources1)
    map2 := GetResourcesByKey(resources2)
    
    // 3. Deterministic iteration (sorted keys for reproducibility)
    sortedKeys := GetSortedKeys(keysMap)
    
    // 4. Compare each resource field-by-field
    for _, key := range sortedKeys {
        if exists1 && !exists2 {
            // Resource removed
        } else if !exists1 && exists2 {
            // Resource added
        } else {
            // Resource modified - deep field comparison
            changes := e.compareResources(resource1, resource2)
            
            // Add semantic metadata to each change
            for i := range changes {
                changes[i].SemanticType = classifySemanticType(changes[i].Path)
                changes[i].Importance = determineImportance(changes[i].Path, changes[i].SemanticType)
            }
        }
    }
    
    return result, nil
}

The engine parses Kubernetes manifests into normalized structures, matches resources by their identity, and performs field-level comparisons. Each change gets enriched with semantic metadata that drives the risk assessment in the UI.

The Result #

The output is structured JSON that’s both human-readable and machine-parseable:

{
  "resources": [{
    "identity": {
      "apiVersion": "v1",
      "kind": "Service",
      "name": "api"
    },
    "changeType": "modified",
    "changes": [{
      "op": "add",
      "path": "spec.selector.version",
      "before": null,
      "after": "v2",
      "semanticType": "service.selector",
      "importance": "high",
      "flags": ["traffic-impact", "breaking-change", "availability-risk"]
    }, {
      "op": "replace",
      "path": "spec.ports[0].port",
      "before": 8080,
      "after": 8000,
      "semanticType": "service.port",
      "importance": "high",
      "flags": ["networking-change", "breaking-change", "requires-coordination"]
    }]
  }]
}

The frontend uses this structured output to build an interactive explorer where you can filter by importance, search for specific changes, and see risk summaries at a glance.

What I Learned #

Building ChartImpact taught me three key lessons:

  1. Determinism matters - The diff engine produces identical output for identical inputs by normalizing data structures and using sorted iteration. This makes results cacheable and reproducible.

  2. Domain knowledge is leverage - Understanding Kubernetes resource semantics allowed me to build something far more useful than a generic diff tool. Changes to fields like spec.replicas have real availability implications, while changes to metadata.annotations do not. Encoding that distinction is what makes the diff meaningful.

  3. Native tooling wins - Using the Helm Go SDK directly instead of shelling out to CLI tools made the engine fast, reliable, and much easier to test. No subprocess overhead, no parsing CLI output, no version compatibility issues.

What’s Next #

This is the first in a series of posts about ChartImpact. Coming up:

  • Deep dive into the diff engine - How it parses and normalizes Kubernetes manifests
  • Risk assessment algorithms - How changes are categorized and scored
  • Frontend architecture - Building an interactive diff explorer with Next.js

ChartImpact is open source under MIT • Built with Go + Next.js

GitHub Stars License Go Report Card
Docker Contributors Last Commit
Issues PRs Welcome Release

Quick start: git clone https://github.com/dcotelo/ChartImpact && docker compose up

Check it out, try it with your charts, and let me know what you think. Contributions welcome!