I added a real dictionary to Alfazy this weekend so the game stops accepting gibberish. Wired it up, felt good. Then it hit me that on a fresh Windows checkout the whole thing would reject every valid word -- and I would not have seen it until a real player did.
Alfazy is my Wordle-style game on the site. Until yesterday it let you guess any five letters -- 'qwrtz' counted as a turn. Cheap to ship, annoying to play. So I dropped in a ~15,000 word list as one big raw string and built a Set to check membership: split the text on newlines, done in a single line.
The one-liner was new Set(RAW.split(backslash-n)). Reads fine. Passes on my machine. Ships the bug anyway.
The bug I could not see
Here is the trap. I develop on Windows with git autocrlf on. When the file lands on disk every line ends with carriage-return plus newline, not just newline. Split that raw string on the newline alone and every word keeps a trailing carriage return. So the Set holds 'apple\r', 'table\r', 'sloth\r'. A player types 'apple', I look it up, it is not there. Every legal word silently rejected. The game becomes unwinnable and it looks like my dictionary is just wrong.
The nasty part is invisibility. A carriage return does not print. In a console log 'apple\r' looks exactly like 'apple'. You can stare at the two strings side by side and swear they match. I only caught it because the validation felt too strict when I played, and line endings are the first thing I check now after years of getting burned by exactly this on Windows.
The fix, and the lesson
The fix is boring: split on a regex that eats an optional carriage return before the newline, then filter out empty strings. Four lines instead of one.
Split on \r?\n so a CRLF checkout does not leave a carriage return on each word.
— the comment I left so future-me does not repeat this
The comment matters more than the code. The code looks obviously fine, so the next person -- me, in six months -- will 'simplify' it back to a plain newline split unless a note is screaming why not. Half of defensive code is really a message to the future.
The real lesson is not about line endings. It is that 'works on my machine' and 'works on a fresh checkout' are different claims, and the gap between them is where Windows quietly eats you. Newline handling, path separators, case-sensitive filenames -- none of it bites until the code runs somewhere that is not your laptop. Dictionary is live now, guesses are validated, and a stray blank line can never sneak an empty string in either. Small fix, but the kind that would have turned into a confusing bug report from a real player. I would rather kill those before they are typed.

