Skip to content

Platform Engineering with Git

Lesson 8 of 8Advanced17 min readGit for DevOps & Infrastructure · DevOps RepositoriesVerified: GitHub Actions, rulesets and template documentation, September 2026

A platform team’s real product is not the infrastructure. It is the answer to “how do I get an environment”, and how quickly a developer can act on it without asking anybody.

Every technique in this pillar has been building toward that. This lesson assembles them into something a team can actually operate — and its central claim is that the interface should be a repository and a pull request, because that is an interface developers already know and a platform team can govern.

Self-service through Git

A vertical sequence: a developer request; a repository or template providing the shape; a pull request; platform policy evaluating it; reusable automation running; infrastructure provisioned; GitOps reconciling; and a working environment.

Developer requestI need an environmentRepository / templateThe shape, providedPull requestThe interface — reviewablePlatform policyGuardrails, enforcedReusable automationOwned by the platform teamInfrastructure provisioningTerraform, or an operatorGitOps reconciliationContinuousEnvironmentWorking, and traceable

A pull request is the API. A developer describes what they want by changing a file. The platform evaluates, provisions and reconciles.

Why this beats a ticket: it is self-service, it is reviewable, it produces a history, and it scales — a platform team of four can serve a hundred developers because the interface does not require the team’s attention on every request.

Why it beats a bespoke portal, for most teams: it needs no new software, developers already have the skills, and the audit trail comes free. A portal is a legitimate addition later — a form is genuinely friendlier than a YAML file for somebody’s first request — and it should sit on top of the repository interface rather than replacing it. A portal that opens a pull request keeps everything inspectable and debuggable; one that writes directly to a database has become a system nobody outside the platform team can reason about, and it is the point at which the platform stops being auditable by its own consumers.

The central idea, and it is frequently misunderstood as a restriction.

A golden path is the well-supported way to do a common thing. Deploying a web service, adding a database, exposing an endpoint. It works, it is documented, it is monitored, and the platform team supports it.

It is a default, not a mandate. A team with a genuinely different requirement should be able to leave the path. What they lose is support, and that is the honest trade.

What makes a path golden:

It is faster than the alternative. If following the path is slower than doing it yourself, nobody follows it, and no amount of policy fixes that.

It is documented in one place, current, with an example somebody can copy.

It is supported. When it breaks, the platform team fixes it.

It carries the guardrails. Following the path means getting the security defaults, the monitoring, the resource limits and the backup configuration without thinking about them.

It is versioned. Teams adopt improvements deliberately rather than being changed underneath.

The failure mode is a paved path with no pavement: a documented approach that does not work, or that the platform team no longer maintains. That is worse than no path, because people follow it and then discover they are alone.

What the platform offers, as Git artifacts

Section titled “What the platform offers, as Git artifacts”

Everything a platform provides can be a versioned artifact in a repository.

OfferingArtifact
A new serviceA repository template
CI/CDA reusable workflow, pinned by tag
InfrastructureA versioned module
DeploymentA Kustomize base or a Helm chart
GuardrailsPolicies and rulesets
Standard componentsCharts consumed from a registry
Operational knowledgeRunbooks in the repository
Environment provisioningA pull request against a config repository

The common property: versioned, pinned, and adopted deliberately. A platform whose offerings change underneath consumers is one teams stop trusting, and the mechanism is the same as everywhere in this pillar — tags, not branches.

The first thing a new service touches.

A template should produce something that works. A repository created from it should build, test, deploy to development and be observable, without the team writing anything but application code.

What belongs in one:

A minimal working application, or a clear placeholder. CI calling the platform’s reusable workflow, pinned. A Dockerfile following the conventions. Deployment configuration referencing the standard base. A CODEOWNERS skeleton. A README explaining what to change first. Issue and pull request templates. A .gitignore covering the language and the tools.

What does not belong: anything specific to one team, anything that will be wrong in most cases, and elaborate configuration a team must delete.

The template’s limitation: it applies at creation and never again. A repository created eighteen months ago has the template as it was then, and nothing updates it.

Closing that gap needs one of: a scheduled check reporting which repositories are missing an expected file, automated pull requests from a central definition, or organisation-level rulesets for the parts that must hold. The check is cheapest and reports rather than fixes; the pull requests are more work and actually converge things.

The mechanism that stops N repositories having N divergent pipelines.

