The Principle of Least Astonishment, and Naming
The Principle of Least Astonishment states that code should behave in expected ways without hidden surprises. The following is an example of how improperly named things will introduce bugs into systems.
Given a list of players, each player has multiple games, we want to
build a map from game IDs to game names. We could do so using a Map
and loops Java:
Map<Integer, String> games = ...;
for (var player : players) {
for (var game : player.games()) {
games.put(
game.id(),
game.name()
);
}
}
We could also do it using the stream API with the toMap
collector:
Map<Integer, String> games =
players
.stream()
.flatMap(Player::games)
.collect(toMap(
Game::id,
Game::name
));
Both seems reasonable and clear at first.
A Map
in Java doesn't contain duplicates, when putting a key value
into a map it will override existing mapping that has the same key,
this is standard behavior and everybody expects it. So when multiple
players share the same game, putting the same game ID to name mapping
into a map has no effect.
However the toMap
collector breaks this expectation, it will throw
an exception if it sees duplicates, this is rather surprising given
the name toMap
implies it would behave like a normal map would. As
more and more developers are refactoring loops into streams, hopefully
this difference is caught during testing instead of things breaking in
production.
The other problem is you tend to forget about things after a while, so
you started to make the same mistake again by using toMap
thinking
it behaves like a normal map, only to find yourself relearning the
same lesson again, and again, when it surprises you by breaking
things. Same goes for your friends.
It should have been given an abnormal name that fits with the abnormal behavior.