2026-05-04Use string-backed enums
Because sometimes you're worth a few extra bytes.
Our app is just lousy with enums. I have a couple of tics when it comes to data modeling, and this is one of them—I suffer from a deep-seated fear of combinatorics, so instead of peppering my models with booleans like sold and approved I'll do my damnedest to pull my state apart into discrete buckets and cram them into as few status enums as I can get away with. Don't have to worry about two states being incompatible if it's only possible to be in one of them at a time! Maybe it's a framework thing, too, because having enums in Rails gets you cool freebies. Look:
enum :published_status, { draft: 0, published: 1 }
enum :lifecycle_status, { live: 0, sold: 1, hidden: 2, awaiting_moderation: 3 }
scope :visible, -> { published.where(lifecycle_status: %w[live sold]) }
This nets me a bunch of convenience methods like draft? on instances, and lets me easily chain together scopes like I've done with visible up there. I love enums, and enums love me back ❤️
But another quirk of mine is that I will sometimes worry about unimportant stuff like database row size. Why use VARCHAR when INT do trick? Nobody should optimize prematurely, except when you totally should. That's why you hire us senior developers, we've been around the block a few times and we know that everyone ends up with that polymorphic "app events" table that eventually balloons to preposterous size and bogs down your database backups and queries. If you're going to have millions of records, putting 3 instead of awaiting_moderation in your database is going to save you a lot of time and space.
Until the AI gives you a frowny face
Problem is, nobody knows what that 3 means except for me. I'm looking at that hash, all smug in my Sublime Text window, while a BI person sits at the other end of Sweden pulling their hair and trying to work out what on God's green earth lifecycle_status=11 means. And with the advent of AI agents, verbose and readable is what wins the day. Code is documentation—but so is data. And having to teach (and re-teach, because you know what they're like) our stats agent which field means what is pretty tiresome. So I've gotten into the habit of doing this instead:
enum :published_status, { draft: "draft", published: "published" }
Nowadays when I need to retire an enum state, I no longer have to pretend like I'm an NHL team raising a jersey into the rafters. Just put down a new string, it explains itself and won't collide with anything else. Is the syntax uglier? YES. Honestly, aesthetics are another reason I think I've gone with integer enums in the past! But that is a bullet I get to chew on, because I really shouldn't be doing those idiot micro-optimizations, and neither should you. Store your enums as strings.