Wise Frontend Pair Programming Interview: JavaScript Tasks and Clear Reasoning

Prepare for Wise frontend pair programming with JavaScript quote-state tasks, stale-response tests, data transformations, and clear collaborative reasoning.

Author: PracHub

Published: 9/8/2026

Wise Frontend Pair Programming Interview: JavaScript Tasks and Clear Reasoning

September 8, 2026

Quick Overview

Prepare for Wise frontend pair programming with official format and AI-use rules, carefully limited candidate evidence, and two original JavaScript exercises. Practise latest-request quote state, revision-aware data transformation, deterministic tests, and clear explanations of changing requirements.

Frontend EngineerFree

In a Wise frontend pair programming interview, correct JavaScript is only part of the answer. Your partner also needs to understand the contract you are implementing, why the code has that shape, and how you know it behaves correctly when inputs change or asynchronous work finishes unexpectedly.

Official facts: Wise describes a hands-on JavaScript exercise that assesses collaboration, problem solving, and code structure. Our preparation recommendation: practise small functions with explicit state and testable boundaries before rehearsing a large framework application. The two exercises below are original practice material, not Wise interview questions.

Use PracHub's Frontend Engineer interview questions to build a focused practice queue, then explain your decisions aloud while solving them.

Clarify, implement, explain, and verify a small JavaScript task

What Wise confirms about the frontend round

Official format: Wise says the interview lasts around 60 minutes, including approximately 5–10 minutes of introductions and 40 minutes of collaborative coding, with questions at the end. The exercise uses JavaScript and an interactive platform such as HackerRank. Wise explicitly prohibits AI support during this stage. Its assessment includes how you communicate choices, incorporate suggestions, solve the problem, and write readable, testable code. Wise's frontend pair programming guidance.

Treat the invitation as the operational reference for your session. An example platform is not a promise about the exact editor, and a JavaScript exercise does not establish that React, a particular browser API, or a specific library will be required.

Official distinction: Wise describes frontend system design separately and says senior frontend applicants might be invited to that interview. Do not spend a small coding task delivering an architecture presentation before anything works. Wise's frontend system design guidance.

Candidate report, historical: a Tallinn frontend applicant's July 2025 account mentions a technical interview with live coding but preserves little task detail. It supports only that limited observation. We could not substantiate two independent same-cycle frontend accounts with detailed prompts, so the preparation below follows official guidance and original exercises rather than an alleged recurring question list. Taro's candidate account.

Start by agreeing on a small, testable contract

Before writing a function, establish its input, output, failure behaviour, and ownership of state. Ask questions that change the implementation. “Can a new input arrive while a request is pending?” is useful; asking for every possible future feature usually is not.

Then give your partner a short plan: “I'll make the normal path work, keep the request dependency injectable, and test an older response arriving after a newer one.” That statement identifies both the first increment and the main risk.

Our suggested rehearsal pacing: spend a few minutes clarifying, build a minimal working path, and leave time at the end for counterexamples and changes. This is a practice strategy, not Wise's internal scoring timetable. If the task is larger than expected, agree on which behaviour to complete first rather than quietly leaving several half-built branches.

Useful narration explains decisions. Describe why you copy an input, which request owns the screen, or why a rejected Promise should produce an error state. Reading each line aloud adds noise without exposing your reasoning.

Original JavaScript task: keep the latest quote on screen

Imagine a fictional transfer form where changing the amount requests a new quote. The network may return responses out of order. Implement a controller that publishes idle, loading, ready, or error states.

Agree on these exercise assumptions: inputs contain only an amount and two currency strings; a supplied validator checks them; the quote service returns a Promise; and the renderer is synchronous, does not throw, and does not call the controller recursively. A valid new input clears the displayed quote while loading. An invalid input immediately returns to idle. No money is sent by this exercise.

The central invariant is: only the newest input may publish a result or an error. “Newest” refers to input order, not response arrival time.

A minimal implementation uses a monotonically increasing version:

function createQuoteController(fetchQuote, render, isValid) {
  let version = 0;

  return async function update(input) {
    const mine = ++version;
    if (!isValid(input)) {
      render({ status: "idle" });
      return;
    }

    const request = { ...input };
    render({ status: "loading", request });

    let quote;
    try {
      quote = await fetchQuote(request);
    } catch (error) {
      if (mine === version) {
        render({ status: "error", request, error });
      }
      return;
    }

    if (mine === version) {
      render({ status: "ready", request, quote });
    }
  };
}

Incrementing the version before validation matters. If the customer clears the amount while a request is pending, that older request must lose permission to update the display. Incrementing only for valid inputs would leave a stale quote eligible to reappear.

The shallow copy also has a specific purpose: the primitive request fields retain their values if the caller later edits its input object. It is not a general deep-cloning solution. Under this contract, the request and rendered state should be treated as read-only by the service and renderer.

The try block covers the service call, including a synchronous throw before it returns a Promise. Rendering the successful result happens outside that block so a rendering bug is not mislabeled as a service failure. JavaScript async functions return Promises, and a rejection at an awaited operation can be handled with try/catch. MDN's async function reference.

Explain the race with a concrete sequence

EventActive versionExpected visible state
Enter amount 100; request A starts1Loading for 100
Change to 200; request B starts2Loading for 200
B resolves first2Quote for 200
A resolves later2Still the quote for 200
Clear the input3Idle; no old quote

