Side Effects

Writes, network calls, process launches, and other work outside a pure computation.

26 items in this guidance cluster.

Tagged guidance

Guide item

Boundary rules

Avoid Global Mutable StateBOUNDARY-AVOID-GLOBAL-MUTABLE-STATEKeep shared process state behind explicit owners, handles, synchronization, and reset policy. This preserves test isolation and lifecycle reasoning while still allowing deliberate globals such as caches or registries when their contract is visible.Give Tools Identity Policy And LimitsBOUNDARY-GIVE-TOOLS-IDENTITY-POLICY-AND-LIMITSGive effectful callable units typed identity, authorization policy, cancellation, and bounded output. The added ceremony is reserved for real tool boundaries where auditability, recovery, and blast-radius control matter.Keep Backend Adapters At EdgeBOUNDARY-KEEP-BACKEND-ADAPTERS-AT-EDGEKeep provider-specific terminal, storage, network, and runtime APIs in adapter layers at the boundary. Core logic stays stable and testable while real backend differences remain modeled instead of hidden behind a false common API.Make Ambient Inputs ExplicitBOUNDARY-MAKE-AMBIENT-INPUTS-EXPLICITPass time, randomness, environment, locale, terminal size, network clients, and process state through visible inputs when they affect behavior. Injecting only the relevant ambient values improves tests and portability without spreading oversized context objects.Make Policy Boundaries ExplicitBOUNDARY-MAKE-POLICY-BOUNDARIES-EXPLICITRoute writes, network calls, shell execution, publication, telemetry, redaction, and credential use through a visible policy decision before effects run. Callers can then understand allowed, denied, redacted, fallback, preserved, and unsupported outcomes.Read Normalize Compare MutateBOUNDARY-READ-NORMALIZE-COMPARE-MUTATEReconcile external state by reading the current provider view, normalizing it, comparing intent, and mutating only the real difference. The loop avoids destructive or noisy writes when formatting, defaults, ordering, or outside actors create drift.Separate Pure Core From EffectsBOUNDARY-SEPARATE-PURE-CORE-FROM-EFFECTSMove domain computation away from rendering, I/O, mutation, and global state when effects obscure the decision being tested. The split gives tests a stable surface, but should be skipped when it adds indirection without a useful boundary.

Documentation rules

Observability rules

Refactoring rules

Rust rules

Pattern items

Contain Observability Policycontain-observability-policyLogging, metrics, tracing, and privacy choices become inconsistent when every call site invents its own policy. Put observability decisions at owned boundaries so lower layers expose facts without duplicating policy.Inject Time And Randomnessinject-time-and-randomnessDirect reads of clocks and random sources make behavior hard to reproduce, test, and explain. Pass time and randomness through explicit seams at the boundary where nondeterminism is chosen.Keep Async Boundaries Explicitkeep-async-boundaries-explicitHidden async work obscures cancellation, ordering, resource ownership, and failure behavior. Make task spawning, awaiting, retries, and background ownership visible at the boundary where callers need to reason about them.Log At Owned Boundarieslog-at-owned-boundariesLogs become noisy and inconsistent when every helper records the same failure from a partial viewpoint. Log where the code owns the operation and can attach useful, safe diagnostic context.Make Parameters Explicitmake-parameters-explicitHidden configuration, time, randomness, globals, or environment reads make behavior hard to reason about and test. Gather ambient data at the outer boundary, then pass typed inputs or a cohesive context to the inner operation.Make Side Effects Visiblemake-side-effects-visibleCallers need to know when an operation touches persistence, networks, time, global state, caches, metrics, processes, or other external systems. Make caller-relevant effects visible in names, docs, or examples without documenting implementation noise.

Principle items