Flags Are a Code Smell (2014)
sebastiansylvan.com
sebastiansylvan.com
Your comment sounds like you didn't even read the submission. Here's, quite literally, the second paragraph:
> "In my opinion these are code smells. Smells don’t necessarily indicate actual issues, just patterns that are likely to be used incorrectly and thus warrants extra care."
You see, the author points out quite unambiguously that context is key.
It's a very appropriate descriptor. Think about it for a second. A smell all by itself is not a sign of a problem.
> Then again, I suppose “maybe be careful with that pattern” isn’t quite the click-inducing thought leadership demanded in our present day.
Your personal assertion does not make any sense. "Code smell" is a widely-established, age-old concept. The concept of a "code smell" even precedes the concept of a "click".
I researched this throughly by reading HN and Twitter, and I can report that frontend JavaScript is the only form of software development now.
Global variables and computed gotos, all over the shop.
Does it work, well, safely, and quickly? Of course.
Would you write code like that? Well, that kind of depends, eh? Is it the correct solution to the problem you have *absolutely right now* and will it remain the correct solution in the face of changing requirements?
The author argues that instead of flags you could use some convoluted array of pointers to the objects for each flag to separate concerns.
Code working around dogmas is the worst "code smell" of them all.
Which is absolutely lovely if you have limitless memory and processing cycles. Make a sparse hash table of values to make sure you're only storing valid flags? Genius! How much memory is that going to take up?
I don't even know how much memory this modern desktop PC I have here has, hundreds or possibly even thousands of kilobytes of RAM. One of the microcontrollers I write code for has 128 bytes of RAM (this paragraph wouldn't fit) and a clock speed in the hundreds of kHz, so, if you're implementing hash tables Good Luck With That.
It reminds me of the BCS guys who reckoned that every bit of code should be written in Java, no matter what.
> Smells don’t necessarily indicate actual issues, just patterns that are likely to be used incorrectly and thus warrants extra care.
This talk gives a great overview of why boolean flags (rather, if-statements) can be a code smell: https://www.youtube.com/watch?v=4F72VULWFvc
OP's blogpost advocates for data-oriented design (e.g. Entity Component Systems) as a mechanism for avoiding this, whereas the talk I've linked advocates for OOP. Both mechanisms are equally valid (imho) and are inline with widely-adopted industry practices for software architecture.
But, yes, it's usually a different smell in that context because you just know someone is hot-gluing the exact bitmask for a thing into some hardware-agnostic controller code without writing a proper interface that translates from an internal representation (say, an enum) to actual physical bits.
[1] https://stackoverflow.com/questions/4941953/the-composite-pa...
More generally, I usually take boolean flags as an indication something may be able to be factored out. Rather than a sort function with a flag alternating between LEQ or GEQ (ie sort(boolean ascending)) , I would prefer separate sort_ascending or sort_descending for example.
These are bad flags and should not be mutually exclusive. "Disabled" usually applies to the physics system, and "visible" applies to the rendering system. You can certainly have something that renders but does not interact with the physics system (e.g. particle effects).
It's like throwing out all operator overloading because somebody decided to make "+" subtract instead of add.
In CRUD apps, database tables with lots of boolean columns are a data-architecture smell.
In my experience, the model encoded by such a record is almost always a — perhaps implicit — finite state machine of some kind. Usually, in CRUD stuff, this is a "lifecycle state" machine. (For example, the BlogComment lifecycle: draft → awaiting moderation → visible → deleted.)
But, because the people who create and evolve data schemas aren't usually familiar with FSMs, there's often no explicit "lifecycle state" enum-typed column in the DB record.
Instead, you get this pile of boolean columns, where each column represents the answer to a predicate that would take the model's lifecycle state as its input. Think e.g. `can_login?(User)` — which really just boils down to `can_login?(UserLifecycleState)`.
If you add the lifecycle-state column, then all the predicate-answer columns can go away. As long as you formally define+document the meaning of each lifecycle state, you can then statelessly recompute all those predicate-answers any time you like, on any layer of the stack you like. And now those predicate-answers will never be able to end up in an incoherent state, because they're never persisted; only the lifecycle-state itself is.
Of course, if you actually go all the way and make your model an FSM on the business layer as well, then you'll also get as a free benefit, the ability to validate lifecycle-state transitions — e.g. "Articles should only be able to enter the Published state from the Reviewed state."
This happens. It's not code smell. It's failure to forecast. An enum for one flag can be silly. Five flags instead of an enum is also silly. The latter doesn't just happen because the programmer didn't know about enums -- it happens organically over time.
1. A very small dimension table, defining the set of flags in terms of the current state: (user_lifecycle_state, is_a, is_b, is_c, ...). Join it to the fact table when you need it.
2. Computed columns in a writable view wrapping the table.
3. Procedures/functions that take the lifecycle state as input, so you can `SELECT u.id, ..., is_active(u.lifecycle_state) AS is_active FROM users`.
> which just hides/black-boxes the ugly code
I don't see what's ugly about it. The point of defining something as a FSM is that that's a formal definition on the design level, separate from any particular implementation; you can write a single stateless module that defines these predicates in terms of the lifecycle-states on any or all layers of the stack they're needed, and they'll all work the same, because they're all working off a formal definition of the meaning of each state.
Compare and contrast: Postgres has an `inet` type for IP addresses. It has a function `family(inet)`, which returns 4 or 6. Your business layer's runtime probably also has a function like that somewhere. They won't necessarily have the same outputs (i.e. your business layer's version might be a pair of predicates `is_ipv4?` and `is_ipv6?`) — but they'll both answer the same way semantically given the same inputs, because they're both working off of a shared formal definition of what an IP address is.
But in my experience, in almost all of these cases, the flags are stored in lieu of an FSM state; with the current FSM state implicit as a constraint-solution over the current set of predicate-answers. Which allows for incoherent states, because no FSM is actually being modelled in the business logic — even though the underlying business process the code is supposed to be modelling is inherently one involving state transitions.
Are you saying that sometimes people say "x is a code smell" without saying what the issue is?
Oh yeah... CRs with lines marked simply "code smell", not even an "x is a".
Article Driven Development. People are allowed to have their opinions, but sheesh. Just say "here is a problem you can run into with flags, better be careful!"
Though the author veers in another direction, I think his core point is related to this discussion:
Applying “make invalid states unrepresentable” https://news.ycombinator.com/item?id=24685772
Are you implying that Undead aren't people?
Also, magical Undead states aside, I'm pretty sure it is possible for a Person to be Walking while they are not technically Alive if something else is able to control their motor system as happens in nature with various Wasps, Slime Balls, and more.
You can also restructure the code with dependency injection. There's plenty of options that don't require duplication
Flags are for when future changes affect both options. "On top of the usual functionality, we can also optionally handle this other related thing" or so. Or --dry-run handling.
function doTheThing({typeOfThing, ...rest}) {
if (typeOfThing === Thing.FOO) {
doThingWithFoo(rest)
}
if (typeOfThing === Thing.BAR) {
doThingWithBar(rest)
}
}It’s a good argument for a custom sum type in place of the booleans.
data Boolean = True | False
The pipe character would be read as “or”. Boolean has two inhabitants. True being one, False being another one. 1 + 1
Alternatively, when you have a data structure like the example suggested by the author, it’s a product type with 3 fields, with Booleans inhabiting each (isDynamic, isVisible, and isEnabled), 2 * 2 * 2, 8 inhabitants.
The argued criticism of this encoding is that when isEnabled is false, no valid values exist for isDynamic or isVisible. They have no meaning for a disabled object.
My earlier definition for Boolean looks like an enum in some languages, but sum types can be more powerful if you’re allowed to define inhabitants with different shapes (curly braces denoting a record with named fields, “::” read as “has type”):
data GameObjectStatus = Disabled | EnabledWith {isVisible :: Boolean, isDynamic :: Boolean}
Now instead of having 8 inhabitants, you have 5 (a sum of 1 and 4) and they each have a valid conceptual meaning.
Languages with algebraic data types (sums and products) like Haskell (and IMO even better PureScript) let you easily define types that have only valid states.
Lots of game examples, saw some sql too.
What for me is the main reason not to like flags/booleans, is the current hype of MVP.
Yeah, sure, a decision cam be yes or no for the current process. But what about, yes, but.... or no, unless.... for later iterations?
Also, focussing on current user requirements leads to many booleans. What about, when the process is optimal, is it still a yes/no question?
I also notice questions on different levels. In case of visible and static, for instance. One has to do (i guess) with rendering behaviour, while the other has to do with (let me call it) game engine behaviour.
Is it then maybe also a case of (re-)defining the data models, as the element you're trying to specify gains depth?
Even, possibly, that your definitions have become convoluted? In the game example, a difference between the element as an actor in a game scene as opposed to its representation for a render engine?
But this actually seems to be talking about having object state represented internally as a pile of flags when something more semantically meaningful is available.
I wouldn't describe these open source games as having code smell, but rather just being very complex. Using flags seems to be a very effective way of encapsulating the complexity.
I have not looked at a popular open source game that has really used the approach described in the article. Does anyone have any references for that?
The switch/jmp table can be optimized to be inline, vtable will likely incur a stack frame, plus usually 3 indirections.
When you have a closed set of types, then you know all cases for branching, in polymorphism you lose this power -- it's open ended.
This is another reason why languages should have exhaustive pattern matching with sum types.
These don't matter much for big heavy functions, but tiny ones can seriously benefit from being inlined.
I have heard elsewhere that "game objects are just an index into a bunch of arrays for all the object properties" is fairly common practice, but well I don't write games myself.
That pretty much sums up all kinds of “smelly” practices.
In my experience, “God said it, I believe it, and that settles it” type pronouncements end up looking fairly silly. We do what needs doing, to achieve our ends.
At least, that’s what most experienced ship programmers do.
What I prefer (I program in Swift), are computed properties that examine the current state, and synthesize a report on demand.
Can’t always do that, though. Things like performance and threading can be factors. I just ran into something like that, tonight. Not sure how I’ll solve it. I’ll sleep on it.
I think the main point of this post is be careful with state explosion since each flag grows the number of states exponentially and likely not all of then are legal or make sense.
The other point is grouping similar things in memory is good for performance. Less failed branch predictions from checking flags and better cache coherence.
Thats not as a click bite-y of a title though.
Although the above is sound advice.
But now how do I tell if a company is a qualified vendor or not? If a user is active and can login.
I could have sworn I just read an article about putting code that's not ready behind a feature flag instead of long running git branches... guess that was a fever dream?