Standing up a new service is never just one pull request. The service gets deployed once, but its metadata gets copied everywhere — and that duplication is where platform drift begins.

Application-Driven Infrastructure — a four-part series:

Part 1: Your Helm Chart Already Knows More Than Terraform Does · Part 2: Generating Terraform from Helm · Part 3: Building a Self-Describing Platform · Part 4: Your Helm Chart Is Really an Abstract Syntax Tree

Standing up a new service on a Kubernetes platform is never just one pull request. The Helm chart is the star of the show, sure — but then come the supporting acts. A Terraform module for the monitoring. An alert policy. A dashboard. A DNS record. A synthetic monitor. A service catalog entry. A documentation page. A runbook. A cost allocation tag. An ownership record living in a spreadsheet nobody wants to admit exists.

The service gets deployed exactly once. Its metadata gets copied everywhere.

That duplication is where drift begins, and drift is what this series is about. Over four articles I’m going to make the case for a pattern I call Application-Driven Infrastructure: treating your application’s deployment metadata as the authoritative source of truth for the operational infrastructure attached to it. Helm is the implementation vehicle here — including, yes, using Helm to generate Terraform, which we’ll do for real in Part 2 — but the idea is bigger than Helm, and by Part 4 we’ll have zoomed all the way out.

First, though, we need to feel the pain.

The Traditional Split

Most Kubernetes platforms treat application deployment and operational infrastructure as two separate worlds, and on paper the split looks perfectly reasonable:

The traditional split — deployment and operational infrastructure treated as separate domains

The traditional split: Helm owns the Kubernetes application, Terraform owns everything operational, and docs own the rest — each configured separately.

There’s nothing wrong with any individual line in that table. Terraform is genuinely good at reconciling cloud resources. Helm is genuinely good at packaging Kubernetes applications. Dashboards belong in dashboard tools.

The trouble is hiding in what all those lines have in common. Every one of those systems needs to know something about the service — its name, its port, its domain, who owns it, how to tell whether it’s healthy. The problem isn’t that they need to know. The problem is that each of them learns it separately.

Where the Drift Begins

If you’ve run a platform for any length of time, you already know how this story goes.

Take a service called wallet. Its facts are written down in a lot of places: the service name appears in the Helm chart, the Terraform module, the dashboard query, the alert condition, the synthetic monitor, the catalog entry, and the docs. So does the namespace. So does the port, the domain, the health endpoint, the owner, the environment list, the criticality tier, and the label schema.

A single service's facts copied across Helm, Terraform, monitoring, alerts, dashboards, DNS, synthetics, the catalog, docs, and cost tags

The drift problem: the service is deployed once, but its facts are copied into every downstream system — and each copy drifts on its own schedule.

Then something changes. wallet becomes wallet-api. Port 8080 becomes 3000. The health endpoint moves from /healthz to /version. The service moves namespaces, or the owning team hands it off, or a new production environment comes online, or somebody finally standardizes the labels.

The Helm chart gets updated, because it has to — the deployment literally won’t work otherwise. The Terraform might get updated. The dashboard query might. The alert threshold might. The docs almost certainly won’t.

And here’s the nasty part: nothing fails loudly. The synthetic monitor keeps cheerfully probing an endpoint that no longer means anything. The dashboard keeps rendering, just filtered on a label that no longer matches any pods. The runbook keeps pointing the 2 AM on-call engineer at a namespace the service left months ago. Every one of those systems is green right up until the moment you actually need it.

It’s the same problem as moving to a new house. Your address is written down at the bank, the DMV, your dentist’s office, and a dozen subscriptions, and no matter how diligent you are, something is still getting mailed to the old place a year later. The address didn’t go stale because anyone was careless. It went stale because it was written down in twelve places to begin with.

Helm Already Knows the Application

Now here’s the thing. Open the chart of any reasonably built service and look at what’s already sitting in there. The chart knows the service name and the release name. It knows the namespace, the image, and the version. It knows the service port and the container port, the liveness and readiness probes, the ingress host and path, the labels and annotations, the per-environment overrides, the HPA behavior, the secret references, and a pile of runtime assumptions besides.

Read that list again, because it’s exactly the metadata every downstream system is starving for. The dashboard needs the name, namespace, and labels. The alert needs the service name and environment. The synthetic monitor needs the domain and the health endpoint. The DNS record needs the host. The catalog needs the owner.

