Prefer explicit state machines over boolean flags
When a process has more than two states, boolean flags silently lie about what the system can do.
A boolean flag like `isApproved` looks clean at first, but workflows rarely stay binary. Once you add `isRejected`, `needsReview`, `isEscalated`, the booleans multiply and every combination must be accounted for — including the impossible ones. Invalid states like `isApproved && isRejected` compile happily and crash at runtime.
An explicit state machine with documented transitions (pending → review → approved | rejected) makes invalid states unrepresentable by design. Any code path that violates the transition table either won't compile (with a tagged union) or fails at a clear guard clause, not deep in a tangle of compound conditionals.
The overhead is one enum and one switch. In return you get readable logs, safe refactoring, and a model that both developers and product people can read without translation. When a ticket says 'add cancellation', you add one variant and one transition instead of editing every boolean check in the codebase.