What are common EK9 anti-patterns to avoid?
← Design Patterns and Idioms · Ref: Q267
Here are common anti-patterns in EK9 with their corrections.
1. OVERUSING ANY
Bad: Taking Any parameters everywhere loses type safety. The compiler cannot check operations at compile time and dispatchers must handle every type.
Good: Use specific types or define a trait. When you need polymorphism, traits give you a controlled contract.
2. DEEP INHERITANCE HIERARCHIES
Bad: Chains of A extends B extends C extends D create fragile, tightly coupled code. EK9 types are closed by default for this reason.
Good: Use composition. Wrap the inner type as a field and expose only the methods you need. Use trait delegation to forward methods automatically.
3. IGNORING GUARD EXPRESSIONS
Bad: Assuming a value is always set and using it directly. This leads to operating on unset values with undefined results.
Good: Always use guard expressions for Optional and Result access. The guard ensures the block only executes when the value is set.
4. EXCEPTIONS FOR EXPECTED FAILURES
Bad: Using try/catch for validation, parsing, or user input errors. Exceptions are for unexpected failures.
Good: Use Result for expected failures. Result makes the caller handle both success and error paths explicitly.
5. MUTABLE SHARED STATE
Bad: Multiple threads accessing the same mutable variable without synchronization.
Good: Use MutexLock to protect shared state. The lock ensures only one thread accesses the state at a time.
6. GIANT MONOLITHIC PROGRAMS
Bad: One massive program block with hundreds of lines doing everything.
Good: Decompose into small, focused functions. Mark functions as pure where possible for testability and compiler verification.
See Q29 for guard expressions and tri-state. See Q212 for composition over inheritance. See Q213 for mutex lock thread safety. See Q254 for when Any is appropriate. See Q259 for Result vs exceptions. See Q313 for code smell detection including god classes and data clumps.
Example
defines module qa.patterns.antipatterns defines trait // GOOD: Specific trait instead of Any Describable describe() as abstract <- rtn as String? defines class // GOOD: Implement the trait for type safety Product with trait of Describable productName as String? default private Product() Product() -> pName as String this.productName :=? String(pName) override describe() <- rtn as String: String(productName) default operator ? // GOOD: Composition over deep inheritance OrderSummary items as List of String: List() of String addItem() -> item as String items += item itemCount() as pure <- rtn as Integer: length items summary() as pure <- rtn as String: `Order with ${length items} items` default operator ? defines program AntiPatternsDemo() stdout <- Stdout() // === GOOD: Specific types via trait === product <- Product("Widget") stdout.println(product.describe()) // === GOOD: Composition wrapper === order <- OrderSummary() order.addItem("Item A") order.addItem("Item B") order.addItem("Item C") stdout.println(order.summary()) // === GOOD: Guard expression === parsed <- Integer("not-a-number") if validNum <- parsed stdout.println(`Valid: ${validNum}`) else stdout.println("Invalid input handled safely") // === GOOD: Guarded assignment for defaults === setting <- String() setting :=? "safe-default" stdout.println(`Setting: ${setting}`) stdout.println("Anti-patterns avoided")
Common mistakes
E50001 — Renaming the variable means later references to 'order' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.
Incorrect:
orderXYZ <- OrderSummary()
Correct:
order <- OrderSummary()
E50001 — Renaming the variable means later references to 'product' become unresolved, triggering E50001. Variable names must be consistent. See ek9 -h E50001 for details.
Incorrect:
productXYZ <- Product("Widget")
Correct:
product <- Product("Widget")
Other ways to ask this
- What mistakes should I avoid when writing EK9 code?
- What are bad practices in EK9?
- What should I not do in EK9?
Coming from another language?
Java: null everywhere, deep inheritance (AbstractFactoryBeanProcessor), checked exception abuse, mutable shared state, God classes. Python: duck typing hides type errors, mutable default arguments, global state, bare except clauses. Go: error return values ignored, interface pollution, goroutine leaks. Rust: fighting the borrow checker instead of redesigning, unwrap() everywhere, unsafe blocks. EK9: anti-patterns center on overusing Any (losing type safety), ignoring guards (operating on unset values), and inheritance over composition (fragile hierarchies).
Keywords: pattern, mistake, idiom, error, practice, design, anti-pattern, common, avoid, bad, pitfall, wrong, smell