TT Lab
Get started
Learn Learning paths Courses

I searched for a whale, but got a cat

I searched for a whale, but got a cat

Continue in TT Lab

In one line

The text in the search box, the request sent to the server, and the result shown on screen are information from different points in time. If you think of all three as a single string, a slow past response takes over the latest screen.

Why this was needed

In a moon-base library, you searched for cat and immediately changed it to whale. Requests went to two observatories, and the whale response came back first. Right after whale appeared on screen, the cat response that had waited so long arrived. There is no error window and the HTTP response is a success, yet wrong data remains on screen. This is less a problem of the search algorithm than of who is entitled to change the screen right now.

If you disable the search button here, you can hide the symptom. But users must be able to fix a typo or change their mind. You must first judge whether making them wait fits the product requirements. This experiment allows new searches and chooses the policy of discarding responses that are no longer valid.

How it works

The draft being typed is a sentence that has not yet been submitted. A submitted request holds a search term and a submission number, because resubmitting the same search term can still be a new intent. Distinguish the screen states as follows.

State What the screen knows Guidance the user needs
idle No search has been submitted yet Enter a search term
loading Waiting for the response to a specific request What it is waiting for
success A list of results matching the request Which search the results belong to
empty The response succeeded but has no results Change the search term
error The request failed, and why How to try again

If you manage loading: boolean, error: boolean, and items: Result[] separately, you can also create a combination where loading and error are on at the same time. A union type discriminated by kind bundles the information each state must have. If you read items only in success and message only in error, contradictions are easy to spot in the render function as well. However, type checking does not guarantee response order, because cat and whale are both results of the correct type.

Let's write the event table first. The numbers are order, not milliseconds.

Event Description Search to display
1 Cat request Cat pending
2 Whale request Whale pending
3 Whale response Whale results
4 Cat response Still whale results

The last row is the product policy you must implement. You should be able to explain separately the fact that a response succeeded and the fact that it is still valid now.

Design the possible states, not the data

Compare the following two states. The strings in the example are samples to illustrate the format, and actual response values vary by request.

type View =
  | { kind: 'idle' }
  | { kind: 'loading'; query: string }
  | { kind: 'success'; query: string; titles: string[] }
  | { kind: 'error'; query: string; message: string };

const waiting: View = { kind: 'loading', query: '별 지도' };
const failed: View = {
  kind: 'error', query: '별 지도', message: '자료실에 연결할 수 없습니다'
};

Instead of turning both states on at once, pick one current state. If you choose error, it must carry which request failed and a guidance sentence together. The actual lab adds empty to this. When you add a new state, don't stop at changing the type definition — also look for the screen text and the transition checks.

If you move to loading when retrying after a failure but the old error text is still visible, the connection between state and screen is out of alignment. Don't look only at the function that creates the new request; check which fields the render branch reads. Conversely, having the results disappear first is not always the right answer. Some products keep the previous results while waiting for the new ones. If you choose that policy, you additionally need a type that expresses "previous results" and the current request state. This experiment uses the simple policy of clearing the previous results so that it can focus on the race.

What it looks like in the field

The same problem arises not only in search but also in address autocomplete, per-option product inventory, map location changes, and per-customer detail panels. If an FDE runs into a screen that occasionally flips to past values during a customer demo, they should first record the input time and the response time together. If you look only for failure logs, you miss the old responses that succeeded.

The Canonical Web Developer job posting checked on 2026-09-11 asks for TypeScript, responsive UI, accessibility, and complex UI performance, and lists large-scale React+TypeScript experience as a plus. This assignment turns those requirements into a small, observable task. A single EMEA posting does not guarantee hiring frequency across the whole market or a successful application.

What to check yourself

In the lab's request queue, deliver whale first and cat later. Read the input box and the result title separately, and record at which point the two diverge. Try to explain in one sentence why the screen can still be wrong even though the starter code passes type checking.

For the type representation, see TypeScript's explanation of discriminated unions.