Have No Side Effects
Clean Code introduces side effects in a somewhat casual terms:
To make it more formal: a side effect is any operation that:
- Modifies state outside the function's scope
- Interacts with the external world (I/O, network, database)
- Relies on non-deterministic behavior (e.g., random number generation, system clock)
- Throws exception (which can alter the program's control flow in unexpected ways)
Pure functions—those without side effects—are easier to reason about, test, and reuse. Like mathematical functions, they produce the same outputs given the same inputs, with no hidden interactions.
Martin's example misses critical issues:
public class UserValidator {
private Cryptographer cryptographer;
public boolean checkPassword(String userName, String password) {
User user = UserGateway.findByName(userName);
if (user != User.NULL) {
String codedPhrase = user.getPhraseEncodedByPassword();
String phrase = cryptographer.decrypt(codedPhrase, password);
if ("Valid Password".equals(phrase)) {
Session.initialize();
return true;
}
}
return false;
}
}
"The side effect is the call to Session.initialize(), of course. The checkPassword function, by its name, says that it checks the password."
He focuses on Session.initialize() as the side effect, suggesting a rename to checkPasswordAndInitializeSession. But this misses deeper problems:
UserGateway.findByName(userName)
is likely a database call - another side effect. This creates temporal coupling: authentication fails if the database is unavailable.UserGateway
is a singleton - a global implicit dependency.
This illustrates why formal understanding of side effects matters.
It also reveals a contradiction: Martin's advice to move input arguments to object fields creates side effects by definition - methods must mutate shared state.
In rewrite suggestion from chapter 2, all new methods have a side effect - they mutate fields - Modify "state outside the function's scope":
private void printGuessStatistics(char candidate,
int count) {
String number;
String verb;
String pluralModifier;
if (count == 0) {
number = "no";
verb = "are";
pluralModifier = "s";
} else if (count == 1) {
number = "1";
verb = "is";
pluralModifier = "";
} else {
number = Integer.toString(count);
verb = "are";
pluralModifier = "s";
}
String guessMessage = String.format(
"There %s %s %s%s", verb, number,
candidate, pluralModifier
);
►print(guessMessage);Interracts with the outside world - prints to STD-IO
}
public class GuessStatisticsMessage {
private String number;
private String verb;
private String pluralModifier;
public String make(char candidate, int count) {
►createPluralDependentMessageParts(count);Calling a function with side effects spreads those effects to the caller
return String.format(
"There %s %s %s%s",
verb, number, candidate, pluralModifier );
}
private void createPluralDependentMessageParts(int count) {
if (count == 0) {
►thereAreNoLetters();Calling a function with side effects spreads those effects to the caller
} else if (count == 1) {
►thereIsOneLetter();Calling a function with side effects spreads those effects to the caller
} else {
►thereAreManyLetters();Calling a function with side effects spreads those effects to the caller
}
}
private void thereAreManyLetters(int count) {
►number = Integer.toString(count);Modifies state outside function scope
►verb = "are";Modifies state outside function scope
►pluralModifier = "s";Modifies state outside function scope
}
private void thereIsOneLetter() {
►number = "1";Modifies state outside function scope
►verb = "is";Modifies state outside function scope
►pluralModifier = "";Modifies state outside function scope
}
private void thereAreNoLetters() {
►number = "no";Modifies state outside function scope
►verb = "are";Modifies state outside function scope
►pluralModifier = "s";Modifies state outside function scope
}
}
The "improved" version spreads side effects everywhere:
- Every helper method mutates shared state
- Calling methods with side effects propagates those effects upward
- The entire class becomes a web of interdependent state changes
This perfectly demonstrates why moving local variables to class fields often makes code harder to reason about, not easier.