Your Helm chart already knows more about the deployed service than Terraform does. It knows more than the dashboard does. In most shops it knows more than the documentation does, by a comfortable margin.

So here’s the question worth sitting with for a minute: why are we describing the same service again in Terraform, and again in the dashboard, and again in the alerting tool, and again in the service catalog, and again in the docs?

The Key Shift

The shift I’m proposing is small to state and big in consequence: stop treating Helm as only a Kubernetes manifest renderer.

Strip away the templating syntax and a Helm chart is basically two things — a structured description of an application, and a set of instructions for turning that description into artifacts. Today those artifacts happen to be Kubernetes YAML. Nothing about that is fundamental. Helm renders text, and it has no opinion about what kind of text. The same description that produces a Deployment can just as easily drive monitoring configuration, alert policies, dashboards, synthetic checks, DNS metadata, ownership records, catalog entries, documentation stubs, runbooks, and cost allocation tags — anything whose contents are a function of the service’s facts.

Looked at that way, the chart stops being just a deployment mechanism. It becomes the service’s operational identity.

Application-Driven Infrastructure

Let’s give the pattern a name and a definition:

Application-Driven Infrastructure is a platform engineering pattern where application deployment metadata becomes the authoritative source for the infrastructure, observability, documentation, and governance artifacts attached to that application.

Two words in that definition are doing heavy lifting: attached to. This is emphatically not “put all infrastructure in Helm,” and if that’s the sentence you walk away with, I’ve failed. The pattern applies to infrastructure that exists because the application exists — the stuff whose entire reason for being is a particular service.

What Belongs and What Doesn’t

The boundary I use is a lifecycle test: if an artifact should live and die with the service, generate it from the service. If it shouldn’t, don’t.

The lifecycle test — application-adjacent infrastructure generated from the app model, foundational infrastructure managed independently

The lifecycle test: if an artifact should live and die with the service, generate it from the service; if it shouldn’t, leave it independently managed.

Good candidates are the things that depend directly on application metadata: dashboards, alerts, synthetic monitors, APM configuration, DNS records, service catalog entries, ownership and cost tags, runbook stubs, PagerDuty services, Prometheus rules, Grafana dashboards, New Relic monitors. Delete the service and every one of those should disappear with it.

Bad candidates are the foundations: VPCs, Kubernetes clusters, cloud accounts, base networking, IAM foundations, shared databases, cluster bootstrap systems. Delete a service and the VPC had better still be there. That infrastructure has its own lifecycle, its own owners, and its own change cadence, and it should stay independently managed — in plain old Terraform, or whatever you use today.

If a given resource has you genuinely stumped — a service-specific database is the classic edge case — the lifecycle test still works. Ask whether decommissioning the service should decommission the resource. If the answer makes you nervous, keep it out of the generated set.

Standards Become Defaults

Here’s why platform teams specifically should care about this pattern.

Every platform team has a wiki page somewhere titled something like “Production Readiness Checklist.” Every service should have a dashboard. Every service should have alerts. Every service should have an owner, a runbook, a synthetic check, and cost tags. And every platform team knows exactly how well that page is followed — which is to say, unevenly, decaying over time, and audited roughly once a year in a burst of guilt.

Application-Driven Infrastructure moves those standards out of documentation and into generation. Instead of asking every team to remember six operational artifacts, the platform ensures every service gets them automatically, because they fall out of the chart the team was going to write anyway. Nobody has to remember the checklist, because the checklist is the render step.

Standards stop being requests and become defaults. This is huge! A standard you have to remember is a standard that erodes. A standard that’s generated is one you’d have to actively work to avoid.

A fair question before you build any of this: at what scale does it pay off? If you’re running three services, honestly, don’t — the checklist in your head is cheaper than the machinery, and drift at that size is a five-minute fix. The pattern earns its keep somewhere around the point where new services arrive faster than the platform team can hand-audit the old ones, or where the same fact lives in more than a handful of systems. Below that line, this series is a preview of a problem you’ll have later. Above it, you already know exactly which stale dashboard I’m talking about.

What’s Next

So far this has all been argument, and arguments are cheap. In the next article in this series we’ll get our hands dirty: rendering actual Terraform out of an actual Helm chart, wiring the render step into CI/CD, keeping the Terraform state layout sane, and walking through the validation layers and production lessons that make the whole thing safe enough to trust. Your Helm chart already knows more than Terraform does — next time, we put that knowledge to work.