Intent-constraining facades
At their core, Courier interpreters exist to act as intent-constraining facades. This isn’t an implementation detail; it’s the defining design principle that enables Courier to scale across users, environments, and time.
An interpreter sits between a user’s declared intent and the underlying systems that perform work. Its role is to translate, constrain, and stabilize that interaction so users can rely on consistent behavior without needing to understand or manage execution complexity.
Why facades exist
Facades exist to absorb complexity on behalf of users.
Underlying execution engines are often powerful but volatile. They expose large surfaces of flags, options, APIs, and behaviors that are optimized for flexibility rather than safety or stability. Left unmediated, this complexity leaks directly into user workflows and automation.
Interpreters exist to prevent that leakage by:
Absorbing complexity
Interpreters encapsulate engine-specific details, multi-step workflows, and operational nuances so users interact with a simplified, purpose-driven contract rather than a low-level interface.Preserving stability over time
Engines evolve. Flags change, APIs are deprecated, authentication mechanisms shift, and platforms diverge. Interpreters exist to shield users from that churn by maintaining a stable contract even as the underlying implementation changes.Simplifying the user experience
By expressing operations in terms of intent rather than mechanism, interpreters make complex tasks approachable, repeatable, and easier to reason about. Users select capabilities, not tools.
Without this facade layer, Courier jobs would devolve into collections of fragile, engine-specific instructions. The facade is what makes interpreters reliable building blocks rather than brittle wrappers.
Translating intent to execution
Interpreters translate declarative intent into concrete execution.
Users express what they want to accomplish using the interpreter’s contract. The interpreter is responsible for mapping that intent into the appropriate execution steps against its underlying engine. This translation may involve:
- Executing one or more CLI commands
- Performing API interactions
- Invoking runtime operations or embedded logic
- Coordinating multiple low-level actions internally
These details are intentionally hidden. From the user’s perspective, a single interpreter invocation represents a single, coherent action, even if it requires multiple operations under the hood.
This design allows interpreters to:
- Encapsulate sequencing and coordination safely
- Enforce validation and constraints before execution
- Normalize outcomes and errors
- Maintain a consistent interface regardless of execution complexity
Critically, this translation happens entirely within the interpreter. Users should never be required to understand how many steps are involved or how those steps are implemented.
Absorbing engine churn
One of the most important responsibilities of an interpreter is to absorb change in the systems it depends on.
Execution engines are expected to evolve. Common sources of churn include:
- Changes to CLI flags or command structure
- API versioning and deprecation
- Shifts in authentication or authorization models
- Differences across platforms, operating systems, or environments
Interpreter contracts exist to isolate users from this instability. When engines change, the interpreter adapts internally while preserving the external contract. This allows users to continue relying on existing jobs and workflows without modification.
Exposing engine churn directly through interpreter contracts undermines this model. When interpreter commands mirror engine interfaces too closely, every engine change becomes a user-facing breaking change.
A well-designed interpreter treats its engine as an internal dependency, not as part of its public interface. By absorbing engine churn, interpreters protect user workflows, preserve trust, and enable long-term evolution.