fn foo(verbose: bool, recursive: bool) | Callers write foo(true, false) — meaning is invisible | Use enums: Verbosity::Quiet, Traversal::Flat |
pub struct Range { pub start: u32, pub end: u32 } | Nothing enforces start <= end | Private fields + Range::new(start, end) -> Result<Range> |
fn process(id: u64, parent_id: u64) | Easy to swap arguments silently | fn process(id: RuleId, parent: ParentId) |
fn configure(opts: HashMap<String, String>) | Unbounded input, typos compile fine | Typed config struct or builder |
fn init() -> *mut State | Caller must remember to free, can use-after-free | Return Box<State> or Arc<State> |
Returning i32 error codes | Caller can ignore, misinterpret, or mix with valid values | Return Result<T, E> with typed error |
fn send(data: &[u8], compress: bool, encrypt: bool, sign: bool) | 3 bools = 8 combinations, many invalid | Options struct or builder with valid combinations |
| Silent default on invalid input | Caller doesn't know their input was wrong | Return Err or use a type that can't be invalid |
| Parsing config twice into similar structs | Layers silently drift when defaults change in only one place | Parse once, validate once, derive downstream views from the validated config |
| Treating a single positional token as multiple possible modes | Callers cannot predict how edge cases are classified | Use explicit syntax or add parser tests for each ambiguous shape |