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
Document Lifecycle and Side EffectsDOCS-DOCUMENT-LIFECYCLE-AND-SIDE-EFFECTSDocument caller-visible lifecycle, ownership, platform, feature, and side-effect obligations. Skip internal narration, but make operational responsibilities clear.Show Side Effects in Live ExamplesDOCS-SHOW-SIDE-EFFECTS-IN-LIVE-EXAMPLESShow setup, visible effects, and cleanup when examples touch live resources or persistent state. Gate costly or externally visible actions so examples stay honest and safe to adapt.
Observability rules
Refactoring rules
Prefer Local ReasoningREFACTORING-PREFER-LOCAL-REASONINGShape code so relevant state, invariants, and effects are visible near the change. Centralize only when it reduces total reasoning, because distant reconstruction raises cognitive load and error risk.Prefer Loops for Side EffectsREFACTORING-PREFER-LOOPS-FOR-SIDE-EFFECTSUse ordinary loops when the main purpose is mutation, I/O, logging, or other side effects. Iterator chains are better for value transformation; using them for effects can hide order, early exits, and error handling.
Rust rules
Avoid Broad Context and CallbacksRUST-AVOID-BROAD-CONTEXT-AND-CALLBACKSPass explicit inputs and keep control flow local instead of hiding it in context bags or callbacks. This makes ownership, effects, and ordering easier to audit.Document Lifecycle Side EffectsRUST-DOCUMENT-LIFECYCLE-SIDE-EFFECTSDocument construction, start, stop, drop, and cleanup behavior when side effects matter. Callers need to know when resources are acquired, released, spawned, or blocked.Document Scheduling for Long Async WorkRUST-DOCUMENT-SCHEDULING-FOR-LONG-ASYNCExplain executor, cancellation, blocking, and fairness expectations for async work that can run long. Callers need those constraints to avoid starvation and runtime surprises.Inject Host Interactions at BoundariesRUST-INJECT-HOST-INTERACTIONS-AT-BOUNDARIESPass filesystem, network, time, randomness, and process behavior through boundaries when tests or alternate environments need control. This keeps the core deterministic and effects explicit.Make Side Effects ExplicitRUST-MAKE-SIDE-EFFECTS-EXPLICITPut mutation, I/O, registration, cleanup, and background work in names, call sites, or docs when callers must account for them. Keep tiny private helpers plain when the surrounding code already makes the effect obvious.
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
Avoid Global Mutable Stateavoid-global-mutable-stateMutable process-wide state hides ownership, lifecycle, reset, ordering, and concurrency policy. Prefer explicit owners or narrow handles unless the global behavior has a documented policy.Explicit Boundaries Preserve Correctnessexplicit-boundaries-preserve-correctnessMove uncertainty into named boundary policy before core logic uses the data. Parsed inputs, explicit side effects, and visible lifecycle transitions reduce repeated downstream guessing.