<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Karuha</title>
    <description>The latest articles on DEV Community by Karuha (@karuha).</description>
    <link>https://dev.to/karuha</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3841947%2Fd1e11c25-c7f9-49c8-b876-d9ef993f9f9e.jpg</url>
      <title>DEV Community: Karuha</title>
      <link>https://dev.to/karuha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/karuha"/>
    <language>en</language>
    <item>
      <title>WebSocket Reconnects Are a State Machine: Build a Deterministic Interview Drill</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Sat, 22 Aug 2026 10:27:11 +0000</pubDate>
      <link>https://dev.to/karuha/websocket-reconnects-are-a-state-machine-build-a-deterministic-interview-drill-1p07</link>
      <guid>https://dev.to/karuha/websocket-reconnects-are-a-state-machine-build-a-deterministic-interview-drill-1p07</guid>
      <description>&lt;p&gt;A reconnect loop is not just &lt;code&gt;call connect()&lt;/code&gt; again after a delay. It is a small state machine with a clock, cancellation, and an upper bound. In this drill, you will build one in plain Node.js and prove four contracts: backoff grows and caps, a successful connection resets the attempt count, stopping cancels pending work, and an old timer can never start a second connection.&lt;/p&gt;

&lt;p&gt;That last contract is the one I listen for in interviews. Most reconnect bugs are not caused by the formula. They happen when two close events schedule two timers, or when a timer created before &lt;code&gt;stop()&lt;/code&gt; still fires after logout.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should a reconnect controller promise?
&lt;/h2&gt;

&lt;p&gt;Before writing code, write the observable behavior:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A closed socket schedules at most one reconnect attempt.&lt;/li&gt;
&lt;li&gt;Delays grow exponentially, but never exceed a cap.&lt;/li&gt;
&lt;li&gt;The first successful &lt;code&gt;open&lt;/code&gt; returns the client to a clean baseline.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stop()&lt;/code&gt; makes future callbacks harmless.&lt;/li&gt;
&lt;li&gt;A callback from an older schedule cannot change the current state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is deliberately smaller than a production WebSocket client. The point is to isolate the policy so that the policy can be tested without a real network, browser, or fake server.&lt;/p&gt;

&lt;p&gt;A useful state vocabulary is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;idle&lt;/code&gt;: no connection attempt has started.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;connecting&lt;/code&gt;: a connection attempt is in flight.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open&lt;/code&gt;: the socket is usable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;waiting&lt;/code&gt;: a reconnect timer exists.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stopped&lt;/code&gt;: the owner has cancelled the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Transitions should be explicit. If a timer callback can “just call connect,” you have hidden a transition and made it hard to reason about races.&lt;/p&gt;

&lt;h2&gt;
  
  
  A dependency-free controller
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;reconnect-drill.mjs&lt;/code&gt; and run it with Node 18 or newer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exponential&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exponential&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exponential&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReconnectController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connecting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connecting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;onOpen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// invalidates every older timer&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opened&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;waiting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scheduled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;random&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Contract 1: exponential delay grows, then caps.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;random&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delays&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Contract 2: an open connection resets the next failure to the base delay.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReconnectController&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onOpen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;afterOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;afterOpen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;afterOpen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Contract 3: stopping makes a pending timer harmless.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stopped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReconnectController&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;stopped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stopped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;stopped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stopped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Contract 4: an older timer cannot win a race with a newer schedule.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReconnectController&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;waiting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connecting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reconnect state-machine assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node reconnect-drill.mjs
&lt;span class="c"&gt;# reconnect state-machine assertions passed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;generation&lt;/code&gt; value is a cheap cancellation primitive. Every operation that invalidates a timer increments it. A callback carries the generation it was created with, and &lt;code&gt;fire()&lt;/code&gt; refuses to act when the token is old. In a browser implementation, you would still call &lt;code&gt;clearTimeout()&lt;/code&gt; for efficiency. The token check is the correctness boundary because a callback may already be queued when &lt;code&gt;clearTimeout()&lt;/code&gt; runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the obvious implementation fails
&lt;/h2&gt;

&lt;p&gt;A common first draft looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has at least four failure modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two &lt;code&gt;close&lt;/code&gt; events create two timers and two simultaneous sockets.&lt;/li&gt;
&lt;li&gt;A fixed delay causes many clients to reconnect together after an outage.&lt;/li&gt;
&lt;li&gt;A successful connection does not reset the attempt counter, so a later short outage starts at a needlessly large delay.&lt;/li&gt;
&lt;li&gt;A logout path cannot prove that a queued callback will not reconnect the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exponential backoff reduces synchronized pressure, but it is not a complete strategy. In a real client, add a small random jitter, cap the delay, and decide whether the server can send a retry hint. Treat server-provided hints as untrusted input: clamp them to a policy maximum and record why they were accepted.&lt;/p&gt;

&lt;p&gt;The policy also needs an owner. “Reconnect forever” is not a requirement; it is an accidental resource leak. A page hidden for hours, a signed-out account, or a mobile app in the background may need a stopped state. The owner should be able to cancel the loop and observe that cancellation in metrics.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to narrate this in an interview
&lt;/h2&gt;

&lt;p&gt;When asked to design reconnects, answer in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State the invariant.&lt;/strong&gt; “There is at most one pending reconnect and at most one active socket.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name the transitions.&lt;/strong&gt; “Close moves to waiting; a valid timer moves to connecting; open resets attempts; stop invalidates all future work.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Give the timing rule.&lt;/strong&gt; “Use capped exponential backoff with jitter, for example &lt;code&gt;min(cap, base * 2^(attempt-1))&lt;/code&gt;.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Describe the race.&lt;/strong&gt; “A stale timer can fire after a newer close or after logout, so every callback carries a generation token.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the recovery boundary.&lt;/strong&gt; “Reset the attempt count only after &lt;code&gt;open&lt;/code&gt;, not after merely starting a connection.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add observability.&lt;/strong&gt; Log attempt number, chosen delay, close reason, and final stop reason. Do not log message payloads just to debug connectivity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That explanation is stronger than reciting “use exponential backoff.” It shows you understand time, ownership, and failure ordering.&lt;/p&gt;

&lt;h2&gt;
  
  
  What would change in production?
&lt;/h2&gt;

&lt;p&gt;The drill intentionally leaves out transport details. A production implementation still needs to answer these questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the handshake authenticated, and how are expired credentials refreshed?&lt;/li&gt;
&lt;li&gt;Should messages be queued while &lt;code&gt;waiting&lt;/code&gt;, or dropped with an explicit error?&lt;/li&gt;
&lt;li&gt;Are outbound messages idempotent if a reconnect causes a retry?&lt;/li&gt;
&lt;li&gt;Does the server close with a code that means “do not reconnect”?&lt;/li&gt;
&lt;li&gt;How do multiple browser tabs coordinate so they do not all maintain a connection?&lt;/li&gt;
&lt;li&gt;Which metrics distinguish a flaky client network from a server outage?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep those decisions outside the backoff function. That separation lets you test the policy with deterministic inputs and test the transport with a small number of integration cases.&lt;/p&gt;

&lt;p&gt;For practice, record yourself explaining the four contracts, then replay the answer and challenge each assumption with a failure scenario. A tool such as &lt;a href="https://aceround.app" rel="noopener noreferrer"&gt;aceround.app — AI interview assistant&lt;/a&gt; can generate follow-up questions, but the useful part is still the evidence: show the invariant, run the assertion, and explain the trade-off.&lt;/p&gt;

&lt;h2&gt;
  
  
  A compact checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping a reconnect loop, verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] One active socket and one pending timer at most&lt;/li&gt;
&lt;li&gt;[ ] Delay grows, includes bounded jitter, and has a cap&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;open&lt;/code&gt; resets the attempt counter&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;stop&lt;/code&gt; invalidates queued callbacks&lt;/li&gt;
&lt;li&gt;[ ] Stale timers are rejected by a token or equivalent&lt;/li&gt;
&lt;li&gt;[ ] Server retry hints are clamped&lt;/li&gt;
&lt;li&gt;[ ] Close reasons and attempts are observable&lt;/li&gt;
&lt;li&gt;[ ] Message replay semantics are explicit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interview lesson is simple: a reconnect policy is a state machine with a clock. Once you write down the states and make stale work unconditionally harmless, the hard part becomes testable instead of hand-wavy.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket" rel="noopener noreferrer"&gt;MDN: WebSocket&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc6455" rel="noopener noreferrer"&gt;RFC 6455: The WebSocket Protocol&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I used an AI writing assistant to help outline and edit this article. I reviewed the code and claims, and the embedded Node.js assertions are intended to run as shown.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>career</category>
      <category>interview</category>
      <category>testing</category>
    </item>
    <item>
      <title>Turn a Pull Request Into a 10-Minute Technical Interview Drill</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Fri, 21 Aug 2026 02:50:46 +0000</pubDate>
      <link>https://dev.to/karuha/turn-a-pull-request-into-a-10-minute-technical-interview-drill-2l9</link>
      <guid>https://dev.to/karuha/turn-a-pull-request-into-a-10-minute-technical-interview-drill-2l9</guid>
      <description>&lt;p&gt;The fastest way to make a technical answer sound generic is to rehearse it without a real change in front of you. Pick a pull request you can explain, spend 10 minutes turning its diff into a walkthrough, and you will have concrete decisions, risks, and verification steps to discuss instead of a memorized architecture speech.&lt;/p&gt;

&lt;p&gt;This is not a trick for performing expertise you do not have. It is a way to surface work you already did: why the change exists, where it can fail, and what evidence would make you trust it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a PR instead of an interview prompt?
&lt;/h2&gt;

&lt;p&gt;Interview prompts are intentionally underspecified. A real pull request is the opposite: it contains constraints. There are files, tests, callers, migrations, and review comments. That makes it a useful rehearsal artifact.&lt;/p&gt;

&lt;p&gt;For a 45-minute technical interview, a strong answer usually needs four things:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What the interviewer can learn&lt;/th&gt;
&lt;th&gt;Evidence you can point to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;Why the work mattered&lt;/td&gt;
&lt;td&gt;Issue, incident, or user path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decision&lt;/td&gt;
&lt;td&gt;Why this approach won&lt;/td&gt;
&lt;td&gt;Constraints and rejected options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safety&lt;/td&gt;
&lt;td&gt;How you controlled risk&lt;/td&gt;
&lt;td&gt;Tests, compatibility, rollout plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outcome&lt;/td&gt;
&lt;td&gt;What you checked afterward&lt;/td&gt;
&lt;td&gt;Metrics, logs, or manual verification&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The mistake is reading the diff line by line. A reviewer can do that alone. Your job is to connect the changes to the system behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  A ten-minute rehearsal loop
&lt;/h2&gt;

&lt;p&gt;Use this sequence once per PR. It is deliberately short enough to repeat.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Minute 0–2 — name the before-and-after.&lt;/strong&gt; Write one sentence: “Before, &lt;em&gt;X&lt;/em&gt; happened when &lt;em&gt;Y&lt;/em&gt;; now, &lt;em&gt;Z&lt;/em&gt;.” If you cannot make that sentence concrete, you are not ready to explain the change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minute 2–4 — choose the risk.&lt;/strong&gt; Pick the one failure that would matter most: a stale write, an authorization bypass, a breaking API response, or a dropped background job. Do not list every theoretical risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minute 4–7 — narrate the control.&lt;/strong&gt; Explain the code path that prevents or exposes that failure. Mention the test or observable signal that backs it up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minute 7–10 — answer one follow-up.&lt;/strong&gt; Ask yourself what you would change at 10× traffic, with an older client, or after a retry.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtQUiBkaWZmXSAtLT4gQltCZWhhdmlvcl0KICBCIC0tPiBDe01haW4gcmlza30KICBDIC0tPiBEW0NvbnRyb2xdCiAgRCAtLT4gRVtUZXN0IG9yIHByb2R1Y3Rpb24gc2lnbmFsXQ%3D%3D%3Fwidth%3D1000%26height%3D420%26bgColor%3D0f172a" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtQUiBkaWZmXSAtLT4gQltCZWhhdmlvcl0KICBCIC0tPiBDe01haW4gcmlza30KICBDIC0tPiBEW0NvbnRyb2xdCiAgRCAtLT4gRVtUZXN0IG9yIHByb2R1Y3Rpb24gc2lnbmFsXQ%3D%3D%3Fwidth%3D1000%26height%3D420%26bgColor%3D0f172a" alt="A PR walkthrough moves from changed files to a user-facing behavior, a risk, a control, and verification." width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate the questions from a real diff
&lt;/h2&gt;

&lt;p&gt;The script below is intentionally small. It reads the names of files changed against a base branch and produces follow-up prompts based on likely risk areas. It does not inspect source code or claim to understand your system. You supply the context; the script makes it harder to skip the important questions.&lt;/p&gt;

