The FizzBuzz test asks you to print the numbers from 1 to 100, but print “Fizz” for multiples of 3, “Buzz” for multiples of 5, and “FizzBuzz” for multiples of both. It is trivial on purpose: interviewers use it to filter out candidates who cannot turn a short written spec into working code. If you can solve it calmly and explain your choices, you pass. The interesting part is what a good interviewer does next.
Where the test comes from
FizzBuzz is a children’s counting game. It became a programming screen after Imran Ghory wrote about it in January 2007 in a post titled Using FizzBuzz to Find Developers who Grok Coding. His point was that some candidates talk well about programming but cannot actually write code, and a tiny exercise exposes that in a couple of minutes. It has been a standard first filter ever since.
The exact problem
- Go through the numbers 1 to 100.
- If the number is divisible by 3, print “Fizz”.
- If it is divisible by 5, print “Buzz”.
- If it is divisible by both, print “FizzBuzz”.
- Otherwise print the number.
The only trap is the third rule. If you check “divisible by 3” first, 15 prints “Fizz” and never reaches the “both” case. Most failed attempts are this exact ordering bug.
A correct solution
The version below returns a list instead of printing, so it can be tested. It builds the word for each number from the rules that match, so “FizzBuzz” appears naturally when both rules match, with no separate case to get wrong.
function fizzBuzz(n, rules = [[3, "Fizz"], [5, "Buzz"]]) {
const out = [];
for (let i = 1; i <= n; i++) {
let word = "";
for (const [divisor, text] of rules) {
if (i % divisor === 0) word += text;
}
out.push(word || String(i));
}
return out;
}
console.log(fizzBuzz(15).join(" "));
Output:
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
If you want the plain version that most interviewers expect first, this Python one checks the combined case first using i % 15 (15 is the least common multiple of 3 and 5):
def fizzbuzz(n):
result = []
for i in range(1, n + 1):
if i % 15 == 0:
result.append("FizzBuzz")
elif i % 3 == 0:
result.append("Fizz")
elif i % 5 == 0:
result.append("Buzz")
else:
result.append(str(i))
return result
print(" ".join(fizzbuzz(15)))
It prints the same line as above. Both versions run in O(n) time. The first version does one pass per rule for each number, so with r rules it is O(n * r), which is fine for a handful of rules.
Which version should you write?
Start with the plain one. A working, readable answer in a minute beats an elegant one that you fumble. Then, if the interviewer asks “what if the rules change?”, move to the rule-list version. Three things are worth saying out loud:
- Why check the combined case first (or concatenate): it shows you understand the ordering trap.
- Why return a list: printing inside the function makes it hard to test.
- Why not
i % 3 == 0 and i % 5 == 0: it is correct, just longer.i % 15is fine if you can say where 15 comes from, but it silently breaks if the divisors change to 4 and 6 (their common multiple is 12, not 24).
What interviewers look at beyond “does it work”
Because almost everyone who reaches an interview can produce a working FizzBuzz, interviewers use it as a conversation starter. These are the usual follow-ups.
Extensibility
“Add 7 as ‘Bazz’.” In the plain version this means a new branch for 7, plus 21, 35 and 105 for the combinations, which is where bugs appear. In the rule-list version it is one extra entry:
console.log(fizzBuzz(21, [[3, "Fizz"], [5, "Buzz"], [7, "Bazz"]]).slice(14).join(" "));
Output:
FizzBuzz 16 17 Fizz 19 Buzz FizzBazz
Note that 21 becomes “FizzBazz” with no extra code. That is the payoff of building the word from rules rather than listing combinations.
Testing
“How would you test this?” Because the function returns a list, you can assert on it. Cover the first value, a multiple of 3 only, a multiple of 5 only, a multiple of both, the length, and an empty input:
const assert = require("assert");
assert.deepStrictEqual(fizzBuzz(0), []);
assert.strictEqual(fizzBuzz(3)[0], "1");
assert.strictEqual(fizzBuzz(15)[14], "FizzBuzz");
assert.strictEqual(fizzBuzz(100).length, 100);
console.log("tests ok");
This prints tests ok. The point is not the assert library; it is that you think of the boundary values (0 numbers, the first number, 15) without being prompted. For more on this habit see the post on dry running code with sample test cases.
Readability
Interviewers read your code as a teammate would. Names like word, divisor and fizzBuzz are fine; x, tmp and a2 are not. Avoid clever one-liners that you could not explain line by line.
Edge cases and inputs
Ask what should happen for n = 0, negative n, or a non-integer. Often the answer is “assume a positive integer”, but asking shows you notice the question exists. Also ask whether you should print or return, since the original statement says print.
Variants
Some interviewers change the rules to see whether you adapt rather than recite: no modulo operator, no if statements, print in reverse order, or make the rules come from configuration. Without modulo you can keep counters that reset at 3 and 5. Be ready to explain the trade-off, because these versions are usually less readable than the simple one.
Common mistakes
- Wrong order of checks: testing 3 before “both” so 15 prints “Fizz”.
- Off-by-one: looping from 0 (0 is divisible by everything and prints “FizzBuzz”) or stopping at 99.
- Printing “FizzBuzz” plus the number by forgetting an
else. - Hard-coding 100 inside the function with no parameter.
- Freezing: the common failure in a real interview is nerves, not knowledge. Say the rules back, write the plain version, then improve it.
How to practice
FizzBuzz itself takes minutes to learn, so spend the effort on the habits it tests: reading a spec carefully, writing something that works first, then testing and refactoring it while talking. To practice it with step-by-step hints, AlgoCademy has a Fizz Buzz lesson in JavaScript (a Python version exists too). After that, the real interview questions get harder quickly, so move on to arrays, strings and hash maps rather than polishing FizzBuzz further.
