Stop Using Flags to Control Your Loops

Stop Using Flags to Control Your Loops Featured Image

As someone who looks at a lot of beginner code, I often see students use flags to terminate loops. While there is nothing inherently wrong with this practice, and sometimes it might even be necessary, I find that code is often easier to write and read using a different approach. Let’s talk about it!

Table of Contents

What’s a Flag?

In code, a flag is a boolean variable (though, this is a fairly loose definition). That’s it! Here are some examples:

boolean isHurt = false;
boolean onCooldown = true;
boolean hasStrength = false;

In general, I can see why they’re enticing. After all, when I make a flag that has a descriptive name, it can make my code easier to read. And of course, booleans make a lot of sense: they have two states. For instance, from one of the examples above, I’m either hurt or I’m not.

That said, more often than not, I find flags to be troublesome in code, specifically loops. Let’s talk about it!

What’s Wrong With Flags?

There are a couple reasons why I don’t like flags. First, boolean variables in general don’t store a lot of information: only true or false. As a result, they don’t scale well as variables. For example, what if I had a game where I wanted to know if my player was hurt? We might have a boolean flag that tells us this. Though, I would argue that it’s better to make a method that tells us if the player is hurt based on their HP:

public boolean isHurt() {
  return this.hp < this.maxHP;
}

Here, the information about being hurt comes from the HP, not a boolean that we have to update anytime HP related events occur (e.g., every time you are attacked, drink a potion, and take environment damage). That’s a lot of updates!

Of course, that’s my broad complaint about flags. The focus of the article today is on the use of flags in loops, specifically loop conditions. For example, we might have a loop that deals decaying damage over time to our player. The loop should only terminate if the player dies or the ailment decays to zero, so we might write something like this:

boolean isDead = false;
boolean isCured = false;
while (!isDead && !isCured) {
  this.hp -= this.maxHP * this.dot;
  this.dot -= .01;
  if (this.hp <= 0) {
    isDead = true;
  }
  if (this.dot <= 0) {
    isCured = true;
  }
}

Now, experienced developers might say, “hey, wait a minute! You can simplify the if statements.” (Though, they might also point out the double comparison with equals…). As a result, you get something like this:

boolean isDead = false;
boolean isCured = false;
while (!isDead && !isCured) {
  this.hp -= this.maxHP * this.dot;
  this.dot -= .01;
  isDead = this.hp <= 0;
  isCured = this.dot <= 0;
}

To be fair, this is a lot cleaner. However, I don’t love it because we have to dig into the loop to figure out how these conditions are computed. Instead, I would prefer something like this:

while (this.hp > 0 && this.dot > 0) {
  this.hp -= this.maxHP * this.dot;
  this.dot -= .01;
}

And going back to what I said before, I would probably make these loop conditions into methods:

while (!this.isDead() && !this.hasDot()) {
  this.hp -= this.maxHP * this.dot;
  this.dot -= .01;
}

Regardless, the point is that I should be able to look at the loop condition to know when the loop will terminate. Having vague flags does not give me a feel for how or when they’ll be updated. Not to mention, they have to be updated regularly in various places in the code. Failure to do so will result in bugs.

Going a Bit Deeper: Loop Invariants

At this point, you may not be totally sold on removing flags from your code. That’s fine. However, let me share with you one last concept: loop invariants.

A loop invariant is a statement that is true about a loop before, during, and after the loop. In my experience, these types of statements are somewhat hard to write, but they have a bit of payoff. See, with a strong loop invariant, we can trace over a loop in a single step (without ever seeing inside the loop). For instance, here’s a simple loop invariant:

int x = 3;
int y = 7;
// x + y = 10 
while (x != 6) { ... }

Here, we have a loop where we don’t know what’s going on inside. However, we do know that two variables are changing: x and y. We know that because the loop condition depends on x becoming equal to 6 and x and y have to sum to 10. With this information, we can guarantee that the final value of x is 6 (assuming the loop terminates). Therefore, y has to be 4 to maintain the loop invariant.

To solve for y, we needed two pieces of information: the loop invariant and the loop condition. If for some reason the loop condition lacks critical information, the loop invariant becomes useless. For example, what if we had the same loop but it looked like this:

int x = 3;
int y = 7;
// x + y = 10 
while (!stop) { ... }

In this case, we have no clue what stop means, how it’s calculated, or how its updated. Therefore, we don’t know when the loop will terminate, so we can’t solve for either x or y. In fact, all we know is that the two variables will sum to 10, so the loop invariant itself is somewhat useless.

That said, people in general don’t write loop invariants, so who cares? Well, loop invariants force us to write explicit loop conditions which actually leads to better formed loops. I know the logic is somewhat circular, but the idea that the loop condition and loop body are separated follows a broader best practice: decoupling.

When we decouple the loop body from the loop condition, we always know where to look when trouble arises. Specifically, we know exactly when our loop will terminate strictly by looking at the condition. Therefore, if we have an infinite loop, chances are the issue is in the body. Similarly, if we ever need to rethink our loop condition, we don’t have to track down branches and flags in the body to make sure things work out.

Take My Advice With a Grain of Salt

In the world of writing, everything has to follow the trend of clickbait. Naturally, this article is no different. My general approach to software development is to do what makes sense to you. That said, folks when read my work if I don’t use sensational headlines like “NEVER USE FLAGS” or something like that. In this case, feel free to use them as needed. My only advice is that you may find loops easier to write and debug if your loop condition is all in one place.

With that said, thanks for sticking around! If you like this sort of thing, check out some of these related articles:

Likewise, consider showing your support by heading over to my list of ways to grow the site. There, you’ll find links to my newsletter and Patreon. Otherwise, take care!

Coding Tangents (22 Articles)—Series Navigation

As a lifelong learner and aspiring teacher, I find that not all subjects carry the same weight. As a result, some topics can fall through the cracks due to time constraints or other commitments. Personally, I find these lost artifacts to be quite fun to discuss. That’s why I’ve decided to launch a whole series to do just that. Welcome to Coding Tangents, a collection of articles that tackle the edge case topics of software development.

In this series, I’ll be tackling topics that I feel many of my own students have been curious about but never really got the chance to explore. In many cases, these are subjects that I think deserve more exposure in the classroom. For instance, did you ever receive a formal explanation of access modifiers? How about package management? Version control?

In some cases, students are forced to learn these subjects on their own. Naturally, this forms a breeding ground for misconceptions which are made popular in online forums like Stack Overflow and Reddit. With this series, I’m hoping to get back to the basics where these subjects can be tackled in their entirety.

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master's in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Posts