I'll expand on the first example, the datetime one.

Many user databases use soft-deletes where fields can change or be deleted, so user's actions can be logged, investigated or rolled back.

When user changes their e-mail (or adds another one), we add a row, and "verifiedAt" is now null. User verifies new email, so its time is recorded to the "verifiedAt" field.

Now, we have many e-mails for the same user with valid "verifiedAt" fields. Which one is the current one? We need another boolean for that (isCurrent). Selecting the last one doesn't make sense all the time, because we might have primary and backup mails, and the oldest one might be the primary one.

If we want to support multiple valid e-mails for a single account, we might need another boolean field "isPrimary". So it makes two additional booleans. isCurrent, isPrimary.

I can merge it into a nice bit field or a comma separated value list, but it defeats the purpose and wanders into code-golf territory.

Booleans are nice. Love them, and don't kick them around because they're small, and sometimes round.

I would say for your specific example, you shouldn't have boolean flags for that in the user_emails table, but instead have a primary_email column in the users table, that has a foreign key reference to the user_emails table. That way you can also ensure that the user always has exactly one primary email.

And for is_current, I still think a nullable timestamp could be useful there instead of a boolean. You might have a policy to delete old email addresses after they've been inactive for a certain amount of time, for example. But I'll admit that a boolean is fine there too, if you really don't care when the user removed an email from the current list. (Depending on usage patterns, you might even want to move inactive email addresses to a different table, if you expect them to accumulate over time.)

I think booleans are special in a weird way: if you think more about what you're using it for, you can almost always find a different way to store it that gives you richer information, if you need it.

> you can almost always find a different way to store it that gives you richer information, if you need it.

The trade-off here is DB speed/size and the secondary information you can gather from that DB.

In my eyes, after a certain point the DB shall not be the place to query and rebuild past actions from scattered data inside it. Instead, you can delegate these things to a well-formatted action log, so you can just query that one.

Unless it's absolutely necessary, tiering sounds and feels much more appropriate than bloating a DB.