<?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: Emily Woods</title>
    <description>The latest articles on DEV Community by Emily Woods (@emilywoodsnyc).</description>
    <link>https://dev.to/emilywoodsnyc</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%2F3912599%2Fc95a7ee1-41d2-4e1e-994d-e1aedab86d39.png</url>
      <title>DEV Community: Emily Woods</title>
      <link>https://dev.to/emilywoodsnyc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emilywoodsnyc"/>
    <language>en</language>
    <item>
      <title>Your Next Coding Interview Might Start With Code You Didn’t Write</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Fri, 23 Oct 2026 22:16:43 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/your-next-coding-interview-might-start-with-code-you-didnt-write-4179</link>
      <guid>https://dev.to/emilywoodsnyc/your-next-coding-interview-might-start-with-code-you-didnt-write-4179</guid>
      <description>&lt;p&gt;&lt;em&gt;A practical debugging rehearsal: trace one request, isolate the mistake, and prove the repair without rewriting everything.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You prepare to build a solution from an empty editor. Then you open the interview project and discover that the solution already exists. It just does not work.&lt;/p&gt;

&lt;p&gt;There are routes you did not name, a storage layer you did not choose, and a failing test whose description sounds almost embarrassingly simple. Somewhere inside this unfamiliar code, a change that appears to succeed disappears when the user refreshes.&lt;/p&gt;

&lt;p&gt;I find that a more useful practice challenge than another evening spent recognizing patterns. You have to decide where to look before you can decide what to write.&lt;/p&gt;

&lt;p&gt;The uncomfortable part is the urge to make yourself useful immediately. You spot an awkward function, start cleaning it up, and ten minutes later the code is prettier while the original failure remains unexplained.&lt;/p&gt;

&lt;p&gt;For this kind of rehearsal, I prefer a stricter rule: &lt;strong&gt;every edit needs a specific observation that justifies it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the complaint, then draw one request
&lt;/h2&gt;

&lt;p&gt;Imagine a small car-rental service. A user changes a daily price from 80 to 60. The update response contains 60. A fresh request returns 80.&lt;/p&gt;

&lt;p&gt;This is an illustrative exercise, with invented data and a deliberately simplified repository interface. It is not a reconstruction of a company’s private source code.&lt;/p&gt;

&lt;p&gt;Before opening every folder, write the contract: an authorized update should change the stored price, and a subsequent read should return that change. For this example, reads go straight to the same store and no other writer is active.&lt;/p&gt;

&lt;p&gt;That last sentence matters. Without it, a stale replica, a cache, or a concurrent edit could produce a similar symptom for a different reason.&lt;/p&gt;

&lt;p&gt;Now follow just one path:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request → authenticated user → handler → repository → stored record → response.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first useful question is whether the stored record changed. If it still says 80, investigate the write. If it says 60 but the read returns 80, investigate how the read found its answer.&lt;/p&gt;

&lt;p&gt;You have already cut the search space without changing a line.&lt;/p&gt;

&lt;h2&gt;
  
  
  The response looks right because the response is the bug
&lt;/h2&gt;

&lt;p&gt;Here is a small service function. Assume &lt;code&gt;findOwned&lt;/code&gt; returns a detached copy of a record owned by the authenticated user, or null. Also assume &lt;code&gt;changes&lt;/code&gt; has already been validated and contains only permitted fields.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;editCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;carId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changes&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;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOwned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;carId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&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;car&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;null&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;updated&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;car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;changes&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;updated&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;Pause before reading further. Which operation changes the store?&lt;/p&gt;

&lt;p&gt;There isn’t one. The function creates a new object and returns it. An HTTP handler can serialize that object into a perfectly convincing success response while the stored record remains untouched.&lt;/p&gt;

&lt;p&gt;For our example, the response contains 60, the store still contains 80, and the next read correctly returns the old value. The read is exposing the missing write.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftszayp4vkrmeuc274ion.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftszayp4vkrmeuc274ion.png" alt="Three-panel debugging trace: a user requests a price change from 80 to 60. The response says 60 but storage remains 80, so a new GET returns 80. Changing a returned object does not save the record." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why I want an expected result written down before I inspect the implementation. A response that agrees with the input can feel like evidence even when it proves very little.&lt;/p&gt;

&lt;p&gt;My diagnosis note would contain four lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Observed:&lt;/strong&gt; the edit response contains 60; a fresh read contains 80.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hypothesis:&lt;/strong&gt; the service changes only the returned object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence:&lt;/strong&gt; this path calls a read method and never calls a write method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repair to check:&lt;/strong&gt; perform the scoped update, then return its stored result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That note is short enough to explain aloud. It also makes the reasoning easy to challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the smallest repair that restores the contract
&lt;/h2&gt;

&lt;p&gt;In this sketch, the repository offers &lt;code&gt;updateOwned&lt;/code&gt;. Its contract is explicit: match both the car ID and owner ID, apply only validated fields, and resolve with the saved record after a successful write. Return null when no owned record matches; reject on a storage failure.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;editCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;carId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changes&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;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateOwned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;carId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changes&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;saved&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 shorter function is not the achievement. The achievement is that the repository now performs the operation the endpoint promises.&lt;/p&gt;

&lt;p&gt;This is an interface sketch, not a drop-in database implementation. In the real project, inspect the actual update method: does it return the old record, the new record, or only an affected-row count? Can it report success when nothing matched?&lt;/p&gt;

&lt;p&gt;The handler must translate those outcomes into the documented response. It must also handle a rejected write rather than announce success before the operation finishes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await" rel="noopener noreferrer"&gt;JavaScript’s await behavior&lt;/a&gt; is useful to keep precise here: it waits for the supplied promise to settle. It cannot turn a missing write into a real one, and it cannot repair a repository that resolves before its underlying work finishes.&lt;/p&gt;

&lt;p&gt;I also want the owner restriction inside the update operation. A prior lookup alone is not a reason to let the eventual write target any record with that ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prove the repair through a separate read
&lt;/h2&gt;

&lt;p&gt;A check that only inspects the edit response would have accepted our broken version. The stronger check follows the user’s next action.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Seed car C1, owned by user A, with a daily price of 80.&lt;/li&gt;
&lt;li&gt;As A, change C1’s price to 60 and wait for the request to finish.&lt;/li&gt;
&lt;li&gt;Read C1 again through the application’s normal read path.&lt;/li&gt;
&lt;li&gt;Confirm that both the response and the stored record contain 60.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then make the check harder in two specific ways. Attempt the edit as user B and confirm that no change occurs. Make the storage write fail and confirm that the endpoint does not return a successful saved record.&lt;/p&gt;

&lt;p&gt;Use the project’s documented error contract for the status codes. There is no value in replacing its conventions with your favorite API style halfway through a repair.&lt;/p&gt;

&lt;p&gt;One subtle trap belongs in the test setup too. If a fake repository returns the same object reference that it stores, an accidental mutation can appear to persist. A fake should reproduce the storage behavior that matters, including detached reads when that is how the real repository behaves.&lt;/p&gt;

&lt;p&gt;For a disk-backed service that promises persistence across restarts, add that check separately. Reading twice from a shared in-memory object cannot establish crash durability.&lt;/p&gt;

&lt;h2&gt;
  
  
  A different symptom needs a different investigation
&lt;/h2&gt;

&lt;p&gt;The useful habit is to locate the first point where actual state disagrees with the contract. The same habit works when the complaint changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A new record never appears.&lt;/strong&gt; Inspect the store first. If the record is missing, follow creation and its completion. If it exists, compare the fields used by the list query with the fields creation actually saved. An owner field mismatch can hide a perfectly successful insert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A list includes another user’s records.&lt;/strong&gt; Trace the authenticated identity into the query. Filtering the browser display is insufficient; the server must enforce access. Check sibling fetch, edit, and delete paths against the same ownership rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A change disappears only sometimes.&lt;/strong&gt; Write down the order of events. Two requests might read the same old state, make different changes, and overwrite each other. Adding await to each request does not serialize them against one another.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1529j67tptxndhzdmege.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1529j67tptxndhzdmege.png" alt="Decision map: read the stored record. If it has the old value, inspect a missing save, wrong match, or incomplete write. If it has the new value, inspect the read filter, cache, or environment. Find the first disagreement, repair it, and read again." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the project stores everything in one JSON file, this deserves special attention. &lt;a href="https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options" rel="noopener noreferrer"&gt;Node’s file-system documentation&lt;/a&gt; warns against overlapping writeFile calls to the same file without waiting. Even individually completed writes can lose updates when two read–modify–write sequences race. Coordination must cover the whole operation and every writer involved.&lt;/p&gt;

&lt;p&gt;That is a follow-up to investigate when the evidence points there. It is not a reason to replace a small service with a new architecture before understanding the original bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn this into your next practice session
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://prachub.com/interview-questions/debug-a-node-js-car-rental-backend-leaked-cars-missing-new-cars-lost-edits?utm_source=medium&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;Amazon-tagged car-rental debugging exercise&lt;/a&gt; describes an unfamiliar Node.js project with three complaints: records leaking between users, missing new records, and edits lost after refresh. The original source code is not included, so use it to rehearse diagnosis, then build or choose a small local service for hands-on repair. The listing is a reported interview exercise, not a promise about your next assessment.&lt;/p&gt;

&lt;p&gt;For a 35-minute rehearsal, this is the structure I prefer. It is a practice plan, not a hiring score.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First 5 minutes:&lt;/strong&gt; read the API contract, run the supplied checks, and map failures to the affected operations. Confirm that the service is using the intended environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next 10 minutes:&lt;/strong&gt; reproduce one failure with a tiny fixture. Trace only the relevant request and write down the first wrong value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next 10 minutes:&lt;/strong&gt; make one justified repair. Check the original symptom and a neighboring behavior that the repair could damage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final 10 minutes:&lt;/strong&gt; rerun the full supplied suite, inspect the change, and explain the cause, repair, evidence, and remaining uncertainty.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep your diagnosis before opening a solution. If you use PracHub Premium for guided review, its solution walkthroughs and AI Mentor can be the comparison step after your own attempt. The useful question is where your reasoning diverged, not how quickly you can recognize the explanation afterward.&lt;/p&gt;

&lt;p&gt;During an actual assessment, follow its rules for assistants and external resources. During practice, an assistant can help locate a call path or propose a counterexample; you still need to explain why the repair follows from the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leave yourself something better than a green screen
&lt;/h2&gt;

&lt;p&gt;At the end, keep a small repair record:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Given this state → this request should cause this change → this observation proved the defect → this patch restored the behavior → these checks protect it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you cannot fill in the observation, you may have guessed correctly without understanding the failure. If you cannot name the checks, you have not shown which behavior the repair protects.&lt;/p&gt;

&lt;p&gt;This is the part of interview preparation I want to make less mysterious. You do not need to understand an entire repository before becoming useful inside it. You need enough of the system to make one claim, find evidence for it, and avoid breaking the next request.&lt;/p&gt;

&lt;p&gt;Before your next practice session, choose a small project and ask someone to plant one bug. Keep the expected behavior, hide the changed line, and explain the repair aloud when you find it.&lt;/p&gt;

&lt;p&gt;Could you prove the fix if the success response were lying to you?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;~ Emily&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>interview</category>
      <category>debugging</category>
      <category>javascript</category>
      <category>career</category>
    </item>
    <item>
      <title>15 Trending GitHub Repos I’d Use Before Building Another AI Project</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Tue, 13 Oct 2026 21:14:20 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/15-trending-github-repos-id-use-before-building-another-ai-project-h56</link>
      <guid>https://dev.to/emilywoodsnyc/15-trending-github-repos-id-use-before-building-another-ai-project-h56</guid>
      <description>&lt;p&gt;&lt;em&gt;What each one does, the small project I’d try first, and what I’d want to show when someone asks how it works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can spend an entire weekend collecting GitHub repositories and still have nothing new to show on Monday.&lt;/p&gt;

&lt;p&gt;It is easy to find something impressive now. You open a thread, save five links, watch an agent finish a task, and suddenly the project you were excited about yesterday feels outdated. Another weekend disappears into deciding what to learn.&lt;/p&gt;

&lt;p&gt;I want a resource list that helps me get out of that loop. Give me a useful starting point, something small to change, and a reason to care about what happens afterward.&lt;/p&gt;

&lt;p&gt;So I checked GitHub’s &lt;a href="https://github.com/trending?since=weekly" rel="noopener noreferrer"&gt;weekly&lt;/a&gt; and &lt;a href="https://github.com/trending?since=monthly" rel="noopener noreferrer"&gt;monthly Trending pages&lt;/a&gt; on October 13, 2026, then read the repositories behind the names. All 15 below appeared on at least one of those lists when I checked. Their order here follows the work they help with, rather than their star counts.&lt;/p&gt;

&lt;p&gt;This is a research-based shortlist. The project ideas are what I would try; I am not claiming to have installed and benchmarked every tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the problem you actually have
&lt;/h2&gt;

&lt;p&gt;You do not need fifteen installations to get value from this article. Pick the part of your work that currently feels hardest to explain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your AI keeps building the wrong thing:&lt;/strong&gt; begin with repositories 1–3, covering requirements, engineering workflows, and code review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your demo works until something changes:&lt;/strong&gt; explore 4–6, covering application tests, native debugging, and agent permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your agent forgets or retrieves the wrong information:&lt;/strong&gt; compare 7–9, which address different parts of context and memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want a stronger AI or data project:&lt;/strong&gt; look at 10–12 for retrieval, evaluation, and local inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need to coordinate or explain what you built:&lt;/strong&gt; use 13–15 for agent orchestration, architecture diagrams, and video walkthroughs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read each repository’s current setup, license, and requirements before choosing it. Several depend on a model provider or substantial local resources, so a public repository does not automatically mean a zero-cost project.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyabqtla7s6pwvy1f7h3n.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyabqtla7s6pwvy1f7h3n.png" alt="Repository field guide: specify and review with 1 to 3; test, debug and contain with 4 to 6; handle context and memory with 7 to 9; build and measure AI with 10 to 12; coordinate and explain with 13 to 15." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First, make the work easier to specify and review
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. mattpocock/skills — find the question you forgot to ask
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/mattpocock/skills" rel="noopener noreferrer"&gt;Repository: mattpocock/skills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This collection contains agent skills for clarifying requirements, diagnosing bugs, developing with tests, and improving codebase structure. I would start by reading the requirements-focused skills, especially &lt;code&gt;grill-me&lt;/code&gt; or &lt;code&gt;grill-with-docs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project I’d try:&lt;/strong&gt; take an ambiguous feature such as exporting a report and turn it into a short specification. Decide who can export, which records are included, how dates are interpreted, and what happens when the result is empty.&lt;/p&gt;

&lt;p&gt;Keep the original request beside the revised specification. That comparison gives you something concrete to discuss: which unanswered question would have caused the most rework?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary:&lt;/strong&gt; a skill can surface questions, but you still need to choose and justify the answers. Installing the whole collection is optional; understanding one useful workflow is enough to begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. addyosmani/agent-skills — turn a feature into a reviewable sequence
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/addyosmani/agent-skills" rel="noopener noreferrer"&gt;Repository: addyosmani/agent-skills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This repository packages engineering practices for AI coding agents, including specification, planning, incremental implementation, testing, and review.&lt;/p&gt;

&lt;p&gt;I would use it on a small change to an existing app: add pagination to a list that currently loads every record. Before implementation, define the ordering, page size, and behaviour when records are added between requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start here:&lt;/strong&gt; read the specification and planning workflows before enabling the build workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep afterward:&lt;/strong&gt; the plan, the resulting diff, and the checks that cover the behaviour you promised.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Question to answer:&lt;/strong&gt; which part still needs a human decision when a check passes but the experience is confusing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The README notes that some individual-skill installations omit shared reference material. Check that the workflow you choose has the files it expects.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. alibaba/open-code-review — practise disagreeing with an AI review
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/alibaba/open-code-review" rel="noopener noreferrer"&gt;Repository: alibaba/open-code-review&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open Code Review reads Git changes and uses a configurable language model to produce structured, line-level review comments. It can inspect surrounding code as part of that review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project I’d try:&lt;/strong&gt; prepare a tiny change in a throwaway app with two known mistakes, then review it yourself before comparing the tool’s findings.&lt;/p&gt;

&lt;p&gt;Keep a three-column note: finding, evidence, and decision. Include a real problem it found, anything it missed, and any comment you rejected with a reason. If there are no false positives, record that outcome instead of manufacturing one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary:&lt;/strong&gt; model-generated comments still need evidence. The configured model receives code for analysis, so use a project you are allowed to share with that provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next, find out what breaks beyond the happy path
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4. tester-army/e2e — connect an agent action to an explicit check
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/tester-army/e2e" rel="noopener noreferrer"&gt;Repository: tester-army/e2e&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This framework combines natural-language agent actions with locators and assertions for web and mobile application tests. Its README also describes replaying previously verified actions until the app changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with:&lt;/strong&gt; the example closest to your stack, such as Vite, Next.js, or a mobile app. Build one test around a local form: submit valid input, reject invalid input, and confirm the saved result survives a reload.&lt;/p&gt;

&lt;p&gt;I would keep the assertions readable enough that somebody can tell what success means without trusting the agent’s description.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I’d show:&lt;/strong&gt; a failing run, the smallest correction, and the passing result. Make the underlying requirement visible alongside them.&lt;/p&gt;

&lt;p&gt;Agent-driven steps need a supported model setup. Ordinary tests without those steps do not require a model, according to the README.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. EpicGames/raddebugger — learn to inspect a running program
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/EpicGames/raddebugger" rel="noopener noreferrer"&gt;Repository: EpicGames/raddebugger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RAD Debugger is a graphical debugger for native programs. At the time of review, its README describes an alpha debugger supporting local Windows x64 debugging with PDB information.&lt;/p&gt;

&lt;p&gt;This is a focused choice for someone working with native code. I would compile a small C or C++ program with an incorrect loop boundary, then inspect the index and relevant values as execution progresses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The deliverable:&lt;/strong&gt; a short explanation of where the observed state first diverges from the expected state, followed by the correction.&lt;/p&gt;

&lt;p&gt;A screenshot of a breakpoint is useful only when you can explain why it is there. If your current work is entirely Python notebooks or browser JavaScript, another entry will give you a more direct starting point.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. NVIDIA/OpenShell — make agent permissions part of the project
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/NVIDIA/OpenShell" rel="noopener noreferrer"&gt;Repository: NVIDIA/OpenShell&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenShell provides a runtime for agents with policy-controlled access to files, processes, and network destinations. Its architecture is useful reading even before you try the runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project I’d try:&lt;/strong&gt; create a small sandbox exercise with one allowed working directory and one intentionally disallowed location containing dummy files. Describe the expected access decisions, then inspect the actual outcomes.&lt;/p&gt;

&lt;p&gt;Keep a diagram of what the agent can reach and the policy that enforces it. The interesting follow-up is what changes when the agent needs one additional destination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary:&lt;/strong&gt; configuring a sandbox is an engineering task of its own. Check the support matrix; the README currently lists Windows through WSL 2 as experimental. Successful setup alone does not prove your policy matches your intention.&lt;/p&gt;

&lt;h2&gt;
  
  
  These three repositories solve different memory problems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7. thedotmack/claude-mem — carry useful history into the next session
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/thedotmack/claude-mem" rel="noopener noreferrer"&gt;Repository: thedotmack/claude-mem&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude-Mem captures coding-session activity, processes it into memory, and makes relevant context available in later sessions. The repository now documents integrations beyond Claude Code.&lt;/p&gt;

&lt;p&gt;I would try it on a small project with three deliberate decisions: a naming convention, an API constraint, and a rejected approach. In a later session, ask for a change that depends on those decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I’d record:&lt;/strong&gt; what was retrieved, what was missing, and whether an outdated decision was treated as current. Compare the result with a short hand-written project note.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary:&lt;/strong&gt; inspect the capture settings and chosen memory provider. The current setup includes provider and account options; persistent memory may involve model usage and storage, rather than being a free notebook with a new name.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. mksglu/context-mode — investigate what reaches the context window
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/mksglu/context-mode" rel="noopener noreferrer"&gt;Repository: mksglu/context-mode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Context Mode keeps large tool outputs outside the model’s immediate context and provides ways to query relevant information. It also tracks session events for continuity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project I’d try:&lt;/strong&gt; use a synthetic application log with a known failure hidden among routine messages. Compare whether the agent can locate the relevant evidence with raw output and with the context-management workflow.&lt;/p&gt;

&lt;p&gt;Record the material the agent actually sees, the answer it gives, and whether it cites the right lines. A smaller output is useful only if the needed evidence remains recoverable.&lt;/p&gt;

&lt;p&gt;I would treat the README’s reduction figures as the project’s reported results, not a promise about my own workload. Client support and hook behaviour also differ, so start with the integration for your actual tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. vectorize-io/hindsight — build memory into an application
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;Repository: vectorize-io/hindsight&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hindsight exposes operations for retaining information, recalling relevant memories, and reflecting over them. Its memory banks give you a concrete structure to work with when building an agent-backed application.&lt;/p&gt;

&lt;p&gt;I would build a small assistant for a fictional project whose delivery date changes. Give it a dated initial plan, a later revision, and an unrelated note, then ask which deadline currently applies and why.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start here:&lt;/strong&gt; follow the documented quickstart, then use explicit retain and recall calls so you can see the information flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep afterward:&lt;/strong&gt; the input events, retrieved evidence, final answer, and a case it handled incorrectly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Question to answer:&lt;/strong&gt; how will your application distinguish a new fact from an old fact that remains in storage?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Budget for the model and storage configuration you choose. Adding memory does not automatically resolve contradictory information.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi5o12pftg55y9pyggdvh.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi5o12pftg55y9pyggdvh.png" alt="Three separate memory problems: Claude-Mem carries coding history between sessions; Context Mode keeps bulky tool output outside immediate context; Hindsight stores and retrieves application memories. In every case, inspect what was retrieved and what became outdated." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build an AI project with something measurable inside it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  10. Tencent/WeKnora — make a document assistant answerable to its sources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/Tencent/WeKnora" rel="noopener noreferrer"&gt;Repository: Tencent/WeKnora&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WeKnora is a knowledge platform with document ingestion, retrieval, and question-answering capabilities, alongside agent and wiki features. Its retrieval workflow includes citations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project I’d try:&lt;/strong&gt; load a small set of public documents and write ten questions before asking the system anything. Include questions the documents can answer, a conflicting pair of sources, and a question with no supported answer.&lt;/p&gt;

&lt;p&gt;Keep the source passage next to every expected answer. When a result is wrong, separate a retrieval miss from an answer that misuses correctly retrieved evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary:&lt;/strong&gt; begin with one document collection and one retrieval workflow. Enabling every integration at once would make it harder to identify where a mistake entered the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. rohitg00/ai-engineering-from-scratch — go deeper on retrieval evaluation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/rohitg00/ai-engineering-from-scratch" rel="noopener noreferrer"&gt;Repository: rohitg00/ai-engineering-from-scratch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This educational repository covers model foundations, LLM systems, and agent development. Its project section includes a Retrieval Evaluation Lab with staged work and local graders.&lt;/p&gt;

