istio/istio
Patterns and conventions
The unstated rules. Not all of these are written down anywhere else; most are inferred from the codebase. The official conventions doc is Development-Conventions on the GitHub wiki, and the performance-oriented Writing-Fast-and-Lean-Code is required reading before changing hot paths.
Go style
- Follow Effective Go and the Go Code Review Comments. Repo-specific lints (
tools/golangci-override.yaml) extendcommon/config/.golangci.yml. - Package names are lowercase and match their directory.
- File names use lowercase with underscores when needed (
push_context.go), never camelCase. - Type names use CamelCase: singular nouns for instances (
Service,Endpoint), plural for collections (Endpoints). - Interface names commonly drop the
Iprefix and use-er(Generator,ConfigStore,ServiceDiscovery). - Errors propagate; do not log-and-return. Use
fmt.Errorf("...: %w", err)to wrap. Sentinel errors live in the package they belong to. panicis reserved for invariant violations. Useassertpackage orif cond { panic(...) }only behind-tags=assert.
Logging
Use pkg/log/, not the standard library logger and not zap directly. The pattern:
var log = istiolog.RegisterScope("foo", "what foo logs")
func bar() {
log.WithLabels("proxy", proxyID).Debugf("processing %d items", n)
}Rules:
- Each subsystem has its own scope. Don't reuse
defaultfor new code. - Use
Debugf/Infof/Warnf/Errorfwith format strings; do not pre-format withSprintf. - Attach structured labels with
WithLabels(...)before the level call. - Errors and warnings should be actionable. Include enough context (proxy ID, resource name) that an operator can find the relevant resource.
Metrics
All Prometheus and OpenTelemetry metrics go through pkg/monitoring/. The pattern:
var pushTime = monitoring.NewSum(
"pilot_xds_push_time_seconds",
"Total time spent pushing xDS to proxies, by type",
monitoring.WithLabels(typeLabel),
)
func init() { monitoring.MustRegister(pushTime) }Naming:
- Prefix with the component:
pilot_*,citadel_*,istio_agent_*,istio_cni_*,istio_*for cross-cutting. - Use snake_case.
- Histograms get
_seconds, counters get_total(and the typeSum).
Do not register raw prometheus.* collectors; the wrapper handles dual emission to Prom and OTel and centralizes label registration.
Errors
- Functions return
erroras the last return value. - Wrap errors at boundaries:
return fmt.Errorf("listing services: %w", err). - Use
errors.Is/errors.Asrather than==for error comparisons. multierror.Append(hashicorp/go-multierror) is the standard for collecting multiple errors during validation.
Configuration
Runtime knobs live in pilot/pkg/features/pilot.go (and similar features.go files in other components). The pattern:
var enableSomething = env.Register(
"PILOT_ENABLE_SOMETHING",
true,
"When true, do the new thing",
).Get()Rules:
- Always go through
pkg/env. Do not callos.Getenvdirectly. - Names are SCREAMING_SNAKE_CASE with a component prefix.
- Provide a defaulted value and a one-line description; CI checks that all env vars are documented (
make envvarlinter). - Default to the safe behavior. Most new features start with
PILOT_ENABLE_*=falseand flip totrueafter stability.
User-facing config lives in:
MeshConfig(mesh-wide;istio/api)IstioOperator(install-time;istio/apiplusoperator/pkg/apis/)- Helm
values.yaml(per chart;manifests/charts/<chart>/values.yaml) - Per-CRD APIs (Istio networking, security, telemetry)
Controllers: krt vs informers
The codebase has two controller styles. Both are accepted; use the one that fits.
pkg/kube/krt/— declarativeCollection+Fetch. New code, especially anything that touches Ambient, uses krt. The framework manages event delivery, dependency tracking, and re-computation. Tests are easier (state is just a function of inputs).- Hand-written informer + queue —
pkg/kube/kclient/,pkg/queue/. Older code, including most sidecar-mode networking, uses this style. It is fine to extend; do not rewrite working code just to migrate.
A useful rule: if your controller computes derived data from 3+ sources, krt will save you time. If it does one thing in response to one event, an informer + queue is simpler.
Generated code
Several types of code are generated:
- Protobufs under
pkg/config/xds/and other places, regenerated viamake proto. - CRD manifests under
manifests/charts/base/files/, regenerated viamake update-crds. - Golden test outputs under
testdata/, regenerated viamake refresh-goldens. - License manifests under
licenses/, regenerated viamake mirror-licenses.
Generated files include a Code generated by ... DO NOT EDIT. header. Do not hand-edit them. CI gates on make gen-check — if the output diffs against committed files, the PR fails.
Performance hot paths
The xDS push path and the cache are performance-critical. Conventions:
- Pre-allocate slices and maps with capacity hints when the size is knowable.
- Prefer values over pointers when the value fits in a cache line; the existing networking code does this throughout.
- Reuse buffers via
sync.Poolfor objects that are allocated per request. - Do not spawn goroutines per request. The xDS server uses a fixed worker pool (
pilot/pkg/xds/pushqueue.go) and the push pipeline is single-writer per stream. - Cache encoded
protobuf.Anyoutputs keyed by their inputs (seepilot/pkg/networking/core/cluster_cache.go). Caching with the wrong key has caused CVEs in the past, so the cache asserts on key collisions when-tags=assertis set.
The performance guide on the wiki lists more (memory pooling, allocation profiling). Always benchmark before and after a hot-path change.
Tests
- Co-locate tests:
foo.go↔foo_test.go. - Table-driven tests for translation and validation logic. Each case has a
namefield and is wrapped int.Run(tc.name, ...). - Goldens for outputs that are unwieldy to assert in code. Regenerate with
make refresh-goldensand inspect the diff. - Avoid
time.Sleepin tests; useassert.EventuallyEqualor framework retries. - Do not depend on package-level mutable state across tests. The xDS code in particular has historically had this kind of leak; new tests should
t.Cleanupeverything they create.
Comments
- Public APIs have GoDoc comments. The first sentence repeats the symbol name.
- Internal helpers can be terse but should explain why if the what is non-obvious.
- TODOs include a username or issue link:
// TODO(@howardjohn): switch to delta xDS once #1234 lands.
Copyright header
Every Go, shell, and YAML file starts with the Apache 2.0 header:
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// ...make precommit adds it for you. The linter rejects PRs without it.
Imports
Goimports groups: standard library, third party, istio.io/.... The common/scripts/run_goimports.sh and gofumpt enforce this.
import (
"context"
"fmt"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"istio.io/api/mesh/v1alpha1"
"istio.io/istio/pkg/log"
)File / directory rules
- New top-level packages should have a 1-2 line doc comment in
doc.goor the package's primary file. - Test fixtures under
testdata/are not formatted bygofmtand not bound by the same lint rules. - Files generated by
make genbelong with the source they generate; do not put generated files undertestdata/.
When in doubt
Read the surrounding code and match its style. Istio's conventions are dense but consistent: a function in pilot/pkg/networking/core/ and one in pkg/kube/krt/ look the same way. If your PR introduces a stylistic outlier, expect a reviewer to point it out.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.