A Helm named template needs two inputs, but `include` takes one argument — how do you pass both?
answer
- A partial has exactly one parameter
- Bundle the inputs into a single value
- Alternating keys and values build a map
- Carry the root context in under a key
basics
~20 sBundle the inputs into one value with the dict function and pass that: include "fraud-scoring.probe" (dict "ctx" $ "port" .Values.service.port). Inside the partial, dot is the dict, so the inputs are read as .ctx and .port.
solid answer
~50 sA named template receives exactly one value, bound to dot, so multiple inputs have to travel inside a single structure. The idiom is `dict`, which builds a map from alternating key/value pairs: `{{ include "fraud-scoring.probe" (dict "ctx" $ "port" .Values.service.port "delay" 7) }}`. Inside the partial you read `.port` and `.delay`. Because dot is now the dict and not the render context, `.Values`, `.Release` and `.Chart` are unreachable — which is why the convention is to pass the root context in under a key such as `ctx` or `top` and write `.ctx.Release.Name` inside. Note that `$` inside the partial is also the dict, so it is no escape hatch. Defaults are handled either by merging the caller's dict over a defaults dict, or more simply with the `default` function on each key inside the partial.
code
yaml · 11 lines{{- define "fraud-scoring.probe" -}}
httpGet:
path: /healthz
port: {{ .port }}
initialDelaySeconds: {{ .delay | default 5 }}
{{- end }}
containers:
- name: scorer
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
readinessProbe:
{{- include "fraud-scoring.probe" (dict "ctx" $ "port" .Values.service.port "delay" 7) | nindent 6 }}go deeper
Remember the shape of the call: one argument only, so wrap several inputs in dict "key" value "key" value and put the whole thing in parentheses. Inside the partial, read them by key.
Explain why dot inside the partial is now the dict and what that costs you — .Values and .Release are gone unless you pass the context in under a key, and $ does not get them back.
Talk about the partial's argument map as an interface: document the keys, validate the required ones, keep the set small, and never mutate values-derived maps to smuggle data between templates.
Own the chart-wide convention — one key name for the context, a documented parameter map per partial, and a rule for when a helper should take plain dot instead. Consistency here is what keeps a shared chart library reviewable.
## The constraint Calling a named template — with `include` or with the `template` action — passes exactly one value, and that value becomes dot inside the partial. There is no parameter list, no keyword arguments, and no way to add a second positional argument. Any partial that needs more than one input therefore needs its caller to bundle those inputs into a single value. ## The dict idiom The standard bundle is a map built by the `dict` function, which takes alternating keys and values: ``` {{- include "fraud-scoring.probe" (dict "ctx" $ "port" .Values.service.port "delay" 7) }} ``` The parentheses matter: they make the whole `dict` call one expression, evaluated first, so `include` sees a single argument. Inside the partial, dot is that map, and each input is read by key — `.port`, `.delay`. A missing key is simply absent, so `{{ .delay | default 5 }}` inside the partial is the cheap way to make a parameter optional. ## Why the root context usually rides along Rebinding dot to the dict has a consequence that surprises people: the render context is gone. `.Values`, `.Release`, `.Chart` and `.Capabilities` are all fields of the context that the manifest was rendering with, and the partial no longer holds that context — it holds your two-key map. `$` is no rescue either: it is bound to the data value the current template execution started with, which for an included partial is the argument you passed, so `$` and dot are the same dict. That is why almost every real chart passes the context in as one of the keys, conventionally named `ctx`, `top` or `root`: ``` {{- include "fraud-scoring.probe" (dict "ctx" $ "port" 8443) }} ``` and inside the partial writes `{{ .ctx.Release.Name }}` or `{{ .ctx.Values.image.tag }}` alongside `{{ .port }}`. Pick one key name and use it across the whole chart; a chart where half the partials say `ctx` and half say `top` produces exactly the sort of empty render that costs an afternoon. ## Building the dict from existing data `dict` is not the only way to make the bundle. A partial that mostly wants an existing map with one extra field can be handed a merged map — merge the caller's small dict over a defaults dict — and a partial that genuinely wants a list can be given one with `list`. Two habits are worth avoiding. First, do not build a positional list and index into it inside the partial (`index . 0`, `index . 1`): the call site stops being readable and reordering breaks silently. Second, do not mutate the context: `set` on a map derived from `.Values` changes shared data for every later template in the render, which produces order-dependent output that is miserable to debug. Build a fresh dict instead. ## Variables do not cross the boundary Another instinct is to set a variable in the caller and read it in the partial. Template variables are lexically scoped to the template that declares them, so `$port` defined in the Deployment is invisible inside the partial; the partial sees only its argument. The same applies in the other direction: a variable assigned inside the partial does not leak back. The single argument really is the entire interface, which is a feature — it makes a partial testable by rendering it with a known dict. ## Designing the partial's interface Because the interface is one map, it pays to treat it like a function signature. Document the keys the partial expects in a comment above the `define`. Validate the ones that must be present, so a caller that forgets a key gets a clear failure rather than a Deployment with an empty port. Keep the key set small: a partial taking eight keys is usually two partials, or a partial that should have been given the context and read the values itself. A useful rule of thumb: if the partial is generic — a probe block, a resource block, a name truncator — take explicit keys plus `ctx`. If the partial is really "the labels for this chart", take plain `.` and read `.Values` and `.Release` directly; wrapping that in a dict adds ceremony and nothing else. ## Reading a failure When a dict-taking partial renders an empty or half-built block, the diagnosis is almost always one of three things: the caller passed `.` where the partial expects a dict, so every key lookup is missing; the caller passed a dict where the partial expects the context, so `.Values` is missing; or a key name is misspelled on one side. Rendering the chart and reading the block that came out — rather than the template — tells you which, because a missing key renders as nothing at all rather than as an error.
- Inside a partial called with a dict, why can't you just write `$.Release.Name`?Because `$` is bound to the data the current template execution was given, and for an included partial that is the argument you passed — the dict. So `$` and dot are the same map there, and `$.Release` is missing rather than the release object. The root context has to be handed in explicitly, which is why the convention is a `ctx` or `top` key holding `$` from the call site.
- How do you give a dict-taking partial sensible defaults for keys the caller omits?Two ways. Simplest is to apply `default` at each use inside the partial — `{{ .delay | default 5 }}` — which keeps the defaults next to the thing they describe. For a larger parameter set, merge the caller's dict over a defaults dict at the top of the partial and use the merged result throughout. Either way, validate keys that have no sensible default so a forgotten one fails loudly instead of rendering blank.
saying these in an interview costs you the question
- Passes two arguments to include and expects both to arrive
- Uses $ inside the partial to reach the root context
- Thinks a variable set in the caller is visible in the partial
- Assumes every partial receives the full render context
- Passes a positional list and indexes into it by number