# Remove Control Flag

> Replace a boolean variable that steers a loop or function with break, continue or return so control flow is explicit.

- kind: technique
- category: simplifying-conditional-expressions
- aliases: Replace Control Flag with Break
- canonical: https://aipatterns.dev/refactoring/techniques/remove-control-flag
- inspired-by: https://refactoring.guru/remove-control-flag

## Problem

A loop or function uses a boolean such as `found`, `done` or `keepGoing` to decide whether to continue. The flag is set in one place and tested in another, so you have to trace every assignment to know when the code actually stops.

## Solution

**Delete the flag and exit at the point where it would have been set, using `break`, `continue` or `return`.** If the flag also carried a result, return that result directly.

## Before

```ts
function firstConflict(slot: TimeSlot, bookings: Booking[]): Booking | null {
  let conflict: Booking | null = null;
  let searching = true;
  for (let i = 0; i < bookings.length && searching; i++) {
    const b = bookings[i];
    if (b.status === "cancelled") {
      continue;
    }
    if (b.start < slot.end && slot.start < b.end) {
      conflict = b;
      searching = false;
    }
  }
  return conflict;
}
```

## After

```ts
function firstConflict(slot: TimeSlot, bookings: Booking[]): Booking | null {
  for (const b of bookings) {
    if (b.status === "cancelled") continue;
    if (b.start < slot.end && slot.start < b.end) return b;
  }
  return null;
}
```

## Why

- The exit point is where the decision is made, so a reader sees it at once.
- The loop header goes back to a plain iteration with no hidden second condition.
- Fewer mutable variables means fewer states to reason about and test.

## Steps

1. Find every assignment to the flag. Confirm what each one means: stop the loop, skip the rest of this iteration, or stop the function.
2. At the assignment that ends the work, add the matching `break`, `continue` or `return` right after it. Keep the flag for now. Run the tests.
3. If the flag also held a result, change that exit to `return result` directly. Run the tests.
4. Remove the flag from the loop condition, then delete its declaration and remaining assignments. Run the tests.
5. If code after the loop only ran when the flag was set, move it to the exit point or extract the loop into a function so `return` is available.

## Watch out

- A `break` inside a nested loop only exits the inner loop. Extract the loops into a function and `return`, or use a labeled `break` if your team accepts labels.
- If the code after the flag assignment did meaningful work in the same iteration (logging, counting), an early exit skips it. Move that work above the exit.
- In languages or style guides that require a single exit point, extract the search into its own small function so early returns stay local.
- Do not remove a flag that is part of an object's real state, such as `isCancelled` on a job. That is data, not control flow.
- There is no inverse technique.

## Related

- Shortens [Long Method](https://aipatterns.dev/refactoring/smells/long-method.md).
- Usually followed by [Replace Nested Conditional with Guard Clauses](https://aipatterns.dev/refactoring/techniques/replace-nested-conditional-with-guard-clauses.md).
- Use [Extract Method](https://aipatterns.dev/refactoring/techniques/extract-method.md) first when the loop sits in the middle of a larger function.

## Agent checklist

- [ ] Every flag assignment has been mapped to `break`, `continue` or `return`.
- [ ] Work that ran after the flag assignment in the same iteration still runs.
- [ ] Nested-loop exits leave the correct loop.
- [ ] The flag variable no longer exists, and tests pass.
