I don’t know about you but I always heard people say that immutability is so much better and safer than mutability and I never really understood why. Like yeah ok instead of returning the same object mutated, I return a new object so what? How does that help avoid bugs exactly? Well today I just found out about why and it’s actually simple so I’d like to explain it.
So I’m working on a game right now which start when there are 6 players in the lobby. The actual code uses Effect.ts, a TypeScript library which has a syntax that can be confusing, so I’ll this is a simple TypeScript implementation of it
type LobbyReturnValue =
| { status: "error", reason: string }
| { status: "success", player: Player }
class Lobby {
const MAX_PLAYERS_COUNT = 6;
const players: LobbyPlayers[] = [];
const isLobbyFull =
this.players.length >= MAX_PLAYERS_COUNT;
const isNameTaken = (name: string)
=> this.players.some((p) => p.name === name);
addPlayer(name: string) {
if (isNameTaken(name)) {
return {
status: "error",
message: "name already taken"
}
}
const newPlayer = new Player(name);
players.push(newPlayer)
return {
status: "success",
player: newPlayer
}
}
getPlayers() {
return this.players;
}
}
When a player joins a game, we have to check if his name is already taken and if the lobby is already full if it’s not, we create the player and return it.
Now we have the SocketHandler class (it’s a game that uses websocket) that has a startGame event that makes sure the lobby has at least 6 players and starts the game if it does
const canGameStart = () => lobby.getPlayers.length >= MAX_PLAYERS_COUNT;
socket.on("start-game", () => {
if !canGameStart return;
handleStartGame();
})
handleStartGame() {
const players = lobby.getPlayers();
for (const player of players) {
players.assignRole();
}
...
}
When we got the player’s list from the lobby, we got passed a reference to the list and this is the big issue with mutable data. It turns out that if the another player joins during the game, we now have the list in an unexpected state (containing 7 players instead of 6).
By returning a new state, we make sure that the data is in the exact same shape as we first got it.
class Lobby {
...
getPlayer() {
return [...this.players]
}
}
Yeah I have to admit that’s pretty cool I couldn’t wrap my head around why people died by the immutable data principle but now I see the light!