&lt;p&gt;Save it as &lt;code&gt;pr-walkthrough.mjs&lt;/code&gt;, then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch origin main
node pr-walkthrough.mjs origin/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;origin/main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--name-status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;...HEAD`&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Could not compare &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; with HEAD.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch a base branch with shared history, then try again.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Example: git fetch --deepen=100 origin main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;src|app|pages&lt;/span&gt;&lt;span class="se"&gt;)\/&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Which user-facing behavior changes, and how would you verify it manually?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;api|router|route|server&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the request contract, and which old callers could break?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;db|schema|migration|prisma|drizzle&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Can old and new code run against this data shape at the same time?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;auth|permission|role|token|session&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Who is allowed to take this path, and how is that checked on the server?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;test|spec&lt;/span&gt;&lt;span class="se"&gt;)\.&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Which failure would this test catch that a happy-path demo would miss?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;queue|worker|job|cron&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What happens on retry, duplicate delivery, or a partial failure?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`No changes found against &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Fetch the base branch or pass a different ref.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;questions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`# PR walkthrough: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; changed file&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;## Start with the change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`- …and &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; more`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;## Rehearse these follow-ups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`- (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; file&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;- What did you deliberately leave unchanged, and why?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;- What observable signal would tell you the rollout is safe?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what a useful result looks like for a change that touches an API route, a database migration, and tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PR walkthrough: 4 changed files

## Rehearse these follow-ups
- (1 file) What is the request contract, and which old callers could break?
- (1 file) Can old and new code run against this data shape at the same time?
- (1 file) Which failure would this test catch that a happy-path demo would miss?
- What did you deliberately leave unchanged, and why?
- What observable signal would tell you the rollout is safe?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That output is a prompt sheet, not a script you should recite. Answer every line with a detail visible in the PR. “We added a migration” is not enough; “the new column is nullable first, so the old worker can continue writing while the backfill runs” is an explanation a teammate can challenge productively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn one change into a concise answer
&lt;/h2&gt;

&lt;p&gt;Suppose the change adds an idempotency key to a payment endpoint. A vague answer sounds like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I made the endpoint retry-safe and wrote tests.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The rehearsal version has a shape:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Retries from the client could create two charges when the response was lost. I stored a key plus a request fingerprint before calling the provider, so the same key and payload return the first result while a conflicting payload is rejected. The test starts two requests with the same key and asserts one provider call. In production, I would watch duplicate-charge support tickets and the rate of key conflicts; an unexpected rise in conflicts suggests a client bug rather than a successful retry.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice the answer is still short. It establishes a cause, a control, a proof, and a signal. If the interviewer asks about multi-region writes or provider timeouts, you now have a real boundary to discuss instead of inventing one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do when the PR is too large
&lt;/h2&gt;

&lt;p&gt;Large diffs create bad rehearsal sessions because they invite a tour of unrelated files. Narrow the artifact before you practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select one user journey, such as “checkout after a network retry.”&lt;/li&gt;
&lt;li&gt;Select one invariant, such as “a completed order is charged exactly once.”&lt;/li&gt;
&lt;li&gt;Select one proof: a test, a dashboard, or a manual reproduction.&lt;/li&gt;
&lt;li&gt;Put the remaining changes in the “what I would cover next” bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also a good way to answer “tell me about a challenging project” without wandering through three months of work. You can say which slice you are choosing and why it best demonstrates the trade-off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice the follow-up, not just the opening
&lt;/h2&gt;

&lt;p&gt;The best self-check is to ask a hostile-but-fair question after each answer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If the interviewer asks…&lt;/th&gt;
&lt;th&gt;Do not say…&lt;/th&gt;
&lt;th&gt;Try explaining…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;“Why not just retry?”&lt;/td&gt;
&lt;td&gt;“Retries are bad.”&lt;/td&gt;
&lt;td&gt;Which action is safe to repeat, which one is not, and where the deduplication record lives.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;“How did you test that?”&lt;/td&gt;
&lt;td&gt;“I added unit tests.”&lt;/td&gt;
&lt;td&gt;The exact precondition, failure mode, and invariant the test asserted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;“What would you do differently?”&lt;/td&gt;
&lt;td&gt;“I would improve it.”&lt;/td&gt;
&lt;td&gt;The constraint that would justify a different design: scale, consistency, cost, or time to recover.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Record one run, then listen for two failure modes: unsupported claims (“it was more scalable”) and missing evidence (“we monitored it”). Replace either with a concrete number, test name, log field, or user behavior from the work.&lt;/p&gt;

&lt;p&gt;If you want a structured place to repeat these sessions, &lt;a href="https://aceround.app" rel="noopener noreferrer"&gt;aceround.app — an AI interview assistant&lt;/a&gt; can be useful for running a mock round. Bring the PR and your own evidence; treat any feedback as a prompt to sharpen the explanation, not as a substitute for understanding the change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources and disclosure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The command interface used here follows the &lt;a href="https://git-scm.com/docs/git-diff" rel="noopener noreferrer"&gt;Git documentation for &lt;code&gt;git diff&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;AI assistance was used for copyediting. The workflow and code were reviewed and tested by the author.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>interview</category>
      <category>javascript</category>
      <category>git</category>
    </item>
    <item>
      <title>Conditional Requests Are an Interview Contract: Build an ETag Drill in Node.js</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Wed, 19 Aug 2026 08:47:31 +0000</pubDate>
      <link>https://dev.to/karuha/conditional-requests-are-an-interview-contract-build-an-etag-drill-in-nodejs-285l</link>
      <guid>https://dev.to/karuha/conditional-requests-are-an-interview-contract-build-an-etag-drill-in-nodejs-285l</guid>
      <description>&lt;h1&gt;
  
  
  Conditional Requests Are an Interview Contract: Build an ETag Drill in Node.js
&lt;/h1&gt;

&lt;p&gt;An ETag is not just a cache header. In a backend interview, it is a compact way to show that you can define an HTTP contract, avoid needless transfer, and prevent an older edit from silently overwriting a newer one. This small Node.js drill implements both paths: &lt;code&gt;304 Not Modified&lt;/code&gt; for cache revalidation and &lt;code&gt;412 Precondition Failed&lt;/code&gt; for stale writes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtDbGllbnQgY2FjaGVdIC0tPiBCe0lmLU5vbmUtTWF0Y2ggbWF0Y2hlcz99CiAgQiAtLT58eWVzfCBDWzMwNDogcmV1c2UgY2FjaGVkIGJvZHldCiAgQiAtLT58bm98IERbMjAwOiBzZW5kIHJlcHJlc2VudGF0aW9uIGFuZCBFVGFnXQogIEVbVXBkYXRlIHJlcXVlc3Qgd2l0aCBJZi1NYXRjaF0gLS0-IEZ7VGFnIGN1cnJlbnQ_fQogIEYgLS0-fHllc3wgR1syMDA6IHdyaXRlIG5leHQgdmVyc2lvbl0KICBGIC0tPnxub3wgSFs0MTI6IHJlZmV0Y2ggYW5kIHJlc29sdmVd%3FbgColor%3D0f172a%2526width%3D1000%2526height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtDbGllbnQgY2FjaGVdIC0tPiBCe0lmLU5vbmUtTWF0Y2ggbWF0Y2hlcz99CiAgQiAtLT58eWVzfCBDWzMwNDogcmV1c2UgY2FjaGVkIGJvZHldCiAgQiAtLT58bm98IERbMjAwOiBzZW5kIHJlcHJlc2VudGF0aW9uIGFuZCBFVGFnXQogIEVbVXBkYXRlIHJlcXVlc3Qgd2l0aCBJZi1NYXRjaF0gLS0-IEZ7VGFnIGN1cnJlbnQ_fQogIEYgLS0-fHllc3wgR1syMDA6IHdyaXRlIG5leHQgdmVyc2lvbl0KICBGIC0tPnxub3wgSFs0MTI6IHJlZmV0Y2ggYW5kIHJlc29sdmVd%3FbgColor%3D0f172a%2526width%3D1000%2526height%3D420" alt="A flow diagram showing cache revalidation with If-None-Match and guarded writes with If-Match" width="890" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A common interview prompt sounds deceptively simple: "How would you add caching to this profile endpoint?" A weak answer jumps to Redis. A stronger one first asks what a client is allowed to reuse, how it knows that a representation changed, and what happens when two clients edit the same record.&lt;/p&gt;

&lt;p&gt;HTTP already gives us useful vocabulary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Server condition&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;GET&lt;/code&gt; with &lt;code&gt;If-None-Match&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Client's validator still matches&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;304&lt;/code&gt;, no body&lt;/td&gt;
&lt;td&gt;The client reuses its cached representation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PUT&lt;/code&gt; with &lt;code&gt;If-Match&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Client's validator matches current state&lt;/td&gt;
&lt;td&gt;&lt;code&gt;200&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The write is based on the version the client read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PUT&lt;/code&gt; with &lt;code&gt;If-Match&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Validator is stale&lt;/td&gt;
&lt;td&gt;&lt;code&gt;412&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The server refuses a lost update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PUT&lt;/code&gt; without &lt;code&gt;If-Match&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;This API requires a precondition&lt;/td&gt;
&lt;td&gt;&lt;code&gt;428&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A caller cannot accidentally bypass concurrency control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The semantics come from &lt;a href="https://www.rfc-editor.org/rfc/rfc9110#section-13" rel="noopener noreferrer"&gt;RFC 9110's conditional-request section&lt;/a&gt;. The important part is not memorizing status codes. It is making the resource lifecycle explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we proving?
&lt;/h2&gt;

&lt;p&gt;Our tiny endpoint will preserve four properties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A client that already has the current representation gets a &lt;code&gt;304&lt;/code&gt; and no response body.&lt;/li&gt;
&lt;li&gt;A changed representation gets a new ETag.&lt;/li&gt;
&lt;li&gt;A write without an &lt;code&gt;If-Match&lt;/code&gt; precondition is rejected for this resource.&lt;/li&gt;
&lt;li&gt;A stale write cannot overwrite the current record.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is enough to practice a complete answer without hiding the hard parts behind a framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the smallest useful contract
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;etag-drill.js&lt;/code&gt; and run it with Node 18 or later. It has no dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="s2"&gt;`"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cache-control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private, max-age=60&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;if-none-match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;304&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cacheHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cacheHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;if-match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;428&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentTag&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;If-Match required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;if-match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;currentTag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;412&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentTag&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stale representation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;u1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mina&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstRead&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstRead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstRead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cachedRead&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;if-none-match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firstRead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;etag&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedRead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;304&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedRead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;updateProfile&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mina K.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;428&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;updateProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;if-match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;old&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mina K.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;412&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accepted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;updateProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;if-match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firstRead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;etag&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mina K.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accepted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accepted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accepted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;firstRead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;etag contract assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hash is only a stand-in for a production validator. The point is that every representation has a stable, opaque identifier, and a material change produces a different one. Clients should treat an ETag as opaque; they do not need to know whether it came from a version column, a content hash, or an object-store generation number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does &lt;code&gt;304&lt;/code&gt; need an empty body?
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;304&lt;/code&gt; means "your stored representation is still valid." Sending the body again defeats the main purpose: a browser, mobile app, or CDN can reuse bytes it already owns. The response should still include the relevant cache metadata, especially the ETag and cache-control policy.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;private&lt;/code&gt; directive is intentional here. A profile may vary by viewer or contain user-specific fields, so a shared cache must not serve one person's representation to another. For a truly public resource, &lt;code&gt;public&lt;/code&gt; can be appropriate, but only after checking authorization, locale, content encoding, and every other input that changes the response.&lt;/p&gt;

&lt;p&gt;That caveat is a useful interview signal. "Add an ETag" is incomplete until you say what exact representation it validates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is &lt;code&gt;If-Match&lt;/code&gt; different from cache revalidation?
&lt;/h2&gt;

&lt;p&gt;Cache revalidation asks, "Can I reuse what I read?" A guarded write asks, "Am I still editing what I read?"&lt;/p&gt;

&lt;p&gt;Imagine two browser tabs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Both read profile version 7 and receive ETag A.&lt;/li&gt;
&lt;li&gt;Tab one changes the display name. The server accepts ETag A, stores version 8, and returns ETag B.&lt;/li&gt;
&lt;li&gt;Tab two submits an older form with ETag A.&lt;/li&gt;
&lt;li&gt;The server sees that A is no longer current and returns &lt;code&gt;412 Precondition Failed&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without the precondition, tab two may silently erase tab one's change. With it, the client can refetch, show a conflict UI, or ask the user to merge the fields.&lt;/p&gt;

&lt;p&gt;Notice that the example requires &lt;code&gt;If-Match&lt;/code&gt; and returns &lt;code&gt;428 Precondition Required&lt;/code&gt; when it is absent. That policy is not universal. It is appropriate when a blind write would be harmful. For an append-only event endpoint, insisting on a representation validator could be needless friction. State the resource-specific rule rather than claiming one status code fits every API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What would change in a real service?
&lt;/h2&gt;

&lt;p&gt;The drill intentionally keeps state in one object. Production code needs a few decisions that should be named out loud.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generate validators from durable state.&lt;/strong&gt; A database version column is often cheaper and clearer than hashing a large serialized response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make the comparison atomic with the write.&lt;/strong&gt; In SQL, update with both the record ID and expected version in the &lt;code&gt;WHERE&lt;/code&gt; clause, then check whether one row changed. Reading, comparing, and writing in separate transactions reintroduces the race.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define the representation boundary.&lt;/strong&gt; If permissions or feature flags change the returned fields, the validator must reflect the variant the caller saw.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use weak ETags carefully.&lt;/strong&gt; Weak validators are useful for semantic cache equivalence, but &lt;code&gt;If-Match&lt;/code&gt; is about avoiding conflicting updates and needs a strong validator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure the result.&lt;/strong&gt; Track revalidation rate, &lt;code&gt;304&lt;/code&gt; rate, &lt;code&gt;412&lt;/code&gt; conflicts, and payload bytes saved. A cache is a performance feature only if the access pattern makes it one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those choices turn a header-level answer into a system-design answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  How would I say this in an interview?
&lt;/h2&gt;

&lt;p&gt;A concise answer can follow this sequence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"For reads, I would return an ETag for the exact authorized representation. Clients send it back in If-None-Match; unchanged data gets a 304 without a body. For edits, I would require If-Match and atomically compare a durable version during the update. A mismatch becomes 412 so the client can refetch instead of overwriting a newer edit. I would scope cache-control to the data sensitivity and monitor both 304 savings and 412 conflicts."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then invite the follow-up: "Do you want the database query for the atomic update, or the client conflict-recovery flow?" That shows you have a next level without burying the interviewer in implementation detail.&lt;/p&gt;

&lt;p&gt;For practice, say the explanation while running the assertions, then have a partner challenge one assumption at a time. An interview practice tool such as &lt;a href="https://aceround.app" rel="noopener noreferrer"&gt;aceround.app&lt;/a&gt; can be useful here because the value is in responding to the follow-up, not reciting the first answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is an ETag a security control?
&lt;/h3&gt;

&lt;p&gt;No. It helps a client validate freshness and can support optimistic concurrency. Authorization must still happen before a representation is read or updated. Do not rely on an unguessable-looking ETag as access control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should every GET return an ETag?
&lt;/h3&gt;

&lt;p&gt;Not automatically. It adds the most value when clients revisit a resource and the response body is worth avoiding. Start from request patterns and payload size, then measure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use a timestamp?
&lt;/h3&gt;

&lt;p&gt;A timestamp can work, but clocks and precision create awkward boundaries. A monotonic version is usually easier to compare atomically. The contract matters more than the token format.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does the client do after a &lt;code&gt;412&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;It fetches the current representation, compares it with the user's pending edit, and either retries with the new validator or asks the user to resolve a meaningful conflict. Never blindly retry an old patch without deciding how fields should merge.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance disclosure: AI helped draft and edit this article. The example, HTTP semantics, code execution, and technical claims were reviewed before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>backend</category>
      <category>career</category>
      <category>interview</category>
    </item>
    <item>
      <title>Streaming LLMs Need a Terminal Contract: A Node.js Interview Drill</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Mon, 17 Aug 2026 08:36:37 +0000</pubDate>
      <link>https://dev.to/karuha/streaming-llms-need-a-terminal-contract-a-nodejs-interview-drill-26lh</link>
      <guid>https://dev.to/karuha/streaming-llms-need-a-terminal-contract-a-nodejs-interview-drill-26lh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Streaming text is evidence of progress, not evidence of success. Treat the terminal event as part of your API contract: normalize it, test it, record it, and never silently present a partial model response as complete. That distinction makes for a much stronger AI-engineering interview answer than saying "I would use streaming for lower latency."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A streamed response can look healthy right up until it is not. Tokens appear in the UI, the socket stays open for a while, and then the connection disappears or the provider reports a non-successful finish state. If the product displays whatever arrived as a completed answer, it has converted an unknown result into a confident one.&lt;/p&gt;

&lt;p&gt;That is a small implementation mistake with large consequences. In an AI product, a partial answer can look plausible enough to be acted on. In an interview, it is also a useful way to show that you distinguish &lt;em&gt;transport progress&lt;/em&gt; from &lt;em&gt;application completion&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This post builds a tiny Node.js contract test for that boundary. It has no SDK dependencies and is deliberately provider-neutral.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtUZXh0IGNodW5rcyBhcnJpdmVdIC0tPiBCe1Rlcm1pbmFsIGV2ZW50IHJlY2VpdmVkP30KICBCIC0tIG5vIC0tPiBDW01hcmsgcmVzdWx0IHVua25vd25dCiAgQiAtLSB5ZXMgLS0%252BIER7RmluaXNoIHJlYXNvbiBpcyBzdG9wP30KICBEIC0tIHllcyAtLT4gRVtEZWxpdmVyIGNvbXBsZXRlIHJlc3BvbnNlXQogIEQgLS0gbm8gLS0%252BIEZbU3VyZmFjZSBpbmNvbXBsZXRlIG9yIHJlZnVzZWQgc3RhdGVdCiAgY2xhc3NEZWYgaW5wdXQgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBkZWNpc2lvbiBmaWxsOiNiNDUzMDksc3Ryb2tlOiNmY2QzNGQsY29sb3I6I2ZmZixmb250LXNpemU6MThweAogIGNsYXNzRGVmIGdvb2QgZmlsbDojMGU3NDkwLHN0cm9rZTojMjJkM2VlLGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBiYWQgZmlsbDojOTkxYjFiLHN0cm9rZTojZmNhNWE1LGNvbG9yOiNmZmYsZm9udC1zaXplOjE4cHgKICBjbGFzcyBBIGlucHV0CiAgY2xhc3MgQixEIGRlY2lzaW9uCiAgY2xhc3MgRSBnb29kCiAgY2xhc3MgQyxGIGJhZA%253D%253D%3FbgColor%3D0f172a%2526width%3D1000%2526height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtUZXh0IGNodW5rcyBhcnJpdmVdIC0tPiBCe1Rlcm1pbmFsIGV2ZW50IHJlY2VpdmVkP30KICBCIC0tIG5vIC0tPiBDW01hcmsgcmVzdWx0IHVua25vd25dCiAgQiAtLSB5ZXMgLS0%252BIER7RmluaXNoIHJlYXNvbiBpcyBzdG9wP30KICBEIC0tIHllcyAtLT4gRVtEZWxpdmVyIGNvbXBsZXRlIHJlc3BvbnNlXQogIEQgLS0gbm8gLS0%252BIEZbU3VyZmFjZSBpbmNvbXBsZXRlIG9yIHJlZnVzZWQgc3RhdGVdCiAgY2xhc3NEZWYgaW5wdXQgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBkZWNpc2lvbiBmaWxsOiNiNDUzMDksc3Ryb2tlOiNmY2QzNGQsY29sb3I6I2ZmZixmb250LXNpemU6MThweAogIGNsYXNzRGVmIGdvb2QgZmlsbDojMGU3NDkwLHN0cm9rZTojMjJkM2VlLGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBiYWQgZmlsbDojOTkxYjFiLHN0cm9rZTojZmNhNWE1LGNvbG9yOiNmZmYsZm9udC1zaXplOjE4cHgKICBjbGFzcyBBIGlucHV0CiAgY2xhc3MgQixEIGRlY2lzaW9uCiAgY2xhc3MgRSBnb29kCiAgY2xhc3MgQyxGIGJhZA%253D%253D%3FbgColor%3D0f172a%2526width%3D1000%2526height%3D420" alt="A stream contract: text chunks arrive, a terminal event confirms completion, and non-stop states become an explicit incomplete or refused result." width="1197" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why chunks are not a success signal
&lt;/h2&gt;

&lt;p&gt;A streamed answer has at least two layers of state:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The transport layer:&lt;/strong&gt; bytes or events are still arriving.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The generation layer:&lt;/strong&gt; the provider has declared why generation ended.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those states are independent. A request can emit 90% of a useful answer and then stop because it hit an output limit, a safety policy, a cancelled request, or a broken connection. The first layer says "some work happened." Only the second can say "this result is complete."&lt;/p&gt;

&lt;p&gt;Provider event names differ, which is exactly why application code should not spread raw event checks throughout a UI. OpenAI documents lifecycle events for &lt;a href="https://platform.openai.com/docs/guides/streaming-responses" rel="noopener noreferrer"&gt;streaming responses&lt;/a&gt;, while Anthropic documents a distinct &lt;a href="https://docs.anthropic.com/en/api/messages-streaming" rel="noopener noreferrer"&gt;message streaming event sequence&lt;/a&gt;. Keep the provider-specific parsing at one adapter boundary and give the rest of the product a small, stable contract.&lt;/p&gt;

&lt;p&gt;For this drill, the adapter produces just two relevant events:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{ type: "text", value }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A displayable text fragment arrived.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{ type: "final", reason, requestId }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The provider completed the stream and supplied an outcome.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Build the terminal contract
&lt;/h2&gt;

&lt;p&gt;Here is the collector. It accepts text only when it has seen a final event with &lt;code&gt;reason: "stop"&lt;/code&gt;. It also rejects the surprisingly common bug where a late event is processed after the response has already been finalized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;collectText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;terminal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;received an event after the terminal event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;final&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;terminal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stream ended without a terminal event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`stream did not complete normally: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point is not that every provider literally calls the successful state &lt;code&gt;stop&lt;/code&gt;. Map the provider's documented terminal state to your own domain vocabulary. The point is that "the iterator ended" is not a substitute for a terminal contract.&lt;/p&gt;

&lt;p&gt;In production, I would return a tagged result instead of throwing directly from a request handler. Throwing keeps the drill compact; a real client needs a state it can render and telemetry it can aggregate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prove the unhappy paths
&lt;/h2&gt;

&lt;p&gt;A happy-path demo is not a contract test. The valuable cases are the ones that make a partial answer tempting to show.&lt;/p&gt;

&lt;p&gt;Save this as &lt;code&gt;stream-contract-drill.mjs&lt;/code&gt; and run &lt;code&gt;node stream-contract-drill.mjs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;collectText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;terminal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;received an event after the terminal event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;final&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;terminal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stream ended without a terminal event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`stream did not complete normally: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;collectText&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ship &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;the test.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;final&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;req_42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ship the test.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;req_42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;throws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;collectText&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;half&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;final&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]),&lt;/span&gt;
  &lt;span class="sr"&gt;/length/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;throws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;collectText&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;half&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]),&lt;/span&gt;
  &lt;span class="sr"&gt;/without a terminal event/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;throws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;collectText&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;final&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;late&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="sr"&gt;/after the terminal event/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stream terminal contract assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those four assertions answer four different operational questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does a normal stream preserve every fragment and the request identifier?&lt;/li&gt;
&lt;li&gt;Does an output-limit termination refuse to masquerade as a full answer?&lt;/li&gt;
&lt;li&gt;Does a dropped connection remain visibly unknown?&lt;/li&gt;
&lt;li&gt;Does the consumer reject an ordering violation rather than corrupting a finalized answer?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fifth worthwhile test in a real system is cancellation: assert that a client-aborted request is recorded as cancelled, not as a provider failure. That distinction matters when you decide whether the next change is a UI fix, a timeout adjustment, or a model-capacity investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide the product behavior before you ship
&lt;/h2&gt;

&lt;p&gt;Once a terminal reason is explicit, the UI and operations choices become much less vague.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Terminal state&lt;/th&gt;
&lt;th&gt;User-facing behavior&lt;/th&gt;
&lt;th&gt;Operational action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render the answer as complete.&lt;/td&gt;
&lt;td&gt;Record latency and token counts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark it incomplete; offer a deliberate continuation where safe.&lt;/td&gt;
&lt;td&gt;Track prompt/output sizing and continuation rate.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safety refusal&lt;/td&gt;
&lt;td&gt;Show the refusal, not a clipped substitute.&lt;/td&gt;
&lt;td&gt;Preserve the reason for policy and product review.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No terminal event&lt;/td&gt;
&lt;td&gt;Say the response was interrupted.&lt;/td&gt;
&lt;td&gt;Record provider, request ID, elapsed time, and retry eligibility.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The subtle row is retry. A blind retry can duplicate writes, tool calls, or side effects. For an idempotent text-only request, a retry or continuation may be reasonable. For an agent that can send messages or mutate data, first make the request idempotent and ensure the tool layer can distinguish "not attempted" from "attempted but response lost."&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn it into an interview answer
&lt;/h2&gt;

&lt;p&gt;When an interviewer asks how you would build a streaming AI feature, start with the user-visible latency benefit, then make the reliability boundary concrete:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I would stream text to improve time-to-first-token, but I would not treat the arrival of chunks as completion. I would normalize provider events behind an adapter, require a terminal outcome, test successful, length-limited, interrupted, and out-of-order cases, and instrument the terminal reason with the request ID. That lets the UI be honest about partial answers and lets us decide whether a retry is safe."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a compact answer, but it carries several senior signals: an interface boundary, failure-state design, observability, and idempotency awareness.&lt;/p&gt;

&lt;p&gt;For rehearsal, &lt;a href="https://aceround.app" rel="noopener noreferrer"&gt;aceround.app&lt;/a&gt; is an AI interview assistant that can help keep this four-part explanation visible while you practice responding to follow-up questions under time pressure. The important part is still being able to defend the trade-off in your own words.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should I automatically continue every &lt;code&gt;length&lt;/code&gt;-limited response?
&lt;/h3&gt;

&lt;p&gt;No. A continuation can be useful for a read-only explanation, but it may repeat or contradict the partial answer. Give the continuation a clear boundary, preserve the incomplete state, and never replay a request with side effects unless the downstream action is idempotent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a transport error the same as a refusal?
&lt;/h3&gt;

&lt;p&gt;No. A refusal is a known terminal outcome. A disconnected stream without a terminal event is unknown. Conflating them makes support, monitoring, and user messaging worse because the recovery options differ.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need this abstraction if I use only one model provider?
&lt;/h3&gt;

&lt;p&gt;Yes. Even one provider can evolve event names and finish states. A narrow adapter keeps that churn out of the UI, and the test documents the behavior your product relies on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI, &lt;a href="https://platform.openai.com/docs/guides/streaming-responses" rel="noopener noreferrer"&gt;Streaming responses&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Anthropic, &lt;a href="https://docs.anthropic.com/en/api/messages-streaming" rel="noopener noreferrer"&gt;Streaming Messages&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: This article was drafted with AI assistance and reviewed, tested, and edited by the author.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>career</category>
      <category>interview</category>
    </item>
    <item>
      <title>Stop Guessing srcset: Build a Responsive-Image Selection Drill</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Fri, 14 Aug 2026 06:40:17 +0000</pubDate>
      <link>https://dev.to/karuha/stop-guessing-srcset-build-a-responsive-image-selection-drill-3nl8</link>
      <guid>https://dev.to/karuha/stop-guessing-srcset-build-a-responsive-image-selection-drill-3nl8</guid>
      <description>&lt;p&gt;A responsive image bug is often a reasoning bug, not an HTML bug. Before adding another &lt;code&gt;srcset&lt;/code&gt; candidate, write down three values: the image's rendered slot in CSS pixels, the device pixel ratio (DPR), and the candidate widths you actually ship. The smallest candidate that can cover the requirement is usually the useful starting point.&lt;/p&gt;

&lt;p&gt;This post turns that sentence into a tiny JavaScript drill you can run before a frontend interview. It is deliberately smaller than a browser's real selection algorithm, but it gives you a clean way to explain &lt;code&gt;srcset&lt;/code&gt;, &lt;code&gt;sizes&lt;/code&gt;, DPR, and the trade-off between sharpness and transferred bytes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtMYXlvdXQgc2xvdAo0ODAgQ1NTIHB4XSAtLT4gQltEaXNwbGF5IGRlbnNpdHkKMnggRFBSXQogIEIgLS0%2BIENbUmVxdWlyZWQgc291cmNlCjk2MCBweF0KICBDIC0tPiBEW0NhbmRpZGF0ZXMKNDgwdywgOTYwdywgMTQ0MHddCiAgRCAtLT4gRVtTbWFsbGVzdCB2YWxpZCBjYW5kaWRhdGUKQ2hvb3NlIDk2MHddCiAgY2xhc3NEZWYgaW5wdXQgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBjYWxjIGZpbGw6I2I0NTMwOSxzdHJva2U6I2ZjZDM0ZCxjb2xvcjojZmZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgcmVzdWx0IGZpbGw6IzBlNzQ5MCxzdHJva2U6IzIyZDNlZSxjb2xvcjojZWNmZWZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3MgQSxEIGlucHV0CiAgY2xhc3MgQixDIGNhbGMKICBjbGFzcyBFIHJlc3VsdA%3D%3D%3FbgColor%3D111827%26width%3D1000%26height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtMYXlvdXQgc2xvdAo0ODAgQ1NTIHB4XSAtLT4gQltEaXNwbGF5IGRlbnNpdHkKMnggRFBSXQogIEIgLS0%2BIENbUmVxdWlyZWQgc291cmNlCjk2MCBweF0KICBDIC0tPiBEW0NhbmRpZGF0ZXMKNDgwdywgOTYwdywgMTQ0MHddCiAgRCAtLT4gRVtTbWFsbGVzdCB2YWxpZCBjYW5kaWRhdGUKQ2hvb3NlIDk2MHddCiAgY2xhc3NEZWYgaW5wdXQgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBjYWxjIGZpbGw6I2I0NTMwOSxzdHJva2U6I2ZjZDM0ZCxjb2xvcjojZmZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgcmVzdWx0IGZpbGw6IzBlNzQ5MCxzdHJva2U6IzIyZDNlZSxjb2xvcjojZWNmZWZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3MgQSxEIGlucHV0CiAgY2xhc3MgQixDIGNhbGMKICBjbGFzcyBFIHJlc3VsdA%3D%3D%3FbgColor%3D111827%26width%3D1000%26height%3D420" alt="Flow from layout slot and DPR to the selected responsive-image candidate" width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What problem are &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; solving?
&lt;/h2&gt;

&lt;p&gt;An image displayed at 480 CSS pixels needs roughly 960 source pixels on a 2x screen to stay sharp. Sending a 2,000-pixel asset to that slot can waste bytes; sending a 480-pixel asset can look soft. Responsive images let the browser choose among candidates before it fetches one.&lt;/p&gt;

&lt;p&gt;The roles are easy to mix up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute or input&lt;/th&gt;
&lt;th&gt;What it tells the browser&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;srcset="... 480w, ... 960w"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The intrinsic width of each available file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sizes="(max-width: 600px) 100vw, 50vw"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The expected layout slot, in CSS pixels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DPR&lt;/td&gt;
&lt;td&gt;How many physical pixels each CSS pixel maps to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;td&gt;The actual rendered layout; it must agree with the &lt;code&gt;sizes&lt;/code&gt; hint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important detail: a &lt;code&gt;w&lt;/code&gt; descriptor is not a display instruction. It says how wide the file is. The &lt;code&gt;sizes&lt;/code&gt; attribute supplies the layout hint that makes choosing a candidate possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a selection drill
&lt;/h2&gt;

&lt;p&gt;Here is a simplified chooser. It intentionally ignores network quality, browser heuristics, zoom, image format support, and cache state. Real browsers consider more than this. The point is to test the explanation you will give in an interview, not to replace the HTML standard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;chooseWidth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;slotCssPx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidates&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slotCssPx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;slotCssPx&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slotCssPx must be a positive number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dpr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dpr must be a positive number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;widths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;provide at least one width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slotCssPx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;chooseWidth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;slotCssPx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;960&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="mi"&gt;960&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;chooseWidth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;slotCssPx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;960&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="mi"&gt;960&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;chooseWidth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;slotCssPx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;960&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;responsive-image selection assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with &lt;code&gt;node responsive-image-drill.mjs&lt;/code&gt;. The first case is the common 480 CSS pixel / 2x DPR example: 960 is the first candidate that meets 960 required source pixels. The second shows that a small mobile layout can still need a large source on a dense screen. The last case makes the compromise explicit: when no candidate is large enough, the script picks the largest available file, which may still be upscaled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Map the drill to real markup
&lt;/h2&gt;

&lt;p&gt;For a card image that is full width on small screens and half the viewport on larger screens, the markup might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/profile-960.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
    /images/profile-480.jpg 480w,
    /images/profile-960.jpg 960w,
    /images/profile-1440.jpg 1440w
  "&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 100vw, 50vw"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1440"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"960"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Developer reviewing a pull request"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At a 480-pixel mobile viewport, &lt;code&gt;100vw&lt;/code&gt; tells the browser the slot is about 480 CSS pixels. At a wide viewport, &lt;code&gt;50vw&lt;/code&gt; gives it a smaller slot estimate. &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; reserve the aspect ratio, which helps prevent layout shifts while the image loads.&lt;/p&gt;

&lt;p&gt;Do not copy those values blindly. If a sidebar, grid gap, or max-width changes the rendered width, update &lt;code&gt;sizes&lt;/code&gt; too. A misleading &lt;code&gt;sizes="100vw"&lt;/code&gt; on a narrow card can cause a browser to select a much larger resource than the layout needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you debug a candidate that looks wrong?
&lt;/h2&gt;

&lt;p&gt;Start with evidence rather than an asset-size guess:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect the image's rendered width in DevTools. This is your observed CSS slot.&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;window.devicePixelRatio&lt;/code&gt;, then multiply it by that slot for a useful first estimate.&lt;/li&gt;
&lt;li&gt;Open the Network panel in a private window or with cache disabled and see which URL was fetched.&lt;/li&gt;
&lt;li&gt;Compare that URL's intrinsic width with the selected slot and DPR.&lt;/li&gt;
&lt;li&gt;Change one thing: either the candidate set, &lt;code&gt;sizes&lt;/code&gt;, or the layout. Reload and observe again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sequence is useful in an interview because it separates diagnosis from a proposed fix. Saying “I would compress the image” skips the key question: was the browser given an accurate choice in the first place?&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you use &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; instead?
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;img&lt;/code&gt; plus width-descriptor &lt;code&gt;srcset&lt;/code&gt; when the image content is the same and only the resolution changes. Use &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; when the image itself should change, such as a wide desktop crop that would hide the subject on a narrow screen. That is art direction, not just resolution switching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px)"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/images/team-close.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/team-wide.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Product team at a whiteboard"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use &lt;code&gt;&amp;lt;source type="image/avif"&amp;gt;&lt;/code&gt; with a fallback format, but treat format support as a separate decision from layout width. Mixing all of those ideas into one untested snippet is how responsive-image configuration becomes hard to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concise interview answer
&lt;/h2&gt;

&lt;p&gt;A strong answer does not need to recite the full browser algorithm:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I first estimate the layout slot in CSS pixels, then account for DPR. With width-based &lt;code&gt;srcset&lt;/code&gt;, I use &lt;code&gt;sizes&lt;/code&gt; to tell the browser that slot so it can select an appropriate source before downloading it. I verify the selected resource in DevTools, and I keep image dimensions on the element to avoid layout shift. If the mobile composition needs a different crop, I switch to &lt;code&gt;picture&lt;/code&gt; rather than shipping a smaller version of the same bad crop.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That answer names the decision inputs, acknowledges verification, and draws the important boundary between sizing and art direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to practice the explanation
&lt;/h2&gt;

&lt;p&gt;A code snippet proves that you can calculate a candidate; a mock conversation tests whether you can explain the trade-off when the constraints change. For frontend interview preparation, &lt;a href="https://www.aceround.app/blog/ai-job-interview-preparation/" rel="noopener noreferrer"&gt;aceround.app — an AI interview assistant&lt;/a&gt; is useful as a practice setting for follow-ups such as “what happens at 3x DPR?” or “why did the browser fetch the 1440w resource?”&lt;/p&gt;

&lt;p&gt;Use it after you can explain the fundamentals in your own words. A tool can give you more repetitions, but it should not replace opening DevTools and seeing what the browser actually requested.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Responsive_images" rel="noopener noreferrer"&gt;MDN: Responsive images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/use-srcset-to-automatically-choose-the-right-image" rel="noopener noreferrer"&gt;web.dev: Use srcset to automatically choose the right image size&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/serve-responsive-images" rel="noopener noreferrer"&gt;web.dev: Serve responsive images&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: AI assisted with outlining and copy editing. The examples, constraints, and technical review were checked before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>career</category>
      <category>interview</category>
    </item>
    <item>
      <title>Persisted GraphQL Queries: Test the APQ Fallback Before Your Interview</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Thu, 13 Aug 2026 09:48:05 +0000</pubDate>
      <link>https://dev.to/karuha/persisted-graphql-queries-test-the-apq-fallback-before-your-interview-1cb1</link>
      <guid>https://dev.to/karuha/persisted-graphql-queries-test-the-apq-fallback-before-your-interview-1cb1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Persisted GraphQL queries are two different designs that are often described as one. Automatic persisted queries (APQ) can recover from a cache miss by sending the full document once. A strict safelist must reject an unknown hash and never learn from an arbitrary client. Make that distinction executable and your interview answer becomes a production contract instead of a vocabulary test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A GraphQL interview question about persisted queries sounds simple: “Why send a query hash instead of the whole document?” The shallow answer is bandwidth. The useful answer is about the failure path.&lt;/p&gt;

&lt;p&gt;A hash-only request can miss the server’s cache. The client may then retry with the complete operation, depending on the APQ policy. A security-sensitive endpoint may instead accept only build-time registered operations. If you describe these as the same behavior, you will miss the trade-off an interviewer is actually testing: who is allowed to introduce a new query?&lt;/p&gt;

&lt;p&gt;This tutorial builds a dependency-free Node.js model for both policies. It uses SHA-256, a tiny in-memory registry, and assertions you can run with &lt;code&gt;node persisted-query-contract.js&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should happen after a hash miss?
&lt;/h2&gt;

&lt;p&gt;A persisted query maps an identifier to a known GraphQL document. The identifier is commonly a SHA-256 digest of the exact query text. The request can carry only the digest, which makes the payload smaller and gives an intermediary a stable cache key.&lt;/p&gt;

&lt;p&gt;There are two legitimate policies:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Unknown hash&lt;/th&gt;
&lt;th&gt;Full-document retry&lt;/th&gt;
&lt;th&gt;Typical use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;APQ (automatic persisted queries)&lt;/td&gt;
&lt;td&gt;Return a miss&lt;/td&gt;
&lt;td&gt;Allowed once, then the server may cache it&lt;/td&gt;
&lt;td&gt;Controlled first-party clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strict safelist&lt;/td&gt;
&lt;td&gt;Reject&lt;/td&gt;
&lt;td&gt;Rejected unless registered during deployment&lt;/td&gt;
&lt;td&gt;Public or high-risk endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither policy is “more GraphQL.” They are operational choices. APQ optimizes rollout convenience. A strict safelist optimizes control over query cost and attack surface. The important part is that the client can tell a recoverable miss from a permanent rejection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSOyBIW0hhc2gtb25seSByZXF1ZXN0XSAtLT4gTXtIYXNoIGtub3dufTsgTSAtLT58eWVzfCBEW1JldHVybiBkYXRhXTsgTSAtLT58QVBRIG1pc3N8IEZbUmV0cnkgb25jZSB3aXRoIGZ1bGwgZG9jdW1lbnRdOyBNIC0tPnxzdHJpY3Qgc2FmZWxpc3QgbWlzc3wgWFtSZWplY3QgYW5kIGFsZXJ0XTsgRiAtLT4gRA%3D%3D%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSOyBIW0hhc2gtb25seSByZXF1ZXN0XSAtLT4gTXtIYXNoIGtub3dufTsgTSAtLT58eWVzfCBEW1JldHVybiBkYXRhXTsgTSAtLT58QVBRIG1pc3N8IEZbUmV0cnkgb25jZSB3aXRoIGZ1bGwgZG9jdW1lbnRdOyBNIC0tPnxzdHJpY3Qgc2FmZWxpc3QgbWlzc3wgWFtSZWplY3QgYW5kIGFsZXJ0XTsgRiAtLT4gRA%3D%3D%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" alt="Persisted GraphQL query decision flow: hash-only request, known hash, APQ miss retry, or strict safelist rejection" width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A runnable contract test
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;persisted-query-contract.js&lt;/code&gt; and run it with Node 18 or newer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query Viewer { viewer { id name } }&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PersistedQueryServer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;strict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HASH_QUERY_MISMATCH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ada&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PERSISTED_QUERY_NOT_FOUND&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PERSISTED_QUERY_NOT_FOUND&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ada&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PersistedQueryServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;known hash should execute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query New { viewer { email } }&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;miss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newDocument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;miss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PERSISTED_QUERY_NOT_FOUND&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newDocument&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newDocument&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;APQ may learn a missing query only by policy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newDocument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;the next request can send only the hash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PersistedQueryServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PERSISTED_QUERY_NOT_FOUND&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;strict mode must not learn from clients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PERSISTED_QUERY_NOT_FOUND&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;an unregistered document is still rejected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query Different { viewer { id } }&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HASH_QUERY_MISMATCH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;persisted-query contract assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first assertions establish the happy path. A registered query executes with only its hash. An APQ miss returns a sentinel error, then the same hash plus the full document succeeds and becomes available for the next hash-only request.&lt;/p&gt;

&lt;p&gt;The strict server behaves differently. It starts with an empty registry. An unknown hash is rejected, and even sending the full document cannot register it at request time. That is the property you want when the server should execute only operations reviewed and shipped by your application.&lt;/p&gt;

&lt;p&gt;The final assertion catches a subtle integrity bug: a known hash paired with different query text must fail. Without that check, a proxy, cache layer, or client bug could associate one identifier with another operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the fallback must be bounded
&lt;/h2&gt;

&lt;p&gt;A common implementation mistake is “retry on every GraphQL error.” That is not an APQ policy. The fallback should happen only for the explicit persisted-query-miss signal, and it should happen at most once.&lt;/p&gt;

&lt;p&gt;A safe client sequence is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send the operation hash.&lt;/li&gt;
&lt;li&gt;If the response says the hash is unknown and the endpoint allows APQ, retry once with the full query and the same hash.&lt;/li&gt;
&lt;li&gt;For any other GraphQL error, network error, authorization failure, or validation error, stop or use the endpoint’s normal retry policy.&lt;/li&gt;
&lt;li&gt;Record whether the fallback happened so you can measure registry drift and unexpected clients.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The distinction matters for cost. Retrying a validation error repeats work without changing the input. Retrying an authorization error can turn one request into a noisy loop. A bounded, typed fallback is easier to observe and explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes in a strict safelist?
&lt;/h2&gt;

&lt;p&gt;Strict mode moves registration into deployment. Extract operations from the client build, review the manifest, and load it into the server before traffic arrives. At runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unknown hashes return a stable error such as &lt;code&gt;PERSISTED_QUERY_NOT_FOUND&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Full query text is not accepted as an implicit registration request.&lt;/li&gt;
&lt;li&gt;The server still validates variables, authorization, and resolver cost for known operations.&lt;/li&gt;
&lt;li&gt;Removing an operation is a deployment change, not a cache eviction accident.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a replacement for authorization. A safelisted query can still expose another user’s record if resolver-level access checks are missing. It is also not a replacement for depth, complexity, or timeout limits. It narrows the set of executable documents; it does not make every permitted document cheap.&lt;/p&gt;

&lt;p&gt;For APQ, use an explicit registry policy and a bounded cache. If the registry is shared across instances, give it a durable store and an eviction strategy. If it is per-process memory, a deploy or scale-out can create a wave of misses. That is acceptable only when the client and server have a tested fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to explain this in an interview
&lt;/h2&gt;

&lt;p&gt;A concise answer can follow four beats:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contract.&lt;/strong&gt; “The client sends a SHA-256 operation identifier. Known identifiers execute without the document.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure.&lt;/strong&gt; “On an APQ miss, the client retries once with the full document. In strict safelist mode, the server rejects and registration happens during deployment.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safety.&lt;/strong&gt; “I verify the hash matches the document, keep authorization and query-cost limits, and never retry unrelated errors.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence.&lt;/strong&gt; “I have assertions for known hashes, one-time APQ recovery, strict rejection, and hash/document mismatch.”&lt;/p&gt;

&lt;p&gt;That answer shows you understand the protocol at the boundary where systems fail. It leaves room for the follow-up questions interviewers usually ask: how do you roll out a new operation, how do you invalidate a removed one, and what happens when two application versions are live during a deployment?&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical rehearsal loop
&lt;/h2&gt;

&lt;p&gt;Take one real operation from a project you know. Write down its exact document, variables, expected cost, and the version that introduced it. Then answer these questions out loud:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which clients may send a full document?&lt;/li&gt;
&lt;li&gt;What metric tells you APQ misses are rising?&lt;/li&gt;
&lt;li&gt;Can an old mobile build still use the operation after the server deploy?&lt;/li&gt;
&lt;li&gt;Which error is safe to retry, and which one should fail fast?&lt;/li&gt;
&lt;li&gt;How would you revoke one query without taking the endpoint down?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need a GraphQL server to rehearse this. The small contract above is enough to expose whether your explanation has a missing branch.&lt;/p&gt;

&lt;p&gt;For candidates who want a structured rehearsal partner for technical explanations, &lt;a href="https://aceround.app" rel="noopener noreferrer"&gt;AceRound AI&lt;/a&gt; — an AI interview assistant — can turn this contract into follow-up prompts and mock rounds. Use it before the interview to practice the explanation; the value is in making the trade-offs your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Are persisted queries the same as query caching?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. A persisted-query registry maps an identifier to a document. Response caching stores the result for a particular operation, variables, identity, and freshness policy. You can use one without the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does APQ prevent GraphQL denial-of-service attacks?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not by itself. APQ reduces repeated document transfer and can improve cacheability. Attackers may still send expensive registered operations, and a permissive APQ server may accept arbitrary documents after fallback. Add authorization, depth or complexity limits, timeouts, and rate limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should every API use strict safelisting?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. First-party web clients with frequent releases may prefer APQ for rollout flexibility. Public APIs, mobile clients with long upgrade tails, and endpoints with costly resolvers often benefit from a strict manifest. Choose based on who controls clients and how much query cost you can tolerate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I test a real client?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keep the contract test for policy, then add an integration test against your GraphQL transport. Assert the exact error extension, whether the fallback includes the document, and whether a second request sends only the hash. Test both an old and a new client during a rolling deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GraphQL Learn, “Persisted Queries” (protocol concepts and trade-offs).&lt;/li&gt;
&lt;li&gt;Apollo GraphQL, “Persisted GraphQL Queries with Apollo Client” (APQ request sequence).&lt;/li&gt;
&lt;li&gt;GraphQL over HTTP specification (transport and error semantics).&lt;/li&gt;
&lt;li&gt;Adobe Experience Manager documentation, “Persisted GraphQL queries” (GET/cache behavior).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I used an AI writing assistant to outline and edit this article, then reviewed the code and claims manually.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>javascript</category>
      <category>career</category>
      <category>interview</category>
    </item>
    <item>
      <title>Stop Rehearsing Technical Interview Answers: Build an Evidence Ledger Instead</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Wed, 12 Aug 2026 14:17:38 +0000</pubDate>
      <link>https://dev.to/karuha/stop-rehearsing-technical-interview-answers-build-an-evidence-ledger-instead-hcf</link>
      <guid>https://dev.to/karuha/stop-rehearsing-technical-interview-answers-build-an-evidence-ledger-instead-hcf</guid>
      <description>&lt;h1&gt;
  
  
  Stop Rehearsing Technical Interview Answers: Build an Evidence Ledger Instead
&lt;/h1&gt;

&lt;p&gt;The fastest way to make a technical interview answer more credible is not to make it longer. It is to make every important statement traceable to a decision, an observation, or a trade-off. A small evidence ledger gives you that structure. In 20 minutes, it turns a vague story such as "we improved performance" into an answer you can defend under follow-up questions.&lt;/p&gt;

&lt;p&gt;This post builds a dependency-free Node.js reviewer for that ledger. It is useful after a mock interview, after a take-home walkthrough, or when you are preparing stories from projects you have actually worked on. The point is not to memorize a script. The point is to know what evidence you would offer when an interviewer asks, "How did you know?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgUVtJbnRlcnZpZXcgUXVlc3Rpb25dIC0tPiBDW0NsYWltOiB3aGF0IEkgZGVjaWRlZF0KICBDIC0tPiBFW0V2aWRlbmNlOiBtZXRyaWNzLCB0cmFjZXMsIGxvZ3NdCiAgRSAtLT4gVFtUcmFkZS1vZmY6IHdoYXQgSSBhY2NlcHRlZF0KICBULS0-IENbTmV4dCBjaGVjazogaG93IEkgdmVyaWZ5XQogIGNsYXNzRGVmIGlucHV0IGZpbGw6IzE2NGU2MyxzdHJva2U6IzY3ZThmOSxjb2xvcjojZWNmZWZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgYWN0aW9uIGZpbGw6I2I0NTMwOSxzdHJva2U6I2ZjZDM0ZCxjb2xvcjojZmZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgZXZpZGVuY2UgZmlsbDojMGU3NDkwLHN0cm9rZTojMjJkM2VlLGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzcyBRIGlucHV0CiAgY2xhc3MgQyxUIGFjdGlvbgogIGNsYXNzIEUsQyBldmlkZW5jZQ%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgUVtJbnRlcnZpZXcgUXVlc3Rpb25dIC0tPiBDW0NsYWltOiB3aGF0IEkgZGVjaWRlZF0KICBDIC0tPiBFW0V2aWRlbmNlOiBtZXRyaWNzLCB0cmFjZXMsIGxvZ3NdCiAgRSAtLT4gVFtUcmFkZS1vZmY6IHdoYXQgSSBhY2NlcHRlZF0KICBULS0-IENbTmV4dCBjaGVjazogaG93IEkgdmVyaWZ5XQogIGNsYXNzRGVmIGlucHV0IGZpbGw6IzE2NGU2MyxzdHJva2U6IzY3ZThmOSxjb2xvcjojZWNmZWZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgYWN0aW9uIGZpbGw6I2I0NTMwOSxzdHJva2U6I2ZjZDM0ZCxjb2xvcjojZmZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgZXZpZGVuY2UgZmlsbDojMGU3NDkwLHN0cm9rZTojMjJkM2VlLGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzcyBRIGlucHV0CiAgY2xhc3MgQyxUIGFjdGlvbgogIGNsYXNzIEUsQyBldmlkZW5jZQ%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" alt="A technical interview answer becomes defensible when it links a claim to evidence, a trade-off, and the next check." width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does a technical answer become shaky under follow-ups?
&lt;/h2&gt;

&lt;p&gt;Most weak answers have a familiar shape:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The service was slow, so I added caching and it got better."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That can be true and still leave an interviewer with four unanswered questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What made you believe caching was the bottleneck rather than the database, an external dependency, or an overloaded worker?&lt;/li&gt;
&lt;li&gt;Which metric changed, and over what window?&lt;/li&gt;
&lt;li&gt;What risk did the cache introduce?&lt;/li&gt;
&lt;li&gt;What would have made you reverse or adjust the decision?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those are not gotchas. They are how an interviewer distinguishes remembered terminology from engineering judgment. A defensible answer is a compact chain:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it proves&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claim&lt;/td&gt;
&lt;td&gt;You made a specific decision&lt;/td&gt;
&lt;td&gt;"I cached inventory reads for 30 seconds."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evidence&lt;/td&gt;
&lt;td&gt;The decision came from observation&lt;/td&gt;
&lt;td&gt;"Traces showed inventory accounted for 1.1 s of a 1.8 s p95."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trade-off&lt;/td&gt;
&lt;td&gt;You understand the cost&lt;/td&gt;
&lt;td&gt;"A short TTL could briefly show stale stock."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next check&lt;/td&gt;
&lt;td&gt;You can operate the change&lt;/td&gt;
&lt;td&gt;"I would watch checkout p95 and oversell rate."&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This format works for debugging, system design, performance work, and behavioral answers about technical leadership. It also makes rehearsal more honest: if you cannot fill one cell from a real project, do not invent it. Mark it as a question to investigate before the interview.&lt;/p&gt;

&lt;h2&gt;
  
  
  What belongs in an evidence ledger?
&lt;/h2&gt;

&lt;p&gt;Use one small record per answer, not a giant document. Keep the prompt as written because the wording often tells you what an interviewer is testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How would you reduce checkout latency?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I would measure the slow path before changing the cache.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p95 was 1.8 s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;traces put 1.1 s in inventory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;tradeOff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A short cache TTL can return briefly stale stock.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;nextCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Compare p95 and oversell rate after the change.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important constraint is that &lt;code&gt;evidence&lt;/code&gt; is plural. One number can be noisy or misleading. A latency percentile plus a trace segment, a log count plus a customer-visible symptom, or a benchmark plus a production guardrail gives you a more useful basis for a decision.&lt;/p&gt;

&lt;p&gt;For a project you cannot disclose, preserve the relationship but generalize the value: "the dependency represented roughly two thirds of our p95" is better than pretending there was no measurement at all. Do not bring internal dashboards, credentials, or customer data into an interview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can a tiny script catch the holes before a mock interview?
&lt;/h2&gt;

&lt;p&gt;Yes. The reviewer below accepts only four answer fields. It reports missing evidence separately from a missing trade-off, because those produce different practice tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blanks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evidence&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tradeOff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nextCheck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;review&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;missing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;blanks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;evidenceCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;rehearsal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;State the decision, name one measurement, then volunteer the trade-off.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Add &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; before rehearsing this answer.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;review&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What changed?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;We shipped it.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;tradeOff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nextCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evidence&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tradeOff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nextCheck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the complete version with &lt;code&gt;node interview-evidence-ledger.mjs&lt;/code&gt;. The assertion is deliberate. A checklist that has never been exercised is easy to misunderstand in exactly the way an interview answer is easy to hand-wave.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not score answers with one number?
&lt;/h3&gt;

&lt;p&gt;Because a single score encourages gaming. You can make an answer sound polished while leaving out the one fact that would change the decision. A field-level result gives a better next action:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Missing field&lt;/th&gt;
&lt;th&gt;Better rehearsal move&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claim&lt;/td&gt;
&lt;td&gt;Say exactly what you chose or changed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evidence&lt;/td&gt;
&lt;td&gt;Find a metric, trace, test result, incident timeline, or code path.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trade-off&lt;/td&gt;
&lt;td&gt;Name the cost, failure mode, or alternative you rejected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next check&lt;/td&gt;
&lt;td&gt;Define the metric, alert, rollback condition, or customer signal to watch.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How do you rehearse from the ledger without sounding rehearsed?
&lt;/h2&gt;

&lt;p&gt;Use a two-pass drill.&lt;/p&gt;

&lt;p&gt;First, answer the question in 90 seconds with the ledger hidden. Then ask yourself only one follow-up: "What evidence made that the right decision?" If you can answer it naturally, add a second follow-up about trade-offs. This mirrors a good technical conversation better than reciting a four-part template.&lt;/p&gt;

&lt;p&gt;Second, choose exactly one missing cell to repair. Do not rewrite all of your stories in one sitting. If your cache story lacks a rollback condition, research or design that condition, add it, and rehearse the same answer once more. Small, verifiable changes compound faster than restarting your preparation every day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtBbnN3ZXIgaW4gOTAgc2Vjb25kc10gLS0-IEJbQ2hlY2sgbGVkZ2VyIGZpZWxkc10KICBCIC0tPiBDe0FueSBnYXA_fQogIEMgLS0-fHllc3wgRFtHYXRoZXIgb25lIHZlcmlmaWFibGUgZmFjdF0KICBEgLS0-IEVbUmVoZWFyc2UgdGhlIHJldmlzZWQgYW5zd2VyXQogIEMgLS0-fG5vfCBGW0FkZCBhIGZvbGxvdy11cCBxdWVzdGlvbl0KICBGIC0tPiBFCiAgY2xhc3NEZWYgcHJhY3RpY2UgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBjaGVjayBmaWxsOiNiNDUzMDksc3Ryb2tlOiNmY2QzNGQsY29sb3I6I2ZmZixmb250LXNpemU6MThweAogIGNsYXNzIEEsRFIGZyYWN0aWNlCiAgY2xhc3MgQixDIGNoZWNrCiAgY2xhc3MgRCxGIGV2aWRlbmNl%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtBbnN3ZXIgaW4gOTAgc2Vjb25kc10gLS0-IEJbQ2hlY2sgbGVkZ2VyIGZpZWxkc10KICBCIC0tPiBDe0FueSBnYXA_fQogIEMgLS0-fHllc3wgRFtHYXRoZXIgb25lIHZlcmlmaWFibGUgZmFjdF0KICBEgLS0-IEVbUmVoZWFyc2UgdGhlIHJldmlzZWQgYW5zd2VyXQogIEMgLS0-fG5vfCBGW0FkZCBhIGZvbGxvdy11cCBxdWVzdGlvbl0KICBGIC0tPiBFCiAgY2xhc3NEZWYgcHJhY3RpY2UgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBjaGVjayBmaWxsOiNiNDUzMDksc3Ryb2tlOiNmY2QzNGQsY29sb3I6I2ZmZixmb250LXNpemU6MThweAogIGNsYXNzIEEsRFIGZyYWN0aWNlCiAgY2xhc3MgQixDIGNoZWNrCiAgY2xhc3MgRCxGIGV2aWRlbmNl%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" alt="A practical review loop: answer once, inspect a single gap, gather evidence, then rehearse one improved version." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does AI fit, and where does it not?
&lt;/h2&gt;

&lt;p&gt;AI can be useful for producing follow-up questions, checking whether your spoken answer actually contains the four fields, or comparing two versions of a story. It should not become the source of your evidence. A generated benchmark, invented incident detail, or fabricated metric will collapse under a basic follow-up.&lt;/p&gt;

&lt;p&gt;For the review stage, a transcript is more useful than memory. &lt;a href="https://www.aceround.app/blog/ai-engineer-interview-ai/" rel="noopener noreferrer"&gt;aceround.app, an AI interview assistant, has a transcript-review workflow&lt;/a&gt; that can help surface places where your answer lost its decision or measurement. Treat that output as a prompt to inspect your own work, not as a substitute for knowing it.&lt;/p&gt;

&lt;p&gt;The durable skill is simple: connect an engineering claim to the evidence that justified it, the trade-off it accepted, and the check that would prove it wrong. Once you can do that, your answer is shorter, clearer, and much harder to shake.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What counts as evidence in a technical interview?
&lt;/h3&gt;

&lt;p&gt;Metrics, logs, traces, tests, incident timelines, code-level observations, user reports, and benchmark results can all count. State what you observed and why it changed the decision. Avoid exposing confidential values or customer information.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if I did not own the final decision?
&lt;/h3&gt;

&lt;p&gt;Say what you contributed: the investigation you ran, the options you compared, the risk you raised, or the check you owned after rollout. Clear scope is stronger than inflated ownership.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does this work for system design interviews?
&lt;/h3&gt;

&lt;p&gt;Yes. Use estimated load and constraints as evidence, name the cost of each major choice, and say what metric or failure mode would trigger a design change.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI tools assisted with drafting and editing. The code, technical claims, structure, and links were reviewed by the author before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>career</category>
      <category>interview</category>
      <category>testing</category>
    </item>
    <item>
      <title>Stop Guessing the Event Loop: Build a JavaScript Timeline Test for Interviews</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Tue, 11 Aug 2026 06:09:10 +0000</pubDate>
      <link>https://dev.to/karuha/stop-guessing-the-event-loop-build-a-javascript-timeline-test-for-interviews-39hk</link>
      <guid>https://dev.to/karuha/stop-guessing-the-event-loop-build-a-javascript-timeline-test-for-interviews-39hk</guid>
      <description>&lt;p&gt;If you can predict one short program's output &lt;em&gt;and explain why&lt;/em&gt;, you can answer most JavaScript event-loop interview follow-ups without memorizing a diagram. Start with synchronous code, drain microtasks, then let the runtime take the next task. The useful preparation step is to make that order executable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtTeW5jIGNvZGVdIC0tPiBCW01pY3JvdGFzayBxdWV1ZV0KICBCIC0tPiBDW3F1ZXVlTWljcm90YXNrXQogIEIgLS0%2BIERbUHJvbWlzZS50aGVuXQogIEMgLS0%2BIEVbVGltZXIgcXVldWVdCiAgRCAtLT4gRQogIEUgLS0%2BIEZbc2V0VGltZW91dCBjYWxsYmFja3NdCiAgY2xhc3NEZWYgc3luYyBmaWxsOiMxNjRlNjMsc3Ryb2tlOiM2N2U4ZjksY29sb3I6I2VjZmVmZixmb250LXNpemU6MjBweAogIGNsYXNzRGVmIG1pY3JvIGZpbGw6IzRjMWQ5NSxzdHJva2U6I2M0YjVmZCxjb2xvcjojZjVmM2ZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgdGltZXIgZmlsbDojOTI0MDBlLHN0cm9rZTojZmNkMzRkLGNvbG9yOiNmZmZiZWIsZm9udC1zaXplOjIwcHgKICBjbGFzcyBBIHN5bmMKICBjbGFzcyBCLEMsRCBtaWNybwogIGNsYXNzIEUsRiB0aW1lcg%3D%3D%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtTeW5jIGNvZGVdIC0tPiBCW01pY3JvdGFzayBxdWV1ZV0KICBCIC0tPiBDW3F1ZXVlTWljcm90YXNrXQogIEIgLS0%2BIERbUHJvbWlzZS50aGVuXQogIEMgLS0%2BIEVbVGltZXIgcXVldWVdCiAgRCAtLT4gRQogIEUgLS0%2BIEZbc2V0VGltZW91dCBjYWxsYmFja3NdCiAgY2xhc3NEZWYgc3luYyBmaWxsOiMxNjRlNjMsc3Ryb2tlOiM2N2U4ZjksY29sb3I6I2VjZmVmZixmb250LXNpemU6MjBweAogIGNsYXNzRGVmIG1pY3JvIGZpbGw6IzRjMWQ5NSxzdHJva2U6I2M0YjVmZCxjb2xvcjojZjVmM2ZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3NEZWYgdGltZXIgZmlsbDojOTI0MDBlLHN0cm9rZTojZmNkMzRkLGNvbG9yOiNmZmZiZWIsZm9udC1zaXplOjIwcHgKICBjbGFzcyBBIHN5bmMKICBjbGFzcyBCLEMsRCBtaWNybwogIGNsYXNzIEUsRiB0aW1lcg%3D%3D%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" alt="JavaScript event-loop flow: synchronous code, microtasks, then timers" width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most event-loop answers fail because they begin with vocabulary: "macrotasks, microtasks, call stack..." The interviewer then adds one &lt;code&gt;Promise.then()&lt;/code&gt; inside a timer and the explanation turns into guesswork.&lt;/p&gt;

&lt;p&gt;A better answer has two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;State the ordering rule.&lt;/li&gt;
&lt;li&gt;Prove it with the smallest program that exercises it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a 15-minute drill for exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you say before writing code?
&lt;/h2&gt;

&lt;p&gt;Use a precise, short opening:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JavaScript runs the current synchronous work to completion. Before the runtime takes another task such as a timer callback, it drains the microtask queue. Promise reactions and &lt;code&gt;queueMicrotask&lt;/code&gt; use that queue; &lt;code&gt;setTimeout&lt;/code&gt; schedules a later task.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a strong starting point, not a universal promise about every host API. Browsers and Node.js provide different APIs and have runtime-specific phases. In an interview, say which environment you are discussing when the question gets specific.&lt;/p&gt;

&lt;p&gt;The practical consequence is simple: a resolved promise does not interrupt currently running synchronous code, but it usually runs before a zero-delay timer scheduled in that same turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you prove the timeline instead of narrating it?
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;event-loop-drill.mjs&lt;/code&gt; and run &lt;code&gt;node event-loop-drill.mjs&lt;/code&gt;. It uses only Node's built-in assertion module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync: start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timer: initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;queueMicrotask&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;microtask: queueMicrotask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;microtask: promise.then&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timer: scheduled by microtask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync: end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync: start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync: end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;microtask: queueMicrotask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;microtask: promise.then&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timer: initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timer: scheduled by microtask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event-loop timeline assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result has five useful facts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;th&gt;What it demonstrates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Both &lt;code&gt;sync&lt;/code&gt; labels appear first&lt;/td&gt;
&lt;td&gt;The current stack finishes before callbacks run.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;queueMicrotask&lt;/code&gt; precedes &lt;code&gt;Promise.then&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;They were inserted into one FIFO microtask queue in that order.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Both microtasks precede the first timer&lt;/td&gt;
&lt;td&gt;The microtask queue drains before the next timer task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The initial timer precedes the nested timer&lt;/td&gt;
&lt;td&gt;The first timer was scheduled earlier.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A 25 ms wait appears only in the test harness&lt;/td&gt;
&lt;td&gt;It gives both timer callbacks time to run; it is not part of the ordering rule.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The assertion is the important bit. A console log is easy to glance at and misread. A failing assertion gives you a concrete question: did the code, the environment, or my assumption differ?&lt;/p&gt;

&lt;h2&gt;
  
  
  Which follow-up questions expose shallow understanding?
&lt;/h2&gt;

&lt;p&gt;After you can predict the first script, change one thing at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if a timer creates a promise?
&lt;/h3&gt;

&lt;p&gt;Move the promise into the first timeout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timer: first&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;microtask: after first timer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timer: second&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful explanation is not merely "promises are faster." It is: after the first timer callback returns, the runtime drains the microtasks it created before continuing to the next available task. That is why the promise reaction belongs between the two timer callbacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if a microtask keeps scheduling microtasks?
&lt;/h3&gt;

&lt;p&gt;This is the operational edge case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;queueMicrotask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`microtask: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;queueMicrotask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a bounded counter, the timer eventually gets a turn. Without a bound, repeatedly enqueueing microtasks can delay rendering and other tasks. In production code, that is a responsiveness problem, not a clever scheduling technique.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does &lt;code&gt;setTimeout(fn, 0)&lt;/code&gt; mean immediately?
&lt;/h3&gt;

&lt;p&gt;No. It means "eligible after the current work and the relevant scheduling delay." The browser may clamp or delay timers; Node's event loop has its own phases. In an interview, avoid promising an exact millisecond. Explain the &lt;em&gt;relative&lt;/em&gt; ordering that your test establishes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you turn the code into an interview answer?
&lt;/h2&gt;

&lt;p&gt;Use this four-step structure when the interviewer gives you a snippet.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Name the current work.&lt;/strong&gt; "The top-level statements run first."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List queues by insertion order.&lt;/strong&gt; "This queues a timer, then two microtasks."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply the boundary.&lt;/strong&gt; "When the current stack clears, microtasks drain before another task."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State one caveat.&lt;/strong&gt; "The relative order is the point; exact timer timing depends on the host."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then pause. Do not race to define every event-loop term. If asked about browser rendering, I would add that rendering is host-controlled and should not be reduced to a blanket claim that it happens after every callback. If asked about Node, I would distinguish this basic timer-and-microtask example from Node-specific scheduling APIs and phases.&lt;/p&gt;

&lt;p&gt;That restraint matters in senior interviews. It shows that you know where the general model ends.&lt;/p&gt;

&lt;h2&gt;
  
  
  A 15-minute practice routine
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 1-3:&lt;/strong&gt; Predict the first script without running it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 4-6:&lt;/strong&gt; Run it and make the assertion pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 7-10:&lt;/strong&gt; Put a promise inside the first timer and write the new expected order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 11-13:&lt;/strong&gt; Explain the result aloud in four steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 14-15:&lt;/strong&gt; Name one host-specific caveat you would check before shipping code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repeat with a different ordering tomorrow. The goal is not to recite a fixed answer; it is to build a reliable way to reason under a follow-up question.&lt;/p&gt;

&lt;p&gt;For structured rehearsals that pair technical questions with answer feedback, &lt;a href="https://www.aceround.app/blog/ai-interview-coach-guide/" rel="noopener noreferrer"&gt;aceround.app - AI interview assistant&lt;/a&gt; is one option to use after you have built your own explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Are promises always before timers?
&lt;/h3&gt;

&lt;p&gt;For the ordinary case shown here, promise reactions placed in the microtask queue run after the current synchronous code and before a later timer task. Do not generalize that into a claim about every asynchronous API or every runtime phase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I mention &lt;code&gt;process.nextTick&lt;/code&gt; in a Node interview?
&lt;/h3&gt;

&lt;p&gt;Only when asked, or when the code uses it. It has Node-specific behavior and can distract from the standard microtask-versus-timer explanation. First establish the portable model, then discuss Node's extra scheduling semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this enough for a frontend interview?
&lt;/h3&gt;

&lt;p&gt;It is the foundation. Pair it with one real scenario: stale search responses, an event handler that schedules work, or a rendering-performance issue. The interviewer wants to see that you can use the model to prevent a bug, not only recite its terminology.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MDN, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model" rel="noopener noreferrer"&gt;JavaScript execution model&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide" rel="noopener noreferrer"&gt;Using microtasks in JavaScript&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: This article was drafted with AI assistance and reviewed for technical accuracy, examples, and wording by the author.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>career</category>
      <category>interview</category>
      <category>beginners</category>
    </item>
    <item>
      <title>When One Bad Job Freezes the Queue: A Node.js Dead-Letter Drill</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Mon, 10 Aug 2026 14:12:20 +0000</pubDate>
      <link>https://dev.to/karuha/when-one-bad-job-freezes-the-queue-a-nodejs-dead-letter-drill-3f2d</link>
      <guid>https://dev.to/karuha/when-one-bad-job-freezes-the-queue-a-nodejs-dead-letter-drill-3f2d</guid>
      <description>&lt;p&gt;Most queue failures are not "retry problems." They are routing problems. A transient timeout deserves another attempt; a malformed payload should leave the hot path; and a poison message must not hold every healthy job hostage. This short Node.js drill turns those decisions into executable assertions you can explain in a backend interview.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtNZXNzYWdlIGFycml2ZXNdIC0tPiBCe1Byb2Nlc3Mgc3VjY2VlZHM_fQogIEIgLS0-fHllc3wgQ1tBY2tub3dsZWRnZV0KICBCIC0tPnxyZXRyeWFibGUgZmFpbHVyZXwgRHtCdWRnZXQgbGVmdD99CiAgRCAtLT58eWVzfCBFW0JhY2tvZmYgYW5kIHJldHJ5XQogIEQgLS0-fG5vfCBGW0RlYWQtbGV0dGVyIGFuZCBhbGVydF0KICBCIC0tPnxwZXJtYW5lbnQgZmFpbHVyZXwgRgogIEYgLS0-IEdbRml4IGNhdXNlIHRoZW4gY29udHJvbGxlZCByZXBsYXld%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtNZXNzYWdlIGFycml2ZXNdIC0tPiBCe1Byb2Nlc3Mgc3VjY2VlZHM_fQogIEIgLS0-fHllc3wgQ1tBY2tub3dsZWRnZV0KICBCIC0tPnxyZXRyeWFibGUgZmFpbHVyZXwgRHtCdWRnZXQgbGVmdD99CiAgRCAtLT58eWVzfCBFW0JhY2tvZmYgYW5kIHJldHJ5XQogIEQgLS0-fG5vfCBGW0RlYWQtbGV0dGVyIGFuZCBhbGVydF0KICBCIC0tPnxwZXJtYW5lbnQgZmFpbHVyZXwgRgogIEYgLS0-IEdbRml4IGNhdXNlIHRoZW4gY29udHJvbGxlZCByZXBsYXld%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" alt="A queue routing a message to acknowledgement, bounded retry, or a dead-letter path" width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the invariant?
&lt;/h2&gt;

&lt;p&gt;In an interview, start with the invariant instead of naming a broker:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every delivery ends in exactly one observable outcome: acknowledged, retried with a bounded budget, or isolated for inspection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence is more useful than "we will use SQS" or "we will add retries." It gives you something to test.&lt;/p&gt;

&lt;p&gt;A message should carry a stable identifier and an attempt count. The count must increase monotonically. A successful handler acknowledges the message. A retryable failure returns it to the queue, usually with backoff. A permanent failure, or a retry budget that has been exhausted, moves it to a dead-letter destination and raises an alert.&lt;/p&gt;

&lt;p&gt;This is the core idea behind a dead-letter queue (DLQ). In Amazon SQS, a redrive policy uses &lt;code&gt;maxReceiveCount\&lt;/code&gt; to decide when a message is moved from the source queue to its DLQ. AWS describes the DLQ as a place to inspect and diagnose unconsumed messages, then redrive them after the cause is fixed. A DLQ is not a trash can and it is not a substitute for observability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which failures should retry?
&lt;/h2&gt;

&lt;p&gt;The first design decision is classification.&lt;/p&gt;

&lt;p&gt;Retry a failure when the next attempt could plausibly see a different world:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a timeout or connection reset&lt;/li&gt;
&lt;li&gt;a temporary database outage&lt;/li&gt;
&lt;li&gt;a downstream 429 or 503&lt;/li&gt;
&lt;li&gt;a worker crash before the message was acknowledged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not retry just because an exception was thrown. A malformed payload, a missing required record, or a violated business rule will usually fail the same way forever. Retrying it burns worker capacity and can block an ordered lane.&lt;/p&gt;

&lt;p&gt;The classifier does not need to predict the future perfectly. It needs to make the policy explicit and leave unknown errors visible. In production, you might attach an error code, a retry-after duration, and the queue name to the log entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  A minimal routing function
&lt;/h2&gt;

&lt;p&gt;Here is a dependency-free policy function. It does not pretend to be a complete broker client; it isolates the decision you should be able to defend on a whiteboard.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`js&lt;br&gt;
import assert from "node:assert/strict";&lt;/p&gt;

&lt;p&gt;const MAX_ATTEMPTS = 3;&lt;br&gt;
const retryableCodes = new Set(["ETIMEDOUT", "ECONNRESET", "429"]);&lt;/p&gt;

&lt;p&gt;function route(message, process) {&lt;br&gt;
  const attempt = message.attempt + 1;&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
    process(message);&lt;br&gt;
    return {&lt;br&gt;
      action: "ack",&lt;br&gt;
      message: { ...message, attempt },&lt;br&gt;
    };&lt;br&gt;
  } catch (error) {&lt;br&gt;
    const retryable = retryableCodes.has(error.code);&lt;br&gt;
    const exhausted = attempt &amp;gt;= MAX_ATTEMPTS;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return {
  action: retryable &amp;amp;&amp;amp; !exhausted ? "retry" : "dead-letter",
  message: {
    ...message,
    attempt,
    failureCode: error.code,
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There are two details worth calling out.&lt;/p&gt;

&lt;p&gt;First, &lt;code&gt;attempt\&lt;/code&gt; is calculated before the handler runs. The failure record therefore tells you which delivery failed, even when the handler throws immediately.&lt;/p&gt;

&lt;p&gt;Second, "retryable" is not enough. The retry budget is a second gate. A flaky dependency can be transient for hours; without a ceiling, your queue has an infinite loop disguised as resilience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the boundaries, not just the happy path
&lt;/h2&gt;

&lt;p&gt;A good interview answer includes the smallest test that would catch a bad policy. Add these assertions below the function:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`js&lt;br&gt;
const timeout = Object.assign(&lt;br&gt;
  new Error("upstream timed out"),&lt;br&gt;
  { code: "ETIMEDOUT" },&lt;br&gt;
);&lt;br&gt;
const invalid = Object.assign(&lt;br&gt;
  new Error("missing account id"),&lt;br&gt;
  { code: "INVALID_PAYLOAD" },&lt;br&gt;
);&lt;br&gt;
const base = {&lt;br&gt;
  id: "evt-42",&lt;br&gt;
  idempotencyKey: "order-81",&lt;br&gt;
  attempt: 0,&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;assert.equal(&lt;br&gt;
  route(base, () =&amp;gt; { throw timeout; }).action,&lt;br&gt;
  "retry",&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;assert.equal(&lt;br&gt;
  route({ ...base, attempt: 2 }, () =&amp;gt; { throw timeout; }).action,&lt;br&gt;
  "dead-letter",&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;assert.equal(&lt;br&gt;
  route(base, () =&amp;gt; { throw invalid; }).action,&lt;br&gt;
  "dead-letter",&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;assert.equal(&lt;br&gt;
  route(base, () =&amp;gt; {}).action,&lt;br&gt;
  "ack",&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;console.log("queue routing assertions passed");&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run it with Node.js and you should see:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;&lt;br&gt;
queue routing assertions passed&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The useful part is not the number three. It is that the policy has four observable exits:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;timeout on attempt 1&lt;/td&gt;
&lt;td&gt;retry&lt;/td&gt;
&lt;td&gt;the dependency may recover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;timeout on attempt 3&lt;/td&gt;
&lt;td&gt;dead-letter&lt;/td&gt;
&lt;td&gt;the budget is exhausted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;invalid payload&lt;/td&gt;
&lt;td&gt;dead-letter&lt;/td&gt;
&lt;td&gt;another delivery will not repair the data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;successful handler&lt;/td&gt;
&lt;td&gt;ack&lt;/td&gt;
&lt;td&gt;the message can leave the source queue&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What changes in a real worker?
&lt;/h2&gt;

&lt;p&gt;The function above returns an action. The worker has to make that action safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backoff and visibility.&lt;/strong&gt; A retry should not immediately hammer a failing dependency. Use exponential backoff with jitter, and make the broker's visibility timeout longer than the handler's expected processing window. A timeout that expires while the handler is still running can create duplicate deliveries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partial batch failure.&lt;/strong&gt; If a broker delivers ten messages at once, do not fail the whole batch because one payload is bad. Acknowledge successful records, retry only the retryable records, and dead-letter the permanent ones. The exact API differs by broker, but the contract is the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-key ordering.&lt;/strong&gt; A DLQ can intentionally break strict ordering. AWS warns against using a DLQ with FIFO workloads when moving one message out of the sequence would change the meaning of later operations. If ordering is a hard business invariant, pause the affected partition and escalate instead of silently skipping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics.&lt;/strong&gt; Alert on DLQ depth, age of the oldest dead-lettered message, retry rate, and per-key lag. Total queue depth alone can look healthy while one partition is stuck behind a poison message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replay is a new delivery
&lt;/h2&gt;

&lt;p&gt;A replay is not a magical undo. It is another delivery with another chance to create a side effect.&lt;/p&gt;

&lt;p&gt;Before redriving a message, record:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the original message id and failure reason;&lt;/li&gt;
&lt;li&gt;the code or data change that should make the replay succeed;&lt;/li&gt;
&lt;li&gt;a replay id or idempotency key;&lt;/li&gt;
&lt;li&gt;the operator and timestamp;&lt;/li&gt;
&lt;li&gt;the result of the replay.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The handler should use an idempotency key when it calls an external system. Otherwise, a message that succeeded at the payment provider but timed out before acknowledgement can be charged again on replay. Keep the original payload for diagnosis, and make the replay command rate-limited so a fixed bug does not create a second traffic spike.&lt;/p&gt;

&lt;p&gt;For a small system, a manual redrive command plus a dashboard may be enough. For a high-volume system, put replay behind a separate worker with a canary batch, a maximum messages-per-second setting, and a kill switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to say this in an interview
&lt;/h2&gt;

&lt;p&gt;A concise answer can follow this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"I will classify failures into retryable and permanent."&lt;/li&gt;
&lt;li&gt;"I will cap attempts and add backoff so a poison message cannot loop forever."&lt;/li&gt;
&lt;li&gt;"Permanent or exhausted messages go to a DLQ with an alert and enough metadata to diagnose them."&lt;/li&gt;
&lt;li&gt;"I will preserve idempotency across retries and replays."&lt;/li&gt;
&lt;li&gt;"I will call out the ordering trade-off: a DLQ may be wrong for a strict FIFO workflow."&lt;/li&gt;
&lt;li&gt;"I will test one transient failure, one exhausted retry, one permanent failure, and one success."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That answer demonstrates judgment, not just vocabulary. You are showing how the system behaves when the happy path disappears.&lt;/p&gt;

&lt;p&gt;For a timed practice round, spend five minutes drawing the states, five minutes implementing &lt;code&gt;route\&lt;/code&gt;, and five minutes explaining replay and ordering. A tool such as &lt;a href="https://aceround.app" rel="noopener noreferrer"&gt;aceround.app - AI interview assistant&lt;/a&gt; can provide follow-up questions while you practice, but the important artifact is still your own failure policy and tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html" rel="noopener noreferrer"&gt;Using dead-letter queues in Amazon SQS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html" rel="noopener noreferrer"&gt;Amazon SQS visibility timeout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: AI assistance was used for outlining and editing. The code and technical claims were reviewed and the assertions were run with Node.js 22.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>backend</category>
      <category>career</category>
      <category>interview</category>
    </item>
    <item>
      <title>Your Node.js Stream Is Not Safe Until write() Returns False</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Sat, 08 Aug 2026 02:31:45 +0000</pubDate>
      <link>https://dev.to/karuha/your-nodejs-stream-is-not-safe-until-write-returns-false-4jc7</link>
      <guid>https://dev.to/karuha/your-nodejs-stream-is-not-safe-until-write-returns-false-4jc7</guid>
      <description>&lt;p&gt;Backpressure is not a stream feature you turn on. It is a contract you either honor or silently bypass. In Node.js, the contract is tiny: when &lt;code&gt;writable.write(chunk)&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;, stop producing and wait for &lt;code&gt;drain&lt;/code&gt;. Ignore that boolean and your "streaming" code can still queue the whole payload in memory.&lt;/p&gt;

&lt;p&gt;That distinction makes a better backend interview answer than "streams are memory efficient." Let's prove it with a slow consumer and two producers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What failure are we trying to reproduce?
&lt;/h2&gt;

&lt;p&gt;Imagine an export endpoint reading rows quickly and sending them to a slower destination: a compressed file, an HTTP response, or object storage. The producer can create data faster than the destination can flush it.&lt;/p&gt;

&lt;p&gt;Node buffers the excess. A buffer is useful; an unbounded queue is not.&lt;/p&gt;

&lt;p&gt;The official Node.js backpressure guide demonstrates the stakes with a roughly 9 GB compression workload. Its normal binary peaked at about &lt;strong&gt;87.8 MB&lt;/strong&gt; of resident memory. A modified binary that always returned &lt;code&gt;true&lt;/code&gt; from &lt;code&gt;write()&lt;/code&gt; peaked at about &lt;strong&gt;1.52 GB&lt;/strong&gt;. That is a controlled demonstration, not a universal benchmark, but it isolates the contract very clearly: the return value limits how far the producer can outrun the consumer.&lt;/p&gt;

&lt;p&gt;We can reproduce the shape of that failure without a 9 GB file.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we build a deliberately slow writable?
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;Writable&lt;/code&gt; whose &lt;code&gt;_write&lt;/code&gt; method waits 1 millisecond before acknowledging each chunk. Set &lt;code&gt;highWaterMark&lt;/code&gt; to 16 KiB so the queue reaches its threshold quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Writable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HIGH_WATER_MARK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SlowSink&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Writable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;highWaterMark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HIGH_WATER_MARK&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_encoding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;highWaterMark&lt;/code&gt; is a threshold, not a hard memory ceiling. Once the internal queue reaches it, &lt;code&gt;write()&lt;/code&gt; starts returning &lt;code&gt;false&lt;/code&gt;. Already accepted chunks remain queued, and one write can take the length beyond the threshold. The caller's job is to stop adding more.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the broken producer do?
&lt;/h2&gt;

&lt;p&gt;The broken version records &lt;code&gt;canContinue&lt;/code&gt; and then ignores it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canContinue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writableLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Broken: canContinue may be false, but production continues.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is asynchronous in the least useful sense. &lt;code&gt;write()&lt;/code&gt; returns quickly, so the loop creates all 800 chunks before the destination has processed more than a few. The event loop stays responsive, but memory becomes the waiting room.&lt;/p&gt;

&lt;p&gt;This is the interview trap. "It does not block" does &lt;strong&gt;not&lt;/strong&gt; mean "it applies flow control." Non-blocking production can still overwhelm a non-blocking consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the smallest correct fix?
&lt;/h2&gt;

&lt;p&gt;Wait for &lt;code&gt;drain&lt;/code&gt; whenever &lt;code&gt;write()&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:events&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canContinue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writableLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;canContinue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow is a two-state protocol, not a timing guess:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiBBW1JlYWRhYmxlIHByb2R1Y2VzIGNodW5rXSAtLT4gQnsid3JpdGFibGUud3JpdGUoY2h1bmspIn0KIEIgLS0-fHRydWV8IEEKIEIgLS0-fGZhbHNlfCBDW1BhdXNlIHByb2R1Y2VyXQogQyAtLT4gRFtXcml0YWJsZSBkcmFpbnMgYnVmZmVyXQogRCAtLT58ZHJhaW4gZXZlbnR8IEEKIGNsYXNzRGVmIHNvdXJjZSBmaWxsOiMxNjRlNjMsc3Ryb2tlOiM2N2U4ZjksY29sb3I6I2VjZmVmZixmb250LXNpemU6MThweAogY2xhc3NEZWYgZ2F0ZSBmaWxsOiM4NTRkMGUsc3Ryb2tlOiNmY2QzNGQsY29sb3I6I2ZmZixmb250LXNpemU6MThweAogY2xhc3NEZWYgcGF1c2UgZmlsbDojN2YxZDFkLHN0cm9rZTojZmNhNWE1LGNvbG9yOiNmZmYsZm9udC1zaXplOjE4cHgKIGNsYXNzRGVmIHJlYWR5IGZpbGw6IzA2NWY0NixzdHJva2U6IzZlZTdiNyxjb2xvcjojZWNmZGY1LGZvbnQtc2l6ZToxOHB4CiBjbGFzcyBBIHNvdXJjZQogY2xhc3MgQiBnYXRlCiBjbGFzcyBDIHBhdXNlCiBjbGFzcyBEIHJlYWR5%3FbgColor%3Dffffff%26width%3D1000" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiBBW1JlYWRhYmxlIHByb2R1Y2VzIGNodW5rXSAtLT4gQnsid3JpdGFibGUud3JpdGUoY2h1bmspIn0KIEIgLS0-fHRydWV8IEEKIEIgLS0-fGZhbHNlfCBDW1BhdXNlIHByb2R1Y2VyXQogQyAtLT4gRFtXcml0YWJsZSBkcmFpbnMgYnVmZmVyXQogRCAtLT58ZHJhaW4gZXZlbnR8IEEKIGNsYXNzRGVmIHNvdXJjZSBmaWxsOiMxNjRlNjMsc3Ryb2tlOiM2N2U4ZjksY29sb3I6I2VjZmVmZixmb250LXNpemU6MThweAogY2xhc3NEZWYgZ2F0ZSBmaWxsOiM4NTRkMGUsc3Ryb2tlOiNmY2QzNGQsY29sb3I6I2ZmZixmb250LXNpemU6MThweAogY2xhc3NEZWYgcGF1c2UgZmlsbDojN2YxZDFkLHN0cm9rZTojZmNhNWE1LGNvbG9yOiNmZmYsZm9udC1zaXplOjE4cHgKIGNsYXNzRGVmIHJlYWR5IGZpbGw6IzA2NWY0NixzdHJva2U6IzZlZTdiNyxjb2xvcjojZWNmZGY1LGZvbnQtc2l6ZToxOHB4CiBjbGFzcyBBIHNvdXJjZQogY2xhc3MgQiBnYXRlCiBjbGFzcyBDIHBhdXNlCiBjbGFzcyBEIHJlYWR5%3FbgColor%3Dffffff%26width%3D1000" alt="Backpressure flow: write chunks until write returns false, pause, then resume after drain" width="1000" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drain&lt;/code&gt; means the buffered amount has fallen below the threshold and writing may resume. It does not mean the entire stream is finished. &lt;code&gt;finish&lt;/code&gt; is the separate lifecycle event emitted after &lt;code&gt;end()&lt;/code&gt; and after all accepted data has flushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can we turn that claim into an executable contract?
&lt;/h2&gt;

&lt;p&gt;Here is the complete dependency-free script. Save it as &lt;code&gt;backpressure.mjs&lt;/code&gt; and run it with a current Node.js release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:events&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Writable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HIGH_WATER_MARK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_BYTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SlowSink&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Writable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;highWaterMark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HIGH_WATER_MARK&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_encoding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;respectBackpressure&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SlowSink&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CHUNK_BYTES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canContinue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writableLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;respectBackpressure&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;canContinue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;finish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;peakQueuedBytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;carelessPeak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;respectBackpressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;carefulPeak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;respectBackpressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;ignored&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;peakQueuedBytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;carelessPeak&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;respected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;peakQueuedBytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;carefulPeak&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;carelessPeak&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;carefulPeak&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;carefulPeak&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;HIGH_WATER_MARK&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;backpressure assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On this setup, the careless producer queues roughly 1.6 MiB. The careful one stays at or below 16 KiB. The exact timing does not matter; the two assertions describe the behavior we care about.&lt;/p&gt;

&lt;p&gt;That is worth emphasizing in an interview: test the &lt;strong&gt;queue bound&lt;/strong&gt;, not how many milliseconds the machine happened to take.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you use &lt;code&gt;pipeline()&lt;/code&gt; instead?
&lt;/h2&gt;

&lt;p&gt;If you already have a readable source, transforms, and a writable destination, prefer &lt;code&gt;pipeline()&lt;/code&gt; from &lt;code&gt;node:stream/promises&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createWriteStream&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pipeline&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:stream/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createGzip&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:zlib&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large.log&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;createGzip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large.log.gz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pipeline()&lt;/code&gt; wires backpressure between stages and propagates errors while cleaning up the chain. Manual &lt;code&gt;write()&lt;/code&gt; plus &lt;code&gt;drain&lt;/code&gt; is still appropriate when your producer is not itself a &lt;code&gt;Readable&lt;/code&gt;: a database cursor, a message consumer, or a loop generating report rows.&lt;/p&gt;

&lt;p&gt;The decision rule is simple:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Prefer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Readable → Transform → Writable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pipeline()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual producer calling &lt;code&gt;write()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Check the boolean and await &lt;code&gt;drain&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entire payload must be transformed at once&lt;/td&gt;
&lt;td&gt;A buffer may be clearer; enforce a size limit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Streams are not automatically better. For a 4 KiB configuration file, buffering is simpler. Backpressure matters when payload size is unbounded or meaningfully larger than your acceptable memory budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  How would I explain this in a backend interview?
&lt;/h2&gt;

&lt;p&gt;Use four sentences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"Backpressure is flow control between a faster producer and a slower consumer."&lt;/li&gt;
&lt;li&gt;"In Node.js, &lt;code&gt;Writable.write()&lt;/code&gt; returning &lt;code&gt;false&lt;/code&gt; tells the producer to pause until &lt;code&gt;drain&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;"Ignoring that signal allows the internal queue and GC workload to grow with the input."&lt;/li&gt;
&lt;li&gt;"I would use &lt;code&gt;pipeline()&lt;/code&gt; for connected streams, or explicitly test a queue bound for a manual producer."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then offer the trade-off: increasing &lt;code&gt;highWaterMark&lt;/code&gt; may improve throughput by batching more work, but it increases per-stream memory. Multiply that value by concurrent requests before changing it.&lt;/p&gt;

&lt;p&gt;If you are practicing that explanation aloud, use a follow-up that forces a decision: "Would you raise &lt;code&gt;highWaterMark&lt;/code&gt; for 500 concurrent exports? What would you measure first?" A mock interviewer should challenge the memory math, not merely confirm the definition. &lt;a href="https://www.aceround.app/blog/backend-developer-interview-ai/" rel="noopener noreferrer"&gt;aceround.app — AI interview assistant&lt;/a&gt; is one way to rehearse those follow-ups, but the script above is the artifact that keeps the answer honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does &lt;code&gt;write()&lt;/code&gt; returning &lt;code&gt;false&lt;/code&gt; mean the chunk was rejected?
&lt;/h3&gt;

&lt;p&gt;No. The chunk was accepted into the queue. &lt;code&gt;false&lt;/code&gt; means stop adding more until &lt;code&gt;drain&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is &lt;code&gt;highWaterMark&lt;/code&gt; a hard cap?
&lt;/h3&gt;

&lt;p&gt;No. It is the threshold that changes the &lt;code&gt;write()&lt;/code&gt; return value. A caller can exceed it by ignoring that value, and an individual chunk may also carry the queue past it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does &lt;code&gt;pipe()&lt;/code&gt; handle backpressure?
&lt;/h3&gt;

&lt;p&gt;Yes, for a normal readable-to-writable connection. For robust error propagation and cleanup across multiple stages, use &lt;code&gt;pipeline()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should a production test assert?
&lt;/h3&gt;

&lt;p&gt;Assert a behavior tied to risk: bounded queued bytes, cancellation cleanup, output integrity, and error propagation. Avoid a fragile wall-clock threshold unless latency itself is the contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/learn/modules/backpressuring-in-streams" rel="noopener noreferrer"&gt;Node.js: Backpressuring in Streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/api/stream.html" rel="noopener noreferrer"&gt;Node.js Stream API: &lt;code&gt;write()&lt;/code&gt;, &lt;code&gt;drain&lt;/code&gt;, and &lt;code&gt;pipeline()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: AI assisted with outlining and copy editing. I verified the API behavior against the Node.js documentation and ran the complete script locally before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>career</category>
      <category>interview</category>
    </item>
    <item>
      <title>AI-Enabled Coding Interviews: Turn Model Answers Into Test Cases</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Fri, 07 Aug 2026 04:08:16 +0000</pubDate>
      <link>https://dev.to/karuha/ai-enabled-coding-interviews-turn-model-answers-into-test-cases-ii9</link>
      <guid>https://dev.to/karuha/ai-enabled-coding-interviews-turn-model-answers-into-test-cases-ii9</guid>
      <description>&lt;h1&gt;
  
  
  AI-Enabled Coding Interviews: Turn Model Answers Into Test Cases
&lt;/h1&gt;

&lt;p&gt;The useful skill in an AI-enabled coding interview is not producing the cleverest prompt. It is turning a model's suggestion into a claim you can try to break. Give yourself one small function, one edge case the first answer is likely to miss, and one test that makes the result visible. That is a 20-minute drill you can repeat before the real round.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtNb2RlbCBhbnN3ZXJdIC0tPiBCe1N0YXRlIG9uZSBmYWxzaWZpYWJsZSBjbGFpbX0KICBCIC0tPiBDW1dyaXRlIHRoZSBzbWFsbGVzdCBjb3VudGVyZXhhbXBsZV0KICBDIC0tPiBEW1J1biB0aGUgdGVzdF0KICBEIC0tPiBFe0RvZXMgZXZpZGVuY2Ugc3VwcG9ydCBpdD99CiAgRSAtLT58eWVzfCBGW1VzZSBpdCBhbmQgZXhwbGFpbiB0aGUgdHJhZGUtb2ZmXQogIEUgLS0-fG5vfCBHW1JldmlzZSBvciByZWplY3QgaXRdCiAgY2xhc3NEZWYgaW5wdXQgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBhY3Rpb24gZmlsbDojYjQ1MzA5LHN0cm9rZTojZmNkMzRkLGNvbG9yOiNmZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBvdXRjb21lIGZpbGw6IzBlNzQ5MCxzdHJva2U6IzIyZDNlZSxjb2xvcjojZWNmZWZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3MgQSBpbnB1dAogIGNsYXNzIEIsQyxEIGFjdGlvbgogIGNsYXNzIEUsRixHIG91dGNvbWU%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgQVtNb2RlbCBhbnN3ZXJdIC0tPiBCe1N0YXRlIG9uZSBmYWxzaWZpYWJsZSBjbGFpbX0KICBCIC0tPiBDW1dyaXRlIHRoZSBzbWFsbGVzdCBjb3VudGVyZXhhbXBsZV0KICBDIC0tPiBEW1J1biB0aGUgdGVzdF0KICBEIC0tPiBFe0RvZXMgZXZpZGVuY2Ugc3VwcG9ydCBpdD99CiAgRSAtLT58eWVzfCBGW1VzZSBpdCBhbmQgZXhwbGFpbiB0aGUgdHJhZGUtb2ZmXQogIEUgLS0-fG5vfCBHW1JldmlzZSBvciByZWplY3QgaXRdCiAgY2xhc3NEZWYgaW5wdXQgZmlsbDojMTY0ZTYzLHN0cm9rZTojNjdlOGY5LGNvbG9yOiNlY2ZlZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBhY3Rpb24gZmlsbDojYjQ1MzA5LHN0cm9rZTojZmNkMzRkLGNvbG9yOiNmZmYsZm9udC1zaXplOjE4cHgKICBjbGFzc0RlZiBvdXRjb21lIGZpbGw6IzBlNzQ5MCxzdHJva2U6IzIyZDNlZSxjb2xvcjojZWNmZWZmLGZvbnQtc2l6ZToxOHB4CiAgY2xhc3MgQSBpbnB1dAogIGNsYXNzIEIsQyxEIGFjdGlvbgogIGNsYXNzIEUsRixHIG91dGNvbWU%3FbgColor%3D0f172a%26width%3D1000%26height%3D420" alt="A verification loop for AI-enabled coding interviews: model answer, falsifiable claim, counterexample, test, then use or revise" width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some companies are experimenting with interviews where an assistant is part of the environment. That changes the signal. A candidate who can ask a model for a patch but cannot identify the patch's assumptions has not demonstrated much engineering judgment. The stronger move is to narrate a short verification loop: what the answer claims, the smallest input that challenges it, and the evidence that decides whether to keep it.&lt;/p&gt;

&lt;p&gt;This is not a recipe for using tools where they are not permitted. Check the instructions for the round first. When a tool is explicitly part of the exercise, use it like you would use a teammate's code review: useful, fallible, and accountable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does a plausible answer need a counterexample?
&lt;/h2&gt;

&lt;p&gt;A generated solution often looks complete because it handles the happy path cleanly. Interviews become interesting at the boundary: empty input, duplicate work, a zero value, a stale response, a mutation that callers did not expect.&lt;/p&gt;

&lt;p&gt;The fastest way to inspect an answer is to turn its central promise into one sentence:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model claim&lt;/th&gt;
&lt;th&gt;Smallest challenge&lt;/th&gt;
&lt;th&gt;Evidence to collect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"This deduplicates records by ID."&lt;/td&gt;
&lt;td&gt;An ID of &lt;code&gt;0&lt;/code&gt;; numeric &lt;code&gt;1&lt;/code&gt; and string &lt;code&gt;"1"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Returned IDs, input immutability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"This handles invalid rows."&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;null&lt;/code&gt; or an object with no &lt;code&gt;id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A clear, intentional error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"This preserves first-seen order."&lt;/td&gt;
&lt;td&gt;The same ID appears in both lists&lt;/td&gt;
&lt;td&gt;Exact output sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table takes less than two minutes to make. It also gives you a much better explanation than "the model said this should work."&lt;/p&gt;

&lt;h2&gt;
  
  
  A runnable 20-minute drill
&lt;/h2&gt;

&lt;p&gt;Imagine an interviewer asks you to merge two event lists. You ask an assistant for a dedupe implementation. Before accepting it, define the contract:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each row must be an object with its own &lt;code&gt;id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The first row for an ID wins.&lt;/li&gt;
&lt;li&gt;IDs are type-sensitive: &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;"1"&lt;/code&gt; are different IDs.&lt;/li&gt;
&lt;li&gt;The input arrays must not be mutated.&lt;/li&gt;
&lt;li&gt;Invalid rows should fail loudly rather than disappearing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a dependency-free Node.js version with tests. Save it as &lt;code&gt;merge-events.test.js&lt;/code&gt; and run &lt;code&gt;node merge-events.test.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mergeFirstSeen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Every row needs its own id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left-zero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left-number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right-string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right-duplicate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right-new&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;mergeFirstSeen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left-zero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left-number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;throws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mergeFirstSeen&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;missing id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
  &lt;span class="sr"&gt;/needs its own id/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contract holds&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important line is not the &lt;code&gt;Set&lt;/code&gt;. It is the test data. A truthiness check such as &lt;code&gt;if (!row.id)&lt;/code&gt; would silently lose the valid &lt;code&gt;0&lt;/code&gt; record. A coercive key could collapse &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;"1"&lt;/code&gt;. Neither problem is obvious from a tidy-looking implementation.&lt;/p&gt;

&lt;p&gt;Notice the use of &lt;code&gt;Object.hasOwn&lt;/code&gt; rather than a truthiness check. It asks whether the property is actually present, even when its value is &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, or an empty string. That is exactly the distinction this contract needs. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn" rel="noopener noreferrer"&gt;MDN reference for Object.hasOwn&lt;/a&gt; is worth bookmarking for these boundary checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How should you use the drill in an interview?
&lt;/h2&gt;

&lt;p&gt;Do not read the whole test file aloud. Use a four-part narration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State the invariant.&lt;/strong&gt; "First-seen order matters, and I want numeric and string IDs to remain distinct."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name the risk.&lt;/strong&gt; "The tempting implementation is likely to mishandle a falsy ID or coerce keys."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show one discriminating test.&lt;/strong&gt; "I would include &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, and &lt;code&gt;"1"&lt;/code&gt; before trusting the helper."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explain the trade-off.&lt;/strong&gt; "This is in-memory and linear time for this call. At a database boundary, I would enforce the identity rule there as well."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That answer communicates more than algorithm recall. It shows that you can establish a contract, choose evidence, and describe where the current solution stops being sufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you ask the model?
&lt;/h2&gt;

&lt;p&gt;Specific questions create inspectable output. Avoid "write the best merge function." Instead try:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Implement a pure JavaScript function that merges two arrays, preserves first-seen order, distinguishes numeric from string IDs, rejects rows without an own &lt;code&gt;id&lt;/code&gt;, and list three edge cases your code might still miss.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then verify the model's response against your table. The assistant may offer a good implementation, but it can also expose an assumption you did not think to test. Either way, you leave with a stronger solution.&lt;/p&gt;

&lt;p&gt;A useful follow-up is: "Which test would fail if the function used truthiness to validate an ID?" This forces the discussion from general confidence to a falsifiable prediction. If the answer cannot predict the failing test, it has not earned your trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when the assistant is officially allowed?
&lt;/h2&gt;

&lt;p&gt;The task is still engineering. &lt;a href="https://coderpad.io/blog/hiring-developers/ai-in-the-interview-is-not-cheating-it-is-the-job-according-to-meta/" rel="noopener noreferrer"&gt;CoderPad's discussion of AI-enabled interviews&lt;/a&gt; makes the case for evaluating work that resembles the job: investigate unfamiliar code, use tools deliberately, and justify decisions. That makes the verification step visible rather than optional.&lt;/p&gt;

&lt;p&gt;A practical session plan is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 0-4:&lt;/strong&gt; read the code and restate the contract in your own words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 4-9:&lt;/strong&gt; ask for one narrow implementation or explanation, not an entire architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 9-15:&lt;/strong&gt; write two counterexamples that distinguish a robust answer from a merely plausible one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minutes 15-20:&lt;/strong&gt; run the tests and practice a 60-second explanation of the result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Practice tools such as &lt;strong&gt;aceround.app — an AI interview assistant&lt;/strong&gt; can help create follow-up pressure before a live conversation. The goal of the practice is still the same: be able to defend the evidence in your own words.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is prompt quality more important than coding in an AI-enabled interview?
&lt;/h3&gt;

&lt;p&gt;No. A clear prompt helps, but it is only the start. The stronger signal is whether you can identify the assumptions in the output and design a small test that would prove the answer wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I write tests for every line during a timed round?
&lt;/h3&gt;

&lt;p&gt;No. Choose tests with high information value. One falsy ID, one type-boundary case, and one invalid input reveal more than a long list of normal examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if the model's answer passes my first tests?
&lt;/h3&gt;

&lt;p&gt;Explain what the tests establish and what they do not. For this example, the helper is linear in the number of rows and deliberately in-memory. A production system may need a database uniqueness constraint, a memory limit, or a policy for later updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use this workflow when tools are not explicitly allowed?
&lt;/h3&gt;

&lt;p&gt;Use the verification mindset everywhere, but follow the interview's stated tool policy. The method does not depend on a model: turn a proposed solution into a contract, try to break it, then explain the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources and disclosure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CoderPad, &lt;a href="https://coderpad.io/blog/hiring-developers/ai-in-the-interview-is-not-cheating-it-is-the-job-according-to-meta/" rel="noopener noreferrer"&gt;AI in the interview is not cheating; it is the job&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;DEV Community, &lt;a href="https://dev.to/guidelines-for-ai-assisted-articles-on-dev"&gt;Guidelines for AI-assisted articles&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disclosure: This article was drafted with AI assistance and reviewed for technical accuracy.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>interview</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Cursor Pagination Is an Interview Contract, Not a Database Trick</title>
      <dc:creator>Karuha</dc:creator>
      <pubDate>Thu, 06 Aug 2026 02:28:39 +0000</pubDate>
      <link>https://dev.to/karuha/cursor-pagination-is-an-interview-contract-not-a-database-trick-kam</link>
      <guid>https://dev.to/karuha/cursor-pagination-is-an-interview-contract-not-a-database-trick-kam</guid>
      <description>&lt;h1&gt;
  
  
  Cursor Pagination Is an Interview Contract, Not a Database Trick
&lt;/h1&gt;

&lt;p&gt;Cursor pagination is the answer when a feed must remain navigable while rows are arriving. The important part is not the encoded string. It is the contract: define one stable order, place the next query strictly after one visible row, and prove that adjacent pages neither overlap nor skip a row.&lt;/p&gt;

&lt;p&gt;That is a much stronger interview answer than “offset gets slow, so I would use a cursor.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1516116216624-53e697fedbea%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1200%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1516116216624-53e697fedbea%3Fauto%3Dformat%26fit%3Dcrop%26w%3D1200%26q%3D80" alt="A developer working with code on a laptop" width="1200" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does a cursor have to mean?
&lt;/h2&gt;

&lt;p&gt;A cursor is a boundary in an ordered set. It is not an arbitrary database primary key and it is not a page number in disguise.&lt;/p&gt;

&lt;p&gt;Imagine an activity feed sorted newest first. Sorting only by &lt;code&gt;createdAt DESC&lt;/code&gt; looks reasonable until two events share the same timestamp. If page one ends at &lt;code&gt;10:00:01&lt;/code&gt;, a next-page predicate such as &lt;code&gt;createdAt &amp;lt; 10:00:01&lt;/code&gt; drops every sibling created during that second. Using &lt;code&gt;&amp;lt;=&lt;/code&gt; fixes the gap by creating a duplicate.&lt;/p&gt;

&lt;p&gt;The practical fix is a unique, deterministic compound order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORDER BY created_at DESC, id DESC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cursor carries both fields from the final visible row. The next page asks for rows strictly after that boundary in the same order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHERE created_at &amp;lt; :cursorCreatedAt
   OR (created_at = :cursorCreatedAt AND id &amp;lt; :cursorId)
ORDER BY created_at DESC, id DESC
LIMIT :pageSize + 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extra row answers a separate question: is there another page? Return the first &lt;code&gt;pageSize&lt;/code&gt; rows, then derive the next cursor from the final returned row only when the extra row exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you prove the boundary instead of describing it?
&lt;/h2&gt;

&lt;p&gt;Here is a dependency-free Node.js drill. It deliberately gives two rows the same timestamp, which is where a timestamp-only cursor usually fails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;localeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;localeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;boundary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;boundary&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;boundary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-06T10:00:02.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-06T10:00:01.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-06T10:00:01.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-06T10:00:00.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delivered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delivered&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delivered&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delivered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cursor pagination assertions passed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last two assertions are the point of the exercise. Together, they assert the two properties a caller cares about: every expected row appears, and no row appears twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is offset not the same contract?
&lt;/h2&gt;

&lt;p&gt;Offset pagination is fine for a static admin table, exports, or a result set with a fixed snapshot. It becomes misleading when new records can land before the next offset.&lt;/p&gt;

&lt;p&gt;Suppose a client receives rows 1 through 20, then three newer rows arrive. &lt;code&gt;OFFSET 20&lt;/code&gt; now begins three rows later than the caller expects. Depending on the direction of writes, the client sees duplicates, gaps, or both. A cursor anchored to the last seen row does not promise a frozen universe, but it does keep moving forward from a known boundary.&lt;/p&gt;

&lt;p&gt;That distinction matters in an interview. Say what your product needs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Design consequence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infinite feed&lt;/td&gt;
&lt;td&gt;Compound cursor and stable ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jump to page 47&lt;/td&gt;
&lt;td&gt;Offset or a separate indexed navigation model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeatable export&lt;/td&gt;
&lt;td&gt;Snapshot timestamp or transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Newly inserted rows&lt;/td&gt;
&lt;td&gt;Decide whether they appear only after refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A good answer also names the limitation. Cursor pagination cannot provide cheap random page access, and a mutable sort key can move a row across a boundary. For a timeline, prefer an immutable creation timestamp. For a ranking feed, use an explicit snapshot or accept that refreshed results can reorder.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should the API expose?
&lt;/h2&gt;

&lt;p&gt;Keep the token opaque. A base64 JSON token is convenient in a demo, but clients should not build behavior around its fields. The response shape can remain small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"d"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nextCursor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJjcmVhdGVkQXQiOiIuLi4iLCJpZCI6ImMifQ"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the server, validate decoding, field types, and expected sort version. If the feed's ordering ever changes, a versioned cursor lets the API reject old tokens clearly instead of silently returning a wrong continuation.&lt;/p&gt;

&lt;p&gt;In a real database, index the ordering columns in the same direction as the query where the engine supports it. Then inspect the query plan with a realistic cursor, not just the first page. The first page is often cheap; the continuation query is the one this design is meant to protect.&lt;/p&gt;

&lt;h2&gt;
  
  
  How would I say this out loud?
&lt;/h2&gt;

&lt;p&gt;My compact answer would be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I would define a unique order first, usually &lt;code&gt;created_at DESC, id DESC&lt;/code&gt;. The cursor is the last visible tuple, and the next query uses a strict lexicographic predicate against that tuple. I fetch one extra row to compute &lt;code&gt;hasNextPage&lt;/code&gt;, and I test equal timestamps because that is where gaps and duplicates appear. If the product needs page 47 or a repeatable export, I would choose a different contract.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is concrete enough to invite follow-up questions about indexes, snapshots, and changing rank scores.&lt;/p&gt;

&lt;p&gt;For practice, I find it useful to explain the predicate before writing it and then let a mock interviewer challenge one assumption at a time. &lt;a href="https://www.aceround.app/blog/ai-interview-assistant-screen-share-safe/" rel="noopener noreferrer"&gt;aceround.app, an AI interview assistant&lt;/a&gt;, is one option for rehearsing that kind of technical follow-up without turning the session into a memorized script.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why not put only the ID in the cursor?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
That works only when the feed is ordered by that same unique ID. If ordering is by timestamp, score, or another field, the cursor must contain enough data to reproduce the boundary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should a cursor be encrypted?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Not necessarily. It should be opaque to clients and validated by the server. Sign it when tampering would create a meaningful risk or reveal sensitive data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does cursor pagination eliminate all consistency issues?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. It gives a clear continuation boundary. Snapshot requirements, mutable rank fields, and deletions still need an explicit product decision.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance was used for drafting. The code and pagination invariants were reviewed and executed before publication.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>backend</category>
      <category>career</category>
      <category>interview</category>
    </item>
  </channel>
</rss>
