DEV Community

Nikita Grafov
Nikita Grafov

Posted on

The cost of boolean flags

TL;DR

APIs that use boolean flags extensively have reasons to be refactored.

Boolean flags are convenient

Most of the time boolean flags are the way to go for making the behavior of objects configurable. They are convenient for partially hiding text in a UI component or changing the way an entity handles incoming requests a bit. But do they come at a cost?

In the simplest case when an interface or a method has one or two flags they don't make any difference. Especially if all the combinations are actually used in a codebase. Things get more complicated as the number grows.

The cost of convenience

The first consequence of having a lot of flags is that APIs designed in this way might be difficult to understand. It forces an engineer to read more code at once, slowing them down and increasing the risk of misconfiguration.

Another consequence is tests. Ideally, all combinations of flags should be covered with tests which means the number of tests that should be written can grow rapidly.

And lastly, these APIs will probably keep getting more boolean flags, therefore, violating the Single responsibility and Open-closed principles. Instead of extending the system by adding new entities, the APIs will get more and more flags.

Improving the architecture

Inheritance and polymorphism can help to improve the architecture.

// Before
class LogEntry {
  constructor(private isError: boolean, private text: string)
  getColor() {
    if (this.error) {
      return 'red';
    } else {
      return 'black';
    }
  }
}


// After
abstract class LogEntry {
  constructor(private text: string) {}
  abstract getColor(): string;
}

class DebugLogEntry extends LogEntry {
  getColor() {
    return 'black';
  }
}

class ErrorLogEntry extends LogEntry {
  getColor() {
    return 'red';
  }
}

Composition can also be a good fit in other cases.

// Before
function getText(isMainView: boolean) {
  const additionalText = isMainView ? 'additional text' : '';
  return 'text' + additionalText;
}


// After
function getText() {
    return 'text';
}

function getAdditionalText() {
   return 'additional text';
}

function getMainViewText() {
  return getText() + getAdditionalText();
}

Top comments (0)