&lt;p&gt;I would choose that lab if I could already build a document demo but struggled to explain whether a retrieval change helped.&lt;/p&gt;

&lt;p&gt;The supplied scenario includes an aggregate ranking improvement alongside a query-level regression. That is a useful reason to inspect individual results rather than stop at one average.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I’d keep:&lt;/strong&gt; a comparison showing which queries improved, which got worse, and the relevance judgments used to score them. Then explain what those measurements leave unanswered about the final generated response.&lt;/p&gt;

&lt;p&gt;The lab lists Python 3.10 or newer. Treat it as a focused project you can finish before adding more curriculum to your reading list.&lt;/p&gt;

&lt;h3&gt;
  
  
  12. JustVugg/colibri — understand the trade-off behind local inference
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/JustVugg/colibri" rel="noopener noreferrer"&gt;Repository: JustVugg/colibri&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Colibri implements inference engines in C. For supported mixture-of-experts models, it can stream expert weights from disk instead of keeping the entire model resident in memory.&lt;/p&gt;

&lt;p&gt;This is the most infrastructure-heavy option on my shortlist. I would begin by reading how the chosen engine loads weights, then attempt a small supported configuration only if the machine and storage requirements fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The investigation:&lt;/strong&gt; measure startup, first-response delay, memory use, and subsequent generation under a clearly recorded model configuration. Explain which costs moved from memory capacity to disk access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary:&lt;/strong&gt; the README currently asks for at least 8 GB RAM and 22 GB free disk for its smallest model option, with more recommended. A model loading successfully does not establish that its speed is useful for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finish with work that another person can inspect
&lt;/h2&gt;

&lt;h3&gt;
  
  
  13. paperclipai/paperclip — study the coordination around agents
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/paperclipai/paperclip" rel="noopener noreferrer"&gt;Repository: paperclipai/paperclip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paperclip provides a server and interface for coordinating AI agents, assigning work, and tracking activity and costs.&lt;/p&gt;

&lt;p&gt;I would resist beginning with an entire fictional company. Start with a modest workflow: one agent prepares a change summary, another reviews it, and a person decides whether the result is acceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I’d build:&lt;/strong&gt; a task flow with clear ownership, a spending limit, and an explicit completion rule. Then interrupt one step and inspect the state left behind.&lt;/p&gt;

&lt;p&gt;Keep the task history and explain how you would prevent a retry from producing duplicate work. Agent runtimes and model usage remain separate dependencies; an orchestration dashboard does not remove their costs or failure modes.&lt;/p&gt;

&lt;h3&gt;
  
  
  14. cathrynlavery/diagram-design — explain the failure path in one picture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/cathrynlavery/diagram-design" rel="noopener noreferrer"&gt;Repository: cathrynlavery/diagram-design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This agent skill creates diagrams in a self-contained HTML and SVG file. The repository includes architecture and sequence-diagram workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project I’d try:&lt;/strong&gt; diagram a request that writes a record successfully but loses the response before the client receives it. Add the retry and mark which component decides whether another record should be created.&lt;/p&gt;

&lt;p&gt;I would compare that picture with the real code or a clearly stated proposed design. Every arrow should correspond to an interaction you can explain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I’d keep:&lt;/strong&gt; the diagram, its assumptions, and one correction made after checking it. A clean visual can make an incorrect design look convincing, so the architecture still needs your review.&lt;/p&gt;

&lt;h3&gt;
  
  
  15. heygen-com/hyperframes — give your project a useful walkthrough
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/heygen-com/hyperframes" rel="noopener noreferrer"&gt;Repository: heygen-com/hyperframes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HyperFrames turns HTML, CSS, media, and seekable animations into MP4 video. It provides a local rendering workflow and integrations for coding agents.&lt;/p&gt;

&lt;p&gt;I would make a short walkthrough of one of the projects above: introduce the requirement, show the input, reveal a failure, and explain the correction. A viewer should understand what changed without needing to read the whole repository first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start here:&lt;/strong&gt; the documented CLI quickstart and a minimal composition. Use your own screenshots or diagrams and keep the sequence small enough to inspect frame by frame.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary:&lt;/strong&gt; video is the explanation layer. Keep the underlying code and evidence available so another engineer can examine the work beyond the edited recording.&lt;/p&gt;

&lt;h2&gt;
  
  
  If I had one weekend, I would choose one of these routes
&lt;/h2&gt;

&lt;p&gt;The shortlist becomes useful when it turns into a decision. These are starting combinations, and finishing one small exercise is enough; setting up all the tools is not a requirement.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For a software engineer:&lt;/strong&gt; use repository 2 to specify a small change, then repository 4 to check its behaviour. Keep the diff and one meaningful failure case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For an AI or data engineer:&lt;/strong&gt; start with repository 11’s retrieval lab. Use repository 10 later if you want to connect that understanding to a document application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For someone exploring agent infrastructure:&lt;/strong&gt; choose repository 6 or 9, then use repository 14 to explain the permission or memory boundary you investigated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Afterward, I would take a small &lt;a href="https://prachub.com/coding-questions/find-users-with-at-least-one-session-within-a-duration-limit?utm_source=dev.to&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=v1012"&gt;session-log coding problem&lt;/a&gt; and apply the same discipline on a smaller surface: write the expected result, handle the boundary cases, and explain the state you keep for each user. That is a manageable way to practise the reasoning without spending the whole session configuring infrastructure.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcpfafnkamz2exwkbyfg.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcpfafnkamz2exwkbyfg.png" alt="A four-panel project workflow: choose one problem, record the upstream starting point, change and check one behaviour, and keep a README that explains evidence, trade-offs and the next constraint." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before calling the project finished, I would put these five things in its README:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The problem and the scope:&lt;/strong&gt; what the project handles, and which behaviour you deliberately left out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The starting point:&lt;/strong&gt; which upstream repository you used, the version or commit, and what you changed yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The evidence:&lt;/strong&gt; an example with a justified expected result, the observed outcome, and a failure you investigated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The trade-off:&lt;/strong&gt; what your decision costs in time, memory, money, complexity, or maintenance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The next constraint:&lt;/strong&gt; one realistic change that would make you revisit the design.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A small modification can carry a substantial conversation when you understand it. Credit the upstream work, make your contribution visible, and give the reader enough information to reproduce what you are claiming.&lt;/p&gt;

&lt;p&gt;Save the list if it helps you choose. Then choose one repository and leave yourself a note about what you intend to make with it. That is the part I would want waiting for me next weekend.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;— Emily&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Netflix Interview Question: Direct-Sold Ad Campaign Model</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Wed, 07 Oct 2026 20:07:19 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/netflix-interview-question-direct-sold-ad-campaign-model-4enf</link>
      <guid>https://dev.to/emilywoodsnyc/netflix-interview-question-direct-sold-ad-campaign-model-4enf</guid>
      <description>&lt;p&gt;Ten edits to a running campaign, and 68% of your impressions get reported against a configuration that was never live when they served.&lt;/p&gt;

&lt;p&gt;This is a Netflix onsite round, tagged as software engineering fundamentals, and it asks for a data model rather than an architecture. That framing makes a lot of people pause. A data model question looks like an exercise in drawing boxes and naming foreign keys, so candidates produce a tidy diagram, feel finished, and score in the middle.&lt;/p&gt;

&lt;p&gt;Three of the requested bullets are where the question is actually decided, and all three are measurable. I built them before writing, so the numbers below come from running the model rather than from asserting how it behaves.&lt;/p&gt;

&lt;p&gt;Attempt it before reading any further. Set up a time, like 40 or 45 minutes with a blank page, listing entities and then arguing with yourself about which ones deserve their own table. The question is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://prachub.com/interview-questions/model-direct-sold-dsp-orders?utm_source=medium&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;Model direct-sold DSP orders&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reading the prompt properly&lt;br&gt;
Here is the line that decides everything:&lt;/p&gt;

&lt;p&gt;Because this is direct-sold demand, the model should not assume open auction mechanics such as exchange auctions, bid requests from open exchanges, or auction clearing prices.&lt;/p&gt;

&lt;p&gt;Most candidates read that as a simplification. The truth is very nearly the opposite, because removing the auction removes the thing that was making all your pricing and delivery decisions, so you now have to model three things by hand that an auction handled for you.&lt;/p&gt;

&lt;p&gt;Price is negotiated, not discovered&lt;br&gt;
A salesperson agreed a rate with an advertiser weeks ago and it is written into a contract. So price lives on the line item as a fixed attribute with a rate type, rather than being computed at serve time.&lt;/p&gt;

&lt;p&gt;Delivery is promised, not won&lt;br&gt;
Direct-sold inventory is usually guaranteed, which means under-delivery is a contractual failure rather than a bad day. Your model needs somewhere to record that promise and somewhere to record the make-good when you miss it.&lt;/p&gt;

&lt;p&gt;Inventory is booked, not bid for&lt;br&gt;
Selling a guarantee means selling against forecast availability, so the model needs a reservation concept. An auction system has no equivalent of that anywhere.&lt;/p&gt;

&lt;p&gt;If you take one thing from this piece, take that. The absence of the auction creates obligations rather than removing complexity.&lt;/p&gt;

&lt;p&gt;Three of the requested bullets are where the question is actually decided, and all three are measurable. I built them before writing, so the numbers below come from running the model rather than from asserting how it behaves.&lt;/p&gt;

&lt;p&gt;Attempt it before reading any further. Set up a time, like 40 or 45 minutes with a blank page, listing entities and then arguing with yourself about which ones deserve their own table. The question is here:&lt;/p&gt;

&lt;p&gt;Model direct-sold DSP orders&lt;/p&gt;

&lt;p&gt;Reading the prompt properly&lt;br&gt;
Here is the line that decides everything:&lt;/p&gt;

&lt;p&gt;Because this is direct-sold demand, the model should not assume open auction mechanics such as exchange auctions, bid requests from open exchanges, or auction clearing prices.&lt;/p&gt;

&lt;p&gt;Most candidates read that as a simplification. The truth is very nearly the opposite, because removing the auction removes the thing that was making all your pricing and delivery decisions, so you now have to model three things by hand that an auction handled for you.&lt;/p&gt;

&lt;p&gt;Price is negotiated, not discovered&lt;br&gt;
A salesperson agreed a rate with an advertiser weeks ago and it is written into a contract. So price lives on the line item as a fixed attribute with a rate type, rather than being computed at serve time.&lt;/p&gt;

&lt;p&gt;Delivery is promised, not won&lt;br&gt;
Direct-sold inventory is usually guaranteed, which means under-delivery is a contractual failure rather than a bad day. Your model needs somewhere to record that promise and somewhere to record the make-good when you miss it.&lt;/p&gt;

&lt;p&gt;Inventory is booked, not bid for&lt;br&gt;
Selling a guarantee means selling against forecast availability, so the model needs a reservation concept. An auction system has no equivalent of that anywhere.&lt;/p&gt;

&lt;p&gt;If you take one thing from this piece, take that. The absence of the auction creates obligations rather than removing complexity.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8punbap3uuv3exvq5g0.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8punbap3uuv3exvq5g0.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Starting with the contract, not the tables&lt;br&gt;
My instinct on a data modelling question is to start listing entities. That instinct is wrong here, and the better opening is to follow the money from the outside in.&lt;/p&gt;

&lt;p&gt;An agency represents advertisers, an advertiser signs an order, an order contains line items, and line items have flights. That chain is the spine, and everything else hangs off it.&lt;/p&gt;

&lt;p&gt;agency        id, name, billing_entity, default_commission_pct&lt;br&gt;
advertiser    id, agency_id (nullable), name, industry_code, billing_entity&lt;br&gt;
order         id, advertiser_id, agency_id (nullable), io_number,&lt;br&gt;
              sales_owner_id, currency, contracted_amount,&lt;br&gt;
              start_date, end_date, status, signed_at&lt;br&gt;
Two decisions worth saying out loud while you draw it.&lt;/p&gt;

&lt;p&gt;The agency link on the advertiser is nullable, because plenty of advertisers buy directly. The agency link on the order is separate and also nullable, because an advertiser can move between agencies and last quarter’s order stays with the agency that booked it. Copying the agency onto the order is not denormalisation for speed, it is recording a historical fact.&lt;/p&gt;

&lt;p&gt;The contracted_amount sits on the order rather than being derived from the line items underneath it. That looks redundant until the first time the sum of the line items disagrees with what the client signed, at which point the contract is the truth and your derived total is a bug.&lt;/p&gt;

&lt;p&gt;The line item, and the mistake I nearly made&lt;br&gt;
The line item is where the delivery configuration lives.&lt;/p&gt;

&lt;p&gt;line_item     id, order_id, name, priority, rate_type, rate_amount,&lt;br&gt;
              goal_type, goal_quantity, pacing_strategy, status&lt;br&gt;
rate_type covers CPM, CPD and cost per completed view, and rate_amount is the negotiated number. priority replaces the auction, since a guaranteed line item must beat a lower-priority one for the same impression.&lt;/p&gt;

&lt;p&gt;My first instinct was to put the budget and the dates on this row too. That is the mistake, and it is the single most common one in answers to this question.&lt;/p&gt;

&lt;p&gt;A real direct-sold order frequently runs in phases. A brand books a campaign that spends 200,000 in December, pauses for the first week of January, then spends 150,000 through to the end of the month, all under one negotiated rate and one set of targeting. If dates and budgets live on the line item, that campaign needs three line items, and now the advertiser’s frequency cap of three impressions per week fragments across three rows that know nothing about each other.&lt;/p&gt;

&lt;p&gt;So budgets and dates belong on a child entity:&lt;/p&gt;

&lt;p&gt;flight        id, line_item_id, start_at, end_at,&lt;br&gt;
              budget_amount, budget_impressions, pacing_target&lt;br&gt;
One line item, many flights, one shared set of constraints. Say that unprompted and you have demonstrated you understand what these systems actually sell.&lt;/p&gt;

&lt;p&gt;Pacing, and what the strategies really deliver&lt;br&gt;
The prompt asks for pacing and delivery goals, and candidates normally say “even pacing” and move on. I wanted to know what that costs, so I modelled it.&lt;/p&gt;

&lt;p&gt;Take a guaranteed goal of 30 million impressions across a 30-day flight, with forecast inventory that varies by day of week and by noise, totalling about 42 million available impressions. Comfortably more supply than the goal needs, so any shortfall is the pacing strategy’s fault rather than the market’s.&lt;/p&gt;

&lt;p&gt;Serving as fast as possible delivers all 30 million and finishes on day 21. The goal is met and the campaign then goes dark for nine days, which for a brand running a product launch is a failure even though the number on the report is green.&lt;/p&gt;

&lt;p&gt;Even pacing, meaning a fixed target of one thirtieth of the goal each day, finishes the flight at 97.72 per cent. It never catches up, because a day where inventory came in below the daily target is a day whose shortfall is gone forever. That 2.28 per cent gap is 683,208 impressions, and on a guaranteed order it becomes a make-good or a refund.&lt;/p&gt;

&lt;p&gt;Catch-up pacing, where each day’s target is the remaining goal divided by the remaining days, lands on exactly 100 per cent on day 30.&lt;/p&gt;

&lt;p&gt;The modelling consequence is that pacing_strategy cannot be a boolean. It needs at minimum the strategy name, a permitted daily variance, and a flag for whether the line item may front-load when it falls behind. Those three fields are the difference between a system that hits its guarantees and one that generates make-goods every month.&lt;/p&gt;

&lt;p&gt;Targeting, which should not be columns&lt;br&gt;
The temptation is a wide table with a column per dimension. Geography, device, daypart, content rating, audience segment.&lt;/p&gt;

&lt;p&gt;That design fails on the first request for a boolean expression, and direct-sold requests are full of them. Comedy or documentary content, on connected televisions, excluding households that saw the competitor’s campaign, in these five metro areas.&lt;/p&gt;

&lt;p&gt;So targeting is a tree rather than a row:&lt;/p&gt;

&lt;p&gt;targeting_node  id, line_item_id, parent_id, operator (AND|OR|NOT),&lt;br&gt;
                dimension, match_type (INCLUDE|EXCLUDE), value_set_id&lt;br&gt;
Store the expression, evaluate it at serve time, and version the whole tree rather than individual nodes. Versioning the tree matters for a reason I will come back to shortly, and it is the most important point in this answer.&lt;/p&gt;

&lt;p&gt;Creatives and approval&lt;br&gt;
Creatives attach to line items through an assignment, because the same asset frequently runs across several line items with different rotation weights.&lt;/p&gt;

&lt;p&gt;creative            id, advertiser_id, format, duration_ms, asset_uri,&lt;br&gt;
                    click_through_url&lt;br&gt;
creative_assignment id, line_item_id, creative_id, weight, active_from,&lt;br&gt;
                    active_to&lt;br&gt;
creative_review     id, creative_id, policy_context, state, reviewer_id,&lt;br&gt;
                    decided_at, reason_code&lt;br&gt;
The detail that separates answers here is that approval is not a boolean on the creative. It is a state machine, and its subject is the creative in a policy context rather than the creative alone. The same advert may be approved for general audiences and rejected for content rated for children, approved in one country and rejected in another.&lt;/p&gt;

&lt;p&gt;The states are worth naming: submitted, in review, approved, rejected, and expired. That final state is the one that catches people out. Approvals go stale when policy changes, and a model with no expiry has no way to force a re-review across an existing book of business.&lt;/p&gt;

&lt;p&gt;Serving constraints, and how leaky caps really are&lt;br&gt;
Frequency caps live at the level where the promise was made, which is usually the order rather than the line item, and sometimes the advertiser. That scope belongs in the model explicitly:&lt;/p&gt;

&lt;p&gt;serving_constraint  id, scope_type (ADVERTISER|ORDER|LINE_ITEM),&lt;br&gt;
                    scope_id, constraint_type (FREQUENCY|DAYPART|&lt;br&gt;
                    COMPETITIVE_SEPARATION), max_count, window_seconds,&lt;br&gt;
                    payload&lt;br&gt;
I was curious how well a frequency cap actually holds up when the counters are distributed, so I simulated eight ad servers sharing counts on an interval, with a cap of three impressions per user per week.&lt;/p&gt;

&lt;p&gt;At a one-second sync the cap effectively holds. At sixty seconds, 0.27 per cent of capped users received a fourth impression. At five minutes, 0.94 per cent did, for 188 wasted impressions across the run.&lt;/p&gt;

&lt;p&gt;Those are small numbers, and they are not zero, which is the point worth making. A frequency cap is a policy with a tolerance rather than an invariant, so the model should carry the tolerance. A field recording acceptable overshoot is the kind of detail that makes an interviewer stop and look at you properly, because it shows you have shipped one of these rather than diagrammed one.&lt;/p&gt;

&lt;p&gt;Delivery events, and the thing that actually breaks reporting&lt;br&gt;
Now the part I care most about, and the reason this question is better than it looks.&lt;/p&gt;

&lt;p&gt;impression  id, line_item_id, creative_id, flight_id, user_id, served_at,&lt;br&gt;
            config_version, rate_amount_applied, geo, device_type&lt;br&gt;
click       id, impression_id, clicked_at&lt;br&gt;
conversion  id, advertiser_id, impression_id (nullable), converted_at,&lt;br&gt;
            conversion_type, value_amount&lt;br&gt;
Look at the third field from the end on the impression row. config_version is doing enormous work, and here is why.&lt;/p&gt;

&lt;p&gt;Direct-sold orders get renegotiated mid-flight. A rate drops because the advertiser added budget. Targeting narrows because the brand launched somewhere else. These are normal commercial events, not edge cases.&lt;/p&gt;

&lt;p&gt;Suppose a line item is booked at a 22 dollar CPM and renegotiated to 18 dollars on day fifteen of a 30-day flight. If your reporting query joins the impression log to the line item row as it stands today, it applies 18 dollars to every impression, including the two weeks served under the old rate.&lt;/p&gt;

&lt;p&gt;I ran the numbers on the same 30 million impression flight. The correct revenue, computed against the configuration in force when each impression was served, is 599,669 dollars. The naive join to the current row produces 540,000 dollars. That is an understatement of 59,669 dollars, or 9.95 per cent of the campaign’s revenue, on a single rate change.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F76g4x7dhcffmiw4aivi8.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F76g4x7dhcffmiw4aivi8.png" alt=" " width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The query throws no error, and every row it needs is present. It returns a perfectly formatted, entirely wrong number, and it will keep doing so every month until somebody reconciles against the signed contract.&lt;/p&gt;

&lt;p&gt;So versioning is not the audit requirement the prompt lists last. It is a correctness requirement for the reporting requirement the prompt lists second to last, and connecting those two in the room is the strongest move available in this interview.&lt;/p&gt;

&lt;p&gt;The mechanism is ordinary once you decide to do it. Every configuration entity gets a version table with valid_from and valid_to, every impression records which version it was served under, and every report joins on that version rather than on the entity.&lt;/p&gt;

&lt;p&gt;line_item_version  id, line_item_id, version_no, valid_from, valid_to,&lt;br&gt;
                   changed_by, change_reason, snapshot&lt;br&gt;
change_reason earns its place, because six months later somebody will ask why the rate moved and the answer has to be in the system rather than in a colleague's memory.&lt;/p&gt;

&lt;p&gt;What reporting then needs to answer&lt;br&gt;
With that in place, the questions the business actually asks become straightforward rather than heroic.&lt;/p&gt;

&lt;p&gt;Delivery against goal, per flight, revisable as late events arrive.&lt;br&gt;
Revenue recognised per month, computed at the rate in force when each impression served.&lt;br&gt;
Pacing health, meaning delivered against expected, early enough to intervene.&lt;br&gt;
Reach and frequency distribution, which needs the user dimension the impression row carries.&lt;br&gt;
Discrepancy against the advertiser’s own measurement, which is why every event needs a stable identifier.&lt;br&gt;
Wrapping up, the way I would in the room&lt;br&gt;
If I had five minutes left with an interviewer, I would say roughly this.&lt;/p&gt;

&lt;p&gt;The prompt removed the auction, so the model has to carry the things the auction was doing: a negotiated price, a promised quantity, and a booking against forecast supply. Budgets belong on flights rather than on line items, because real campaigns run in phases under one set of constraints. Targeting is an expression rather than a set of columns. Creative approval is a state machine scoped to a policy context. And every configuration entity is versioned, because reporting joins to the configuration that was in force, never to the configuration that exists now.&lt;/p&gt;

&lt;p&gt;Then I would say the 9.95 per cent, because it converts versioning from a box people tick into a number somebody has to explain to finance.&lt;/p&gt;

&lt;p&gt;I would also admit the two places I backed up, and I would admit them deliberately. Putting budgets on the line item was my first instinct and it was wrong. Treating creative approval as a boolean was my second, and it was wrong for the same underlying reason, which is that both collapse something that varies over time or context into a single value.&lt;/p&gt;

&lt;p&gt;That is the habit worth taking away from this question, more than any particular schema. Whenever you are about to put an attribute directly on an entity, ask whether it can differ across time, across context, or across the phases of a contract. If it can, it is not an attribute. It is a child entity with a lifespan of its own, and finding that out in an interview is far cheaper than finding it out in a reconciliation meeting.&lt;/p&gt;