# In a service repository — three lines
jobs:
ci:
uses: example-org/workflows/.github/workflows/service-ci.yml@v4.2.0
with:
language: node
node-version: "22"
secrets: inherit

Consumers reference a tag, never a branch. @main means the platform team changes every repository’s pipeline with no pull request anywhere to notice it. For a workflow holding credentials, that is a larger exposure than for a module.

Be careful with secrets: inherit. It passes every secret the caller can see. Naming the specific secrets is more typing and much more explicit about what a shared workflow can reach.

The workflow repository needs stricter review than its consumers. It runs with the credentials of every repository that calls it. CODEOWNERS, required reviews, protected tags.

Version it properly. Breaking changes get a major version; consumers move deliberately. A platform team that changes a reusable workflow’s inputs without a version bump has broken every repository that calls it, simultaneously, with no warning.

Provide an upgrade path. Release notes describing what changed for a consumer, and an automated pull request offering the bump.

The pattern that converts a ticket into a pull request.

A developer adds a file describing what they want:

platform/services/payments-api.yaml
name: payments-api
team: payments
tier: production
runtime:
language: node
cpu: 500m
memory: 512Mi
dependencies:
postgres:
size: small
backup: daily
queue:
name: payment-events
environments: [development, staging, production]

The platform turns it into infrastructure. A workflow generating Terraform, or an operator reconciling a custom resource, or a controller watching the directory.

Policy evaluates the request before anything is created. Is the tier permitted for this team? Is the resource request within limits? Does the team own the namespace they named?

A human reviews it — the platform team for a new service, or nobody at all once the pattern is trusted and the policy is good.

What makes this work rather than being a second configuration language:

The schema is small. A file with forty fields is a form, and developers would rather write Terraform.

Defaults do the work. A file should be a handful of lines because everything else is defaulted sensibly.

The generated output is visible. A developer should be able to see what their file produced, or they cannot debug anything.

Escape hatches exist. A team needing something the schema does not express should be able to drop to the underlying tool rather than waiting for the platform team to add a field.

The distinction that determines whether developers experience the platform as help or obstruction.

A guardrail is a default that is hard to get wrong. A module that creates an encrypted database because that is what it does. A base that sets resource limits. A template with the security configuration already in it.

A gate is a check that stops you. A policy denying an unencrypted database.

Both are needed and the ratio matters. A platform that is all gates is one where developers experience it as a series of refusals. One that is all guardrails is one where the determined can still do the wrong thing.

Prefer the guardrail where you can. A developer who gets encryption because the module does it never encounters the policy that would have denied them.

Reserve gates for what must not happen, and make the denial message explain the fix rather than citing a rule number.

The measure: how often does a developer hit a gate? Frequently means the guardrails are wrong — the default should be the thing people want.

A question worth answering explicitly, because platforms fail by serving the wrong audience.

The primary audience is the developer who wants to ship something and does not want to learn Terraform, Kubernetes and your organisation’s conventions to do it. Everything about the interface should be designed for them.

The secondary audience is the engineer who does know those tools and needs to do something the golden path does not cover. They need the escape hatch, and they need it to not feel like a punishment.

The platform team is not the audience. A platform designed around what the platform team finds elegant produces abstractions that make sense to four people. The test is whether somebody outside the team can use it from the documentation.

Security and compliance are stakeholders rather than users. Their requirements become guardrails and policies; they should not become steps a developer performs.

What this means practically: the documentation is written for the primary audience, the escape hatch is documented as legitimately as the golden path, and the platform team resists building things that only they will use.

The signal that the audience is wrong: a platform where the platform team is the heaviest user, and product teams file tickets. That is a toolkit rather than a platform, and it is a common place to end up because building the toolkit is the more interesting engineering.

Most teams inherit an estate rather than starting fresh, and the sequence matters.

  1. Find out what teams actually spend time on. Not what you think they need — ask, and look at what tickets they file. The first golden path should be the thing they do most often.

  2. Build one path, completely. A new service, from repository creation to running in development, working end to end. One path that works beats six that half-work.

  3. Use it yourself for something real. A path the platform team has not used is a path with problems nobody has found.

  4. Get one team to adopt it, and sit with them while they do. What they struggle with is the roadmap.

  5. Measure the time saved, honestly. If it is not faster than what they did before, fix that before building anything else.

  6. Add guardrails to the path, so following it produces the right defaults without anybody thinking about them.

  7. Only then add policy for what must not happen, and make the messages explain the fix.

  8. Add a second path once the first is genuinely used.

