Medium zalando java

How would you implement a search box that suggests results on every keystroke?

The 30 second answer

The reported task is a search box that fetches suggestions on every keystroke. The important parts are a complete happy path, tests, clear names, and not calling the backend on every key. I debounce the input so a request goes out only after the user pauses, and I ignore stale responses so a slow earlier query cannot overwrite a later one. A short cache and a minimum query length are the usual next optimizations.

Reported. A 2024 frontend Senior Software Engineer take-home on Codility asked for a React and Node application: a search box with an auto-suggestion list on every keystroke. The write-up says the bar was the required features, unit tests with several scenarios, comments, meaningful names, and a performance habit — debounce on the keystroke handler. The later discussion asked what happens if imports are removed, how to optimize further, and whether you can write your own debounce.

This is a frontend exercise, included because it is a fully described Zalando coding task. On a Java backend interview the same performance idea shows up as "don't call the search API on every key."

Approach.

  1. Debounce the query. After each keystroke, reset a short timer (for example 200–300 ms, Inferred — the report did not name a delay). Only when the user pauses do you send the request. That cuts traffic and load on the suggest endpoint.
  2. Ignore stale responses. Keystrokes 1, 2, 3 can be in flight together. If the response for "sa" arrives after the response for "sal", the list would jump backwards. Give every request a monotonic id, or abort the previous fetch, and only publish a result when it still matches the latest query.
  3. Tests. Typeahead, empty query, no hits, a delayed earlier response, and the debounce itself (rapid keystrokes produce one call).
  4. Further optimizations, which match the reported follow-up: a minimum query length, an in-memory cache keyed by the normalized string, request cancellation, and pagination of the suggestion list.

A small Java debounce and stale-result guard, compiled and run on OpenJDK 21.0.13, behaves as follows: three tasks submitted 20 ms apart, with an 80 ms delay, run only the last one; after a second request id is issued, the first id is no longer current.

import java.util.concurrent.*;

final class Debouncer implements AutoCloseable {
    private final ScheduledExecutorService exec =
            Executors.newSingleThreadScheduledExecutor();
    private final long delayMs;
    private ScheduledFuture<?> pending;

    Debouncer(long delayMs) { this.delayMs = delayMs; }

    synchronized void submit(Runnable task) {
        if (pending != null) pending.cancel(false);
        pending = exec.schedule(task, delayMs, TimeUnit.MILLISECONDS);
    }

    @Override public void close() { exec.shutdownNow(); }
}

final class StaleGuard {
    private long latest;
    synchronized long nextId() { return ++latest; }
    synchronized boolean isCurrent(long id) { return id == latest; }
}

Edge cases. Empty and very short queries; the user clearing the box; duplicate keys; a failed request that must not block the next one; accessibility of the suggestion list (keyboard, screen reader) on a frontend implementation.

Sources (1)
  1. LeetCode Discuss: Zalando Senior Software Engineer (frontend) take-home (candidate report, July 2024)leetcode.com