# ADR-001: Error Codes Over Exceptions ## Context Clean Code (Robert C. Martin) mandates: *"Use exceptions, never return codes."* This principle assumes a language with `try`/`catch`/`throw` semantics (Java, C++, Python). MQL5 has **none of these constructs**. The language provides no mechanism to throw, propagate, or catch exceptions. The native MQL5 API itself is built entirely on error codes: - `OrderSend()` returns `bool` and fills `MqlTradeResult.retcode` - `GetLastError()` returns the last runtime error code - Every trade operation follows the pattern: `bool success` + `int error_code` ## Decision Use the **bool return + output parameter** pattern for all fallible operations in the framework: ```mql5 bool TryExecuteOrder(MqlTradeRequest &request, MqlTradeResult &result); ``` This mirrors the native `OrderSend()` contract exactly. Additionally: 1. **Named constants** (`SYNTH_ERR_OFF_QUOTES`, `SYNTH_ERR_REQUOTE`, etc.) replace raw integer literals to make error-handling paths self-documenting. 2. **A guard function** (`IsExecutionError()`) centralizes the "what counts as failure?" logic, preventing scattered `!= 0` comparisons. 3. **The method name uses `Try` prefix** (`TryExecuteOrder`, not `ExecuteOrder`) to signal at the call site that failure is a normal, expected outcome — not an exceptional case. ## Consequences ### Positive - **Zero friction for MQL5 developers**: the pattern matches what they already use daily with `OrderSend()`. - **Named constants** are grep-searchable and self-documenting, capturing the *spirit* of Clean Code's error handling guidelines. - **Guard function** prevents the bug class where a developer compares against the wrong integer value. ### Negative - **Callers must always check the return value**. Unlike exceptions, nothing forces the caller to handle the error. This is mitigated by the `Try` prefix convention (signals fallibility) and by the test suite (which validates both success and failure paths). - **CQS violation**: `TryExecuteOrder()` both performs an action AND returns a result. This is unavoidable without exceptions. Splitting into separate `ExecuteOrder()` + `GetLastExecutionError()` would create a fragile two-step protocol where the caller can forget step 2. ### Risk - If a future version of MQL5 adds exception support, this pattern should be reevaluated. The interface `IMarketEnvironment` would need a new version.