ai-superpower/.ai/instructions/skills/helm.instructions.md
moilanik a85c7dc0fb feat: add clean-code, clean-architecture and helm skills; fix .gitignore
New skill files:
- clean-code.instructions.md — naming, functions, classes, error handling,
  formatting, DRY/KISS/SOLID, Helm YAML conventions
- clean-architecture.instructions.md — dependency rule, layers, boundaries,
  SOLID foundation, Helm as outermost layer
- helm.instructions.md — resource ownership, values hygiene, required vs
  defaults, umbrella chart pattern, two-file values layering, KISS principle,
  hook ordering, config files pattern, dependency caching, template testing

Register all three in ai-root-instructions.md skills list and routing table.

Remove .ai from .gitignore — .ai/ is the product in this repo and must be
tracked; the apply.sh skip-by-marker mechanism prevents changes to other repos.
2026-03-04 09:54:07 +02:00

6.7 KiB
Raw Blame History

Helm Chart Development Guidelines

🔍 Before Creating Anything New

ALWAYS search the workspace for existing implementations first.

Before writing a new template, config, or resource:

  1. Search the workspace for similar existing files
  2. If found, migrate/adapt it — don't invent a new structure
  3. Only create from scratch if nothing exists

🏠 Resource Ownership

Every Kubernetes resource must have exactly one owner. Never duplicate.

  • If a chart creates a resource (e.g. Traefik creates IngressClass), don't create it elsewhere
  • Before adding a template for a resource, verify no other chart/subchart already manages it
❌ Wrong: subchart A creates IngressClass AND subchart B creates IngressClass
✅ Right: one chart owns IngressClass, others do not touch it

🧹 values.yaml Hygiene

values.yaml must only contain values that templates actually use.

Before adding a value:

  • Verify at least one template references .Values.<key>
  • If no template uses it → don't add it
  • Empty string defaults (key: "") that only serve as documentation are forbidden — use README instead

Umbrella chart values.yaml:

  • Contains only subchart defaults that override upstream chart defaults
  • All environment-specific values belong in the deployment repo
  • Must NOT contain empty placeholders

Subchart values.yaml:

  • Contains only values the subchart's own templates reference
  • Required values (validated with required) must NOT have empty defaults — absence should trigger the error

required vs defaults

Use required for values that MUST come from the deployer. Do NOT put empty defaults in values.yaml for these.

# Good: fails loudly at install if not provided
name: {{ required "ingress.className is required" .Values.ingress.className }}

# Bad: silently passes empty string, fails later in obscure way
name: {{ .Values.ingress.className | default "" }}

📦 Subchart Dependency Conditions

Use condition: in Chart.yaml to enable/disable components. Use flat booleans, not nested .enabled:

# Chart.yaml
dependencies:
  - name: keycloak
    repository: "file://../components/keycloak"
    version: "0.2.0"
    condition: components.keycloak

# values (Good)
components:
  keycloak: true
  traefik: false

# values (Avoid)
keycloak:
  enabled: true

⏱️ Hook Ordering

Resources that depend on other resources must use helm.sh/hook with hook-weight to ensure correct install order.

Established ordering convention:

  • hook-weight: "10" — infrastructure prerequisites (e.g. ClusterIssuer, IngressClass)
  • hook-weight: "15" — resources that depend on prerequisites (e.g. Certificate)
metadata:
  annotations:
    "helm.sh/hook": post-install,post-upgrade
    "helm.sh/hook-weight": "10"

📄 Configuration Files over values.yaml

Prefer files/ + ConfigMap over defining configuration in values.yaml.

When a component needs configuration (CASC, realm config, ini files, etc.):

  1. Put the config file in files/ directory
  2. Create a ConfigMap that reads it with .AsConfig + tpl
  3. Use {{ .Values.global.xxx }} expressions inside the config file for dynamic values
# templates/configmap.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: my-component-config
data: {{ (tpl (.Files.Glob "files/*").AsConfig . ) | nindent 2 }}
# files/config.yaml — contains Helm template expressions
server:
  url: https://{{ .Values.global.domain }}
  env: {{ .Values.global.environment }}

Why: Config files are readable, diffable, and version-controlled as real files. Large configs in values.yaml become unmaintainable and hard to review.

When .AsConfig + tpl is required:

  • Config file contains {{ }} template expressions → always use .AsConfig + tpl
  • Config file contains special characters (*, {, }) → .AsConfig handles escaping safely
# Never use plain Files.Get on files with template expressions or special chars:
{{ tpl (.Files.Get "files/config.json") . }}   # ❌ breaks on * characters

# Always use AsConfig:
{{ tpl (.Files.Glob "files/config.json").AsConfig . }}  # ✅

🔄 Dependency Caching

After editing any subchart template or values, cached charts in charts/ will be stale:

rm -rf charts/ Chart.lock
helm dependency update .

Always rebuild before helm template testing when subchart files have changed.


🎂 Umbrella Chart Pattern

The standard structure is an umbrella chart that integrates multiple component charts.

my-umbrella/
├── Chart.yaml            ← declares component charts as dependencies
├── values.yaml           ← static baseline: shared across all installations
├── templates/            ← only cross-cutting resources that no subchart owns
└── charts/               ← built by helm dependency update

Two-file values layering:

File Where it lives Purpose
values.yaml umbrella chart repo static baseline, same across all IAC installations
values.<installation>.yaml IAC / deployment repo installation-specific overrides and required parameters
  • values.yaml contains defaults valid for every installation; it never contains installation-specific data
  • values.<installation>.yaml provides what that specific installation requires — credentials, hostnames, sizing, feature flags
  • Required values (no sensible default) use required in templates and have no entry in values.yaml — their absence triggers a clear error at install time

KISS — Start Minimal, Add Only When Asked

Do not add knobs, flags, or options that arent needed right now.

  • Build the minimum that meets the stated functional requirement
  • Do not anticipate future needs with extra values, conditions, or template logic
  • Complexity is easy to add; it is hard to remove once deployed
  • AI may suggest what might be needed later — but must not implement it without explicit instruction
# ❌ Wrong: adding a flag “just in case”
features:
  enableFutureThing: false   # not needed yet, adding anyway

# ✅ Right: dont add it. If its needed, the developer will ask.

When in doubt, leave it out. A value that exists but is never used is noise. A template condition that is never toggled is dead code. Raise the question with the developer instead of silently implementing it.


🧪 Testing Templates

Standard verification command:

helm template <release-name> . \
  --set global.key=value \
  ... \
  2>&1 | grep "^kind:" | sort | uniq -c

Verify:

  • Expected resource kinds appear
  • No Error: lines
  • required validation fires when values are missing