The order that fails is building policy first. A platform that starts by telling teams what they cannot do, before offering anything that makes their work easier, spends its first year being resented — and the technical decisions made in that year are rarely the ones that would have been made with real usage to inform them.

The reconciliation layer is what makes the platform’s promises hold rather than being applied once and drifting.

A team’s deployment configuration is in a repository they own or contribute to.

The controller reconciles it continuously, so what the platform provisioned stays provisioned.

Drift is visible, and the platform team knows the estate matches its declarations.

A team can deploy without the platform team, because merging is the deployment.

The platform team retains control through policy and RBAC, not through being in the path of every change.

That last point is the whole argument. A platform team that must approve every deployment is a bottleneck; one that has encoded its requirements as policy and scoped its RBAC correctly can let teams move without giving up the guarantees.

Everything in this design produces a record without anybody building one.

Who requested what — the pull request.

Who approved it — the review, and the environment approval.

What was provisioned — the generated configuration, committed.

When it was applied — the workflow run or the controller’s history.

What is running now — the repository, plus the controller’s status.

Whether anything changed outside the processdrift detection.

The questions this answers cheaply are the ones that are otherwise expensive: why does this resource exist, who decided, and has anything changed since. That is frequently the actual compliance requirement, and it falls out of the design rather than being a separate programme.

Cross-linking Pillar 6, with the same boundary.

Genuinely useful: explaining a plan to a developer who did not write it; explaining a failed reconciliation; drafting a runbook from what a workflow does; generating the first version of a service definition from a description; reviewing configuration against written standards.

Where it must not sit: anywhere that decides. Not as a required check, not gating a deployment, not assessing whether a plan is safe.

The rule from agentic CI/CD holds exactly: deterministic steps decide, AI steps inform.

And the one that matters most here: AI-generated infrastructure changes go through the same plan, the same policy, the same review and the same approval as any other change. Configuration a model wrote is a proposal, and the speed of its production is not evidence of its correctness. A platform whose guardrails hold for human changes and are bypassed for generated ones has guardrails that do not hold.

The numbers that tell you whether it is working, and the ones that mislead.

Time from request to working environment. The headline metric, and the one the platform exists to reduce.

Proportion of changes going through the golden path. Low means the path is slower than the alternative.

Platform team interruptions per week. A platform that scales is one where this falls as adoption rises. Rising means the self-service is not.

How often developers hit a gate. Frequently means the defaults are wrong.

Drift findings. Rising means the process is being bypassed.

Time to onboard a new service. From decision to running in development.

What misleads: the number of modules, templates or policies. Those are inventory. A platform with forty modules that nobody uses is worse than one with six that everybody does, and counting them rewards the wrong thing.

The question worth asking developers directly: what did you do this quarter that would have been easier without the platform? The answers are uncomfortable and more informative than any dashboard.

For a platform, the documentation is not describing the product — it is part of it.

A golden path that is not documented does not exist. A developer who cannot find it will do something else, and that something else is what you will be supporting.

One canonical location. Not a wiki page, a README, a Slack thread and a Confluence space with different versions of the same instructions. The most common platform documentation failure is four half-correct sources.

Write for somebody who has not done it before. The platform team knows the implicit steps and the audience does not.

Include a complete worked example. Not fragments — something somebody can copy and run.

Document the escape hatch as thoroughly as the path. A team leaving the golden path is doing something legitimate, and abandoning them there is how a platform gets a reputation for being restrictive.

Keep runbooks with the thing they describe. A runbook for the deployment pipeline lives with the pipeline.

Test it the way you test anything else: ask somebody who has not used the platform to follow it, and watch without helping. Whatever they hit first is the highest-priority fix, and it is usually a step the author did not know they were performing.

Date the version-sensitive parts. Platform documentation ages, and a reader needs to know whether they are reading something current.

A platform that is a ticket queue. The team is the bottleneck; nothing scales.

A golden path slower than the alternative. Nobody follows it.

A paved path with no pavement. Documented, unmaintained, and people find out alone.

Reusable workflows referenced by branch. Every consumer changes without a pull request.

secrets: inherit by default. A shared workflow reaching everything.

