Documentation navigation
Platform Standards and Guidelines
This document captures the platform engineering team's conventions and requirements for authoring Helm charts and managing infrastructure. They apply to all charts published or maintained by the team, including common-chart and any service-specific charts.
Chart Design & Structure
Charts must be self-contained
Charts must not depend on resources created by other charts (e.g. ServiceAccounts, Secrets, ConfigMaps) unless those resources are explicitly declared as prerequisites in the chart documentation.
Why: Cross-chart coupling introduces hidden deployment ordering dependencies. If Chart B silently assumes Chart A has already run, deployments fail in non-obvious ways and become difficult to test in isolation.
Identities are per-service
Each chart must create and manage its own ServiceAccount and, where applicable, its own managed identity binding. Sharing identities across charts is not permitted.
Why: Shared identities violate the principle of least privilege. If one service is compromised or its credentials leak, a shared identity expands the blast radius to every service that uses it.
Helper templates must be scoped to the chart name
Named templates (defined with {{- define "..." }}) must be prefixed with the chart name, e.g. mychart.fullname.
Why: Helm merges all named templates into a single global namespace at render time. Without scoping, two charts that define fullname will silently shadow each other when composed in an umbrella chart.
Charts must provide meaningful abstractions over Kubernetes primitives
Values files should expose intent (e.g. autoscaling.enabled, ingress.host) rather than raw Kubernetes fields. Avoid "values that are just YAML pass-through."
Why: Charts are interfaces, not wrappers. If a consumer needs to understand the underlying Kubernetes resource to configure the chart, the abstraction has failed. Meaningful abstractions reduce the learning surface and make charts safer to use.
Composition must use umbrella charts
When multiple charts need to be deployed together, use a Helm umbrella chart. Ad-hoc composition (e.g. deploying charts via separate ArgoCD Applications that share a namespace and have implicit dependencies) is not acceptable as a substitute.
Why: Umbrella charts make composition explicit, versioned, and testable. Implicit multi-chart dependencies are fragile — they break silently when deployment order changes and cannot be linted or unit-tested as a unit.
Application names must follow <namespace>-<releaseName> format
The ArgoCD appName in config.json must be set to <namespace>-<releaseName>.
Why: ArgoCD Application names must be unique per cluster. A predictable convention makes names self-documenting (base-agent-api is immediately understood) and eliminates collisions when the same chart is deployed to multiple tenants.
Resource Naming & Configuration
Resource names must include Release.Name
All namespaced Kubernetes resources must include {{ .Release.Name }} in their name. Cluster-scoped resources (e.g. ClusterRole, ClusterRoleBinding) must also include {{ .Release.Namespace }}.
Why: Kubernetes requires unique names within a scope. Without Release.Name, installing the same chart twice in the same cluster (e.g. for two tenants) causes resource conflicts. Cluster-scoped resources additionally need the namespace to avoid collisions across namespaces.
Corti-specific defaults must be overridable via values
Any Corti domain name, registry URL, or other organisation-specific constant may be set as a default, but must be configurable through a documented Helm value.
Why: Hardcoded constants make charts unusable outside the standard Corti environment — for on-premise customers, isolated test environments, or future tenants. Exposing them as values keeps charts portable without requiring chart modifications.
Testing
Every feature toggle must have a Helm unit test
Any chart feature that can be enabled or disabled via a values flag must be covered by at least one Helm unit test that exercises the enabled path.
Why: Helm renders templates at deploy time. Untested feature flags can silently produce invalid YAML or omit required fields, only failing when the chart is applied to a cluster. Unit tests catch these rendering errors in CI, before any cluster is involved.
Registry
All OCI images must be mirrored to the internal registry
Container images referenced by chart defaults must be available in Corti's internal OCI registry. Upstream registry URLs must not be used as defaults.
Why: Pulling from upstream registries at deploy time creates a hard dependency on external availability and network egress. Mirroring guarantees deployments succeed during upstream outages, gives us control over which image versions are available, and ensures images are scanned by our own tooling before reaching production.
Change Management
Breaking changes require a migration runbook in the PR description
Any chart change that requires consumers to update their values or perform manual steps before or after the upgrade must include a step-by-step migration runbook directly in the pull request description.
Why: Breaking changes require coordinated action across multiple teams. Reviewers need to validate the migration path during review, and operators need accurate instructions at the moment they apply the change. A runbook in the PR is the most reliable place — it is version-controlled alongside the change and visible to everyone involved.
Infrastructure changes must be backported across cluster generations
Changes to shared infrastructure charts must be applied to all supported cluster generations, not just the latest.
Why: Clusters run on different generations simultaneously (e.g. different Kubernetes versions, CNI configurations). A change that only lands on the newest generation creates configuration drift, complicates troubleshooting, and eventually makes it impossible to upgrade older clusters because they have diverged too far.
Large scripts and migrations require a second approver (4-eye principle)
Any script or migration that is complex, touches production data, or is difficult to roll back must be reviewed and approved by a second engineer before execution — regardless of normal PR review requirements.
Why: Complex or destructive operations have a higher risk of human error. When you are close to the work it is easy to miss edge cases that are obvious to a fresh reader. A mandatory second set of eyes catches mistakes before they cause an incident.
Observability
JSON-structured logging must be enabled where supported
If the upstream application supports structured logging, the chart must enable JSON log format by default.
Why: Structured JSON logs are parseable by log aggregation pipelines (e.g. Datadog). They enable filtering, alerting, and dashboard construction without fragile regex parsing. Unstructured text logs lose metadata and degrade the signal-to-noise ratio across the platform.
Metrics endpoints must be configured
Charts must expose a Prometheus-compatible metrics endpoint and include the necessary annotations or ServiceMonitor resources for scraping.
Why: Without metrics there is no way to observe the health or performance of a service. Metrics are a prerequisite for SLOs, alerting, and capacity planning. A chart that ships without metrics forces the consuming team to add observability after the fact, which often doesn't happen until after the first incident.
Reliability
Services must be deployed with PDB, ≥2 replicas, pod topology spread, and HPA/KEDA
Production workloads must configure all four:
- PodDisruptionBudget: prevents node drains from taking a service fully offline.
- ≥2 replicas: ensures availability during rolling updates and single-node failures.
- Pod topology spread constraints: prevents both replicas from landing on the same node, so a single node failure does not cause a full outage.
- HPA or KEDA: scales the service automatically under load, avoiding manual intervention during traffic spikes.
Why: Each of these controls addresses a distinct failure mode. Omitting any one of them leaves a gap that will eventually be exposed by a node drain, a rolling update, or a traffic spike. Together they form the minimum baseline for a resilient production workload.
Infrastructure as Code
Terraform must not generate files committed to this repository
IaC tooling (Terraform, Pulumi, etc.) must not write generated files into the deployments repo as part of their normal apply workflow.
Why: Generated files produce noisy, hard-to-review diffs and create a chicken-and-egg dependency between the IaC run and the GitOps reconciliation. They also make it unclear which files are authoritative: the generator or the repo. Configuration that varies per environment should use Helm values files; anything else should be managed outside the GitOps boundary.
Design Checklist
Before merging a new or significantly changed chart, ask:
Would this chart work for a different tenant or customer with no modifications to the chart itself?
If the answer is no, there is likely a hardcoded assumption that should be extracted into a value. Multi-tenancy is a first-class constraint on the platform; charts that can only serve the base tenant in the standard Corti cloud are not reusable and will require costly rework when a new deployment context arises.