Now change the fourth event: A rejects instead of resolving. The result should remain the same. Guarding success while leaving the error handler unguarded creates an equally confusing bug: an obsolete failure can replace a valid current quote.

Debouncing and cancellation address different concerns. Debouncing reduces how often work starts; it does not guarantee that already-started requests finish in order. Cancellation can stop supported work, while the version check determines which completion may affect state.

AbortController.abort() can abort supported operations such as fetch and response-body consumption. It does not make an arbitrary Promise cancelable or define your UI's ownership policy. MDN's abort reference.

If the interviewer asks for cancellation, add it behind the injected service contract and retain an explicit stale-result policy. If they ask for disposal when the component disappears, invalidate pending work and define whether later calls are ignored or rejected. State the new contract before adding another flag.

Request B completes before A; only the current version may update quote state

Test asynchronous behaviour without hoping the timing works

For this exercise, use deferred Promises: your test creates a Promise and keeps its resolve and reject functions. Start A and B, resolve B explicitly, await B's update call, then resolve A and await A's call. Inspect the recorded render states.

This makes the completion order intentional. A test that sleeps for an arbitrary number of milliseconds may pass because of favourable scheduling rather than because the ownership rule is correct.

Cover at least five distinct behaviours: newest success, obsolete success, obsolete failure, current failure, and clearing the input before a pending request settles. Also make the service throw synchronously once. Each case targets a different way the controller might publish the wrong state.

When a test fails, narrate the discrepancy: “The expected final state is ready for 200, but the old rejection emitted error. I guarded the success branch and missed the failure branch.” That is a clear diagnosis your partner can evaluate and extend.

Do not claim these tests prove a complete transfer experience is safe. They verify this controller's ordering contract. Quote expiry, server validation, accessibility announcements, and submission rules belong to separate requirements if the task introduces them.

Original data task: choose the latest route revision

A second fictional service returns quote-option revisions. Each record contains routeId, numeric revision, status, and feeMinor. Select the highest revision for each route, keep only routes whose latest revision is available, and order them by fee with a route-ID tie-breaker. Do not mutate the input.

Assume route IDs are ASCII strings, revisions are nonnegative safe integers, and equal revisions for the same route have identical contents. Fees are nonnegative safe integers in the same specified currency and minor unit. These assumptions keep comparison meaningful; comparing fee values across currencies would require a different contract.

Consider route A at revision 1 with an available quote, followed by revision 2 marked unavailable. Filtering unavailable rows first would resurrect the older offer. The required order is choose the latest revision, then filter availability.

function availableRoutes(rows) {
  const latest = new Map();
  for (const row of rows) {
    const previous = latest.get(row.routeId);
    if (!previous || row.revision > previous.revision) {
      latest.set(row.routeId, row);
    }
  }

  return [...latest.values()]
    .filter(row => row.status === "available")
    .map(row => ({ ...row }))
    .sort((a, b) => {
      if (a.feeMinor !== b.feeMinor) {
        return a.feeMinor - b.feeMinor;
      }
      return a.routeId < b.routeId ? -1
        : a.routeId > b.routeId ? 1 : 0;
    });
}

A Map makes the grouping key explicit. Its entries retain insertion order, but insertion order is not our final display contract; the comparator supplies that order. MDN's Map reference.

The output array is newly created, and each returned row is shallow-copied. sort() changes the array it operates on, so sorting a shared input array directly would violate the mutation requirement. MDN's sort reference.

Test an empty array, out-of-order revisions, an unavailable latest revision, tied fees, and frozen input objects. Then ask whether conflicting equal revisions are possible. The current assumptions exclude that conflict; silently choosing whichever row arrived first would be an undocumented policy if the assumption changes.

Explain the cost as one grouping pass plus sorting the retained routes. With ordinary hash-map behaviour, the grouping is expected linear work; sorting the available routes dominates as their count grows. Avoid claiming JavaScript specifies a particular sorting algorithm or worst-case Map implementation.

Show how you respond to feedback

A useful pair-programming exchange has three parts: acknowledge the observation, identify the affected rule, and make a bounded change. For example: “Keeping the previous quote visible could reduce flicker. I'll add a loading state that carries the previous quote, but label it as stale and keep submission disabled until the current request succeeds.”

That change is more than retaining an object. It changes what users can infer from the display, so the tests should cover both the visible quote and the current request status. Discuss the trade-off before implementing it.

If you forget syntax, describe the operation you intend and ask a precise question. If your partner proposes a simpler approach, evaluate it against the contract instead of defending your first draft automatically. The strongest explanation often ends with a small example both people can run.

Five focused questions for further practice

These are adjacent PracHub practice questions, not a Wise-specific question set. The bounded-concurrency page explicitly identifies its detailed exercise as a practice reconstruction; use it to train the stated skills without treating it as a preserved original prompt.

PracHub questionRehearsal focus
Build a Debounced Autocomplete Search BarSeparate request frequency from stale-response handling.
Run Promise Tasks with Bounded ConcurrencyExplain input order, completion order, and failure policy.
Group Events by an Ordered Attribute ListDefine grouping keys and deterministic output.
Flatten object & Promise.allClarify nested-data rules before implementing transformations.
Clarify and Implement Search Filters for a Library TableTurn ambiguous behaviour into an agreed contract.

Pick one task from PracHub's Frontend Engineer practice collection and solve it with a partner who changes one requirement. Finish by stating what works, what your examples verify, and which assumption still needs confirmation.

Sources and Further Reading


Comments (0)