&lt;p&gt;Go and try the question properly if you skipped it, and pay attention to where your own first instinct puts the budget. The question is &lt;a href="https://prachub.com/interview-questions/model-direct-sold-dsp-orders?utm_source=medium&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I would genuinely like to know whether you put it on the line item too.&lt;/p&gt;

&lt;p&gt;~ Emily&lt;/p&gt;

</description>
      <category>interview</category>
      <category>career</category>
      <category>netflix</category>
      <category>faang</category>
    </item>
    <item>
      <title>Amazon Interview Question: Design a Delayed Job Scheduler (Timers, Leases, and Exactly-Once Execution)</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Wed, 23 Sep 2026 03:31:14 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/amazon-interview-question-design-a-delayed-job-scheduler-timers-leases-and-exactly-once-20en</link>
      <guid>https://dev.to/emilywoodsnyc/amazon-interview-question-design-a-delayed-job-scheduler-timers-leases-and-exactly-once-20en</guid>
      <description>&lt;p&gt;Every candidate can write the first version of this in about thirty seconds.&lt;/p&gt;

&lt;p&gt;new Thread(() -&amp;gt; {&lt;br&gt;
    Thread.sleep(delayMillis);&lt;br&gt;
    runJob(job);&lt;br&gt;
}).start();&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0dxdxe3mp6043nph0a4r.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0dxdxe3mp6043nph0a4r.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That code is correct in the narrowest possible sense. It also falls over at roughly 10,000 jobs, forgets everything the moment the process restarts, and runs a job twice the instant you add a second machine. Working out why, in that order, is the entire interview.&lt;/p&gt;

&lt;p&gt;The prompt itself sounds fairly modest on the surface. Schedule a job to run at some future time, allow it to be cancelled, and survive at scale. Underneath that sits a timing problem, a durability problem, a distributed claiming problem, and a clock problem, and each one has a specific answer that interviewers are listening for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the naive version dies
&lt;/h2&gt;

&lt;p&gt;Two independent failures kill the thread-per-job approach, and naming both is worth doing before you design anything.&lt;/p&gt;

&lt;p&gt;The first failure is plain resource exhaustion. A thread costs around a megabyte of stack, so a hundred thousand pending jobs asks for a hundred gigabytes of memory that you plainly do not have. Long before that, the OS scheduler starts spending more time context switching between sleeping threads than doing useful work.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fthkfuaa68qr0mj9b0hdx.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fthkfuaa68qr0mj9b0hdx.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second failure runs deeper than the first. Everything lives in process memory, so a deploy, a crash, or a routine restart silently deletes every scheduled job. A scheduler that forgets its work during a deploy is a scheduler nobody can build a product on.&lt;/p&gt;

&lt;p&gt;Those two failures point at two separate fixes. One thread should manage all the timing, and the queue itself has to live somewhere durable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The single-process core
&lt;/h2&gt;

&lt;p&gt;Start with one machine and get the timing right, because the distributed version is this design plus claiming.&lt;/p&gt;

&lt;p&gt;The data structure is a min-heap ordered by execution time, which gives you the next job to fire in constant time and insertion in logarithmic time.&lt;/p&gt;

