Skip to main content

Anti-patterns

This section describes common anti-patterns encountered when designing or implementing Courier interpreters. These patterns undermine intent-driven design, weaken contracts, and erode user trust. In many cases, they arise from convenience, familiarity with underlying tools, or pressure to just expose one more option.

Recognizing these anti-patterns early is critical. Most are difficult to reverse once users begin to depend on them.

Thin wrapper interpreters

Description:
The interpreter exposes the underlying engine almost directly, passing flags, arguments, or commands through with minimal abstraction.

Why it’s harmful:

  • Couples users tightly to engine behavior
  • Inherits engine instability and churn
  • Turns the interpreter into an unstable proxy
  • Forces users to understand mechanics rather than intent

Smell:
“If I already know the CLI, I know how to use the interpreter.”

Better approach:
Design an intent-driven contract that absorbs engine complexity and exposes only supported, meaningful outcomes.

Flag bags and flat option spaces

Description:
The interpreter contract exposes a large, flat set of loosely related options, often mirroring CLI flags.

Why it’s harmful:

  • Makes valid combinations difficult to reason about
  • Increases configuration errors
  • Complicates validation and evolution
  • Forces users to self-police correctness

Smell:
“Users need to read documentation to know which options can be combined.”

Better approach:
Use structured, hierarchical contracts that progressively constrain choices and guide users toward valid configurations.

Overloaded commands

Description:
A single command attempts to serve multiple unrelated purposes depending on which fields are supplied.

Why it’s harmful:

  • Makes behavior ambiguous
  • Complicates validation and error handling
  • Obscures intent
  • Breaks contract stability over time

Smell:
“This command behaves very differently depending on which options are present.”

Better approach:
Create separate commands or interpreters with clear, singular responsibility.

Encoding semantics in logs

Description:
The interpreter relies on stdout or stderr content to communicate outcomes, decisions, or data.

Why it’s harmful:

  • Breaks automation and composition
  • Encourages brittle parsing
  • Makes outcomes ambiguous
  • Treats logs as APIs

Smell:
“Downstream steps parse logs to determine success or extract values.”

Better approach:
Return all meaningful results as structured output or artifacts. Treat logs as diagnostic only.

Hidden side effects

Description:
The interpreter performs changes or external actions that aren’t clearly reflected in its output.

Why it’s harmful:

  • Violates user expectations
  • Prevents proper auditing
  • Makes retries unsafe
  • Undermines trust

Smell:
“The interpreter succeeded, but something unexpected changed.”

Better approach:
Make all side effects explicit through structured output and documentation.

Non-deterministic behavior without disclosure

Description:
The interpreter produces different outcomes or outputs under the same inputs without documenting or signaling this behavior.

Why it’s harmful:

  • Makes automation unpredictable
  • Breaks retries and re-execution
  • Undermines confidence in results

Smell:
“Running the same job twice produces different results for unclear reasons.”

Better approach:
Prefer deterministic behavior. Where this isn’t possible, document and signal non-determinism explicitly.

Validation deferred to execution

Description:
Invalid input is only discovered during execution, often through engine failure.

Why it’s harmful:

  • Produces confusing errors
  • Wastes execution time
  • Exposes engine internals
  • Creates a poor User Experience (UX)

Smell:
“The error message comes from the underlying tool.”

Better approach:
Validate early using the interpreter contract and return clear, human-readable errors

Silent partial success

Description:
The interpreter completes some actions successfully but doesn’t clearly communicate partial failure.

Why it’s harmful:

  • Leads to incorrect assumptions
  • Breaks downstream logic
  • Complicates remediation

Smell:
“It said success, but not everything worked.”

Better approach:
Explicitly represent partial success in structured output and detail what succeeded and what didn’t.

Escape hatches as the default

Description:
Advanced or unconstrained interpreters become the primary way users interact with a system.

Why it’s harmful:

  • Normalizes unsafe behavior
  • Undermines simpler, safer contracts
  • Makes governance difficult

Smell:
“Everyone uses the advanced interpreter because it’s more powerful.”

Better approach:
Treat escape hatch interpreters as opt-in exceptions and invest in strong, intent-driven defaults.

By avoiding these anti-patterns, interpreter authors protect users from fragility, preserve long-term contract stability, and reinforce Courier’s core design principles: intent, constraint, and trust.

Thank you for your feedback!

×