Skip to content
spot_the_ai_bug

Race conditions: interview questions

A race condition is a bug whose outcome depends on timing: two goroutines, threads, processes or requests touching the same state in an order the code didn't plan for.

How to spot it in code review

  • Find shared state, then ask who else can modify it at the same time.
  • Be suspicious of check-then-act sequences (exists then create, read then write).
  • Remember that async responses can arrive out of order.
  • Prefer atomic operations, locks, or ownership by a single goroutine.

Why AI-generated code gets this wrong

Single-threaded examples dominate training data, and nothing in a one-request test reveals the interleaving.

Race conditions: 3 practice questions

Q1Pythonmedium

Check-then-create directory

Several worker processes call ensure_dir() at startup. Occasionally one crashes with FileExistsError. Why?

import os  def ensure_dir(path: str) -> str:    """Create the directory if it doesn't exist yet."""    if not os.path.exists(path):        os.makedirs(path)    return path
Show answer

line 7 · Race condition

This is a time-of-check to time-of-use race. Two processes can both see that the path doesn't exist, then both call makedirs, and the slower one raises FileExistsError. Make the operation itself idempotent instead of checking first.

Fix
    os.makedirs(path, exist_ok=True)

Interview follow-up: A good answer generalizes: prefer atomic operations (exist_ok, O_EXCL, INSERT ... ON CONFLICT) over check-then-act.

Search-as-you-type results

onQueryChange() runs on every keystroke. Users sometimes see results for "re" after typing "react". Why?

let results: string[] = []; export async function onQueryChange(query: string, render: (r: string[]) => void) {  const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);  const data: { results: string[] } = await res.json();  results = data.results;  render(results);}
Show answer

line 6 · Race condition

Requests can finish out of order. If the slow response for "re" arrives after the fast one for "react", it overwrites the newer results. Only the latest request should be allowed to update the UI.

Fix
let latest = 0; export async function onQueryChange(query: string, render: (r: string[]) => void) {  const id = ++latest;  const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);  const data: { results: string[] } = await res.json();  if (id !== latest) return; // a newer query has started  render(data.results);}

Interview follow-up: An AbortController that cancels the previous request is an equally good answer, and you can mention debouncing.

Q3Gomedium

Counting words in goroutines

CountWords splits work across goroutines. Under load the process dies with "fatal error: concurrent map writes". Why?

func CountWords(docs []string) map[string]int {	counts := make(map[string]int)	var wg sync.WaitGroup	for _, doc := range docs {		wg.Add(1)		go func(d string) {			defer wg.Done()			for _, w := range strings.Fields(d) {				counts[w]++			}		}(doc)	}	wg.Wait()	return counts}
Show answer

line 9 · Race condition

Go maps are not safe for concurrent writes. Several goroutines update counts at once, which the runtime detects and treats as a fatal error that can't be recovered.

Fix
	var mu sync.Mutex	// ... inside the goroutine:	for _, w := range strings.Fields(d) {		mu.Lock()		counts[w]++		mu.Unlock()	}

Interview follow-up: Better still: count into a local map per goroutine and merge at the end, so the lock is taken once per document.

Every day, everyone gets the same pull request written by AI. It looks clean. It has comments. It has exactly one real bug.

  1. 1

    Find the line

    Tap the buggy line, then hit Submit. You get 3 attempts.

  2. 2

    Read the hints

    A miss within 2 lines is 🔥 Warm. Anything further is ❄️ Cold.

  3. 3

    Name the bug

    Found it? Bonus round: pick the bug type from 4 options. It doesn't affect your win, just your bragging rights.

// your share card

found in 3/3

🟥 cold miss · 🟨 warm miss · 🟩 found · ⬛ unused

Keyboard: ↑ ↓ to move between lines, Enter to submit. A new bug drops every day at midnight IST.

0
Played
0
Win %
0
Streak
0
Max

Attempts distribution