An abstraction that grows to match the underlying tool. Terraform with extra steps.

No escape hatch. Teams with unusual requirements are blocked or fork.

All gates, no guardrails. The platform is experienced as refusals.

Templates with no update mechanism. Repositories frozen at creation.

Measuring inventory rather than adoption. Forty unused modules.

Exempting AI-generated changes from the guardrails. Guardrails that do not hold.

Restraint is most of the skill, because every platform team can build more than it can maintain.

Do not build an abstraction over something teams already understand. A wrapper around kubectl for people who know kubectl is a thing to learn plus a thing to debug.

Do not build for a hypothetical second user. A module with configuration options for a use case nobody has is complexity paid for now against a benefit that may never arrive.

Do not build a portal before the repository interface works. A friendly front end over a broken mechanism is a friendlier way to hit the same problems.

Do not rebuild what the platform you run already does. GitHub, Kubernetes and your cloud provider have features people ignore in favour of building something. Environments, rulesets and admission control are all things teams have reimplemented.

Do not build a service catalogue nobody updates. A catalogue is only useful if it is accurate, and accuracy requires it to be generated from something authoritative rather than maintained by hand.

Do not take ownership of application concerns. A platform that owns each service’s deployment configuration becomes the queue it was built to avoid.

The question to ask before building anything: who asked for this, how often do they need it, and what will it cost to maintain in three years? A platform team’s capacity is its scarcest resource, and every artifact it ships is a permanent commitment.

The corollary: deleting a platform offering nobody uses is real work, and it should be as normal as adding one. A platform accumulating unused modules and stale templates is one where the useful parts are harder to find.

A platform team’s interface is a repository and a pull request. Everything it offers — templates, workflows, modules, policies, reconciliation — is a versioned artifact that teams adopt deliberately. The team’s job is to make the well-supported path the fastest one, and then to stay out of it.

The test of whether it is working: a developer can go from wanting an environment to having one without the platform team being involved, and the platform team is confident that what they got is correct. Both halves matter. Self-service without guardrails is a mess; guardrails without self-service is a queue.

  • The interface is a repository and a pull request — self-service, reviewable, auditable, and it scales
  • A golden path is a default rather than a mandate, and it must be faster than the alternative
  • Every offering is a versioned artifact adopted deliberately: templates, workflows, modules, bases, policies
  • Templates apply at creation only; converging existing repositories needs checks or automated pull requests
  • Reusable workflows are pinned by tag and reviewed more strictly than their consumers
  • Prefer guardrails to gates; frequent gate hits mean the defaults are wrong
  • GitOps is what lets the platform team stay out of the path while keeping its guarantees
  • Measure adoption and interruptions, not inventory
  • AI-generated changes go through the same guardrails as any other

Use disposable repositories.

  1. Write down your platform’s golden path for deploying a new service, as it exists today. Predict: how many steps, and how many require another team?

  2. Time it honestly. From “we want a service” to “it runs in development”.

  3. Build a repository template producing something that builds and deploys to a local environment with no edits.

  4. Extract the CI into a reusable workflow, referenced by tag. Change the workflow on main and confirm consumers do not move.

  5. Tag a new version and open the bump as a pull request in a consumer.

  6. Define a small service schema — under ten fields — and a workflow generating configuration from it. Confirm the generated output is visible to whoever wrote the file.

  7. Add one policy denying something that must not happen, with a message explaining the fix.

  8. Ask a developer outside the platform team to create a service using only the documentation. Watch without helping. Whatever they hit first is the next thing to fix.

This is the last lesson in Pillar 7. You have covered the path from an infrastructure change in a repository, through review and validation, to a running system that continuously reconciles toward what was declared.

The threads that ran through it:

Git is where infrastructure change becomes reviewable. Not where it is stored — where the diff, the plan and the approval come together.

Instructions shape; tools, permissions and policy enforce. True of agents in Pillar 6 and true of platforms here.

Promotion moves something identified and immutable. Rebuilding per environment discards what the lower environment proved.

Reverting a commit restores a declaration, not a system. The most consequential sentence in this pillar.

Drift is a report about your process, not merely a technical difference.

Back to the pillar overview
Lab: ship an infrastructure change through a pull requestPractise the review discipline that makes an infrastructure change reviewable before it is applied.

The GitOps and infrastructure repository templates are in the Professional Toolkit.