&lt;p&gt;PriorityQueue queue;      // ordered by runAt&lt;br&gt;
final Object lock = new Object();&lt;br&gt;
void schedule(Job job) {&lt;br&gt;
    synchronized (lock) {&lt;br&gt;
        queue.add(job);&lt;br&gt;
        if (queue.peek() == job) {&lt;br&gt;
            lock.notify();      // this job fires sooner than the old head&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
void timerLoop() {&lt;br&gt;
    while (running) {&lt;br&gt;
        Job due;&lt;br&gt;
        synchronized (lock) {&lt;br&gt;
            while (queue.isEmpty()) lock.wait();&lt;br&gt;
            Job head = queue.peek();&lt;br&gt;
            long delay = head.runAt - clock.millis();&lt;br&gt;
            if (delay &amp;gt; 0) { lock.wait(delay); continue; }&lt;br&gt;
            due = queue.poll();&lt;br&gt;
        }&lt;br&gt;
        workerPool.submit(() -&amp;gt; execute(due));&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
Two lines in that loop carry the whole design, and both are places candidates lose points.&lt;/p&gt;

&lt;h3&gt;
  
  
  The timer thread waits rather than sleeps
&lt;/h3&gt;

&lt;p&gt;A thread inside Thread.sleep cannot be woken. Schedule a job for one second from now while the timer sits inside an hour-long sleep, and that job fires an hour late. Using lock.wait(delay) paired with the notify() in schedule() means any newly arriving job that becomes the head interrupts the wait immediately. Interviewers ask "what happens if a sooner job arrives while you are waiting" precisely to see whether you already handled it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The timer thread never executes the job itself
&lt;/h3&gt;

&lt;p&gt;Handing the job to a worker pool looks like a small detail and is the difference between a working scheduler and a broken one. Run jobs inline on the timer thread and a single job that takes thirty seconds delays every other job in the system by thirty seconds. The timer thread has exactly one responsibility, which is deciding what is due, and it must return to that decision immediately.&lt;/p&gt;

&lt;p&gt;Worth mentioning out loud that this is what ScheduledThreadPoolExecutor already does internally, pairing a DelayQueue with a worker pool. Naming the standard library implementation signals that you know the pattern rather than having improvised it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it durable
&lt;/h2&gt;

&lt;p&gt;The heap has to be a cache rather than the source of truth.&lt;/p&gt;

&lt;p&gt;CREATE TABLE jobs (&lt;br&gt;
  job_id           UUID PRIMARY KEY,&lt;br&gt;
  payload_ref      TEXT        NOT NULL,&lt;br&gt;
  run_at           TIMESTAMPTZ NOT NULL,&lt;br&gt;
  state            TEXT        NOT NULL,&lt;br&gt;
  attempt          INT         NOT NULL DEFAULT 0,&lt;br&gt;
  lease_owner      TEXT,&lt;br&gt;
  lease_expires_at TIMESTAMPTZ,&lt;br&gt;
  idempotency_key  TEXT UNIQUE,&lt;br&gt;
  created_at       TIMESTAMPTZ NOT NULL DEFAULT now()&lt;br&gt;
);&lt;br&gt;
CREATE INDEX idx_jobs_due ON jobs (run_at)&lt;br&gt;
  WHERE state = 'PENDING';&lt;br&gt;
The partial index deserves a sentence, since it is the kind of detail that reads as production experience. You only ever query jobs that are pending and due soon, so indexing the ten million completed rows alongside them wastes memory and slows every write. Restricting the index to state = 'PENDING' keeps it small enough to stay resident even when the table is enormous.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93iuky6cmi1sy6vpzpqg.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93iuky6cmi1sy6vpzpqg.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the hybrid that makes this both durable and precise. Polling the database every hundred milliseconds for millisecond accuracy is wasteful, and polling every ten seconds gives you ten seconds of jitter. So do both at different layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every few seconds, each worker pulls the jobs due in the next thirty seconds into its local heap.&lt;/li&gt;
&lt;li&gt;The in-memory timer loop fires them with millisecond precision inside that window.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The database provides durability and the heap provides accuracy, and the poll interval only needs to be shorter than the lookahead window.&lt;/p&gt;

&lt;p&gt;Exactly-once across many workers&lt;br&gt;
Add a second worker and the interesting problem arrives. Both machines poll the same table, both see the same due jobs, and without coordination both run them.&lt;/p&gt;

&lt;p&gt;The primitive that solves this is one clause that a surprising number of candidates have never used:&lt;/p&gt;

&lt;p&gt;UPDATE jobs&lt;br&gt;
   SET state            = 'CLAIMED',&lt;br&gt;
       lease_owner      = :worker_id,&lt;br&gt;
       lease_expires_at = now() + interval '60 seconds',&lt;br&gt;
       attempt          = attempt + 1&lt;br&gt;
 WHERE job_id IN (&lt;br&gt;
     SELECT job_id&lt;br&gt;
       FROM jobs&lt;br&gt;
      WHERE state = 'PENDING'&lt;br&gt;
        AND run_at &amp;lt;= now() + interval '30 seconds'&lt;br&gt;
      ORDER BY run_at&lt;br&gt;
      LIMIT 100&lt;br&gt;
      FOR UPDATE SKIP LOCKED&lt;br&gt;
 )&lt;br&gt;
RETURNING *;&lt;br&gt;
FOR UPDATE SKIP LOCKED tells the database to lock the rows it selects and silently step over any row another transaction has already locked. Twenty workers can run that identical query simultaneously, and each one walks away with a disjoint set of a hundred jobs. Without SKIP LOCKED they serialise behind each other and your throughput collapses to a single worker. With plain SELECT and no locking at all, they all claim the same rows.&lt;/p&gt;

&lt;p&gt;Say the alternative you rejected too, because interviewers like hearing the comparison. A distributed lock in Redis works and adds a second system that can fail independently, along with all the lease and fencing problems that come with it. The database row you already have is a lock with better durability guarantees, and reaching past it for infrastructure you do not need reads as inexperience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leases, heartbeats, and the guard people forget
&lt;/h2&gt;

&lt;p&gt;A claimed job whose worker dies must eventually run somewhere else, which is what lease_expires_at is for. Extend the claim query to sweep up abandoned work:&lt;/p&gt;

&lt;p&gt;WHERE (state = 'PENDING' AND run_at &amp;lt;= now() + interval '30 seconds')&lt;br&gt;
   OR (state IN ('CLAIMED', 'RUNNING') AND lease_expires_at &amp;lt; now())&lt;br&gt;
A job that takes longer than the lease has to extend it, or another worker will steal it mid-execution and run it concurrently:&lt;/p&gt;

&lt;p&gt;UPDATE jobs&lt;br&gt;
   SET lease_expires_at = now() + interval '60 seconds'&lt;br&gt;
 WHERE job_id = :job_id&lt;br&gt;
   AND lease_owner = :worker_id;      -- the guard that matters&lt;br&gt;
That second condition is the line to point at. If the lease already expired and another worker took ownership, this update affects zero rows, and the original worker must treat that as a signal to abort immediately rather than carrying on. A heartbeat without an ownership check silently lets two workers believe they own the same job, which is the exact failure the lease existed to prevent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest answer about exactly-once
&lt;/h2&gt;

&lt;p&gt;At some point the interviewer will ask whether this guarantees exactly-once execution. The correct answer is that it does not, and that nothing does.&lt;/p&gt;

&lt;p&gt;Walk through the scenario that proves it. A worker claims a job, executes it completely and successfully, and then loses power before it can write SUCCEEDED. The lease expires, another worker legitimately picks the job up, and the work happens twice. Closing that window requires the execution and the state write to be atomic across two systems, which is the distributed transaction problem, and it remains unsolved in the general case.&lt;/p&gt;

&lt;p&gt;So state the contract you can actually deliver: at-least-once execution paired with idempotent handlers. The job payload carries a stable job_id, handlers deduplicate on it, and a repeated execution becomes a no-op rather than a second charge or a second email.&lt;/p&gt;

&lt;p&gt;Framing this as at-least-once plus idempotency, rather than claiming exactly-once, is one of the clearest seniority signals available in this question. Candidates who promise exactly-once are usually describing a system they have never operated.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0cfbyppg3yjz3lyz2kyy.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0cfbyppg3yjz3lyz2kyy.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Retry is just rescheduling
&lt;/h2&gt;

&lt;p&gt;Failure handling gets pleasantly small once the scheduler exists, and this is the observation worth making explicitly.&lt;/p&gt;

&lt;p&gt;UPDATE jobs&lt;br&gt;
   SET state       = 'PENDING',&lt;br&gt;
       run_at      = now() + :backoff,&lt;br&gt;
       lease_owner = NULL&lt;br&gt;
 WHERE job_id = :job_id;&lt;br&gt;
A retry is a job scheduled for slightly later, and the machinery for running something later is the entire system you already built. Rather than bolting on a retry queue, you set the state back and move the timestamp forward.&lt;/p&gt;

&lt;p&gt;The backoff itself follows the usual shape, doubling with a cap and randomised to avoid synchronised retry storms:&lt;/p&gt;

&lt;p&gt;delay = min(BASE * 2^attempt, CAP)&lt;br&gt;
run_at = now() + random(delay / 2, delay)&lt;br&gt;
Bound it on both attempts and wall-clock age, since a job that was supposed to run on Tuesday is often worse than useless by Friday. Exhausted jobs move to a terminal state carrying the last error, where an operator can inspect and replay them.&lt;/p&gt;

&lt;p&gt;Working through more problems framed this way, with explicit sub-parts and follow-ups, is easier when you can see the &lt;a href="https://prachub.com/interview-questions/design-delayed-job-scheduler-lld?utm_source=medium&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;original prompts companies actually used&lt;/a&gt; rather than a cleaned-up retelling, because the constraints in the real wording are usually what the interviewer intends to press on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two scaling problems that actually bite
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The poll query becomes the bottleneck.
&lt;/h3&gt;

&lt;p&gt;At tens of millions of rows, ORDER BY run_at LIMIT 100 under heavy contention from twenty workers stops being cheap even with the partial index.&lt;/p&gt;

&lt;p&gt;The fix for that is time bucketing. Add a bucket column derived from run_at truncated to the minute, and have workers scan only the current and next bucket. The working set shrinks from the whole table to a few minutes of jobs, and completed buckets can be dropped wholesale instead of deleted row by row.&lt;/p&gt;

&lt;h3&gt;
  
  
  Everybody schedules for midnight.
&lt;/h3&gt;

&lt;p&gt;This one is more interesting and rarely anticipated. Humans and cron expressions cluster hard on round numbers, so a million jobs land on exactly 00:00:00 and your scheduler faces a stampede at that instant while sitting idle at 00:00:30.&lt;/p&gt;

&lt;p&gt;Two mitigations, and the choice between them is a product question. Where the caller tolerates approximate timing, add jitter at schedule time and spread the work across the minute. Where exact timing genuinely matters, keep the timestamps and rate-limit dispatch out of the bucket, accepting that some jobs start late and making that lateness a metric you watch rather than a surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whose clock is it
&lt;/h2&gt;

&lt;p&gt;Several machines comparing run_at against their own idea of the current time will disagree, and a worker whose clock runs two seconds fast fires jobs two seconds early.&lt;/p&gt;

&lt;p&gt;The clean answer is to let the database clock be the only clock that matters. Every comparison uses now() evaluated server-side inside the query rather than a timestamp computed in application code. Skew between workers stops affecting correctness because no worker's opinion of the time is ever consulted.&lt;/p&gt;

&lt;p&gt;Where application-side timing is unavoidable, run NTP, define an explicit tolerance band, and state plainly that sub-second ordering across machines is outside what the system promises. Claiming precision you cannot deliver is worse than documenting the bound.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cancellation, and being honest about its limits
&lt;/h2&gt;

&lt;p&gt;UPDATE jobs SET state = 'CANCELLED'&lt;br&gt;
 WHERE job_id = :job_id AND state = 'PENDING';&lt;br&gt;
One row affected means the cancellation won cleanly. Zero rows means the job was already claimed, and now you have to say something uncomfortable and correct: work already in flight cannot be reliably cancelled without the job’s cooperation.&lt;/p&gt;

&lt;p&gt;The options are to accept that a job claimed before the cancellation arrived will complete, or to set a cancellation flag that long-running handlers poll at checkpoints and exit voluntarily. Cooperative cancellation is the only version that actually works, and pretending otherwise invites a follow-up you will lose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing something built entirely out of time
&lt;/h2&gt;

&lt;p&gt;One decision determines whether this system is testable, and it should be made on the first day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Never call the system clock directly.
&lt;/h3&gt;

&lt;p&gt;Inject a Clock everywhere, so tests can advance time instantly rather than waiting for it. A scheduler tested with real sleeps produces a suite that takes twenty minutes and still fails intermittently.&lt;/p&gt;

&lt;p&gt;class Scheduler {&lt;br&gt;
    private final Clock clock;      // tests inject a controllable clock&lt;br&gt;
}&lt;br&gt;
With that in place, the tests worth naming are the concurrent ones:&lt;/p&gt;

&lt;p&gt;Claim contention. Twenty threads run the claim query against the same due jobs across ten thousand iterations, asserting every job is claimed exactly once and none are skipped.&lt;/p&gt;

&lt;p&gt;Crash after execution. Kill a worker between finishing the work and writing SUCCEEDED, then assert the job is re-run after lease expiry and that the idempotent handler makes the second run harmless. This is the scenario from the exactly-once discussion, and testing it proves you meant it.&lt;/p&gt;

&lt;p&gt;Heartbeat versus expiry. Race a heartbeat against a lease expiring, asserting that the losing worker sees zero rows updated and aborts rather than continuing.&lt;/p&gt;

&lt;p&gt;Midnight stampede. Schedule a hundred thousand jobs at an identical timestamp and assert that dispatch spreads without the poll query timing out.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp11ntezu5hasbpsluywn.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp11ntezu5hasbpsluywn.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What decides this interview
&lt;/h2&gt;

&lt;p&gt;Six moments, and they arrive roughly in this order.&lt;/p&gt;

&lt;p&gt;Handing execution to a worker pool instead of running jobs on the timer thread is the first fork, and running them inline is an immediate signal that the candidate has not built one of these.&lt;/p&gt;

&lt;p&gt;Using wait and notify rather than sleep shows you thought about a job arriving sooner than the current head.&lt;/p&gt;

&lt;p&gt;Reaching for SKIP LOCKED rather than a distributed lock demonstrates that you know the database you already have is a coordination primitive.&lt;/p&gt;

&lt;p&gt;Guarding the heartbeat with an ownership check is a small line that proves you traced the failure path all the way through.&lt;/p&gt;

&lt;p&gt;Describing the contract as at-least-once with idempotent handlers, rather than promising exactly-once, is the clearest seniority marker in the whole question.&lt;/p&gt;

&lt;p&gt;Anticipating the midnight stampede before being asked separates people who have run a scheduler in production from people who have designed one on a whiteboard.&lt;/p&gt;

&lt;p&gt;The problem is labelled low level design, which makes it sound like a data structures exercise. What it actually tests is whether you can keep two machines from doing the same work twice while a clock ticks and processes die underneath you, and that is a far better question than its title suggests.&lt;/p&gt;

&lt;p&gt;~ Emily&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>career</category>
      <category>hiring</category>
    </item>
    <item>
      <title>Solving Amazon LLD Interview Question: Design a Concurrent Restaurant Waitlist and Seating System</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Wed, 05 Aug 2026 21:29:16 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/solving-amazon-lld-interview-question-design-a-concurrent-restaurant-waitlist-and-seating-system-3gd1</link>
      <guid>https://dev.to/emilywoodsnyc/solving-amazon-lld-interview-question-design-a-concurrent-restaurant-waitlist-and-seating-system-3gd1</guid>
      <description>&lt;p&gt;Amazon had this on their technical screen for software engineers, and the tag is doing a lot of damage.&lt;/p&gt;

&lt;p&gt;The problem sounds like a warm-up. Tables, a waitlist, seat people in order. Anyone who has written a queue thinks they can do this in fifteen minutes. Then you get four sentences in and hit the part where two tables free up at the same moment while a party is cancelling and an SMS is halfway out the door, and the fifteen-minute answer falls apart in front of the interviewer.&lt;/p&gt;

&lt;p&gt;The tag is not wrong about the data structures. The data structures really are easy. The tag is wrong about what is being tested, which is whether you can keep two pieces of state consistent when four things touch them at once, and whether you know that a text message must never decide whether a table is booked.&lt;/p&gt;

&lt;p&gt;Here is the whole problem, worked.&lt;/p&gt;

&lt;p&gt;The problem&lt;br&gt;
Design a restaurant system with a configurable set of tables and a FIFO waitlist of parties. Each party has an ID, a size, an arrival sequence, and a phone number.&lt;/p&gt;

&lt;p&gt;When a table frees up:&lt;/p&gt;

&lt;p&gt;Prefer the earliest waiting party whose size exactly equals the table’s capacity.&lt;br&gt;
If there is no exact match, seat the earliest party that fits.&lt;br&gt;
So an eight-person party that arrived at 7:40 can jump an earlier two-person party when an eight-seat table opens. That is intentional. It is the whole reason the problem is interesting.&lt;/p&gt;

&lt;p&gt;The rest of the constraints:&lt;/p&gt;

&lt;p&gt;One party holds at most one active waitlist entry or table assignment.&lt;br&gt;
One table holds at most one active party.&lt;br&gt;
SMS may fail or arrive twice, and must not determine whether the assignment commits.&lt;br&gt;
Cancellation can race with seating, and you must define a deterministic winner.&lt;br&gt;
For Detailed Question, You can check here: Amazon Interview Question&lt;/p&gt;

&lt;p&gt;Ask these four questions first&lt;br&gt;
Do not start drawing classes. Every one of these changes the design, and the interviewer is watching to see whether you know that.&lt;/p&gt;

&lt;p&gt;“Can tables be combined, and can a party accept a table smaller than its size?” If tables combine, this stops being a matching problem and becomes a packing problem. Get it excluded from scope, then offer it back as an extension at the end. Free points.&lt;/p&gt;

&lt;p&gt;“How long does a party get to confirm after the text?” This is the question that reveals there is a whole state between “assigned” and “seated.” If there is a confirmation window, you need a hold, a deadline, and a background process that reclaims expired holds. If there is no window, seating is instant and you just saved yourself a component.&lt;/p&gt;

&lt;p&gt;“Does the exact-size preference override FIFO forever, or is aging required?” This is the strongest question in the set, because you are telling the interviewer you already spotted the starvation bug before they asked about it.&lt;/p&gt;

&lt;p&gt;“One process, or multiple hosts?” A single process means one lock. Multiple hosts means conditional updates in a database or a single-writer leader. Answering the wrong one wastes ten minutes.&lt;/p&gt;

&lt;p&gt;For the rest of this walkthrough: no combining, a five-minute confirmation window, aging required, single process first, then multi-host.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnzgy0hlrdyjupxkq6b85.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnzgy0hlrdyjupxkq6b85.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The state machines&lt;br&gt;
Almost every failure in this problem is a state machine that was never written down. So write it down.&lt;/p&gt;

&lt;p&gt;A party moves WAITING → NOTIFIED → SEATED → DONE, and can exit to CANCELLED or EXPIRED.&lt;/p&gt;

&lt;p&gt;A table moves FREE → HELD → OCCUPIED → FREE.&lt;/p&gt;

&lt;p&gt;The critical part: (party WAITING → NOTIFIED) and (table FREE → HELD) are one transition, not two. If those can ever be observed separately, you have a party holding a table that thinks it is free, or a free table that thinks it is held. Everything below exists to make that pair atomic.&lt;/p&gt;

&lt;p&gt;class Party {&lt;br&gt;
    final String id;&lt;br&gt;
    final int size;&lt;br&gt;
    final long seq;        // global arrival order, never reused&lt;br&gt;
    final String phone;&lt;br&gt;
    State state;           // WAITING, NOTIFIED, SEATED, CANCELLED, EXPIRED&lt;br&gt;
    String tableId;        // set only in NOTIFIED / SEATED&lt;br&gt;
}&lt;br&gt;
class Table {&lt;br&gt;
    final String id;&lt;br&gt;
    final int capacity;&lt;br&gt;
    State state;           // FREE, HELD, OCCUPIED&lt;br&gt;
    String partyId;&lt;br&gt;
    long holdExpiresAt;&lt;br&gt;
}&lt;br&gt;
seq comes from a single AtomicLong. That one counter is what makes "earliest" a total order rather than an argument about clock skew, and it is why you never compare wall-clock arrival times.&lt;/p&gt;

&lt;p&gt;The data structure that makes the policy cheap&lt;br&gt;
This is the part people overthink. They reach for a priority queue keyed on some composite score, then spend five minutes explaining a comparator that does not actually implement the rule.&lt;/p&gt;

&lt;p&gt;The rule is simpler than it looks. Bucket the waitlist by exact party size:&lt;/p&gt;

&lt;p&gt;Map&amp;gt; bySize;   // size -&amp;gt; FIFO of waiting parties&lt;br&gt;
Each bucket is already in arrival order, because parties only ever get appended. Now both halves of the policy are almost free:&lt;/p&gt;

&lt;p&gt;Party findBestParty(int capacity) {&lt;br&gt;
    // 1. exact match: the head of the exact-size bucket&lt;br&gt;
    Party exact = head(bySize.get(capacity));&lt;br&gt;
    if (exact != null) return exact;&lt;br&gt;
    // 2. fallback: earliest head among all buckets that fit&lt;br&gt;
    Party best = null;&lt;br&gt;
    for (int s = capacity - 1; s &amp;gt;= 1; s--) {&lt;br&gt;
        Party h = head(bySize.get(s));&lt;br&gt;
        if (h != null &amp;amp;&amp;amp; (best == null || h.seq &amp;lt; best.seq)) best = h;&lt;br&gt;
    }&lt;br&gt;
    return best;&lt;br&gt;
}&lt;br&gt;
Say this out loud in the interview, because it is the thing that makes the loop acceptable: the fallback scan is bounded by the largest table in the restaurant, not by the number of people waiting. A restaurant has tables that seat up to maybe twenty. Twenty pointer reads is a constant. A thousand people can be waiting and the scan does not get slower.&lt;/p&gt;

&lt;p&gt;Then add the upgrade, because interviewers like hearing the general case even when you do not need it: if party sizes were unbounded, replace the scan with a Fenwick tree over size, storing the minimum seq in each bucket. Range-min over [1, capacity], point update on insert and remove, O(log S).&lt;/p&gt;

&lt;p&gt;One detail that matters more than it looks. head() is not peekFirst(). Cancelled parties are left in place as tombstones and skipped lazily:&lt;/p&gt;

&lt;p&gt;Party head(Deque q) {&lt;br&gt;
    if (q == null) return null;&lt;br&gt;
    while (!q.isEmpty() &amp;amp;&amp;amp; q.peekFirst().state != WAITING) q.removeFirst();&lt;br&gt;
    return q.peekFirst();&lt;br&gt;
}&lt;br&gt;
Removing a cancelled party from the middle of a deque is O(n). Tombstoning makes cancellation O(1) and each dead entry is discarded exactly once, so the amortised cost of head() stays constant. Small thing, and the kind of small thing that reads as "has actually built this."&lt;/p&gt;

&lt;p&gt;Concurrency: use one lock, and be able to defend it&lt;br&gt;
Here is where candidates hurt themselves. They hear “concurrent” and start striping locks per table, then per party, then spend the rest of the interview reasoning about acquisition order and deadlock.&lt;/p&gt;

&lt;p&gt;Do not. A restaurant has forty tables. The critical section is a handful of pointer reads and six field writes, measured in nanoseconds. Contention is effectively zero.&lt;/p&gt;

&lt;p&gt;void onTableFree(String tableId) {&lt;br&gt;
    Assignment a = null;&lt;br&gt;
    synchronized (lock) {&lt;br&gt;
        Table t = tables.get(tableId);&lt;br&gt;
        if (t.state != FREE) return;&lt;br&gt;
        Party p = findBestParty(t.capacity);&lt;br&gt;
        if (p == null) return;&lt;br&gt;
        bySize.get(p.size).removeFirst();&lt;br&gt;
        p.state = NOTIFIED;  p.tableId = t.id;&lt;br&gt;
        t.state = HELD;      t.partyId = p.id;&lt;br&gt;
        t.holdExpiresAt = now() + HOLD_WINDOW;&lt;br&gt;
        a = new Assignment(newId(), p.id, t.id, p.phone);&lt;br&gt;
        outbox.append(a);          // inside the same critical section&lt;br&gt;
    }&lt;br&gt;
    notifier.wake();               // outside it&lt;br&gt;
}&lt;br&gt;
Two things to point at explicitly.&lt;/p&gt;

&lt;p&gt;The outbox append is inside the lock. The assignment and the intent to notify commit together. There is no window where a table is held but nothing remembers to tell the guest.&lt;/p&gt;

&lt;p&gt;The SMS is not. Sending a text takes hundreds of milliseconds and talks to a third party that can hang. Holding a lock across that turns a nanosecond critical section into a restaurant-wide stall. If you take one line from this article into an interview, make it that one: never hold a lock across a network call.&lt;/p&gt;

&lt;p&gt;If the interviewer pushes for finer granularity, the answer is not to refuse. It is: shard the lock by restaurant, because restaurants are completely independent, and that is the axis that actually scales. Per-table locks buy nothing and cost a deadlock proof.&lt;/p&gt;

&lt;p&gt;The cancellation race, decided properly&lt;br&gt;
The problem explicitly asks for a deterministic winner, so give it a rule rather than a description.&lt;/p&gt;

&lt;p&gt;The commit inside the lock is the linearization point. A cancellation that acquires the lock first wins outright. A cancellation that arrives after the assignment committed does not fail, and does not undo anything. It degrades into a release.&lt;/p&gt;

&lt;p&gt;boolean cancel(String partyId) {&lt;br&gt;
    String freed = null;&lt;br&gt;
    synchronized (lock) {&lt;br&gt;
        Party p = parties.get(partyId);&lt;br&gt;
        switch (p.state) {&lt;br&gt;
            case WAITING:                 // cancel won&lt;br&gt;
                p.state = CANCELLED;      // tombstone; bucket cleans up lazily&lt;br&gt;
                return true;&lt;br&gt;
            case NOTIFIED:                // seating won; become a release&lt;br&gt;
                p.state = CANCELLED;&lt;br&gt;
                freed = p.tableId;&lt;br&gt;
                Table t = tables.get(freed);&lt;br&gt;
                t.state = FREE;  t.partyId = null;&lt;br&gt;
                break;&lt;br&gt;
            default:&lt;br&gt;
                return false;             // SEATED / already terminal&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
    if (freed != null) onTableFree(freed);   // re-match, outside the lock&lt;br&gt;
    return true;&lt;br&gt;
}&lt;br&gt;
The guest may still get a text for a table they cancelled off. That is correct and worth saying out loud: the alternative is delaying every notification until the cancellation window closes, which makes the whole system slower to protect against a mildly awkward text message.&lt;/p&gt;

&lt;p&gt;The invariant to state plainly: there is no reachable state where a party is CANCELLED and a table is still HELD for it. Both writes happen under the same lock, so no observer sees a half-applied cancellation.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx7plx94ke7bojf0hb4rt.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx7plx94ke7bojf0hb4rt.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SMS: the outbox, and why it comes after the commit&lt;br&gt;
“SMS must not determine whether the assignment commits” is not a hint. It is the answer, handed to you.&lt;/p&gt;

&lt;p&gt;Become a Medium member&lt;br&gt;
Assignment commits in memory (or in a transaction). A durable outbox record goes in with it. A separate notifier drains the outbox:&lt;/p&gt;

&lt;p&gt;while (running) {&lt;br&gt;
    for (Record r : outbox.pending()) {&lt;br&gt;
        try {&lt;br&gt;
            sms.send(r.phone, text(r), /* idempotencyKey */ r.assignmentId);&lt;br&gt;
            outbox.markSent(r.id);&lt;br&gt;
        } catch (Exception e) {&lt;br&gt;
            outbox.backoff(r.id);      // exponential, with a dead-letter cap&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
    park();&lt;br&gt;
}&lt;br&gt;
Now the follow-up they will ask: what if the SMS succeeds but the notifier crashes before recording success?&lt;/p&gt;

&lt;p&gt;Answer it without flinching. On restart the record is still PENDING, so it is sent again with the same idempotency key, and the provider deduplicates it. You cannot get exactly-once across a network boundary. What you get is at-least-once from your side plus dedup on theirs, which is exactly-once in practice.&lt;/p&gt;

&lt;p&gt;Then name the tradeoff, because this is what a senior answer sounds like: the failure mode is a guest occasionally receiving a duplicate text. The alternative ordering, sending first and committing after, has a failure mode of a table sitting empty while the guest waits. One of those costs an eye-roll. The other costs money. Pick the eye-roll, deliberately, and say why.&lt;/p&gt;

&lt;p&gt;Timeouts&lt;br&gt;
The hold needs a reaper, or a party that never replies keeps a table locked all night.&lt;/p&gt;

&lt;p&gt;// scheduled, every second&lt;br&gt;
List freed = new ArrayList&amp;lt;&amp;gt;();&lt;br&gt;
synchronized (lock) {&lt;br&gt;
    for (Table t : tables.values()) {&lt;br&gt;
        if (t.state == HELD &amp;amp;&amp;amp; now() &amp;gt; t.holdExpiresAt) {&lt;br&gt;
            parties.get(t.partyId).state = EXPIRED;&lt;br&gt;
            t.state = FREE;  t.partyId = null;&lt;br&gt;
            freed.add(t.id);&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
for (String id : freed) onTableFree(id);&lt;br&gt;
Whether an expired party is dropped or re-queued is a business decision, not a technical one. Say that, offer re-queueing at their original seq as the guest-friendly option, and move on. Do not spend interview time on it.&lt;/p&gt;

&lt;p&gt;Preventing starvation, which they will ask about&lt;br&gt;
The exact-size preference is a starvation bug wearing a nice hat.&lt;/p&gt;

&lt;p&gt;Picture a restaurant where every two-top is occupied by a long, leisurely dinner, but four-tops and six-tops keep turning over, and parties of four and six keep walking in. Every one of those gets an exact match. A party of two sits there for two hours while parties that arrived after them get seated repeatedly. The policy is working exactly as written, and the guest is furious.&lt;/p&gt;

&lt;p&gt;The fix is aging, and it is small:&lt;/p&gt;

&lt;p&gt;Party findBestParty(int capacity) {&lt;br&gt;
    // 0. aged parties first: anyone past the threshold takes any table that fits&lt;br&gt;
    Party aged = agedQueue.earliestFitting(capacity);   // ordered by seq&lt;br&gt;
    if (aged != null) return aged;&lt;br&gt;
    // 1. exact match, 2. fallback ... as before&lt;br&gt;
}&lt;br&gt;
A party crosses into agedQueue once it has waited longer than T. Aged parties are served in arrival order and ignore the exact-match preference entirely. Note that an aged party is always the head of its own size bucket, since buckets are FIFO by seq, so removal stays O(1).&lt;/p&gt;

&lt;p&gt;Now state the guarantee precisely, because the precision is the point. Aging does not promise a fixed wait. What it promises is this: once a party ages in, no newly arriving party can ever be seated ahead of it. The aged queue drains strictly in arrival order, so the party’s position only moves forward. Its wait is T plus the turnover time of the aged parties already ahead of it, and that set can only shrink.&lt;/p&gt;

&lt;p&gt;That is the difference that matters. Without aging, a small party’s wait is unbounded, because the stream of later arrivals that outrank it never ends. With aging, the wait is finite and monotonically improving. Be careful not to overclaim a constant bound here, because an interviewer who is paying attention will ask you to prove it and you will not be able to.&lt;/p&gt;

&lt;p&gt;Then frame T as the business dial it actually is. Exact matching maximises seat utilisation, which is revenue. Aging bounds the worst wait, which is whether people come back. T is where the restaurant chooses between the two, and it belongs in a config file, not in your code.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26gyo3uwpda8pnh7cogz.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26gyo3uwpda8pnh7cogz.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two hosts, one table&lt;br&gt;
Move the truth into the database and make the claim conditional:&lt;/p&gt;

&lt;p&gt;BEGIN;&lt;br&gt;
UPDATE tables&lt;br&gt;
   SET state = 'HELD', party_id = :pid, hold_expires_at = :deadline,&lt;br&gt;
       version = version + 1&lt;br&gt;
 WHERE id = :tid AND state = 'FREE' AND version = :version;&lt;br&gt;
-- require exactly 1 row affected, else ROLLBACK and re-match&lt;br&gt;
UPDATE parties&lt;br&gt;
   SET state = 'NOTIFIED', table_id = :tid&lt;br&gt;
 WHERE id = :pid AND state = 'WAITING';&lt;br&gt;
-- require exactly 1 row affected, else ROLLBACK and re-match&lt;br&gt;
INSERT INTO outbox (assignment_id, party_id, table_id, phone, state)&lt;br&gt;
VALUES (:aid, :pid, :tid, :phone, 'PENDING');&lt;br&gt;
COMMIT;&lt;br&gt;
Two hosts race, both run the first UPDATE, exactly one sees a row count of 1. The loser sees 0, rolls back, and re-runs matching against fresh state. No distributed lock, no lease, no coordination service. The row is the lock.&lt;/p&gt;

&lt;p&gt;The outbox insert riding along in the same transaction is what closes the last gap: the notification intent is as durable as the assignment.&lt;/p&gt;

&lt;p&gt;But do offer the simpler option too, because at this scale it is probably the right one: elect a single seater per restaurant. One restaurant generates a handful of events per minute. A single writer removes the entire race class, and a lease in the same database is enough to elect it. Distributed correctness you did not need is still complexity you have to maintain.&lt;/p&gt;

&lt;p&gt;If tables can be combined&lt;br&gt;
This is the follow-up that changes the shape of the problem, so it is worth ninety seconds even though you scoped it out at the start.&lt;/p&gt;

&lt;p&gt;Matching against single tables is a lookup. Matching against combinations is a search, because a party of eight might be one eight-top, or two adjacent four-tops, or a four and two twos. You are no longer picking a table, you are picking a set.&lt;/p&gt;

&lt;p&gt;The move that keeps it tractable: precompute the valid combinations as virtual tables. Physical adjacency is fixed by the floor plan and does not change at runtime, so enumerate the joinable groups once at startup, each with capacity = sum of members. Matching then runs unchanged against a list that happens to include virtual entries.&lt;/p&gt;

&lt;p&gt;Two things break and you should name both.&lt;/p&gt;

&lt;p&gt;Committing a virtual table means transitioning several physical tables at once. In one process, acquire them in sorted ID order so no two commits can deadlock against each other. Across hosts, put every member row in the same conditional UPDATE transaction and require the full row count, so it is all or nothing.&lt;/p&gt;

&lt;p&gt;The second is subtler and worth raising because it shows you thought past correctness. Seating a party of eight across two four-tops can strand the floor: you have consumed two tables that two separate parties of four could have used. That is not a bug, it is an optimisation objective, and it is where the problem stops being a data structures question and becomes bin packing. Say that you would prefer the smallest sufficient combination, and prefer a single physical table over any combination of equal capacity. Then say that anything beyond that heuristic is a scheduling problem and out of scope for a screen.&lt;/p&gt;

&lt;p&gt;Tests, and make them race-focused&lt;br&gt;
Generic test lists get generic credit. Name the races.&lt;/p&gt;

&lt;p&gt;Policy:&lt;br&gt;
The eight-person party bypasses the earlier two-person party when an eight-top opens. This is the example from the problem statement, so it is the first test.&lt;/p&gt;

&lt;p&gt;Fallback:&lt;br&gt;
No exact match, and the earliest party that fits wins, not the largest one that fits.&lt;/p&gt;

&lt;p&gt;No double-assignment:&lt;br&gt;
Free every table simultaneously from N threads, ten thousand iterations, assert no table has two parties and no party has two tables.&lt;/p&gt;

&lt;p&gt;Cancel versus seat:&lt;br&gt;
Fire cancel(p) and onTableFree(t) from two threads released by the same barrier. Assert the outcome is always one of exactly two states, never a third. The forbidden state, party CANCELLED while the table is still HELD for them, is the assertion that actually earns the point.&lt;/p&gt;

&lt;p&gt;Crash recovery:&lt;br&gt;
Kill the notifier between sms.send and markSent, restart, assert the provider records exactly one delivery for that assignment ID.&lt;/p&gt;

&lt;p&gt;Starvation:&lt;br&gt;
Build the two-top scenario above, then assert the property aging actually guarantees, which is that no party arriving after the aged party is seated before it. Testing for a fixed wait time gives you a flaky test and a claim you cannot defend.&lt;/p&gt;

&lt;p&gt;Invariant checker:&lt;br&gt;
After every operation in every test, assert each party has at most one active claim and each table has at most one active party. This single check catches more than the individual tests do, and mentioning it is worth more than three more test names.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5ji01hu7198635zp7onl.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5ji01hu7198635zp7onl.png" alt=" " width="800" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What actually separates the answers&lt;br&gt;
I have watched enough of these to see the same 5 moments decide it, and none of them are about the queue.&lt;/p&gt;

&lt;p&gt;The first is the SMS&lt;br&gt;
Candidates who put the send inside the assignment path have failed the question that was being asked, even if every other part is clean. The problem statement told you the answer in advance and they read past it.&lt;/p&gt;

&lt;p&gt;The second is the cancellation race&lt;br&gt;
“We would handle that with a lock” is not an answer. Naming a linearization point and then showing which state the loser lands in is an answer.&lt;/p&gt;

&lt;p&gt;The third is the bounded scan&lt;br&gt;
If you cannot say why looping over table sizes is acceptable, it looks like you did not notice you wrote a loop.&lt;/p&gt;

&lt;p&gt;The fourth is starvation&lt;br&gt;
Everyone can answer it once asked. Very few raise it themselves, and raising it is what makes the difference between someone implementing a policy and someone who can tell that a policy is wrong.&lt;/p&gt;

&lt;p&gt;The fifth is the lock&lt;br&gt;
Reaching for one coarse lock and defending it with numbers reads as senior. Striping locks across forty tables and then reasoning about acquisition order reads as someone who has read about concurrency but not shipped it.&lt;/p&gt;

&lt;p&gt;The tag says easy. What it means is that there is no algorithmic trick to discover, so there is nowhere to hide. Everything you get graded on is the part most people skip.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>coding</category>
      <category>interview</category>
      <category>career</category>
    </item>
    <item>
      <title>I Spent 3 Weeks Auditing My Team’s AI-Generated Code. Here Is What We Found.</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Sat, 20 Jun 2026 02:17:50 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/i-spent-3-weeks-auditing-my-teams-ai-generated-code-here-is-what-we-found-1kj5</link>
      <guid>https://dev.to/emilywoodsnyc/i-spent-3-weeks-auditing-my-teams-ai-generated-code-here-is-what-we-found-1kj5</guid>
      <description>&lt;p&gt;The codebase we built in record time looks functional. It passes CI. It has 91% test coverage. It is also quietly becoming unmaintainable in ways that are hard to see until you look very closely.&lt;/p&gt;

&lt;p&gt;6 months ago, my team adopted AI-assisted development aggressively and our velocity numbers became something we were genuinely proud of. We were shipping features in a 3rd of the time. The product was growing. The engineering org was happy. In May, we started hitting a wall, a series of production bugs that were unusually difficult to diagnose, some refactoring work that turned into a four-week project that should have taken four days, and a new hire who told me, candidly, that she had never seen a codebase she found so hard to reason about despite the code itself being syntactically clean. We spent the first three weeks of June doing a structured audit. The technical debt we found was not the kind that shows up in a linter or a dependency vulnerability scanner. It was architectural and behavioral — the kind that only becomes visible under the specific pressure of trying to change something.&lt;/p&gt;

&lt;p&gt;The codebase that nobody actually understands&lt;br&gt;
I want to be precise about what I mean by AI-generated technical debt because the term is getting used loosely right now.&lt;/p&gt;

&lt;p&gt;The code my team wrote over the past year is not bad code in the obvious sense. It does not have egregious algorithmic errors. It handles most edge cases. The tests pass. The functions are named reasonably well. If you read any individual function in isolation, it reads as competent Python.&lt;/p&gt;

&lt;p&gt;The problem is not at the function level. It is at the level of how the system holds together across functions, modules, and services.&lt;/p&gt;

&lt;p&gt;When AI coding assistants generate code, they optimize for the problem as described in the prompt. They solve the immediate question. They do this extremely well. What they do not do — because they cannot, given how they work — is reason about the broader system consequences of the solution they are producing. They do not know what changed last sprint. They do not know that this service is being deprecated in six months. They do not know that the data model this function depends on was designed as a temporary workaround and was supposed to be replaced eight months ago.&lt;/p&gt;

&lt;p&gt;The code that results from generating against the immediate problem, without the context of the broader system, is locally coherent and globally fragile.&lt;/p&gt;

&lt;p&gt;The four specific patterns we found&lt;br&gt;
Pattern 1: The function that works but that no one can explain&lt;br&gt;
We found eighteen instances across the codebase of what I started calling “oracle functions” — functions that produce correct output and have passing tests but where none of the engineers who reviewed the PR at the time could give a confident explanation of exactly why the implementation works for all inputs.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnr27440q1dzsu5is9iyw.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnr27440q1dzsu5is9iyw.png" alt=" " width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a simplified example of the category:&lt;/p&gt;

&lt;p&gt;def normalize_user_event_sequence(events: list[dict]) -&amp;gt; list[dict]:&lt;br&gt;
    seen = {}&lt;br&gt;
    result = []&lt;br&gt;
    for event in sorted(events, key=lambda e: (e.get("timestamp", 0), e.get("id", ""))):&lt;br&gt;
        key = (event.get("user_id"), event.get("event_type"), event.get("timestamp", 0) // 3600)&lt;br&gt;
        if key not in seen or event.get("priority", 0) &amp;gt; seen[key].get("priority", 0):&lt;br&gt;
            seen[key] = event&lt;br&gt;
    for key in sorted(seen.keys()):&lt;br&gt;
        result.append(seen[key])&lt;br&gt;
    return result&lt;br&gt;
This function was generated by the AI, passed its tests, and went into production. But when I asked three engineers to explain the bucketing logic — specifically, why the timestamp is divided by 3600 and what happens at bucket boundaries when two events from the same user occur in the same hour window — nobody could give me a confident answer. Including the engineer who merged it.&lt;/p&gt;

&lt;p&gt;The function might be correct. Or there might be an edge case at bucket boundaries that has not surfaced yet because the test suite did not cover it. We do not know. That is the problem.&lt;/p&gt;

&lt;p&gt;The specific AI behavior that produces this pattern: AI tools are very good at writing code that passes the tests provided. They are not good at generating tests that cover their own edge cases, because generating comprehensive edge case tests requires understanding the full behavior space of the implementation, not just the happy path specified in the prompt.&lt;/p&gt;

&lt;p&gt;The fix is not to reject AI-generated implementations. It is to require that the engineer who merges the code can explain the implementation in their own words, without looking at it, to a colleague who is not familiar with the problem. If they cannot do that, the code does not merge.&lt;/p&gt;

&lt;p&gt;Pattern 2: Test coverage theater&lt;br&gt;
Our overall test coverage number is 91%. This number is nearly useless as a quality signal for the AI-generated portions of the codebase.&lt;/p&gt;

&lt;p&gt;The problem is that AI coding tools, when asked to write tests, generate tests that cover the code that exists rather than tests that cover the behavior that should exist. The distinction matters enormously.&lt;/p&gt;

&lt;h1&gt;
  
  
  What AI generated as "tests" for the payment processing module
&lt;/h1&gt;

&lt;p&gt;def test_process_payment_returns_success():&lt;br&gt;
    result = process_payment(amount=100, currency="USD", user_id="u123")&lt;br&gt;
    assert result["status"] == "success"&lt;br&gt;
def test_process_payment_with_zero_amount():&lt;br&gt;
    result = process_payment(amount=0, currency="USD", user_id="u123")&lt;br&gt;
    assert result["status"] == "success"   # This should probably not succeed&lt;br&gt;
def test_process_payment_stores_record():&lt;br&gt;
    process_payment(amount=100, currency="USD", user_id="u123")&lt;br&gt;
    records = get_payment_records("u123")&lt;br&gt;
    assert len(records) == 1&lt;br&gt;
Three passing tests, 94% line coverage on the function, and none of these tests would catch:&lt;/p&gt;

&lt;p&gt;What happens when the payment provider API returns a 503&lt;br&gt;
What happens when the same request is retried and idempotency is not handled&lt;br&gt;
What happens when amount is negative&lt;br&gt;
What happens when currency is not in the supported list&lt;br&gt;
What happens when user_id does not exist in the database&lt;br&gt;
AI-generated tests are optimized for coverage metrics. They test the code that exists. A useful test suite tests the contract that should exist, including the failure modes that are not yet in the code.&lt;/p&gt;

&lt;p&gt;The fix we implemented: any AI-generated test suite goes through a mandatory “adversarial review” before merge. A different engineer from the one who wrote the feature spends twenty minutes trying to break the implementation and adds tests for anything they find that is not already covered.&lt;/p&gt;

&lt;p&gt;Pattern 3: Invisible coupling discovered during refactoring&lt;br&gt;
This was the most expensive pattern we found, and it is the one that directly caused the four-week refactoring project I mentioned.&lt;/p&gt;

&lt;p&gt;When we decided to extract our notification service into a standalone microservice, we expected the work to take roughly four days: identify the notification-related code, move it, update the callers, deploy. Standard extraction refactoring.&lt;/p&gt;

&lt;p&gt;What we found was that notification logic had been deeply woven into modules that had no obvious relationship to notifications. The user profile module was calling a notification helper directly. The event ingestion pipeline had notification logic embedded in three separate places with slightly different implementations. The billing module was constructing notification payloads using a local utility function that duplicated the logic in the notification module, with a subtle difference in how it handled timezone offsets.&lt;/p&gt;

&lt;p&gt;Every one of these entanglements was introduced by an AI coding assistant responding to a prompt along the lines of “when this event occurs, send a notification to the user.” The AI generated the most direct solution to the prompt, which is to add the notification call in the function that processes the event. It did this correctly, each time, for each feature that needed notifications. What it did not do is recognize that a pattern was emerging that would eventually make the notification system impossible to extract cleanly.&lt;/p&gt;

&lt;p&gt;The pattern has a name in architecture literature: it is called implicit coupling, and it is the natural consequence of solving local optimization problems without a global view of the system.&lt;/p&gt;

&lt;p&gt;The fix at the code review level: require that any PR that introduces a dependency on a service or module that was not previously a dependency of the modified module be reviewed by a senior engineer. The AI will add dependencies freely. A human reviewer needs to explicitly approve each new one.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fem7n1zjb1di6vpgnkajj.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fem7n1zjb1di6vpgnkajj.png" alt=" " width="720" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pattern 4: Error handling that looks thorough but is not&lt;br&gt;
AI tools generate error handling that is syntactically comprehensive and semantically shallow. They produce try/except blocks. They log errors. They return appropriate error codes. But the error handling they generate tends to treat all errors in a category as equivalent when the correct behavior often differs substantially based on the specific error.&lt;/p&gt;

&lt;h1&gt;
  
  
  AI-generated error handling pattern we found repeatedly
&lt;/h1&gt;

&lt;p&gt;async def fetch_user_data(user_id: str) -&amp;gt; dict | None:&lt;br&gt;
    try:&lt;br&gt;
        response = await http_client.get(f"/users/{user_id}")&lt;br&gt;
        response.raise_for_status()&lt;br&gt;
        return response.json()&lt;br&gt;
    except httpx.HTTPStatusError as e:&lt;br&gt;
        logger.error("HTTP error fetching user %s: %s", user_id, e)&lt;br&gt;
        return None&lt;br&gt;
    except httpx.RequestError as e:&lt;br&gt;
        logger.error("Request error fetching user %s: %s", user_id, e)&lt;br&gt;
        return None&lt;br&gt;
This catches both 404s (user does not exist) and 500s (upstream service is broken) and treats them identically: log, return None. The callers of this function cannot distinguish between “the user does not exist” and “we could not reach the service.” These require completely different handling. A 404 should result in a clean error to the client. A 500 should result in a retry, or a circuit breaker trip, or an alert. Collapsing them both into return None makes the system appear to handle errors gracefully while actually hiding failures that require operational response.&lt;/p&gt;

&lt;p&gt;The audit process that surfaced all of this&lt;br&gt;
We did not find these patterns by reading code. We found them by applying structured pressure to the codebase.&lt;/p&gt;

&lt;p&gt;The most revealing exercise was the “change test.” We picked a feature that had been built over the AI-assisted sprint and we tried to make a specific, bounded change: add a new field to a data model. We documented every file that required modification, every test that broke, and every unexpected side effect. The radius of a change is a direct measure of coupling. Features built with disciplined architecture tend to have small change radii. The AI-assisted features had change radii that were consistently larger than expected.&lt;/p&gt;

&lt;p&gt;The second exercise was “explain it without looking.” For the functions that tested at high coverage but that the team was uncertain about, we asked the engineer who built each feature to explain the implementation to someone unfamiliar with it. When they needed to consult the code to finish the explanation, we flagged that function for deeper review.&lt;/p&gt;

&lt;p&gt;The third exercise was a dependency audit. We mapped every cross-module import that was introduced during the AI-assisted sprint and looked for patterns that indicated implicit coupling rather than designed dependency.&lt;/p&gt;

&lt;p&gt;What we are doing differently&lt;br&gt;
We have not stopped using AI coding tools. We use Cursor for most greenfield implementation work, GitHub Copilot for inline completion during exploratory sessions, and Claude Code for heavier refactoring tasks where the full codebase context matters. What has changed is the governance around how we use them.&lt;/p&gt;

&lt;p&gt;New code that an AI assistant generates does not merge unless the engineer can explain it, specifically, to a level where a colleague could implement the same solution independently from the explanation alone.&lt;/p&gt;

&lt;p&gt;AI-generated tests are treated as a starting point, not a completion. The adversarial review is now a required step in our code review process for any AI-assisted PR.&lt;/p&gt;

&lt;p&gt;New dependencies introduced by AI-generated code require explicit senior engineer approval. The AI will add them freely. We need a human to consciously decide each time that the coupling is acceptable.&lt;/p&gt;

&lt;p&gt;We have a standing weekly exercise where one engineer picks a module and attempts a bounded change without looking at the implementation details first. If the change is harder than it should be, we investigate why.&lt;/p&gt;

&lt;p&gt;None of this is anti-AI. All of it is standard engineering discipline that we should have been applying consistently regardless of how the code was generated. The AI-assisted sprint revealed the gaps in our process by filling those gaps with locally correct but globally fragile solutions, faster than our review process was equipped to catch.&lt;/p&gt;

&lt;p&gt;For individual engineers trying to build the audit muscle independently, the tools that have been most useful on my team: Sourcegraph for navigating large codebases and tracing where a specific function or pattern is used across the entire repository which is invaluable for discovering the coupling patterns described above. CodeClimate for tracking maintainability trends over time rather than point-in-time snapshots, which is the only way to see whether your codebase is getting harder or easier to change as a trend. GitHub’s code scanning (powered by CodeQL) for the security debt specifically like it catches a meaningful fraction of the vulnerability patterns that AI-generated code tends to introduce without ceremony.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmumynciipkqqic93zg4.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmumynciipkqqic93zg4.png" alt=" " width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One practical note for anyone who is currently job searching or keeping themselves interview-ready alongside this kind of team-level work: the skills this audit required is reading unfamiliar code critically, tracing dependencies, identifying coupling and are the exact skills companies are now testing in technical interviews. The interview bar has shifted away from pure algorithmic output toward code review and architectural judgment, and understanding what a specific company tests in their current loops matters for how you allocate prep time. For that calibration, a combination of recent Glassdoor interview reports, Blind threads from the past six months, and PracHub () is the most efficient way to understand whether a company is running algorithmic rounds, code review rounds, or a mix without wasting weeks preparing for the wrong format.&lt;/p&gt;

&lt;p&gt;The honest version&lt;br&gt;
The velocity numbers were real. We shipped more features in twelve months than we had in any comparable period. The product is better for it. Some of the AI-generated code is excellent and will be in the codebase for years with no problems.&lt;/p&gt;

&lt;p&gt;But the maintainability cliff is also real, and it arrived on a timeline that caught us off guard. The new hire’s observation that the codebase was hard to reason about despite being syntactically clean and is the most precise description of AI-generated technical debt I have encountered. It looks fine. It reads fine. It passes review. And then you try to change something and you discover that the system as a whole has properties that none of the individual pieces would suggest.&lt;/p&gt;

&lt;p&gt;The teams that are going to do this well are the ones that treat AI coding tools the way senior engineers already treat junior engineers: with trust, with expectation, and with the judgment to know when to override the output. The tools are excellent at what they do. What they do is not the full job.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Age of Agentic AI: What Engineering Jobs Actually Look Like in 2026 (second half)</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Sun, 14 Jun 2026 20:50:57 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/the-age-of-agentic-ai-what-engineering-jobs-actually-look-like-in-2026-second-half-159j</link>
      <guid>https://dev.to/emilywoodsnyc/the-age-of-agentic-ai-what-engineering-jobs-actually-look-like-in-2026-second-half-159j</guid>
      <description>&lt;p&gt;The demos are over. Autonomous coding agents are now merging PRs into production codebases. Here is what happens to your career when you are no longer the one writing the code, and how to survive the next 6 months.&lt;/p&gt;

&lt;p&gt;The first half of 2026 was defined by panic over AI demos. The second half of 2026 will be defined by the quiet, brutal reality of agentic AI actually doing the work. Engineering teams are not being entirely replaced, but they are shrinking and restructuring. The job of a software engineer is shifting rapidly from “writing implementation code” to “managing autonomous agents and auditing their output.” If your entire professional value is tied to turning a well-scoped Jira ticket into a React component or a CRUD endpoint, you are competing against an agent that works 24/7 for pennies. If your value is tied to defining ambiguous problems, catching architectural edge cases, and verifying that a system actually solves a business need, you are about to become more valuable than ever. The role is bifurcating. You are either an orchestrator, or you are redundant.&lt;/p&gt;

&lt;p&gt;The end of the implementation era&lt;br&gt;
We spent the last two years treating AI like a really smart autocomplete. We argued about whether Copilot made us 10 percent or 30 percent faster.&lt;/p&gt;

&lt;p&gt;That era is dead.&lt;/p&gt;

&lt;p&gt;Agentic AI does not wait for you to start typing. Agents pull tickets from the backlog. They clone the repository. They read the documentation, spin up a testing environment, write the implementation, generate the tests, and open a pull request. They do this while you are sleeping.&lt;/p&gt;

&lt;p&gt;I sat with an engineering director last week who admitted his team merged 40 pull requests in a single sprint. His engineers only wrote three of them. Autonomous agents wrote the rest. The engineers spent their entire sprint reviewing code, fighting over testing frameworks with the agent, and pushing back on hallucinated dependencies.&lt;/p&gt;

&lt;p&gt;This is what second half 2026 looks like at companies that care about margins. The bulk of the manual typing is gone. The actual coding part of software engineering is becoming a legacy skill, shrinking down to the smallest possible fraction of your day.&lt;/p&gt;

&lt;p&gt;The new engineering hierarchy&lt;br&gt;
When agents write the code, the value of an engineer changes entirely. The industry is currently restructuring around three new tiers of talent.&lt;/p&gt;

&lt;p&gt;The bottom tier is the Prompt Jockey. This is the junior or mid-level engineer who treats the agent like a magic box. They feed it a prompt, wait for the PR, glance at the test results, and hit merge. These engineers are a massive liability. They do not understand the code the agent generated. When production inevitably breaks at midnight, they have no mental model of the system to debug it. Companies are actively firing these engineers right now because they are just expensive middle-men between a Jira board and a repository.&lt;/p&gt;

&lt;p&gt;The middle tier is the Auditor. This engineer reads agent-generated code with deep skepticism. They understand that agents are incredibly convincing liars. An agent will confidently write an API integration that looks perfect but silently fails to handle pagination properly. The Auditor treats the agent like a brilliant but reckless intern. They enforce strict boundaries, demand rigorous unit tests, and manually trace the edge cases. This is a safe job for the next twelve months, but it is grueling work.&lt;/p&gt;

&lt;p&gt;The top tier is the Orchestrator. This is the new senior engineer. The Orchestrator rarely touches the implementation details. Their job is translating messy, contradictory business requirements into explicit, bounded contexts that agents cannot misunderstand. They design the system architecture. They decide which microservices the agents are allowed to touch. They build the guardrails, define the failure states, and manage the deployment pipelines. Orchestrators are currently commanding massive premiums in the labor market because they are the only people keeping agentic systems from tearing production apart.&lt;/p&gt;

&lt;p&gt;The death of the “Junior” pipeline&lt;br&gt;
The most immediate casualty of the agentic shift is the entry-level hiring pipeline.&lt;/p&gt;

&lt;p&gt;Historically, companies hired junior engineers to do grunt work. They fixed typos, wrote basic tests, and handled simple UI updates. This was how they learned the codebase. It was an apprenticeship disguised as employment.&lt;/p&gt;

&lt;p&gt;Agents now do all the grunt work instantly. There is no economic incentive to hire a junior engineer who takes three days to complete a task an agent finishes in three minutes.&lt;/p&gt;

&lt;p&gt;If you are trying to break into the industry in the remaining of 2026, you cannot compete on execution speed. You have to jump straight to the Auditor tier. You must prove you can read complex codebases, spot logical flaws in existing architectures, and communicate technical trade-offs. The barrier to entry has never been higher, and the grace period for learning on the job has completely vanished.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far6355k9sc6kmz3x508a.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far6355k9sc6kmz3x508a.png" alt=" " width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How interviews are changing right now&lt;br&gt;
The way companies hire is adapting to this reality in real time.&lt;/p&gt;

&lt;p&gt;If you are interviewing in the next six months, expect the standard LeetCode to look fundamentally different. Asking a candidate to reverse a linked list on a whiteboard is useless when an agent can write an optimized traversal algorithm in a fraction of a second.&lt;/p&gt;

&lt;p&gt;Instead, companies are testing code review and system constraints.&lt;/p&gt;

&lt;p&gt;In recent loops, I have seen candidates handed a 300-line pull request generated by an agent.&lt;/p&gt;

&lt;p&gt;The prompt is simple: “This code passes all unit tests but will cause a critical failure in production. Find the bug and explain why the agent missed it.”&lt;/p&gt;

&lt;p&gt;Interviewers are looking for your ability to hold a complex system in your head. They want to see if you catch the unhandled race condition. They want to know if you recognize that the agent used an inefficient database query that will bottleneck under load.&lt;/p&gt;

&lt;p&gt;System design interviews are shifting from “design Twitter” to “design the boundaries for an autonomous agent.” You will be asked how to rate-limit an agent, how to prevent it from dropping a production table, and how to gracefully degrade the system when the LLM provider experiences an outage.&lt;/p&gt;

&lt;p&gt;Surviving the next 6 months&lt;br&gt;
You have a very short window to reposition yourself.&lt;/p&gt;

&lt;p&gt;Stop optimizing for writing code quickly. Nobody cares how fast you type anymore. Start optimizing for reading code critically.&lt;/p&gt;

&lt;p&gt;Go to GitHub. Pull down a massive, unfamiliar repository. Pick a complex PR and force yourself to review it as if your job depended on catching a critical vulnerability. Build the muscle of reading other people’s logic or a machine’s logic — and spotting the hidden assumptions.&lt;/p&gt;

&lt;p&gt;When you prepare for interviews, shift your focus entirely toward architecture and code review. Practice explaining why a piece of code works, not just how to write it. When assessing what a company is currently testing, use the tools that aggregate real interview signals. Look at Blind for candid discussions on how different teams are deploying agents. Check &lt;a href="https://www.glassdoor.com/" rel="noopener noreferrer"&gt;Glassdoor&lt;/a&gt; for recent changes in technical questions. Use &lt;a href="https://prachub.com?utm_source=devto&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;PracHub&lt;/a&gt; to see if a specific company has shifted their coding rounds toward code-review formats rather than algorithmic scratchpads. Combine these sources to understand the exact bar you need to clear.&lt;/p&gt;

&lt;p&gt;Final thoughts&lt;br&gt;
The anxiety you feel right now is justified. The profession is fundamentally changing. The work that got you promoted two years ago is the exact work that will make you redundant tomorrow.&lt;/p&gt;

&lt;p&gt;But this is not the end of software engineering.&lt;/p&gt;

&lt;p&gt;We are simply abstracting away the tedious mechanics of typing syntax. The core of engineering has always been solving complex problems, navigating ambiguity, and building reliable systems. The agents are taking the typing. They cannot take the judgment.&lt;/p&gt;

&lt;p&gt;If you cling to your identity as a code writer, second half 2026 will be brutal. If you embrace your identity as a system architect and critical auditor, you are about to have the most leverage of your entire career.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>machinelearning</category>
      <category>leadership</category>
    </item>
    <item>
      <title>9 Out of 10 Engineers Prepare for the Wrong Behavioral Interview. Here Is What the Right One Looks Like.</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Wed, 10 Jun 2026 23:36:53 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/9-out-of-10-engineers-prepare-for-the-wrong-behavioral-interview-here-is-what-the-right-one-looks-4bb2</link>
      <guid>https://dev.to/emilywoodsnyc/9-out-of-10-engineers-prepare-for-the-wrong-behavioral-interview-here-is-what-the-right-one-looks-4bb2</guid>
      <description>&lt;p&gt;Every senior engineer has the experience. Almost none of them have learned to narrate it in a way that produces evidence at the level they are actually interviewing for.&lt;/p&gt;

&lt;p&gt;It is a structured attempt to collect evidence about how you have operated in the past, on the assumption that past behavior predicts future behavior at scale. The companies that take behavioral rounds seriously, and the list is longer than most candidates assume, they have built explicit rubrics that map specific types of story content to specific competency levels. Your stories either contain the evidence those rubrics require or they do not. The evidence required for a senior or staff offer is categorically different from the evidence that passes a junior or mid-level round. Most engineers who fail behavioral rounds do not fail because they lack relevant experience. They fail because they narrate what happened rather than the judgment they exercised while it was happening, and because they describe team outcomes rather than personal decisions. These are fixable problems. But they require understanding what the behavioral round is actually measuring before you can fix them.&lt;/p&gt;

&lt;p&gt;What the behavioral round is actually testing&lt;br&gt;
There is a persistent myth that behavioral interviews are the soft round which is the one where you just need to be personable, answer questions honestly, and let your personality come through. This belief is expensive for anyone interviewing above the junior level.&lt;/p&gt;

&lt;p&gt;Behavioral interviews at serious tech companies are structured around competency frameworks. These frameworks define specific categories of behavior, leadership, conflict resolution, technical decision-making under ambiguity, mentorship, cross-functional influence and map each category to evidence standards that differ by seniority level. An interviewer walking into a behavioral round for a senior engineer is not listening for whether you seem like a pleasant person. They are listening for evidence of specific behaviors in specific contexts, and they are mentally checking whether that evidence meets the threshold their rubric associates with the level they are hiring for.&lt;/p&gt;

&lt;p&gt;The practical implication is that a charming, articulate answer that contains the wrong kind of evidence fails the round just as reliably as a disorganized answer that contains the right kind of evidence but buries it. The content of the story matters more than the delivery. The delivery matters for clarity and confidence. The content matters for the recommendation.&lt;/p&gt;

&lt;p&gt;What constitutes evidence at the behavioral level varies by company but converges on a consistent theme:&lt;/p&gt;

&lt;p&gt;interviewers want to see that you have exercised independent judgment on consequential problems, that your judgment was sound and traceable, and&lt;br&gt;
that the outcome of your involvement was meaningfully different from what would have happened without you. Like meaningfully different.&lt;br&gt;
The level-dependent evidence problem&lt;br&gt;
This is the mechanism that explains most of the behavioral failures I have seen in debrief sessions.&lt;/p&gt;

&lt;p&gt;At the junior level, the behavioral bar requires evidence that you can communicate well, handle feedback without becoming defensive, and complete work that produces a result the team needed. The scope of the story does not need to be large. The decision-making does not need to be independent. What the junior-level behavioral assessment is looking for is coachability, execution, and the ability to work within a team. An answer that describes what you did on a project and what you learned from it is often sufficient.&lt;/p&gt;

&lt;p&gt;At the mid-level, the bar shifts. The story needs to demonstrate that you can own a complete technical task from start to finish without requiring constant direction. You are still operating within a defined scope, but the behavioral evidence needs to show that within that scope, you are making genuine technical decisions rather than executing what others decided. A story about implementing a feature is sufficient at junior. At mid-level, the story needs to show that you identified the right way to implement the feature, made a trade-off decision somewhere in the process, and would make the same decision again for a specific reason.&lt;/p&gt;

&lt;p&gt;At the senior level, the shift is dramatic and frequently underestimated. The behavioral bar at senior requires evidence of at least three things that almost never appear naturally in how engineers tell stories about their work. The first is that you defined the problem rather than receiving a definition. The second is that you influenced people who did not report to you — a product manager, a technical lead, a stakeholder in a different org — toward an outcome you believed was correct. The third is that you handled a specific moment where the technically correct answer and the organizationally expedient answer diverged, and you navigated that divergence consciously.&lt;/p&gt;

&lt;p&gt;At the staff level, the scope expands again. The story needs to involve ambiguity at the level of the company strategy, not just the team’s technical decisions. Staff-level behavioral evidence often involves moments where the right answer was genuinely unclear to everyone, where you brought clarity to a situation that was blocking multiple teams, and where the downstream consequences of your decision or recommendation affected engineers you had never met.&lt;/p&gt;

&lt;p&gt;Most engineers applying for senior roles tell mid-level stories. Most engineers applying for staff roles tell senior stories. The level mismatch is the single most common cause of the down-level outcome that candidates experience as inexplicable.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sg5daw7uyr6vzxs4z5u.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sg5daw7uyr6vzxs4z5u.png" alt=" " width="720" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building the story bank&lt;br&gt;
The preparation mistake that causes the most damage in behavioral rounds is trying to construct stories on the fly in response to interview questions. This produces generic answers, tense inconsistencies, factual imprecision, and — most visibly to the interviewer — the pause before answering that signals the candidate is searching for a relevant story rather than selecting the best one from a prepared set.&lt;/p&gt;

&lt;p&gt;A story bank is a document, not a mental list. It is a structured record of six to ten high-signal experiences from your professional history, each documented with enough detail that you can reconstruct the story accurately and specifically under pressure.&lt;/p&gt;

&lt;p&gt;The structure I use for each entry in the story bank has six fields.&lt;/p&gt;

&lt;p&gt;The situation is the minimum context the interviewer needs to understand what was at stake. This should be two to four sentences. It should include the company stage or size, the team context, and what made this situation consequential. It should not include more than three sentences of background before anything happens.&lt;/p&gt;

&lt;p&gt;The problem you personally identified is what you noticed or diagnosed that set the story in motion. This is distinct from the problem you were assigned. If you were assigned the problem, that is a mid-level story. If you identified the problem yourself and then made the case for working on it, that is beginning to be a senior story.&lt;/p&gt;

&lt;p&gt;The specific decision you made and the alternatives you considered is the core of what makes a story land at the right level. Every strong behavioral story contains a decision point where there was more than one plausible option and you chose one for a specific reason. Interviewers are not interested in what you did. They are interested in why you did that rather than the alternative, and whether your reasoning was sound.&lt;/p&gt;

&lt;p&gt;The people you influenced is where cross-functional impact gets documented. Who did you have to convince? What was their initial position? What was your approach? This is the evidence that separates individual contributors from senior engineers in the behavioral record.&lt;/p&gt;

&lt;p&gt;The outcome and its measurement is the evidence that your decision actually mattered. This needs to be specific. Improved latency by 40% is evidence. Made the API faster is not. Reduced the support ticket volume from this feature from 15 per week to 2 per week is evidence. Customers were happier is not.&lt;/p&gt;

&lt;p&gt;The learning is what you would do differently, and this field is important specifically because interviewers at senior and above often follow up on positive stories by asking what you would change. A candidate who has already thought through the honest limitations of their decisions is demonstrating a level of self-awareness that calibrates with senior expectations. A candidate who says they would not change anything is either telling a sanitized story or has not done the genuine retrospective work.&lt;/p&gt;

&lt;p&gt;The I versus We problem in depth&lt;br&gt;
This deserves more space than it usually gets, because the failure mode is subtle and the fix is specific.&lt;/p&gt;

&lt;p&gt;When engineers describe their professional work naturally, they use the first-person plural because that is how collaborative work feels from the inside. You are genuinely a team. The success belongs to the group. Using the singular feels like taking credit that belongs to others.&lt;/p&gt;

&lt;p&gt;The behavioral interview does not evaluate teams. It evaluates you. The interviewer cannot offer a role to your team. They are evaluating whether you specifically meet the evidence standard, and they can only evaluate evidence about your specific actions, decisions, and influence. Every time you say the team or we where you could say I, you are erasing evidence about yourself that the interviewer needs.&lt;/p&gt;

&lt;p&gt;The fix is not to claim sole credit for team work. It is to be specific about your personal contribution within the team context. Describing what you personally analyzed, decided, communicated, or designed — while being clear that you were working alongside others — is accurate and produces evaluable evidence. Describing what your team accomplished, with yourself as a generic member of the collective, is not evaluable and will not produce a strong recommendation.&lt;/p&gt;

&lt;p&gt;The specific reframe is: whenever you are about to say we, ask yourself what you specifically did within that we. If you can name it — I proposed, I designed, I pushed back on, I escalated, I wrote the spec for — say that instead. If you genuinely cannot name what you specifically contributed, consider whether that story is the right one to tell for this question.&lt;/p&gt;

&lt;p&gt;The failure story problem&lt;br&gt;
Behavioral interviews at every serious tech company include at least one question about a failure, a mistake, or a time something did not go as planned. This is the question that most candidates handle worst, and the failure mode is almost always in the same direction: the candidate tells a story where the failure was small, the circumstances were unusual, and the lesson was generic.&lt;/p&gt;

&lt;p&gt;Interviewers at senior and above have heard hundreds of versions of the story where the candidate missed a deadline because of unexpected complexity and learned to communicate earlier. They do not find this story impressive. They find it safe. Safe is a different evaluation from strong, and safe rarely produces a hire recommendation at the senior level.&lt;/p&gt;

&lt;p&gt;The behavioral evidence that the failure story needs to contain at senior level is specific: you made a judgment call that was genuinely yours, the judgment call turned out to be wrong in a way that had real consequences, and your retrospective analysis of why the judgment was wrong is specific and narrow rather than generic. The learning that registers at senior level is not I learned to communicate more. It is I learned that my assumption about the load distribution was wrong because I was reasoning from a dataset that did not reflect the actual traffic pattern, and now I validate that assumption explicitly before making architecture decisions that depend on it.&lt;/p&gt;

&lt;p&gt;The reason most engineers avoid real failure stories is that talking about genuine failure feels professionally risky in an evaluation context. The counter-intuitive reality is that a genuine, specific failure story with honest reasoning about why the judgment was wrong is significantly more compelling to a senior interviewer than a sanitized story where nothing really bad happened. The ability to reflect honestly on your own errors, understand their root causes, and articulate specific changes to your decision-making process is itself evidence of senior-level professional maturity.&lt;/p&gt;

&lt;p&gt;The Amazon Leadership Principles as a case study&lt;br&gt;
Amazon is worth discussing specifically because their behavioral framework is the most codified and the most transparent of any major tech company, and studying it teaches you something generalizable about how all serious behavioral frameworks work.&lt;/p&gt;

&lt;p&gt;Amazon’s Leadership Principles are not a list of values. They are a behavioral competency taxonomy. Each principle describes a specific pattern of behavior that Amazon believes produces business outcomes at scale. When you interview at Amazon, each behavioral question is anchored to one or more of these principles, and the interviewer is collecting evidence about whether your stories demonstrate those specific behavioral patterns, not whether you seem to agree with the principle in the abstract.&lt;/p&gt;

&lt;p&gt;The implication for preparation is that you cannot prepare for Amazon behavioral rounds by learning the principles as concepts. You have to map your story bank to the specific behavioral evidence patterns each principle describes. Customer Obsession, for example, is not satisfied by a story about how you care about customers. It is satisfied by a story where you identified a gap between what the customer was experiencing and what your team believed they were experiencing, and you took a specific action to close that gap, even when it was inconvenient or politically costly to do so.&lt;/p&gt;

&lt;p&gt;The specific preparation move for Amazon is to take your story bank entries and annotate each one with which Leadership Principles it provides evidence for, and at what depth. An entry that provides surface-level evidence for three principles is less valuable than an entry that provides deep evidence for one. Deep evidence means that the story contains a decision point that is specific to that principle, not a tangential mention.&lt;/p&gt;

&lt;p&gt;The broader lesson from the Amazon framework is that every serious tech company has an equivalent rubric, even when it is not publicly documented. Companies with stated engineering values or tenets — Google, Stripe, Databricks, and most companies that have been through a significant scaling phase — have mapped those values to behavioral evidence patterns in their internal calibration guidelines. The preparation for any of these companies is the same: understand what the stated values actually mean as behavioral patterns, and then find or construct stories that demonstrate those patterns at your target level.&lt;/p&gt;

&lt;p&gt;The questions that function as traps&lt;br&gt;
A small set of behavioral questions function as traps not because they are designed to deceive but because the natural, intuitive answer consistently misses what the question is measuring.&lt;/p&gt;

&lt;p&gt;The tell me about yourself opener is an invitation to demonstrate concision, selectivity, and professional narrative control. The answer that works is a two-minute arc that establishes your current level and domain, highlights one or two experiences that are directly relevant to this role, and ends with a clear statement of why you are interested in this conversation specifically. The answer that fails is a chronological recitation of every role on your resume.&lt;/p&gt;

&lt;p&gt;The what is your greatest weakness question is not a question about what you are bad at. It is a question about whether you have genuine self-awareness, whether you take active steps to address your limitations, and whether you can talk about professional vulnerability without either deflecting or spiraling into self-criticism. The answer that works names a real and specific limitation, describes the observable consequence of that limitation in your work, and describes a specific practice you have adopted to manage or improve it. The answer that fails names a strength disguised as a weakness or describes something irrelevant to the work.&lt;/p&gt;

&lt;p&gt;The tell me about a time you disagreed with your manager question is looking for evidence of professional courage and the ability to advocate for a technical position through legitimate channels without damaging the relationship. The answer that works describes a specific technical or strategic disagreement where you had a well-reasoned position, explains how you presented that position and what evidence you used, and describes the outcome honestly regardless of whether you prevailed. The answer that fails either describes a trivial disagreement or frames the manager as wrong in a way that reads as poor professional judgment.&lt;/p&gt;

&lt;p&gt;The prep approach that actually works&lt;br&gt;
Effective behavioral prep has two phases and a specific sequence.&lt;/p&gt;

&lt;p&gt;The first phase is story bank construction. This takes four to six hours spread over several sessions rather than done in one sitting, because the retrospective work of identifying your genuine decision points requires time to surface memories that were not salient at the moment they happened. The output of this phase is six to ten fully documented story entries in the format described above, covering at least one story from each of the following categories: a significant technical decision and its trade-offs, a cross-functional conflict or misalignment you navigated, a failure or mistake and your retrospective analysis of it, a situation where you mentored or developed another engineer, and a situation where you had to influence an outcome without authority.&lt;/p&gt;

&lt;p&gt;The second phase is rehearsal under the specific constraints of a live interview. You read each question cold, select a story without excessive deliberation, and narrate the full answer out loud in under three minutes. You time yourself. You record yourself if possible and listen for the signal-to-noise ratio — how much of what you said was specific evidence versus general framing and filler. You practice the follow-up layer, which is the interviewer asking for more detail about a specific part of your story. Strong behavioral answers survive one or two follow-up questions without contradiction or vagueness.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo7ov4emrb1zu6mvwo0r.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo7ov4emrb1zu6mvwo0r.png" alt=" " width="720" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the deliberate practice component, particularly for understanding the evidence standard at your specific target level and company which is a combination of resources is more useful than any single platform. Amazon publishes extensive documentation of their Leadership Principles with behavioral examples. The Big Interview offer structured behavioral mock sessions with feedback from practitioners. For company-specific behavioral question patterns and what the calibration criteria have historically looked like at specific levels, Glassdoor interview reports, &lt;a href="https://www.teamblind.com/" rel="noopener noreferrer"&gt;Blind &lt;/a&gt;threads, and &lt;a href="https://prachub.com?utm_source=devto&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;PracHub&lt;/a&gt; are the most practical sources and if used together, they give you both the question inventory and the current calibration context that free sources alone sometimes miss.&lt;/p&gt;

</description>
      <category>behavioral</category>
      <category>ai</category>
      <category>interview</category>
      <category>career</category>
    </item>
    <item>
      <title>I Have Run 80+ Phone Screens. Here Is Why Good Engineers Keep Bombing Them.</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Sat, 06 Jun 2026 23:16:11 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/i-have-run-80-phone-screens-here-is-why-good-engineers-keep-bombing-them-2769</link>
      <guid>https://dev.to/emilywoodsnyc/i-have-run-80-phone-screens-here-is-why-good-engineers-keep-bombing-them-2769</guid>
      <description>&lt;p&gt;Read this, if you think you are a good engineer and you still keep bombing your phone screen interviews.&lt;/p&gt;

&lt;p&gt;The phone screen is not a simplified version of the full interview loop. It is a different kind of evaluation with different criteria, a different time budget, and a different failure mode profile. The candidates who fail phone screens are often the same candidates who would pass the full loop if they got there, it’s because they have never been told what the phone screen is actually measuring. A phone screener is trying to determine whether it is worth six hours of their colleagues’ time to find out if you are a strong engineer. That is a much lower bar than the full loop, and the specific way to clear it is almost entirely different from the way to clear the loop itself.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyfs4jrnz5wt7ia9l455.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyfs4jrnz5wt7ia9l455.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What the phone screen is actually for&lt;br&gt;
The phone screen exists to solve a resource allocation problem. Running a full interview loop costs a company somewhere between 4 to 8 hours of senior engineering time, plus recruiter coordination time, plus the overhead of scheduling across multiple calendars. Doing that for every candidate who passes a resume screen would make hiring economically irrational. The phone screen is the filter that makes the full loop sustainable.&lt;/p&gt;

&lt;p&gt;This means the phone screener’s primary job is elimination. They are looking for reasons to confidently pass you forward to the loop, or confidently not. A candidate who is clearly strong technically and communicates cleanly gets passed forward. A candidate who is clearly not at the required level gets eliminated. The difficult case can be the candidate who is somewhere in the uncertain middle and tends to get eliminated in practice, because the cost of a false negative (missing a good candidate) is lower than the cost of a false positive (running a full loop on someone who fails it).&lt;/p&gt;

&lt;p&gt;Understanding this changes the preparation goal. You are not trying to impress a phone screener with the depth of your system design thinking or the elegance of your behavioral storytelling. You are trying to give them enough clear, confident evidence of competence in 35 minutes that the risk calculation resolves in your favor. The standard is different. The strategy is different. The prep is different.&lt;/p&gt;

&lt;p&gt;The recruiter screen and the technical screen are completely different evaluations&lt;br&gt;
Before talking about the technical phone screen, it is worth distinguishing it from the recruiter screen that often precedes it. Many candidates treat these as interchangeable. They are not.&lt;/p&gt;

&lt;p&gt;The recruiter screen is not a technical evaluation. The person conducting it is typically not an engineer, cannot assess the quality of your code, and is not trying to. What they are evaluating is: whether your stated experience matches the role requirements at a surface level, whether your comp expectations are in the range the role can accommodate, whether you can articulate coherently what you have done and why you are interested in this company, and whether there are any obvious disqualifying factors like a gap in employment that has no explanation, a set of responsibilities that are inconsistent with the seniority being claimed, a comp expectation that is dramatically above what the role supports.&lt;/p&gt;

&lt;p&gt;The failure modes in a recruiter screen are almost entirely communication failures. Engineers who give meandering answers to simple questions, who cannot explain what they worked on without defaulting to technical jargon the recruiter cannot evaluate, or who have not done basic research on the company create an impression of either poor communication skills or low interest in the role. Both eliminate you without a technical evaluation ever happening.&lt;/p&gt;

&lt;p&gt;The technical phone screen is where the engineering evaluation begins. This is typically conducted by a senior engineer, a tech lead, or a hiring manager. Unlike the full loop, the technical phone screen usually covers one coding problem and sometimes a brief system design discussion, with additional time for conversational questions about your background. The coding problem is almost always medium difficulty. The system design component, if it exists, is shallow, one or two components rather than a full architecture.&lt;/p&gt;

&lt;p&gt;The signal the technical phone screener is collecting is not primarily about your solution. It is about your process.&lt;/p&gt;

&lt;p&gt;What the phone screener is documenting in real time&lt;br&gt;
When I conduct a technical phone screen, I am simultaneously writing notes while the candidate is coding. I am not transcribing what they say. I am noting specific signals that I have learned over time predict success or failure in the full loop.&lt;/p&gt;

&lt;p&gt;The signals I am writing down within the first ten minutes are: whether the candidate asked clarifying questions before starting to code, whether those questions were specific and relevant or generic and time-filling, whether their initial approach was stated explicitly or implied by the code they started writing, and whether they handled the moment of uncertainty that almost every phone screen problem has, the point where the problem is ambiguous and requires a judgment call with confidence or with a request for the interviewer to resolve it for them.&lt;/p&gt;

&lt;p&gt;By the 20 minutes mark, I am noting whether the code is readable, whether the variable names mean anything, whether the candidate tests their solution against the obvious edge cases without prompting, and whether their communication has maintained a consistent level of clarity or has started to degrade under the time pressure. Communication degrading under pressure is a very common failure mode in phone screens and almost never comes up in loop prep, because mock interviews are typically run at a more relaxed pace than the real phone screen.&lt;/p&gt;

&lt;p&gt;In the 10 minutes, it matters whether the candidate understood the complexity of their solution, whether they could articulate what they would change if the constraint changed, and let’s say if I ran a brief system design discussion whether they named a specific technology, any technology, and could say in one sentence why that technology rather than an alternative.&lt;/p&gt;

&lt;p&gt;The notes from a phone screen that results in a pass forward typically contain 5to 8 specific positive data points. The notes from a phone screen that results in an elimination typically contain two or three generic positive notes and a specific concern that I could not resolve in the time available. The specific concern is what drives the recommendation.&lt;/p&gt;

&lt;p&gt;The 5 specific ways good engineers bomb phone screens&lt;br&gt;
The first is overexplaining the wrong thing. Good engineers who have prepared thoroughly sometimes spend the first fifteen minutes of a thirty-five minute phone screen clarifying requirements, discussing edge cases, and describing multiple possible approaches before writing a single line of code. In a full loop, this kind of deliberateness signals senior-level thinking. In a phone screen, it consumes the time budget in a way that prevents the interviewer from seeing enough to recommend a pass. The phone screener needs to see you produce something before the session ends. If you spend half the time preparing to produce and run out of time to demonstrate execution, the recommendation is uncertain, and uncertain resolves to no.&lt;/p&gt;

&lt;p&gt;Download the Medium App&lt;br&gt;
The second is optimizing for the wrong audience. Engineers who have prepared extensively for senior loops sometimes bring staff-level scope to a phone screen problem and spend time discussing distributed consistency guarantees for a problem that asked for a function that reverses a linked list. The phone screener is not evaluating depth. They are evaluating whether you can solve the problem in front of you clearly and quickly. Demonstrating depth on the wrong problem signals that you are not calibrated to the actual question, which is itself a signal worth documenting.&lt;/p&gt;

&lt;p&gt;The third is failing the communication test without knowing there is a communication test. Many engineers work through technical problems silently when alone and have to consciously choose to narrate their thinking in an interview. In a full loop, the format and the duration give candidates time to remember to narrate and to settle into the habit. In a 35-minute phone screen, candidates who forget to narrate in the first ten minutes often realize it around the fifteen-minute mark, try to recover, and produce a communication pattern that is choppy and inconsistent. The interviewer notices the inconsistency and cannot determine whether the candidate is normally a clear communicator who had a bad session or normally a poor communicator who partially recovered.&lt;/p&gt;

&lt;p&gt;The fourth is a mismatch between the complexity of the solution and the ability to explain it. Some engineers correctly identify a complex optimal solution, implement it correctly, and then cannot explain clearly why it is correct or what assumptions it depends on. This pattern — impressive implementation, limited articulable understanding — reliably generates a specific negative note: strong coder, concerning depth. That note is enough to cause a pass-forward hold at most companies.&lt;/p&gt;

&lt;p&gt;The fifth is misreading the time signal. When a phone screener says something like there are about ten minutes left, it is almost always a signal to finish what you are doing and move to edge cases or complexity discussion. Candidates who hear this signal and start a new, harder approach to the same problem, or who respond by speeding up their implementation in a way that introduces bugs, create an unnecessarily negative final impression. The last five minutes of the phone screen are the interviewer’s most recent memory of the candidate. That memory is what they write their recommendation note from.&lt;/p&gt;

&lt;p&gt;The timing problem&lt;br&gt;
A full interview loop coding round is typically 45 to 60 minutes. A phone screen is 30 to 45 minutes, and that window includes introductory conversation, clarifying questions, implementation, testing, and often a brief discussion of complexity or follow-up questions. The actual coding window in a phone screen is often 20 to 25 minutes.&lt;/p&gt;

&lt;p&gt;Most candidates do not practice coding problems under a 20-minute timer. They practice under 45-minute timers, which is the appropriate constraint for the full loop. The gap in time pressure is not linear — the cognitive experience of producing correct, readable, narrated code in 20 minutes is qualitatively different from producing it in 45 minutes. It requires faster pattern recognition, faster implementation, and a different set of decisions about what to optimize and what to leave for later.&lt;/p&gt;

&lt;p&gt;The specific prep adjustment for phone screens is to practice medium-difficulty problems with a hard 20-minute timer and a rule that requires narration at all times. Not narration when convenient or narration when you have something interesting to say narration continuously, even when you are doing something mechanical like writing a loop iterator or initializing a data structure. The goal of this constraint is to build the habit of continuous communication under time pressure so that it does not require active cognitive effort during the actual phone screen.&lt;/p&gt;

&lt;p&gt;The other timing adjustment is learning to read a problem faster. In a full loop, you can take five minutes to fully understand the problem before proposing an approach. In a phone screen, you have roughly two minutes. The specific skill is identifying the problem category from the first read of the prompt is that this a two-pointer problem, a sliding window problem, a tree traversaland using that identification to immediately describe the approach before fully implementing it. Saying I think this looks like a sliding window problem, let me state the approach and then we will see if the edge cases complicate it takes twenty seconds and immediately gives the interviewer something positive to write down.&lt;/p&gt;

&lt;p&gt;The recruiter screen is also prep&lt;br&gt;
One thing I did not fully understand as a candidate was that the recruiter screen requires its own specific preparation, completely separate from technical preparation. The recruiter screen is a communication test where the raw material being evaluated is your ability to describe your work clearly to a non-technical audience under mild pressure.&lt;/p&gt;

&lt;p&gt;The specific skills it tests are: the ability to describe what your current or most recent company does in two sentences without jargon, the ability to describe what you personally built or owned in your most recent role in three or four sentences that a non-engineer could understand, the ability to explain why you are interested in this specific company and role with a reason that is more specific than the company being well-known, and the ability to give your compensation expectations without either refusing to answer or giving a number that immediately disqualifies you.&lt;/p&gt;

&lt;p&gt;Each of these feels simple and is actually difficult to do well under the mild pressure of a live call with a stranger who is evaluating you. The preparation for a recruiter screen is to practice each of these answers out loud, by yourself, until the length and content feel natural rather than scripted. Not to memorize the words but to internalize the structure like what you say first, what level of technical detail is appropriate, what to say when you do not know the answer to something.&lt;/p&gt;

&lt;p&gt;The preparation that specifically targets phone screens&lt;br&gt;
The prep for a phone screen is a subset of the prep for a full loop, but it is not a smaller version of the same thing. It is different.&lt;/p&gt;

&lt;p&gt;For the technical phone screen: solve medium-difficulty problems under a hard 20-minute timer with continuous narration required. Practice identifying the problem category within two minutes of reading the prompt — is this a sliding window, a two-pointer, a graph traversal. Practice stating the approach explicitly before writing the first line of code. Practice testing against the first obvious edge case before the interviewer mentions it. LeetCode’s medium problem set is the right material; NeetCode’s topic-grouped roadmap is useful for making sure you are covering the categories that appear most frequently rather than solving randomly.&lt;/p&gt;

&lt;p&gt;For the recruiter screen: practice the four core questions like what your company does, what you personally built, why this company and role, and comp expectations like in a way that is specific, jargon-free, and appropriately concise. A good answer to any recruiter question lands between 30 and 90 seconds. Anything shorter sounds rehearsed. Anything longer loses the recruiter’s attention. Record yourself answering these questions and listen back. Most engineers discover that their answers are either too technical, too long, or both.&lt;/p&gt;

&lt;p&gt;For company-specific calibration: the most useful input is recent interview reports from people who have actually been through the process. Glassdoor and Blind both surface these, and the key qualifier is recency, look for reports from the past three to six months, not older. Note the difficulty level, the style (algorithmic versus design-oriented), and whether follow-up constraints were introduced mid-problem. When multiple recent reports describe the same pattern, you have reliable calibration data. Levels.fyi is useful for understanding what level the role is actually being filled at, which tells you whether the phone screen is calibrated to a junior, mid-level, or senior bar. For companies where recent report volume is low on the free platforms, &lt;a href="https://prachub.com?utm_source=devto&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;PracHub&lt;/a&gt; aggregates company-specific question patterns in a searchable format, which saves the manual cross-referencing across forum threads — but the free sources cover most major companies well enough if you are willing to dig.&lt;/p&gt;

&lt;p&gt;Phone screen is the round where the most prepared candidates are the least specifically prepared. The people who spend weeks studying system design and dynamic programming have often spent zero time practicing under a 20-minute hard timer while narrating continuously. The people who know exactly how to answer behavioral questions in the STAR format have often never practiced describing their current role clearly to a non-technical person.&lt;/p&gt;

&lt;p&gt;The phone screen is not hard. The problems are medium difficulty. The criteria are not opaque. The failure mode is almost always preparation targeted at the wrong test.&lt;/p&gt;

&lt;p&gt;Fix the timer. Practice the narration at speed. Prepare two clean answers to the recruiter’s four questions. Know the typical difficulty and style of phone screens at your target company. That is the full preparation list. It takes less time to master than any single algorithm pattern from a top-tier prep course, and it is the filter that most candidates never get past.&lt;/p&gt;

</description>
      <category>interview</category>
      <category>career</category>
      <category>phonescreening</category>
      <category>ai</category>
    </item>
    <item>
      <title>I Have Sat in 100s of Debrief Calls. Here Is What Interviewers Discuss After the Call.</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Thu, 04 Jun 2026 02:11:56 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/i-have-sat-in-100s-of-debrief-calls-here-is-what-interviewers-discuss-after-the-call-47bi</link>
      <guid>https://dev.to/emilywoodsnyc/i-have-sat-in-100s-of-debrief-calls-here-is-what-interviewers-discuss-after-the-call-47bi</guid>
      <description>&lt;p&gt;After your interview loop ends, the people who interviewed you get on a call or a shared document and make a recommendation. This conversation is structured differently at different companies, but the underlying dynamics are consistent: each interviewer submits a rating and written notes, a designated calibration interviewer or bar raiser checks that the decision is consistent with the company’s overall hiring standard rather than just the team’s preferences, and the group works toward a consensus on both the hire decision and the level. The candidates who get the outcome they expected are the ones whose performance gave the interviewers clear, consistent material to work with. The candidates who get surprised by rejections or down-levels are almost always the ones who performed unevenly across rounds and left the debrief with inconsistent signals that resolved in the direction of caution. Understanding what the debrief actually evaluates gives you specific information about how to perform more consistently, not just more impressively.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbkb3gkdisjflx1gorxw.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbkb3gkdisjflx1gorxw.png" alt=" " width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the debrief actually is&lt;/strong&gt;&lt;br&gt;
At most mid-to-large tech companies, the debrief is a structured review that happens within 24 to 72 hours of your final interview. At Google, Meta, Amazon, and most companies that have modeled their process on FAANG practices, the structure typically involves each interviewer submitting an independent written evaluation before seeing anyone else’s feedback, followed by a group calibration session.&lt;/p&gt;

&lt;p&gt;The written evaluation submitted before the group call matters more than most candidates realize, because it is produced while the interview is still fresh and before social dynamics in the group session can influence individual positions. An interviewer who felt genuinely uncertain about a candidate is much less likely to document uncertainty clearly if they submit their notes after hearing two strong positive evaluations from colleagues. The pre-submission requirement is designed to capture independent signal. When it works, the group calibration produces a more accurate picture than any individual round alone.&lt;/p&gt;

&lt;p&gt;The people in the debrief include every interviewer from the loop, typically the recruiter as a logistics and policy voice, and at many companies a designated calibration reviewer — called a bar raiser at Amazon, a calibration interviewer at Google, and various equivalents elsewhere. This calibration reviewer has a specific and important function: they are responsible for ensuring that the hire decision reflects the company-wide standard, not just the preference of the hiring team. A team that is under pressure to hire, has been searching for months, and desperately wants to fill the role will sometimes push toward a hire recommendation that a calibration reviewer will push back on, specifically because the calibration reviewer’s job is to be immune to that pressure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The voting system and what the ratings actually mean&lt;/strong&gt;&lt;br&gt;
The rating scales vary by company, but the underlying spectrum is consistent. Most systems have five positions that map roughly to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strong hire,&lt;/li&gt;
&lt;li&gt;hire,&lt;/li&gt;
&lt;li&gt;lean hire or&lt;/li&gt;
&lt;li&gt;neutral,&lt;/li&gt;
&lt;li&gt;no hire, and&lt;/li&gt;
&lt;li&gt;strong no hire.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relationship between individual ratings and the final decision is not simply arithmetic. A majority of hire votes does not automatically produce an offer, and a majority of no-hire votes does not automatically produce a rejection. The calibration process is specifically designed to resolve inconsistency rather than just count votes.&lt;/p&gt;

&lt;p&gt;A strong no hire from a senior or staff interviewer in a domain-critical round, a staff engineer’s strong no hire on a system design for a senior backend role, for example- carries more weight than two hire votes from interviewers in peripheral rounds. The calibration reviewer is explicitly watching for situations where the overall positive signal is contradicted by a specific expert signal, and they will typically raise that contradiction explicitly rather than letting a numerical majority decide.&lt;/p&gt;

&lt;p&gt;A lean hire is the most unstable position in the debrief. It typically indicates that an interviewer saw enough to consider the candidate acceptable but not enough to be confident. In a group calibration with mostly strong hire votes, a lean hire usually resolves to hire. In a group with mixed signals, lean hire is often the vote that tips toward no hire after the discussion, because it is hard to defend with specific evidence and because the people arguing for hire in those cases often cannot point to concrete moments in the interview that justify overriding the uncertainty.&lt;/p&gt;

&lt;p&gt;The implication for candidates is specific: the interviewer who gave you a lean hire is your most important feedback signal, not your worst rating in isolation. Understanding what caused someone who was otherwise engaged and constructive to stop short of a confident positive recommendation is worth more than understanding what went wrong with the person who gave you a clear no hire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The phrases that reliably doom candidates in the debrief&lt;/strong&gt;&lt;br&gt;
Interviewers at most companies are trained or at least guided toward structured feedback language. But the underlying evaluations behind that language are consistent enough across companies and roles that the phrases function as a readable signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Communication issues&lt;/strong&gt; is almost never about clarity of speech, accent, or verbal fluency. When an interviewer writes this phrase, they mean one of three specific things. The first is that the candidate was silent during moments where the interviewer expected narration — working through a problem without explaining their thinking, making decisions without stating the trade-off, or pivoting approaches without explaining why. The second is that the candidate’s explanations were inconsistent with their code or design — they said they were doing one thing and then implemented something different, which makes the interviewer question whether the verbal explanation was genuine understanding or a memorized script. The third is that the candidate required an unusual amount of prompting to stay on track, which the interviewer interprets as an inability to structure their own thinking rather than a conversational style preference. Communication issues in a debrief report is often the most specific and most actionable negative signal available to a candidate, because the remediation is completely within their control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not quite at level&lt;/strong&gt; means the interviewer saw work that would be appropriate for the role one level below the one being interviewed for. This is not a failure of technical knowledge in most cases. It is a failure of scope. A candidate interviewing for a senior role who solves problems correctly but does not proactively identify the broader system implications of their solution, or who answers behavioral questions with examples of well-executed assigned work rather than examples of self-directed problem definition, will receive this note regardless of how technically correct their outputs were. The calibration reviewer will typically flag this note as the basis for a down-level offer rather than a rejection, which is why the not-quite-at-level note is the signal most correlated with receiving an offer at a lower level than expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Needed prompting or required hints&lt;/strong&gt; is a specific death sentence for a senior or above evaluation. At junior and mid-level, receiving a hint and running with it cleanly is often a positive signal — it shows coachability. At senior level, needing a hint to reach the correct approach demonstrates that you cannot independently navigate to the best solution, which is precisely what the senior evaluation is testing. Calibration reviewers watching for level consistency across the company will push back on any hire recommendation at senior or above that includes needed prompting in the coding or system design round notes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Culture fit concerns&lt;/strong&gt; is the phrase most likely to mean something uncomfortable and least likely to be documented with specificity. It can legitimately mean that the candidate’s stated values were inconsistent with how the team actually operates — someone who describes their ideal environment as highly autonomous but is joining a team that runs closely paired engineering with heavy process. It can also mean that the candidate was abrasive, dismissive, or condescending in a way that one or more interviewers experienced as genuine risk to team dynamics. When this phrase appears alongside positive technical ratings, the debrief becomes difficult. Strong technical signals make a no-hire recommendation hard to defend on pure competency grounds, but interviewers who felt a genuine negative interpersonal reaction will often hold their position even under pressure from the group.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnxcd0idg52bgzp1sul3.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnxcd0idg52bgzp1sul3.png" alt=" " width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bar raiser function and what it means for you&lt;/strong&gt;&lt;br&gt;
The bar raiser or calibration interviewer role exists because of a known failure mode in team-driven hiring: teams under pressure hire people they like rather than people who meet the standard. A manager who has been searching for three months and has a backlog of work will unconsciously lower their bar for a candidate who is enthusiastic, a good cultural fit with the existing team, and technically adequate even if not strong. The bar raiser is structurally independent of the hiring team and has no stake in whether the role gets filled. Their only job is to evaluate whether the candidate would improve the average quality of the engineering org at that company, not just whether the candidate would be useful to this team right now.&lt;/p&gt;

&lt;p&gt;The practical implication is that a bar raiser can veto a hire recommendation from a unanimous hiring team. At Amazon, this veto is explicit in the process. At Google and similar companies, it functions as a strong escalation that typically results in the decision being reviewed at a higher level rather than simply overridden. The hiring team is informed of the objection and must either address it with specific counter-evidence from the interview record or accept the block.&lt;/p&gt;

&lt;p&gt;What the bar raiser is specifically watching for during the debrief and during the interview if they are conducting a round is whether the candidate’s performance would be consistent with what the company’s best engineers would do. They are not asking whether the candidate would be helpful to this team. They are asking whether the candidate would make the engineering org measurably better. This is a higher bar than adequate and a harder bar to hit when the candidate’s performance was solid but unremarkable.&lt;/p&gt;

&lt;p&gt;The specific thing that impresses calibration reviewers in the debrief record is not exceptional technical output in a single round. It is consistency and agency across rounds. A candidate who designed a reasonable architecture, wrote clean and readable code, handled a behavioral question with specific examples that showed genuine judgment, and asked thoughtful questions at the end of each round has given every interviewer something specific and positive to record. The debrief for that candidate resolves quickly and confidently. The debrief for a candidate who had one exceptional system design round, one mediocre coding round with a hint required, and one behavioral round with generic answers is a much harder conversation, even if the technical peak was impressive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What gets discussed that interviewers never tell you&lt;/strong&gt;&lt;br&gt;
The debrief surfaces information that never appears in formal feedback, both because companies are cautious about the legal implications of candid rejection communication and because the specific information is often too granular to communicate usefully in a templated rejection message.&lt;/p&gt;

&lt;p&gt;The comparison conversation happens in most debriefs. If multiple candidates have been through loops for the same role, the calibration session often includes explicit comparison. A candidate who performed well in isolation may lose to a candidate who performed better on the specific dimensions the team weighted most heavily. This comparison is invisible to the individual candidate and produces a rejection that feels inconsistent with their own assessment of how the interview went.&lt;/p&gt;

&lt;p&gt;The team dynamics conversation happens when a candidate has raised an interpersonal concern for any interviewer. This conversation is rarely about anything extreme. It is more often about small signals like the candidate who referred to a previous employer’s engineering team with undisguised contempt, or the candidate who interrupted the interviewer 3vtimes to correct a minor point, or the candidate who answered a question about a past failure with an extended explanation of why the failure was actually caused by other people. None of these things individually justify a rejection on the record. Together they produce a note in the debrief that influences the group’s confidence in the candidate as a teammate.&lt;/p&gt;

&lt;p&gt;The level negotiation conversation is explicit and important. The debrief decides not just whether to hire but what level to offer. If the evidence is mixed like strong technical execution but behavioral examples that suggest mid-level scope of ownership then the group will discuss where the candidate falls and may recommend a hire at the lower level with a note that the candidate could be re-leveled after a review period. This conversation is the source of most of the down-level surprises that candidates experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you can do during the interview to shape what gets said&lt;/strong&gt;&lt;br&gt;
The most useful framing is that your goal in the interview is to give every interviewer something specific and positive to write in their notes. Not impressive in the abstract. Specifically recordable. An interviewer who leaves your session with vivid, detailed, positive memories of specific moments produces a strong hire evaluation. An interviewer who leaves your session feeling generally positive but without specific moments to anchor that feeling produces a lean hire evaluation that is vulnerable in the debrief.&lt;/p&gt;

&lt;p&gt;Making your thinking visible is the single most effective way to give interviewers recordable material. When you make a decision something like choosing an approach or selecting a data structure or like framing a behavioral story, try to state the reasoning explicitly. Not because the interviewer does not understand the technical reasoning, but because hearing you articulate it gives them a direct quote to include in their notes. The debrief note for a candidate who says nothing while implementing is inevitably thinner and less confident than the note for a candidate who narrates at the same time.&lt;/p&gt;

&lt;p&gt;Managing your scope signals deliberately is the behavioral equivalent. In every round, look for opportunities to demonstrate that you think about the work at the level above the immediate task. In a coding round, this means mentioning observability, edge case handling, and what a production version of this solution would need before you finish the implementation. In a system design round, this means asking about operational requirements before drawing anything. In a behavioral round, this means selecting examples that demonstrate ownership of an outcome rather than execution of a task.&lt;/p&gt;

&lt;p&gt;The questions you ask at the end of each round are recorded and discussed in the debrief. Most candidates do not know this. The questions that produce the strongest debrief notes are specific, technically engaged, and reveal genuine curiosity about how the team operates at a deep level. Questions about technology decisions the team made and what they would change, about how production incidents are handled and what the on-call structure looks like, about how the team makes architectural decisions when there is not an obvious right answer — these signal seniority and genuine interest. Questions about vacation policy and remote flexibility signal that the candidate’s primary evaluation of the role is not technical fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on finding the company-specific version of this&lt;/strong&gt;&lt;br&gt;
The debrief process at a Series B startup interviewing for a senior engineer looks meaningfully different from the debrief at Google interviewing for an L5. The number of interviewers, the weight of the bar raiser function, the formality of the voting system, and the specific behavioral competencies being evaluated vary substantially across companies and levels.&lt;/p&gt;

&lt;p&gt;Understanding the specific process at your target company before your loop is worth the research time. What questions the company explicitly evaluates in behavioral rounds, what their calibration criteria at each level look like, and what past candidates have experienced in the debrief communication are all out there if you do your connecting the dots things from a combination of &lt;a href="http://glassdoor.com/" rel="noopener noreferrer"&gt;Glassdoor&lt;/a&gt;, &lt;a href="https://www.teamblind.com/" rel="noopener noreferrer"&gt;Blind&lt;/a&gt;, and company-specific interview data. For the technical side of this, &lt;a href="https://prachub.com/?utm_source=devto&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;PracHub&lt;/a&gt; is useful specifically for the question patterns and follow-up probes that a given company’s interviewers have been using, which tells you what specific dimensions the interviewers are collecting evidence on and therefore what the debrief will be evaluating.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboxvhp05e26jyjd57m3d.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboxvhp05e26jyjd57m3d.png" alt=" " width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The offer decision is not made in your interview. It is made in a room you are not in, by people who are working from written notes of the 45 minutes they spent with you. The quality of those notes like how much specific, positive, level-appropriate evidence they contain, is almost entirely determined by choices you make during the interview itself.&lt;/p&gt;

&lt;p&gt;Giving interviewers something specific and recordable to write down is not the same thing as performing. It is communicating. It is making the work visible instead of just producing correct output quietly. Engineers who are outstanding at their jobs but poor at making their thinking visible will consistently underperform in interviews relative to their actual capability, not because the process is unfair but because the process is measuring something real: the ability to work transparently in a collaborative environment where other people need to understand what you are doing and why.&lt;/p&gt;

&lt;p&gt;The debrief is not adversarial. The people in that room are trying to reach the most accurate conclusion they can with the evidence they have. Give them evidence that is accurate, specific, and at the right level of scope, and the conversation resolves in your favor.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>interview</category>
      <category>career</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Have Reviewed Over 400 Resumes for Tech roles. Here Is What Actually Gets You the Phone Screen</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Sat, 30 May 2026 19:01:13 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/i-have-reviewed-over-400-resumes-for-tech-roles-here-is-what-actually-gets-you-the-phone-screen-ddh</link>
      <guid>https://dev.to/emilywoodsnyc/i-have-reviewed-over-400-resumes-for-tech-roles-here-is-what-actually-gets-you-the-phone-screen-ddh</guid>
      <description>&lt;p&gt;The gap between candidates who get callbacks and candidates who get silence has almost nothing to do with how much experience they have.&lt;/p&gt;

&lt;p&gt;Short Answer: Your resume is not being read but being scanned for approximately 25 seconds by someone who is already behind on their real work and is looking for a reason to close the tab, not open a calendar invite. Most resumes that land in a recruiter or hiring manager’s queue fail not because the candidate is unqualified but because the document was written as a career biography instead of a targeted filtering instrument. A resume has exactly one job: to create enough credibility in a specific technical area that someone schedules 30 minutes to find out more. The candidates who get callbacks are almost never the ones with the most experience. They are the ones whose resumes are the most precise match, in vocabulary and in demonstrated impact, to the specific role they are applying for. Everything else is noise.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnpsyfkp7ccd0je3t5mh.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnpsyfkp7ccd0je3t5mh.png" alt=" " width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What actually happens when your resume lands&lt;br&gt;
Most candidates imagine their resume landing in an inbox, getting opened, and receiving careful attention from a thoughtful human being who is genuinely trying to assess their potential. This is almost never what happens.&lt;/p&gt;

&lt;p&gt;In a high-volume hiring environment and in 2026, every hiring environment is high-volume because the supply of laid-off engineers has significantly outpaced the supply of open roles, a recruiter working a standard technical requisition is processing somewhere between 80 and 200 applications per week for each open role. They have a stack of other requisitions running simultaneously. They have sourcing targets, pipeline metrics, and screening calls to schedule. The resume review window for any individual candidate is not the 10 minutes you hoped for. It is closer to 20 to 30 seconds on the initial pass.&lt;/p&gt;

&lt;p&gt;In those 20 to 30 seconds, the reviewer is doing something closer to pattern matching than reading. They are scanning for signals that answer three implicit questions, and if they do not find those signals immediately, they move to the next application. The signals are not hidden in the body of your paragraph-style job descriptions. They need to be visible at a glance.&lt;/p&gt;

&lt;p&gt;I know this because I have been on both sides of the process. I have submitted resumes that received no response despite being genuinely qualified for the role, and I have reviewed applications as part of hiring loops and watched myself make the same fast, imperfect decisions I know recruiters are making when they look at mine. The process is imperfect on both sides, and understanding that is the starting point for working within it rather than against it.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0c9nm1o8s0orulr67fx.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0c9nm1o8s0orulr67fx.png" alt=" " width="720" height="1370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The three questions every reviewer is unconsciously answering&lt;br&gt;
When a recruiter or hiring manager opens a resume on an initial pass, they are answering three questions. Not consciously, but structurally. These questions shape everything about how the document should be written.&lt;/p&gt;

&lt;p&gt;The first question is whether you have done the thing they need done. This sounds obvious, but it is the question that most resumes fail. A resume filled with generic action verbs and broad technology lists does not answer this question. If the role requires experience building distributed data pipelines, the resume needs to say, explicitly and near the top of the relevant role, that you built distributed data pipelines and what scale they operated at. The reviewer should not have to infer this from adjacent technologies or general-sounding project descriptions.&lt;/p&gt;

&lt;p&gt;The second question is whether anyone else has believed you were good at this. The proxy for this is the company names and titles on your resume, but it extends to the results attached to your work. If you list a project but provide no outcome, the reviewer has no signal about whether the project succeeded or whether it was abandoned. Impact statements are not optional decoration. They are the evidence that answers this question.&lt;/p&gt;

&lt;p&gt;The third question is whether you are a credible match for the level they are hiring. This is where level-appropriate language and scope matter enormously. A resume that describes highly autonomous technical decision-making, cross-functional influence, and system-wide architectural ownership reads as a senior candidate. A resume that describes executing tickets and shipping features reads as a junior or mid-level candidate regardless of how many years are listed. The language of scope tells the reviewer what level you are, faster and more reliably than the number of years in your header.&lt;/p&gt;

&lt;p&gt;The format problem nobody talks about&lt;br&gt;
Before any of the content decisions matter, there is a format problem that eliminates a significant percentage of applications before a human ever sees them.&lt;/p&gt;

&lt;p&gt;Most large tech companies and a growing number of mid-sized companies use Applicant Tracking Systems to process inbound applications. The ATS parses your resume into a structured database record. It extracts your name, contact information, job titles, dates, company names, and skills. It then runs keyword matching against the job description to produce a relevance score. Resumes that score below a threshold are often never surfaced to a human reviewer at all.&lt;/p&gt;

&lt;p&gt;The implications of this are specific. PDFs that were generated from design tools like Canva or Figma often fail ATS parsing because the text layers are not machine-readable. Multi-column layouts frequently parse in the wrong order, producing incoherent extracted text. Tables and text boxes cause similar extraction failures. Graphics, icons, and headshot photos consume parsing budget without contributing any parseable content.&lt;/p&gt;

&lt;p&gt;The format that parses most reliably is also the least visually impressive: a single-column, left-aligned document in a standard font, generated from a word processor or LaTeX, with no tables, no text boxes, and no graphics. This is not what most resume templates sold online look like, which is part of why so many applications disappear silently.&lt;/p&gt;

&lt;p&gt;The keyword matching layer of the ATS is a separate problem from the parsing layer. Once your resume is correctly parsed, the system looks for vocabulary overlap between your document and the job description. This is not a sophisticated semantic analysis. It is largely literal string matching and close variants. If the job description says distributed systems and your resume says large-scale infrastructure without using the words distributed systems anywhere in the document, you may score lower than a candidate with less experience who happened to use the exact phrase.&lt;/p&gt;

&lt;p&gt;The practical implication of this is that tailoring your resume to each application is not optional. It is the minimum bar for having your document seen by a human at all.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femxhf0yixllgnrhfwwn3.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femxhf0yixllgnrhfwwn3.png" alt=" " width="720" height="995"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why the tailoring feels impossible and how to make it tractable&lt;br&gt;
The standard advice is to tailor your resume for every application, and the standard response to that advice is that it is impractical when you are applying to thirty roles simultaneously. Both of these things are true at the same time, and the resolution is not to choose one over the other but to change the architecture of the process.&lt;/p&gt;

&lt;p&gt;The approach I use, and that I have seen work consistently, is to maintain a master document that contains every experience, project, result, and technology you might conceivably reference in any application. This document is not your resume. It is a raw material inventory. From this inventory, you build targeted versions for each application by selecting and ordering the entries that are most relevant to the specific role.&lt;/p&gt;

&lt;p&gt;In practice, this means you maintain approximately three to five resume variants, not thirty. A backend systems variant that leads with distributed infrastructure work, a data engineering variant that leads with pipeline and warehouse experience, a full-stack variant that leads with product-facing features, and so on. When a specific role has unusual requirements that do not fit neatly into a variant, you make targeted adjustments to the closest variant rather than rebuilding from scratch.&lt;/p&gt;

&lt;p&gt;The additional tailoring step for each application is keyword alignment. Before submitting any application, read the job description carefully and note any specific technical terms, frameworks, or domain vocabulary that appears multiple times. Then scan your resume for whether those exact terms appear. If you have the relevant experience and the vocabulary is not in your document, add it. This is not dishonest. It is translating your real experience into the vocabulary the reviewer is searching for.&lt;/p&gt;

&lt;p&gt;The anatomy of a resume bullet that actually works&lt;br&gt;
The single most impactful change most candidates can make to their resume is rewriting their job description bullets to follow a consistent formula: action, system, scale, and outcome.&lt;/p&gt;

&lt;p&gt;Most resume bullets are written in one of two broken formats. The first is the responsibility dump: a list of things the person was supposed to do in the role, written in present tense or with responsibility-oriented language. The second is the vague achievement: a claim of impact without enough specificity to evaluate it.&lt;/p&gt;

&lt;p&gt;A bullet written in the action-system-scale-outcome format contains a specific active verb describing what you did, the system or component you did it to or built, the operational scale or complexity of the context, and the measurable result. Each element does specific work. The verb establishes agency. The system establishes technical domain. The scale establishes the seniority-appropriateness of the work. The outcome establishes that the work mattered.&lt;/p&gt;

&lt;p&gt;A concrete example: a bullet that reads designed caching layer is missing three of the four elements. A bullet that reads designed and implemented a Redis cache-aside layer for the product catalog service, reducing average API latency from 340ms to 28ms and cutting database read load by 65% at 12,000 requests per second contains all four. The second version answers the three unconscious reviewer questions. The first one answers none of them.&lt;/p&gt;

&lt;p&gt;The outcome element is the one that candidates most consistently omit, usually because they genuinely do not remember the numbers or were never told them. If you do not know the exact metric, use a reasonable estimate with an explicit qualifier. Approximately, roughly, and estimated all signal honesty rather than precision fabrication, and a reasonable estimate is significantly more useful to a reviewer than no number at all.&lt;/p&gt;

&lt;p&gt;The skills section is usually doing the opposite of what you intend&lt;br&gt;
The skills section of most technical resumes is a long, comma-separated list of every technology, language, framework, and tool the candidate has ever encountered. The implicit theory behind this is that more coverage means more keyword matching, which means a higher ATS score and a broader pool of roles you are viable for.&lt;/p&gt;

&lt;p&gt;The problem with this theory is that it ignores the reviewer experience on the other side. A skills section that lists 45 technologies provides no signal about depth. It reads as a keyword stuffing attempt rather than a genuine representation of capability. Experienced reviewers — the hiring managers and senior engineers who do technical resume screens — have a strongly negative reaction to this and will often use it as a reason to reject a candidate for a senior role on the grounds that someone trying to appear qualified in 45 different areas is probably not deep in any of them.&lt;/p&gt;

&lt;p&gt;A more effective approach is to organize skills by category and signal depth within each category by the specificity of what you list. Listing Python is universal and tells the reviewer nothing. Listing Python with specific mention of async frameworks, type annotation systems, and profiling tools tells the reviewer something about how you actually use the language. The details signal depth more reliably than the length of the list.&lt;/p&gt;

&lt;p&gt;Technologies you learned in a single tutorial, used once years ago, or know only superficially should be removed entirely. If an interviewer asks you to walk through something you listed and you cannot do it coherently, you have damaged your credibility far more than the line item was worth.&lt;/p&gt;

&lt;p&gt;The projects section: when it helps and when it actively hurts you&lt;br&gt;
For early-career candidates with limited professional experience, a projects section is often the most important part of the resume. For mid-level and senior candidates with five or more years of professional work, a projects section is usually unnecessary and sometimes harmful.&lt;/p&gt;

&lt;p&gt;The reason it can be harmful for experienced candidates is that it shifts the apparent center of gravity of your resume away from your professional work. If a reviewer sees five years of professional experience and then a projects section below it, the implicit message is that the professional work was not sufficient to represent your full capability. That is the opposite of the message you want to send.&lt;/p&gt;

&lt;p&gt;The exception is when you have a side project with genuine scale or usage that is directly relevant to the role. A production-deployed open-source library with active GitHub stars and users, a tool that processes real-world data at a meaningful scale, a published API with documented external adoption, these are worth including because they represent demonstrated real-world impact outside of a controlled employment environment. A tutorial project, a bootcamp assignment, or a local CRUD application that was never deployed should not appear on a mid-level or senior resume regardless of how technically interesting it was to build.&lt;/p&gt;

&lt;p&gt;The referral reality&lt;br&gt;
Everything in this article is about optimizing your resume as a document. The honest follow-up to all of it is that a warm referral from a current employee bypasses most of this process entirely.&lt;/p&gt;

&lt;p&gt;At most large tech companies, employee referrals go directly to a recruiter’s attention queue, skip the ATS keyword filtering, and receive a human review on the first pass. They carry implicit credibility because a current employee has staked their internal reputation on the recommendation. The conversion rate from referred application to phone screen is significantly higher than from a cold application, at some companies by an order of magnitude.&lt;/p&gt;

&lt;p&gt;If you have a connection at a company you are targeting a former colleague, a university contact, someone you met at a conference or in an online community reaching out and asking for a referral is worth more than any resume optimization. The conversation for this does not need to be elaborate. A brief message explaining that you are applying for a specific role, that you thought your background was a strong match, and that you were wondering if they would be comfortable submitting a referral is sufficient. Most people will say yes if they know you even moderately well, because the referral bonus programs at most large tech companies provide a material incentive to do so.&lt;/p&gt;

&lt;p&gt;The part of the job search that feels the most uncomfortable reaching out to people you know and asking for help is also consistently the highest-ROI activity in the process.&lt;/p&gt;

&lt;p&gt;What I watch candidates get wrong the most&lt;br&gt;
Having reviewed applications across a range of roles and levels, the mistake I see most frequently is not skills gaps or format problems. It is scope mismatch between the resume and the role.&lt;/p&gt;

&lt;p&gt;Candidates applying for senior or staff roles often submit resumes that read at a mid-level scope: well-executed individual work, clearly described, with good results. The resume is accurate. The work it describes is real. But it does not demonstrate the ownership of ambiguous problems, the cross-functional influence, or the system-wide architectural thinking that a senior or staff reviewer is looking for.&lt;/p&gt;

&lt;p&gt;The inverse is also common: candidates applying for mid-level roles who write their resumes at a staff-level scope, which makes the reviewer skeptical that the experience is real or makes them worry the candidate will be bored and leave quickly.&lt;/p&gt;

&lt;p&gt;Before submitting any application, read the job description looking specifically at the scope language: the seniority indicators embedded in the responsibilities section. Then read your resume and check whether the scope signals in your bullets match the scope signals in the job description. If they do not, adjust. This is not about misrepresenting your experience. It is about translating the scope of your actual work into the vocabulary that matches where you are applying.&lt;/p&gt;

&lt;p&gt;A note on where to find out what companies actually want&lt;br&gt;
The job description tells you what the company is officially looking for. It tells you very little about what the interview process actually tests, what the team is specifically trying to solve for, or what the hiring bar looks like in practice compared to the stated requirements.&lt;/p&gt;

&lt;p&gt;For that information, the sources that have consistently been most useful to me are &lt;a href="https://www.teamblind.com/" rel="noopener noreferrer"&gt;Blind&lt;/a&gt; and &lt;a href="https://www.glassdoor.com/index.htm" rel="noopener noreferrer"&gt;Glassdoor&lt;/a&gt; for general impressions, and for technical role preparation specifically, &lt;a href="https://prachub.com?utm_source=devto&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;PracHub&lt;/a&gt; for understanding the actual question patterns and evaluation criteria companies use at specific levels. Knowing that a company’s senior engineering bar emphasizes operational decision-making over raw algorithmic speed, for example, is information that should influence not just how you interview but how you position your experience on your resume before you apply.&lt;/p&gt;

&lt;p&gt;The candidates who are most effective at moving through the process efficiently are the ones who treat the resume, the application, and the interview prep as connected parts of the same calibration exercise rather than separate tasks. Your resume gets you the call. The call confirms the fit. The loop confirms the level. All three are easier when you know in advance what each stage is actually optimized to find.&lt;/p&gt;

&lt;p&gt;The resume is a filtering instrument. It is optimized when it answers three questions clearly in 25 seconds: have you done this, did it matter, and are you the right level. Everything else i.e. the design, the length debates, the choice of font is secondary to those three answers.&lt;/p&gt;

&lt;p&gt;Tailor the vocabulary to the job description. Quantify the outcomes even approximately. Remove the technologies you cannot speak to in an interview. Write at the scope level of the role you are targeting, not the role you came from. And when there is any path to a referral, take it before submitting the cold application.&lt;/p&gt;

&lt;p&gt;The process is imperfect and frequently unfair. The volume problem is real and it disadvantages excellent candidates every day. But the optimization is tractable, and the gap between a resume that gets ignored and a resume that gets a callback is almost never the gap between a good engineer and a great one. It is almost always the gap between a document that answered the three questions and one that did not.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>resume</category>
      <category>resources</category>
    </item>
    <item>
      <title>I Left $40,000 on the Table in My First Tech Offer. Here Is Exactly What I Do Now</title>
      <dc:creator>Emily Woods</dc:creator>
      <pubDate>Fri, 29 May 2026 00:03:55 +0000</pubDate>
      <link>https://dev.to/emilywoodsnyc/i-left-40000-on-the-table-in-my-first-tech-offer-here-is-exactly-what-i-do-now-lke</link>
      <guid>https://dev.to/emilywoodsnyc/i-left-40000-on-the-table-in-my-first-tech-offer-here-is-exactly-what-i-do-now-lke</guid>
      <description>&lt;p&gt;The fear of negotiating a job offer is costing you more than a year of savings. A specific, tested framework for the 48 hours after the offer call.&lt;/p&gt;

&lt;p&gt;When my first real tech offer came in, I said yes within the hour. I was so relieved to have the job that the idea of asking for more money felt like tempting fate. That was a $40,000 mistake measured over the two year tenure, because every raise, bonus, and equity refresh I received for the next two years was calculated as a percentage of that initial number. The offer is not a gift. It is an opening bid. Companies expect negotiation and they build room for it into their budget before they call you. The candidates who do not negotiate are not being humble they are subsidizing the ones who do. What follows is the exact framework I now use every time I receive an offer, including what to say, when to say it, what to never say, and how to handle the specific tactics recruiters use to close you fast.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwipoitpro8gle9zgb67c.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwipoitpro8gle9zgb67c.png" alt=" " width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The $40,000 mistake I made at 23&lt;br&gt;
When I moved to New York, I had been through 23 interviews over about 14 months before I got an offer I was genuinely excited about. It came from a fintech startup in Midtown. The recruiter called me on a Thursday afternoon and told me the base salary. It was more than I had ever made. It was more than my mother had ever made. My immediate reaction was gratitude, and I said yes before she finished the sentence.&lt;/p&gt;

&lt;p&gt;That number anchored everything for the next 2 years. My annual raise was calculated against it. My bonus target was a percentage of it. When I eventually got a competing offer and used it to negotiate a higher salary at a new company, the gap between what I was making and what the market was paying became visible in a way that made me genuinely angry at myself for not understanding the structure of the game at the start.&lt;/p&gt;

&lt;p&gt;I have since gone through 8 more formal offer negotiations. I have also coached a significant number of friends through theirs. The pattern is nearly identical across all of them: the candidates who negotiate get more money, the process takes two to five business days longer than the people who skip it, and nobody has ever had an offer rescinded because they negotiated professionally. The fear that stops people from doing this is almost entirely unfounded.&lt;/p&gt;

&lt;p&gt;Why you are afraid to negotiate and why that fear is irrational&lt;br&gt;
The primary emotion most people feel when an offer arrives is relief. If the search has been long and exhausting and in 2026, most searches are the offer feels like the finish line. Asking for more money after crossing the finish line feels ungrateful, risky, and possibly dangerous.&lt;/p&gt;

&lt;p&gt;This framing is wrong on every level.&lt;/p&gt;

&lt;p&gt;Companies do not make offers to candidates they intend to walk away from. By the time the recruiter calls you, the hiring manager has already decided they want you. The calibration committee has already scored you. Legal has already reviewed the terms. The recruiter has a budget range and they are instructed to open at or near the bottom of it. This is not cynical. It is just how compensation planning works. The budget exists. The room exists. The question is only whether you ask for it.&lt;/p&gt;

&lt;p&gt;The other piece of this that most candidates miss: if you accept the first number without any pushback, you have provided the company with information. You have told them that you either did not know you could negotiate or did not feel confident enough to do it. Neither of those signals reflects especially well on your sense of your own market value, which matters in an industry where your compensation trajectory is often tightly linked to the perception of how much others want you.&lt;/p&gt;

&lt;p&gt;Negotiating does not make you greedy. It makes you someone who understands the market. Those are different things.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  The 48 hours after the call
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
When a recruiter calls with a verbal offer, the single most important thing you can do in that moment is say nothing about the number itself. You acknowledge the offer, you express genuine enthusiasm for the role, and you ask for time.&lt;/p&gt;

&lt;p&gt;Something like: I am really excited about this and I want to give it the thought it deserves. Can you send over the written details and give me a few days to review everything?&lt;/p&gt;

&lt;p&gt;Every recruiter will say yes to this. Every single one. If they push back or try to get you to commit on the call, that is a yellow flag about the company’s culture, not a reason to capitulate.&lt;/p&gt;

&lt;p&gt;Once you have the written offer and a few days, do not spend that time agonizing. Spend it gathering two pieces of information: what the market rate for this role actually is at this level and location, and whether you have any competing offers or near-term competing processes you can reference.&lt;/p&gt;

&lt;p&gt;For market rate: Levels.fyi for total compensation at larger tech companies, LinkedIn Salary, and Glassdoor for smaller companies. Get familiar not just with the base salary range but the full comp picture like base, equity, signing bonus, annual bonus target because that is what you will be negotiating across.&lt;/p&gt;

&lt;p&gt;For competing leverage: this is the single most powerful variable in any negotiation, and it has nothing to do with your performance in the interview. If you have a competing offer, you have leverage. If you have active processes at other companies that could plausibly produce an offer in the next two to four weeks, you have softer leverage. If you have neither, you are negotiating on the basis of market data alone, which still works but requires a different approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  The counter
&lt;/h2&gt;

&lt;p&gt;When you come back with your counter, do it in writing over email rather than on a phone call. There are two reasons for this. First, it gives you time to construct your reasoning carefully rather than responding to real-time pressure. Second, it creates a paper trail that protects both sides.&lt;/p&gt;

&lt;p&gt;The structure of the counter email should be short and contain four things: a restatement of your excitement about the role, a specific counter number or range, a one-sentence rationale, and a collaborative close.&lt;/p&gt;

&lt;p&gt;The rationale does not need to be elaborate. Citing market rate data from &lt;a href="https://levels.fyi/" rel="noopener noreferrer"&gt;Levels.fyi&lt;/a&gt; for comparable roles at this level, with my current comp and a competing process I have active, a base of X feels more appropriate. That is enough. You do not need to write a brief. You do not need to explain your expenses, your student loans, or the cost of living in your city. None of that is relevant to the company and including it weakens the counter by making it emotional rather than market-based.&lt;/p&gt;

&lt;p&gt;The specific number you ask for matters. Ask for more than you expect to get, but not so far above the market rate that it signals you have not done your research. A reasonable counter is typically ten to fifteen percent above the initial offer for a base salary, or a request to move to the top of the disclosed range if the company gave you a range. For equity, the lever is usually the number of shares or units, not the vesting schedule, since vesting schedules are often standardized.&lt;/p&gt;

&lt;p&gt;If the company comes back with less than your counter but more than the initial offer, you have successfully negotiated. Most processes end here. If they come back at the same initial number with a statement that it is firm, you have two options: accept with a request to revisit at your six-month review, or attempt one more lever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The levers most people do not use&lt;/strong&gt;&lt;br&gt;
Base salary is the most visible number but often not the most negotiable one. There are several components of an offer that have more flexibility and less visibility than base pay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signing bonus&lt;/strong&gt;&lt;br&gt;
This is frequently the easiest lever because it is a one-time cost to the company rather than a recurring expense. If they cannot move the base, a signing bonus of ten to twenty thousand dollars is a reasonable ask, particularly if you are leaving unvested equity at your current employer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Equity&lt;/strong&gt;&lt;br&gt;
At growth-stage and public tech companies, the equity component often has more room than the base. If they gave you a range on the equity grant, you can ask to be positioned at the top of the range. If they gave you a specific number, you can ask for a refresh schedule that front-loads the vesting rather than the standard cliff structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start date&lt;/strong&gt;&lt;br&gt;
This costs the company very little and occasionally costs you a meaningful amount if you have unvested equity that will cliff before you leave. Asking for a start date four to six weeks out is normal and expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt;&lt;br&gt;
At companies with defined leveling systems, a title one band up can have compounding effects on your compensation trajectory that dwarf the immediate raise. If you were told during the process that you were being evaluated for two levels and they came in at the lower one, it is appropriate to ask whether there is flexibility on the level based on your interview performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The competing offer play&lt;/strong&gt;&lt;br&gt;
If you have a competing offer, use it. This is not a negotiation tactic but an honest information about your market value, and companies expect you to share it.&lt;/p&gt;

&lt;p&gt;The way to use it is specific: you name the company if you are comfortable doing so, you state the total comp package, and you say that you prefer this role and would like to make it work at a comparable level. You are not threatening to leave. You are just providing market data in the form that companies find most credible, which is a real offer from a real competitor.&lt;/p&gt;

&lt;p&gt;A few things to know about this. Companies will sometimes call your bluff, particularly if the competing offer is from a company in a different tier or a meaningfully different role. Let’s say, If you are negotiating at a Staff Software Engineer level at Stripe and your competing offer is from a Series B startup with significant equity risk, a sophisticated recruiter will factor in that the risk profiles are different. Be honest about the comparison and let them respond.&lt;/p&gt;

&lt;p&gt;What you should never do is fabricate a competing offer or exaggerate one. Recruiters talk to each other. Offers can be verified. The downside risk of getting caught fabricating is dramatically larger than any salary upside, and it can cost you the offer entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The exploding offer
&lt;/h2&gt;

&lt;p&gt;Occasionally, a recruiter will tell you that the offer expires in 24 or 48 hours. This is a pressure tactic, and it almost never reflects a real operational constraint. Companies do not lose their headcount approval because a candidate took four days to decide. What they lose is psychological control of the conversation.&lt;/p&gt;

&lt;p&gt;If you receive a very short deadline, acknowledge it and ask for an extension with a specific reason. Something like: I want to make this decision carefully and I have a call with my family this weekend that I need to include in that process. Is it possible to extend to Monday? In almost every case, they will extend. If they do not, you have learned something meaningful about how the company operates that is worth weighing before you accept.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does not work&lt;/strong&gt;&lt;br&gt;
Citing your personal financial situation does not work. Citing what you were making at your last job does not work unless it supports a higher ask. Negotiating emotionally expressing that you feel the offer is insulting or unfair almost never works and frequently damages the relationship before it starts.&lt;/p&gt;

&lt;p&gt;Being aggressive about equity at a late-stage public company does not work the way it does at an early-stage private one. At a public company, the equity is essentially cash with a vesting delay. At an early-stage startup, it is a lottery ticket. Treat them differently in negotiation.&lt;/p&gt;

&lt;p&gt;And the tactic that backfires most often: asking for time repeatedly without eventually committing. If you have asked for two extensions and still have not responded, you are signaling uncertainty about the role itself, which makes the company nervous. Negotiate quickly, negotiate clearly, and commit when the process has run its course.&lt;/p&gt;

&lt;p&gt;The compounding effect&lt;br&gt;
The reason I care so much about this is not the immediate dollar amount. It is the compounding effect on everything that comes after it.&lt;/p&gt;

&lt;p&gt;Your first-year performance review will reference your current compensation when determining your raise band. Your promotion will often come with a percentage increase on top of your current base. When you eventually negotiate your next offer at a different company, you will be asked what you are currently making in jurisdictions where that is legal, and even where it is not, your sense of your own market value will be shaped by the number you accepted the last time.&lt;/p&gt;

&lt;p&gt;The $40,000 I left on the table at 23 compounded into something closer to $90,000 over the following five years when you account for the raises and equity refreshes that were calculated against it. That is not hypothetical math. I can trace it across my own compensation history.&lt;/p&gt;

&lt;p&gt;Negotiation is not optional if you want to build real financial traction in this industry. It is a core skill, and like most core skills, it gets significantly easier the second and third time you do it.&lt;/p&gt;

&lt;p&gt;What I use to prepare&lt;br&gt;
The market rate piece of this knowing what the role actually pays at the specific company and level is the most important input into any negotiation. The more precisely you know that number, the more confident your counter will be.&lt;/p&gt;

&lt;p&gt;For company-specific compensation data, Levels.fyi is the most reliable source for larger tech companies. For smaller companies and startups, Glassdoor and Blind give you a rougher picture. Where I have found &lt;a href="https://prachub.com?utm_source=devto&amp;amp;utm_campaign=v1012" rel="noopener noreferrer"&gt;PracHub&lt;/a&gt; useful is in calibrating my sense of which level a company was actually interviewing me at versus which one they offered because the interview experience and the offer level do not always match, and understanding the discrepancy is often the starting point for a meaningful conversation about whether the leveling is right.&lt;/p&gt;

&lt;p&gt;Knowing your level, knowing the market rate for that level, and knowing where the company typically lands relative to market on each comp component is the combination that turns a vague counter into a specific, well-reasoned one.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehf00jl4sj9pstcdfw3x.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehf00jl4sj9pstcdfw3x.png" alt=" " width="720" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;The offer call is not the end of the interview. It is the beginning of a different kind of conversation. Every company you will ever negotiate with has already decided it wants you by the time that call happens. The budget exists. The range exists. The question is only whether you are going to be the person who asks for their share of it.&lt;/p&gt;

&lt;p&gt;Say thank you, then ask for time. Do your research. Come back with a specific number and a one-sentence rationale. Repeat once if necessary. Then commit.&lt;/p&gt;

&lt;p&gt;That is the whole system. It takes 2–5 business days. The median outcome, in my experience and across the people I have coached through it, is somewhere between eight and twenty thousand dollars more per year than the initial offer. Over a five-year tenure, that number is not trivial.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>offers</category>
      <category>womenintech</category>
    </item>
  </channel>
</rss>
