That boolean should probably be something else | nicole@web
Booleans feel natural but they're usually wrong—they couple data to logic and hide richer types (datetimes, enums, states) that make code more maintainable and prevent bugs when requirements inevitably change.
Read Original Summary used for search
TLDR
• is_confirmed should be confirmed_at—nullable timestamps give you the same boolean check plus when it happened, useful for debugging and analytics
• User roles, job statuses, permissions are enums, not booleans—is_admin becomes a proliferation of is_super_admin, is_guest flags that break everywhere
• Enums let compilers catch missing cases; booleans force you to hunt down every usage when adding new states
• Booleans couple data to application logic—you're storing the result of a check instead of the underlying facts
• Only use booleans for temporary conditional results within a function, not as persistent data
In Detail
The fundamental problem with booleans is they represent application logic, not underlying data. When you store is_confirmed as a boolean, you're throwing away when the confirmation happened. A nullable confirmed_at timestamp gives you the same boolean check (null vs not null) while preserving temporal data that becomes valuable later—like identifying which users were affected by a bug in your confirmation process during a specific time window.
Most remaining booleans are actually enums in disguise. User roles (is_admin), job statuses (is_failed, is_started, is_queued), and permission checks all proliferate into multiple mutually-exclusive boolean flags as requirements evolve. An enum like UserRole { User, Admin, Guest, SuperAdmin } is extensible and lets your compiler catch missing cases. With booleans, you add more flags and manually hunt down every place the old booleans were used. Even two-state cases benefit from enums—PermissionCheck::NotPermitted(reason) is more expressive than false and can carry additional context like why the check failed.
The only legitimate use case for booleans is storing intermediate conditional results temporarily within a function—either for readability (naming a complex conditional) or optimization (reusing a computed value). But even then, the surrounding code often benefits from enums. The key insight: booleans make sense for logic, but data is usually something richer underneath. Storing booleans couples your data model tightly to current application logic, making future changes harder and losing information that would be valuable for debugging and analytics.