Skip to content

Delivery

Delivery is the everyday path from “I changed something” to “the system is safely running it.” In ATO, that path matters because each step creates evidence: the PR explains intent, review proves separation of duties, checks prove verification, release tags identify the artifact, environment approvals prove authorization, and ArgoCD records what actually reached the cluster.

If you are new to this workflow, read this page as the default route. Do not start with Kubernetes, ArgoCD, or a production dashboard. Start with the branch, PR, review, checks, release, and evidence trail.

Delivery model

flowchart LR
subgraph HumanWork["Human work"]
OpenPR["Open PR"] --> ReviewMerge["Review and merge"]
ReviewMerge --> Promote["Promote to production"]
end
subgraph AutoWork["Automation"]
Checks["CI checks and build"]
TestSync["Test auto-sync"]
end
OpenPR -->|on push| Checks
Checks -->|if passing| ReviewMerge
ReviewMerge -->|on merge| TestSync
TestSync -->|if healthy| Promote
style Checks fill:#fef3c7,stroke:#b45309,color:#451a03
style Promote fill:#dcfce7,stroke:#16a34a,color:#14532d

main is the only long-lived code branch. We do not use staging or prod branches because environments are promotion targets, not alternate source histories. The live system follows Git through ArgoCD; if a runtime change is not represented in Git, it is drift to remove or reconcile.

The simple rule is this: a safe change should be explainable from Git history alone. You should be able to answer who changed it, who reviewed it, which checks passed, which image was built, who promoted it, and what ArgoCD synced without opening a shell on a production node.

Branch names

Use prefix/short-kebab-slug. The prefix tells reviewers what kind of risk to expect before they open the diff.

PrefixUse forExample
feat/New user-visible behaviorfeat/language-selector
fix/Bug fixes and remediationfix/duplicate-submission
security/Auth, secrets, hardening, or vulnerability fixessecurity/rotate-signing-key
infra/Helm, ArgoCD, Kubernetes, cloud, or runnersinfra/preview-namespaces
ci/GitHub Actions, checks, or release automationci/add-commitlint-check
build/Build system and packagingbuild/container-cache
docs/Documentation-only changesdocs/ato-delivery
test/Test-only changestest/add-profile-regression
refactor/Behavior-preserving structure changesrefactor/form-state
perf/Performance improvementsperf/query-cache
style/Formatting and style-only changesstyle/lint-cleanup
revert/Reverting a prior changerevert/bad-cache-change
chore/Routine maintenancechore/update-dependencies

What to notice: the branch name is not decoration. It helps route review, automate governance, and search audit history later.

Emergency work still uses fix/ or revert/ plus an incident reference in the PR. Do not use hotfix/ or emergency/; those names imply a second path around the controls.

Conventional commits and release impact

Conventional Commits are the grammar for PR titles and squash commits. They are not just style preferences; release automation reads them to decide whether a release is a patch, minor, major, or non-release change.

Use this shape:

type(optional-scope): short imperative subject
optional body that explains why
optional footer with refs, incident ids, or breaking-change notes

The PR title becomes the squash commit on main, so write the title as the final changelog line. It should describe the result, not the task you performed.

TypeUse forRelease impact
featNew user-visible behaviorMinor
fixBug fixPatch
securitySecurity hardening or vulnerability remediationPatch
perfPerformance improvementPatch
feat!, fix!, security!Backward-incompatible changeMajor
docs, test, ci, style, chore, build, infra, refactorNon-product or internal workNone by itself
revertReverse a previous changeMatches the reverted impact

Good one-line examples:

feat(profile): add language selector
fix(api): reject duplicate permit submissions
security(auth): rotate session signing key
infra(argocd): add preview namespace controls
docs(ato): clarify production promotion evidence

Use a body when the reviewer needs context that the title cannot hold:

feat(profile): add language selector
Allow residents to choose a portal language from the header.
Default remains English for compatibility with existing bookmarks.
Refs: ADR-070
Closes: PROJ-2211

Use ! or a BREAKING CHANGE: footer when callers, data, APIs, or operational behavior must change:

feat(auth)!: migrate login flow to OIDC bearer assertions
The previous cookie-based session format is removed.
Clients must use the token exchange endpoint before this release.
BREAKING CHANGE: API session token exchange now requires the target client id.
Refs: SEC-4412

Avoid vague titles like updates, fix stuff, work in progress, or changes from review. Those titles erase release meaning and make incident review harder.

Pull requests

A PR is the controlled gate between work and main. The reviewer should understand what changed, why it changed, how to verify it, and what could go wrong before reading every file.

Every PR should include:

  • what changed
  • why the change is needed
  • how it was verified
  • screenshots, preview links, or logs when behavior is visible
  • risk, rollout, and rollback notes when the change can affect users
  • ticket, advisory, incident, or ADR references when they exist

Use this body shape when you are unsure what to write:

## What changed
- Added the language selector to the profile page.
- Stored the selected language in the existing profile settings record.
## Why
Residents need to choose a preferred language before the translated notification work ships.
## Verification
- Ran the focused unit tests for profile settings.
- Checked the profile page in the preview environment.
- Confirmed the default remains English for existing users.
## Risk and rollout
- Feature flag stays off by default.
- Rollout requires Product approval before exposure.
- Safe recovery is turning `profile.language-selector` off.

The PR body is not busywork. It is the handoff contract for review, future maintenance, and audit evidence.

Review expectations

Review protects main. It is where we confirm the change is correct, secure, reversible, and ready to merge.

Every PR needs:

  • focused scope with no unrelated cleanup
  • automated checks, tests, and scans where relevant
  • at least one approving non-author review
  • resolved blocking comments before merge
  • squash merge only

Reviewers check the highest-risk questions first:

QuestionWhy it matters
Does the PR do exactly what it says?Prevents hidden scope and surprise behavior
Are the tests and checks appropriate for the risk?Prevents unverified production changes
Are auth, secrets, data, and input boundaries safe?Prevents security regressions
Is the change reversible or safely recoverable?Prevents incidents from becoming manual repair work
Are rollout notes clear?Prevents production promoters from guessing

Review is also the separation-of-duties evidence. The author, reviewer, and production promoter must be distinct actors when the change moves toward production.

Release and promotion

The release pipeline builds once and promotes the same image forward. Suffixes communicate trust stage; bytes remain the same.

StageTriggerOutputHuman decision
TestMerge to mainImmutable image and beta tagNo promotion decision; checks must pass
StagingManual promotionSame image pinned as release candidateDecide whether the change is ready for production-like validation
ProductionProtected production approvalSame image pinned as clean release tagDecide whether the release is ready for public traffic

The important detail is “same image.” Do not rebuild for each environment. If test, staging, and production use different images, you no longer know whether production is running what was verified earlier.

Versioning is calculated from the commit types since the last release.

Commit mix since last tagNext bump
Any breaking change (! or BREAKING CHANGE)Major
Any feat and no breaking changeMinor
Only fix, security, or perfPatch
Only infra, docs, chore, ci, test, style, build, or refactorNo release version bump

Production movement requires an explicit approval action. No production change should depend on a person applying Kubernetes resources by hand.

Feature flags

Feature flags separate deployment from launch. Deploying code means the code is present in the environment. Launching behavior means users can experience it.

A flag is useful when code is complete enough to merge but exposure needs timing, approval, cohort rollout, or fast disablement. A flag is not a hiding place for broken code.

Use a flag whenDo not use a flag when
Complete behavior needs agency sign-off before exposureThe code fails checks
Gradual rollout is neededAuthorization or tenant isolation is missing
A fast disable path is usefulThe value is permanent configuration
Experiment metrics are definedNobody owns cleanup

Every flag needs:

  • a safe default
  • an owner
  • tests for on and off paths
  • a rollout approval path
  • a metric or signal that says rollout is healthy
  • a cleanup issue and removal date

The standard implementation is GrowthBook through @dmwd-io/feature-flags. Do not create a custom flag provider unless a current, concrete requirement justifies it.

Enforcement

GitHub rulesets, central governance workflows, protected environments, and ArgoCD enforce the delivery path.

Enforcement pointWhat it prevents
branch-protectionsDirect default-branch writes, branch deletion, force-pushes, and non-linear history
code-reviewUnreviewed or stale-approved changes reaching main
Conventional branch and commit rulesAmbiguous release metadata and weak audit search
release-tagsMalformed tags driving release automation
Production environment protectionOne-person or branch-based production deployment
ArgoCD reconciliationManual cluster state drifting away from Git

If an emergency requires a bypass, the bypass must name the incident or ticket, the approver, the reason, and the follow-up review. The bypass is evidence too.

ATO evidence

Delivery evidence should be collected as work happens, not reconstructed later. For each release, retain the PR, reviewer approval, required checks, security scan status, workflow run, approving actor, environment, image digest, Git SHA, ArgoCD sync result, release record, and timestamp.

EvidenceSupports
Non-author PR approval and resolved review threadsAC-5, CM-3, SA-10
Required checks, tests, and security scansSA-11, SI-2
Workflow run and production approvalCM-3, CM-5, AU-2
Same image digest across environmentsCM-4
ArgoCD sync and deployment event historyAU-12, CM-3
Feature flag approval and cleanup recordCM-4, CM-5, IR-4

First-change checklist

Use this when you are about to open a PR:

  • branch name uses the right prefix
  • PR title is a valid Conventional Commit
  • PR body explains what changed, why, verification, and risk
  • checks that match the risk have run
  • secrets are referenced, not committed
  • feature flags have owner, safe default, and cleanup plan
  • reviewer is not the author
  • production promotion notes are clear if the change can ship

See also