<?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: Martin</title>
    <description>The latest articles on DEV Community by Martin (@moputa).</description>
    <link>https://dev.to/moputa</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%2F4065327%2Fe7511207-7757-4c52-af26-9d87a6e544be.png</url>
      <title>DEV Community: Martin</title>
      <link>https://dev.to/moputa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/moputa"/>
    <language>en</language>
    <item>
      <title>How to talk about trade-offs without sounding like you are hedging</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Fri, 28 Aug 2026 12:50:00 +0000</pubDate>
      <link>https://dev.to/moputa/how-to-talk-about-trade-offs-without-sounding-like-you-are-hedging-3eia</link>
      <guid>https://dev.to/moputa/how-to-talk-about-trade-offs-without-sounding-like-you-are-hedging-3eia</guid>
      <description>&lt;p&gt;Nuance is the thing that gets you levelled up, and hedging is the thing that gets you levelled down. They sound almost identical from the outside, and the difference is entirely structural.&lt;/p&gt;

&lt;p&gt;Ask a junior engineer whether to use SQL or NoSQL and you get an answer. Ask a senior engineer and you often get "well, it depends", which is correct, and delivered badly it costs them the round.&lt;/p&gt;

&lt;p&gt;The problem is not the nuance. It is the order. Hedging leads with the uncertainty and never arrives at a decision. Judgement leads with the decision and then shows the uncertainty around it. Same knowledge, opposite impression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why hedging reads badly
&lt;/h2&gt;

&lt;p&gt;An interviewer is trying to answer one question: would I trust this person to make a call without me in the room. A candidate who lists options without choosing has actively failed to demonstrate the thing being assessed, no matter how well they understand the options.&lt;/p&gt;

&lt;p&gt;There is a second, less obvious cost. Refusing to commit removes the interviewer's ability to go deeper. They cannot probe a decision you did not make, so the conversation stays shallow, and shallow conversations produce mid-level scores by default.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A candidate who says it depends and stops has told the interviewer nothing except that they know it is complicated. Everyone at this level knows it is complicated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The four-part structure
&lt;/h2&gt;

&lt;p&gt;This works for almost any technical choice you will be asked about, and it takes about twenty seconds to deliver.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Commit.&lt;/strong&gt; Name what you would actually ship. One sentence, no preamble.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Justify.&lt;/strong&gt; Give the specific reason, tied to the constraints in the question rather than to general virtue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost.&lt;/strong&gt; Say what you are giving up. Every choice loses something and naming it is the seniority signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger.&lt;/strong&gt; State the condition that would change your mind, and ideally what you would watch for it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that all the nuance from "it depends" is present. It is simply arranged behind a decision instead of in place of one.&lt;/p&gt;

&lt;h4&gt;
  
  
  Would you use a relational database or a document store for this?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Junior answer:&lt;/strong&gt; It really depends on the use case. Relational is good for structured data with relationships and transactions, document stores are better for flexible schemas and horizontal scaling. There are trade-offs both ways, so it depends what matters most for the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Senior answer:&lt;/strong&gt; I would start relational. The data here is clearly relational, orders belong to users and contain items, and I want transactions when money is involved rather than reinventing them. What I give up is schema flexibility, so every shape change is a migration, and that is a real cost on a fast-moving product. I would revisit if we hit write volume one primary cannot take, and the thing I would watch is write latency at peak rather than total row count.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The first answer is a textbook. The second is a decision, and the interviewer can now push on it, which is exactly what you want.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the justification right
&lt;/h2&gt;

&lt;p&gt;Most weak justifications are weak because they are general. "It scales better" is not a reason, it is a property. The strong version ties the choice to something in the question.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weak:&lt;/strong&gt; it is more performant. &lt;strong&gt;Strong:&lt;/strong&gt; the read path is a hundred to one against writes here, so I optimise for reads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak:&lt;/strong&gt; it is more maintainable. &lt;strong&gt;Strong:&lt;/strong&gt; this team is four people with no dedicated operations, so I want the option that is boring to run at 3am.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak:&lt;/strong&gt; it is industry standard. &lt;strong&gt;Strong:&lt;/strong&gt; we already run it, and the operational knowledge is worth more here than any feature difference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak:&lt;/strong&gt; it is more flexible. &lt;strong&gt;Strong:&lt;/strong&gt; the requirements changed twice in the last quarter, so I want the shape that survives a third change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Team size, existing stack, deadline pressure and operational maturity are all legitimate reasons and they are the ones real engineers actually use. Interviewers recognise them immediately as the voice of someone who has shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming the cost is the whole move
&lt;/h2&gt;

&lt;p&gt;If you take one habit from this piece, take this one. Candidates who present only the upside sound like they are selling something. Candidates who name the downside unprompted sound like they have lived with the consequence.&lt;/p&gt;

&lt;p&gt;It is also tactically useful. The cost you name is usually the thing the interviewer was about to probe, so volunteering it moves the conversation onto ground you have already thought through, and it makes the rest of the answer more credible by demonstrating you are not overselling.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The sentence to practise&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"What that costs me is X." Attach it to every technical decision you describe for a week and it becomes automatic. It is the single highest-return phrase in a senior interview.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  When you genuinely need more information
&lt;/h2&gt;

&lt;p&gt;Sometimes the honest answer really is that you cannot choose yet. That is fine, and there is a way to say it that still demonstrates judgement.&lt;/p&gt;

&lt;p&gt;Name the specific fact that would decide it, say what you would pick under each branch, and then commit to a default. "The deciding factor is whether these writes need to be transactional across entities. If they do, relational, no question. If they genuinely do not, the document store buys us real flexibility. Without knowing, I default to relational, because that mistake is cheaper to unwind." You have shown the analysis and still made a call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the pushback
&lt;/h2&gt;

&lt;p&gt;Commit to something and the interviewer will very likely argue with it. That is a good sign. It means there is now something to push on.&lt;/p&gt;

&lt;p&gt;Two failure modes, both common. Folding instantly suggests the position was never reasoned. Refusing to move suggests you would be exhausting to work with, which is the more expensive conclusion of the two. What works is engaging with the specific counter-argument, and then holding or conceding with a stated reason.&lt;/p&gt;

&lt;p&gt;"You are right that this breaks if the write volume is much higher than I assumed. If that is the constraint, I would go the other way" is a strong sentence. It shows the original position had conditions attached, which is what having judgement means. The same instinct is what &lt;a href="https://practicedepth.com/blog/system-design-interview-what-interviewers-score" rel="noopener noreferrer"&gt;system design rounds&lt;/a&gt; spend most of their time measuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What if I commit to something and it is wrong?
&lt;/h3&gt;

&lt;p&gt;You will find out, and adjusting cleanly in front of the interviewer scores better than never having committed. A wrong-but-reasoned position with a stated trigger is far more informative than a survey of options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does this apply when I genuinely have no experience with either option?
&lt;/h3&gt;

&lt;p&gt;Yes, with the uncertainty stated. Reason from properties you do understand, pick the one you could operate, and say plainly which parts you would need to verify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it arrogant to be this decisive?
&lt;/h3&gt;

&lt;p&gt;Not when the cost and the trigger are attached. Arrogance is an unconditional claim. This structure is explicitly conditional, it just leads with the conclusion.&lt;/p&gt;




&lt;p&gt;Trade-off reasoning only shows up when something pushes back on your answer. Run an architecture-flavoured session and see whether your decisions survive the second question. &lt;a href="https://practicedepth.com/app/practice/playbook" rel="noopener noreferrer"&gt;Practise defending decisions&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React interview questions seniors actually get asked</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Tue, 25 Aug 2026 14:51:00 +0000</pubDate>
      <link>https://dev.to/moputa/react-interview-questions-seniors-actually-get-asked-4b9b</link>
      <guid>https://dev.to/moputa/react-interview-questions-seniors-actually-get-asked-4b9b</guid>
      <description>&lt;p&gt;Senior React interviews are rarely about API surface. They circle a handful of areas where a candidate either understands the rendering model or is pattern-matching, and the follow-ups find out which within about two minutes.&lt;/p&gt;

&lt;p&gt;Nobody senior gets asked to list the lifecycle methods. What they get asked is why a page re-renders more than it should, why an effect fired twice, and why the app feels slow when every request is fast.&lt;/p&gt;

&lt;p&gt;Here are the areas those questions come from, and what a strong answer contains.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually causes a re-render
&lt;/h2&gt;

&lt;p&gt;The most common senior React question in any form, and the one that most reliably separates candidates.&lt;/p&gt;

&lt;p&gt;A component re-renders when its state changes, when its parent re-renders, or when a context it consumes changes. That is the whole list. The part people get wrong is the second one: a child re-renders because its parent did, regardless of whether its props changed at all. Props are not an input to the decision, they are only an input to what the render produces.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why is this component re-rendering when its props have not changed?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Junior answer:&lt;/strong&gt; Because something in its props is changing identity between renders, so React sees a new value and re-renders it. Wrapping it in memo and memoising the props usually fixes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Senior answer:&lt;/strong&gt; By default it re-renders because its parent did, and props do not come into it. Identity only matters once you wrap it in memo, at which point a new object or function prop defeats the comparison, which is why memo so often changes nothing. Before reaching for memo I would find out why the parent is rendering that often, since that is usually the actual bug. On the last one of these I looked at, a context was holding a new object every render, so every consumer rendered on every keystroke.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The follow-up is always what memo actually compares. Answer one gets stuck there.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Effects, and why they are the biggest source of bugs
&lt;/h2&gt;

&lt;p&gt;Expect at least one effect question. The strong framing is that an effect is for synchronising with something outside React: a subscription, a timer, an imperative browser API. Most of the time, a bug that arrives with an effect is a sign the work did not belong in one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Derived state in an effect.&lt;/strong&gt; Computing state from props inside an effect causes an extra render and a frame of stale UI. Compute it during render instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The stale closure.&lt;/strong&gt; An effect captures the values from the render it ran in. Miss a dependency and it quietly reads an old value forever, which is the single most common React bug in production code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing cleanup.&lt;/strong&gt; Subscriptions, intervals and in-flight requests need a cleanup function, or you get leaks and out-of-order responses overwriting newer ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetching in an effect without cancellation.&lt;/strong&gt; Two quick navigations and the slower response wins. Interviewers ask about this specifically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can explain why an effect that fetches needs to handle its own obsolescence, you are well past the median candidate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keys
&lt;/h2&gt;

&lt;p&gt;Looks like a beginner topic, is not. Everyone knows keys should be stable and not the array index. Far fewer can say what actually goes wrong.&lt;/p&gt;

&lt;p&gt;Keys tell React which element in the new list corresponds to which element in the old one. With index keys, deleting the first item means every element shifts, so React reuses the wrong instances: state stays attached to the wrong row, inputs keep the wrong value, animations play on the wrong element. The list still looks right until the components hold state, at which point the bug is bizarre and hard to trace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EditableRow&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;row&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// index key: identity shifts on delete&lt;/span&gt;
&lt;span class="p"&gt;))}&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EditableRow&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;row&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// stable identity survives reorder&lt;/span&gt;
&lt;span class="p"&gt;))}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bug that makes this concrete: remove the first row and the text input's value follows the wrong item, because index keys made React reuse the wrong instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where state should live
&lt;/h2&gt;

&lt;p&gt;Architecture questions in a React interview are usually really state-ownership questions. Expect some version of how you would structure state in a large application.&lt;/p&gt;

&lt;p&gt;The answer that reads as senior starts by separating server state from client state. Data that lives in a database and is cached in the browser has completely different needs from whether a dropdown is open: it needs revalidation, deduplication and staleness handling, none of which a general state container gives you. Once you say that out loud, the question of which global state library to use mostly evaporates, which is exactly the point being tested.&lt;/p&gt;

&lt;p&gt;Then colocate the rest. State should live at the lowest common ancestor of the components that use it, and lifting everything to the top is a performance and maintenance problem disguised as tidiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance, properly
&lt;/h2&gt;

&lt;p&gt;The weak answer to any performance question is a list of memoisation hooks. The strong answer starts with diagnosis, because the three causes have different fixes and applying the wrong one wastes a sprint.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rendering too often: a context or a parent updating more than it should. Fix the source, then memoise if it still matters.&lt;/li&gt;
&lt;li&gt;Rendering too much at once: a large list. Virtualise it. No amount of memoisation helps here.&lt;/li&gt;
&lt;li&gt;Not a React problem at all: a slow request, a large bundle, an expensive synchronous computation blocking the main thread.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mention that memoisation is not free, since every memo adds a comparison and a dependency array that can be wrong, and you have said something most candidates do not.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The question behind the question&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When an interviewer asks about performance, they usually want to know whether you measure. Saying which tool you would open and what you would look for first is worth more than any list of optimisations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Rendering on the server
&lt;/h2&gt;

&lt;p&gt;Increasingly standard in senior frontend rounds, and a place where a lot of otherwise strong candidates are vague.&lt;/p&gt;

&lt;p&gt;Be able to say plainly what runs where, what hydration actually is and why a mismatch happens, and why moving data fetching to the server changes the waterfall problem. The interesting follow-up is usually about anything time or locale dependent, since a date formatted with the server's timezone and then re-rendered in the browser's is the classic mismatch, and explaining that specific case demonstrates real experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prepare
&lt;/h2&gt;

&lt;p&gt;Take a component you have written and answer three questions about it. When does it re-render. What would happen if this effect's dependency array were wrong. Where would this fall over with ten thousand rows.&lt;/p&gt;

&lt;p&gt;That exercise finds gaps faster than any question list, because it produces the layer underneath the answer, which is &lt;a href="https://practicedepth.com/blog/surviving-the-follow-up-question" rel="noopener noreferrer"&gt;where the follow-up will land&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to know class components?
&lt;/h3&gt;

&lt;p&gt;Enough to work in a codebase that has them, and to talk about migration. Nobody senior is being tested on lifecycle trivia.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much state library knowledge is expected?
&lt;/h3&gt;

&lt;p&gt;Less than people think. What is expected is the reasoning about which state is server state, which is local, and why that distinction decides the tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are React internals fair game?
&lt;/h3&gt;

&lt;p&gt;Reconciliation and the rendering model, yes, because they explain behaviour you hit in practice. Fiber implementation details, rarely, and an interviewer who leads with them is testing something other than your ability to do the job.&lt;/p&gt;




&lt;p&gt;The fastest way to find out whether your React answers hold up is to be followed up on them. Run a React-focused interview at your level. &lt;a href="https://practicedepth.com/app/practice/interview?focus=react&amp;amp;mode=senior" rel="noopener noreferrer"&gt;Practise a React interview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>programming</category>
      <category>seniordev</category>
    </item>
    <item>
      <title>Node.js interview deep dive: what they are really testing</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Mon, 24 Aug 2026 06:19:00 +0000</pubDate>
      <link>https://dev.to/moputa/nodejs-interview-deep-dive-what-they-are-really-testing-1m6o</link>
      <guid>https://dev.to/moputa/nodejs-interview-deep-dive-what-they-are-really-testing-1m6o</guid>
      <description>&lt;p&gt;Almost every senior Node question is the same question wearing a different hat: do you understand that there is one thread doing your work, and do you know what happens when you block it.&lt;/p&gt;

&lt;p&gt;Node interviews look varied and are not. Streams, timers, clustering, memory, async error handling: pull on any of them and you end up at the event loop, which is why interviewers use them as entry points.&lt;/p&gt;

&lt;h2&gt;
  
  
  The event loop, said properly
&lt;/h2&gt;

&lt;p&gt;The definition everyone gives is that Node is non-blocking and asynchronous. True, and it does not demonstrate anything. The useful version explains the division of labour.&lt;/p&gt;

&lt;p&gt;Your JavaScript runs on one thread. Input and output does not: it is handed to the operating system or to a thread pool, and when it completes, the callback is queued back onto that single thread. That is why thousands of idle connections cost almost nothing, and it is also why one expensive synchronous function stops every other request on that process, not just the one it belongs to.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens if a request handler does something CPU-heavy?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Junior answer:&lt;/strong&gt; It will be slower and could block other requests, so you should avoid heavy computation in Node and move it to a background job or a different service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Senior answer:&lt;/strong&gt; It stalls the whole process for its duration, because there is one thread running JavaScript, so every other in-flight request waits behind it even though they are doing nothing. It also never shows up in local testing where you are the only user. I have watched a synchronous parse of a large payload add a few hundred milliseconds to every concurrent request. The fix is a worker thread if it is genuinely CPU work, or streaming it if the size is the problem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Naming the concurrency effect, and the fact that it hides locally, is what makes this an operator's answer.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the ordering questions come from
&lt;/h2&gt;

&lt;p&gt;Expect at least one question about what runs before what. The point is not trivia, it is whether you know microtasks and macrotasks are different queues.&lt;/p&gt;

&lt;p&gt;A resolved promise callback runs before a timer that was already due, because the microtask queue is drained completely between each phase. The practically important consequence is that a recursive chain of promise callbacks can starve the loop entirely, and the process will look alive while serving nothing. That consequence is the answer worth having, more than the ordering itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streams and backpressure
&lt;/h2&gt;

&lt;p&gt;The most reliable senior discriminator in a Node interview, because it is the thing you only learn by having run out of memory in production.&lt;/p&gt;

&lt;p&gt;Reading a large file into memory and sending it works fine until the file is large or ten users do it at once. Streaming processes it in chunks. But the question interviewers actually care about is what happens when the consumer is slower than the producer, and the answer is that without backpressure the chunks queue in memory until the process dies.&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="c1"&gt;// No backpressure: 'data' keeps firing regardless of whether res can keep up&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Backpressure honoured: pipeline pauses the source when the sink is full&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is not style. The first version buffers the entire response in memory when the client is slow; the second stops reading until the socket drains.&lt;/p&gt;

&lt;p&gt;Say the words 'the writable returns false and you wait for drain', or simply use the pipeline helper and explain what it does for you, and you have answered a question most candidates talk around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async error handling
&lt;/h2&gt;

&lt;p&gt;A favourite because there are several ways to lose an error silently, and each one is a real production incident someone has had.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An async function called without await.&lt;/strong&gt; The promise rejects with nobody listening. The request has already returned 200.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throwing inside a callback.&lt;/strong&gt; Nothing up the stack catches it, because the stack it was called from is long gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A try/catch around a promise you did not await.&lt;/strong&gt; It catches nothing and looks like it does, which is worse than no handler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An error event on a stream with no listener.&lt;/strong&gt; In Node this is not silent, it is fatal, which surprises people.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The strong closing point is what you do at the boundary: an unhandled rejection handler that logs and exits, with a supervisor restarting the process, because a process in an unknown state serving traffic is worse than a process that died cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory leaks
&lt;/h2&gt;

&lt;p&gt;Usually asked as a debugging scenario rather than a definition: memory climbs over hours, what do you do.&lt;/p&gt;

&lt;p&gt;The reasoning matters more than the tooling. Long-lived references are the cause: a module-level cache with no eviction, listeners added per request and never removed, closures held by a timer that outlives what it captured. Take two heap snapshots under load, compare, and look at what grew. Saying that you would compare snapshots rather than guess is most of the signal here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling a Node process
&lt;/h2&gt;

&lt;p&gt;One process uses one core, so scaling means multiple processes, whether through the cluster module, a process manager or separate containers behind a load balancer.&lt;/p&gt;

&lt;p&gt;The follow-up is what breaks when you go from one process to several, and it is always state. In-memory sessions, in-memory caches, in-memory rate limit counters and scheduled jobs all quietly assume there is exactly one of you. Naming that class of bug before being asked is a strong senior signal, and it is the same instinct &lt;a href="https://practicedepth.com/blog/system-design-interview-what-interviewers-score" rel="noopener noreferrer"&gt;system design rounds&lt;/a&gt; are testing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The sentence that carries most Node answers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"There is one thread running my JavaScript, so the question is always what I am doing to it and for how long." Almost every Node follow-up can be reasoned from there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to know libuv internals?
&lt;/h3&gt;

&lt;p&gt;No. You need the model: one JS thread, I/O offloaded, callbacks queued back. Internals only matter when they explain observable behaviour, like why file I/O uses a thread pool and network I/O generally does not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the event loop question not a bit basic?
&lt;/h3&gt;

&lt;p&gt;The question is basic. The follow-ups are not, and they are where the round is decided. Most candidates give a definition and cannot describe the consequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much of this changes with worker threads?
&lt;/h3&gt;

&lt;p&gt;It gives you a way to move CPU work off the main thread, so the model still holds, you just have more than one thread and a message boundary to reason about.&lt;/p&gt;




&lt;p&gt;These questions all have a second layer, and that is where the round is decided. Run a Node-focused interview and find out where yours stops. &lt;a href="https://practicedepth.com/app/practice/interview?focus=node&amp;amp;mode=senior" rel="noopener noreferrer"&gt;Practise a Node.js interview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>backend</category>
      <category>interview</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Database interview questions for backend engineers</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Wed, 19 Aug 2026 11:21:00 +0000</pubDate>
      <link>https://dev.to/moputa/database-interview-questions-for-backend-engineers-4nob</link>
      <guid>https://dev.to/moputa/database-interview-questions-for-backend-engineers-4nob</guid>
      <description>&lt;p&gt;The database section is where senior backend interviews get decided, because it is the hardest area to fake. You either have read a query plan or you have not, and the follow-ups find out quickly.&lt;/p&gt;

&lt;p&gt;Almost everyone can write a join. Far fewer can say why the query is slow, what the index is doing, or what happens when two of these run at the same time. That gap is what the round is measuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indexes, past the definition
&lt;/h2&gt;

&lt;p&gt;The opening question is usually some form of when would you add an index. The definitional answer, that it avoids scanning the table, is table stakes. Three things separate a senior answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Column order in a composite index.&lt;/strong&gt; An index on the filter column plus the sort column only helps if the leading column is the one you filter on. Reverse them and it is close to useless for that query. This is the single most common follow-up in the whole area.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The write cost.&lt;/strong&gt; Every insert and update maintains every index on the table. On write-heavy tables, removing an index can be the optimisation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selectivity.&lt;/strong&gt; An index on a boolean that is true for most rows will often be ignored, because scanning is cheaper than jumping around. Interviewers like this one because it shows you know the planner makes a decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The strongest habit is to talk about the plan rather than about intentions. "I would look at the plan, see whether it is a scan and a sort, add the index, and confirm the plan changed" is a different level of answer from "I would add an index on the where clause".&lt;/p&gt;

&lt;h2&gt;
  
  
  The N+1 query problem
&lt;/h2&gt;

&lt;p&gt;Universal, because every ORM makes it easy and it is invisible in development where the list has four rows.&lt;/p&gt;

&lt;p&gt;Be able to describe both the mechanism and the detection. One query fetches the list, then accessing a related field on each row issues another query, so a hundred rows becomes a hundred and one round trips. Each is fast, the total is not, and it degrades linearly with data you do not control. The tell in production is a request whose latency scales with page size while every individual query looks healthy.&lt;/p&gt;

&lt;h4&gt;
  
  
  An endpoint got slower as data grew. How would you find out why?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Junior answer:&lt;/strong&gt; I would check the slow query log and add indexes to the columns being filtered on, and probably add caching if it is read heavy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Senior answer:&lt;/strong&gt; First I would look at whether one query is slow or many queries are being issued, because those have completely different fixes and the second one hides from a slow query log. If the count scales with the number of rows returned, it is N+1 and the fix is eager loading, not an index. If it is one slow query, then the plan tells me whether it is a scan, a sort or a bad join order. Caching I would consider last, since caching a query I have not understood usually just moves the problem and adds an invalidation bug.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Distinguishing one-slow-query from many-fast-queries is the whole diagnosis, and it is where most candidates skip straight to a fix.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Transactions and isolation
&lt;/h2&gt;

&lt;p&gt;Expect at least one. The trap is reciting the four ACID letters, which demonstrates reading rather than experience.&lt;/p&gt;

&lt;p&gt;What earns credit is being able to describe a concrete anomaly. Two requests read the same balance, both subtract from it, and one update is lost. Or a report reads a row twice inside one transaction and gets different values because another transaction committed in between. Then say which isolation level prevents which, and why nobody runs everything at the strictest level: it costs throughput and increases the chance of deadlocks and aborts.&lt;/p&gt;

&lt;p&gt;The practical follow-up is usually about the read-then-write pattern. Reading a value in application code and writing a computed result back is a race unless the database is enforcing it, which is why a unique constraint, a conditional update or a select for update is the actual fix rather than checking first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locking and deadlocks
&lt;/h2&gt;

&lt;p&gt;Asked as a scenario more often than a definition: your writes intermittently fail with a deadlock error, what is happening.&lt;/p&gt;

&lt;p&gt;Two transactions taking the same locks in different orders is the classic cause, and the practical fix is to make the order consistent, keep transactions short, and not do slow work such as an external API call while holding a lock. Mentioning that last one tends to land, because it is the version of this bug that people actually ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema and migrations
&lt;/h2&gt;

&lt;p&gt;Increasingly common in senior rounds, because it is where database knowledge meets operational judgement.&lt;/p&gt;

&lt;p&gt;Normalise until you have a reason not to, and be able to name your reason for denormalising rather than presenting it as a default. Then the migration question: how do you change a schema without downtime.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the new column or table, nullable, deploying nothing that requires it.&lt;/li&gt;
&lt;li&gt;Deploy code that writes both old and new.&lt;/li&gt;
&lt;li&gt;Backfill in batches, not one large statement that locks the table.&lt;/li&gt;
&lt;li&gt;Deploy code that reads the new path.&lt;/li&gt;
&lt;li&gt;Stop writing the old one, then drop it in a later release.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That expand-and-contract shape is the answer, and knowing that a naive index creation can lock writes for the duration on a large table is the follow-up worth having ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connection pooling
&lt;/h2&gt;

&lt;p&gt;Quietly one of the most useful things to understand, and one of the least discussed in preparation material.&lt;/p&gt;

&lt;p&gt;Connections are expensive and databases cap them, so applications hold a pool. The interview-relevant part is what happens when the pool is exhausted: requests queue, latency climbs everywhere at once, and it looks exactly like the database being slow while the database is comfortable. Being able to describe that misdiagnosis is a strong signal, because most people have chased it in the wrong direction at least once.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The habit that carries this whole area&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Talk about evidence, not intentions. Reading a plan, comparing query counts, checking pool saturation. Candidates who say what they would look at outscore candidates who say what they would change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every one of these topics has a second layer, and the second layer is what gets asked. If you can only give the definition, that is worth knowing before the interview rather than during it, which is &lt;a href="https://practicedepth.com/blog/surviving-the-follow-up-question" rel="noopener noreferrer"&gt;the audit worth running on yourself&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much SQL should I be able to write live?
&lt;/h3&gt;

&lt;p&gt;Joins, aggregation with grouping, and usually a window function or a subquery. Perfect syntax is rarely the point; being able to explain what the query makes the database do is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to know a specific database?
&lt;/h3&gt;

&lt;p&gt;Know one properly and you can reason about the others. Interviewers care much more about depth in one than familiarity with four.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is NoSQL knowledge expected?
&lt;/h3&gt;

&lt;p&gt;Enough to say when you would choose it and what you give up. The strongest answer starts from access patterns, not from the label on the technology.&lt;/p&gt;




&lt;p&gt;Database rounds are decided by the follow-up, not the first answer. Run a database-focused interview and see how far yours goes. &lt;a href="https://practicedepth.com/app/practice/interview?focus=database&amp;amp;mode=senior&amp;amp;utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=organic" rel="noopener noreferrer"&gt;Practise a database interview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>database</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>Security questions every senior backend engineer should handle</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Sun, 16 Aug 2026 14:57:00 +0000</pubDate>
      <link>https://dev.to/moputa/security-questions-every-senior-backend-engineer-should-handle-2n3g</link>
      <guid>https://dev.to/moputa/security-questions-every-senior-backend-engineer-should-handle-2n3g</guid>
      <description>&lt;p&gt;You are not being interviewed as a security engineer. You are being checked for one habit: whether, while describing a system, you notice who could abuse it.&lt;/p&gt;

&lt;p&gt;Security questions rarely arrive labelled. They show up as follow-ups inside an ordinary technical conversation, and the ones that matter come back to a single instinct: treating input as hostile and asking who this trusts and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication and authorisation are different questions
&lt;/h2&gt;

&lt;p&gt;Everyone can define the difference. The interesting part is that authentication bugs are rare and authorisation bugs are everywhere, because authentication is one well-tested code path and authorisation is a decision at every single endpoint.&lt;/p&gt;

&lt;p&gt;The specific failure worth naming is trusting an identifier from the client. An endpoint that fetches a record by an id in the URL, checks that the caller is logged in, and returns it, is broken: the caller is authenticated and has no right to that record. Being able to describe that in one breath is more valuable than any definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Password storage
&lt;/h2&gt;

&lt;p&gt;A near-guaranteed question, and one where a good answer takes fifteen seconds.&lt;/p&gt;

&lt;h4&gt;
  
  
  How do you store user passwords?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Junior answer:&lt;/strong&gt; Hashed and salted, never plain text, using bcrypt rather than something like MD5 or SHA-256 because those are too fast and not designed for passwords.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Senior answer:&lt;/strong&gt; A deliberately slow, memory-hard hash, so bcrypt or argon2, with the work factor tuned to what the login path can afford and raised as hardware gets cheaper. The slowness is the point, because the real threat is offline cracking after a dump rather than anything at login time. Salts are per user and stored alongside the hash, which is what stops one precomputed table covering every account. I would also keep the failure path constant time, since a login that returns measurably faster for an unknown email leaks which accounts exist.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The timing side channel is the follow-up. Volunteering it moves you into a different category of candidate.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sessions and tokens
&lt;/h2&gt;

&lt;p&gt;Expect a discussion of stateless tokens versus server-side sessions, and expect it to be a trade-off question rather than a right-answer question.&lt;/p&gt;

&lt;p&gt;Stateless tokens avoid a lookup on every request, which is why they scale nicely. What they cost you is instant revocation: until it expires, a stolen token is valid, and there is no server-side record to delete. That is the whole trade, and the mature answer is a short access token plus a refresh token, with a deny list only on the paths where the risk justifies the round trip.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never accept the algorithm from the token itself. Pin it server side.&lt;/li&gt;
&lt;li&gt;Verify signature, issuer, audience and expiry, not just that it decodes.&lt;/li&gt;
&lt;li&gt;The payload is readable by anyone holding it. Signed does not mean private.&lt;/li&gt;
&lt;li&gt;Store it somewhere script cannot read it if you can, which usually means an httpOnly cookie rather than local storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Injection, and why parameterisation is the answer
&lt;/h2&gt;

&lt;p&gt;Still asked, still worth answering precisely, because most candidates say escaping when the actual answer is separation.&lt;/p&gt;

&lt;p&gt;A parameterised query does not clean the input, it sends the query and the data separately, so the database never parses user text as SQL. That distinction matters, because escaping is a filter that can be wrong and separation is structural. The same reasoning covers shell commands and any other interpreter: pass arguments, do not build strings.&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="c1"&gt;// injectable: the value becomes part of the parsed statement&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM users WHERE email = '&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// parameterised: the value is never parsed as SQL&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM users WHERE email = $1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first version concatenates user input into the statement. The second sends the statement and the value on separate channels, so nothing in the value can change the query's structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  XSS and CSRF, and why people mix them up
&lt;/h2&gt;

&lt;p&gt;Worth being able to separate cleanly, because candidates routinely give the mitigation for one when asked about the other.&lt;/p&gt;

&lt;p&gt;Cross-site scripting is untrusted content becoming executable in your page, and the defence is contextual output encoding plus a content security policy. Cross-site request forgery is another site causing the browser to make an authenticated request to yours, and the defence is a token the other site cannot read plus same-site cookies. One is about what runs in your page, the other is about who caused a request. If you have XSS, your CSRF defences are irrelevant, since the attacker is already running inside your origin, and saying that shows you understand the relationship rather than the labels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets
&lt;/h2&gt;

&lt;p&gt;Usually asked practically: where do your credentials live. The expected answer is injected at runtime from a secret manager or the platform's environment, never committed, with rotation possible without a code change.&lt;/p&gt;

&lt;p&gt;The follow-up worth preparing is what you do when a secret leaks. Rotate first, then work out the exposure, and assume anything committed to a repository is compromised permanently regardless of whether the commit was later removed. That last point is the one interviewers listen for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limiting and abuse
&lt;/h2&gt;

&lt;p&gt;Increasingly common, particularly for anything with a login form or an expensive endpoint.&lt;/p&gt;

&lt;p&gt;The reasoning to show is what you are limiting on and why. Per IP is easy and weak, since attackers rotate addresses and shared networks hurt real users. Per account protects against credential stuffing against one target. Both, at different thresholds, is usually the honest answer. The good follow-up is where the counter lives once you run several processes, which lands you back on shared state, the same problem &lt;a href="https://practicedepth.com/blog/nodejs-interview-deep-dive" rel="noopener noreferrer"&gt;scaling a Node process&lt;/a&gt; runs into.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is actually being scored&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a checklist. Interviewers are listening for whether you volunteer the abuse case unprompted, the same way you would volunteer a trade-off. Someone who says which endpoint they would attack first, in their own design, has demonstrated the instinct the round is looking for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much security is expected from a normal backend role?
&lt;/h3&gt;

&lt;p&gt;The common classes and a threat-model instinct. Nobody expects exploit development. Everybody expects you to notice that user input reaches a query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I memorise the OWASP Top Ten?
&lt;/h3&gt;

&lt;p&gt;Know the categories and be able to describe two or three properly. Reciting ten labels with no mechanism behind them is transparent and scores badly.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if I have never worked on anything security sensitive?
&lt;/h3&gt;

&lt;p&gt;Say so and reason from the systems you have built. Naming where you would be worried about your own past work is a strong, honest answer.&lt;/p&gt;




&lt;p&gt;Security questions are almost entirely follow-up territory, which is where a definition stops helping. Run a security-focused interview and see where yours ends. &lt;a href="https://practicedepth.com/app/practice/interview?focus=security&amp;amp;mode=senior&amp;amp;utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=organic" rel="noopener noreferrer"&gt;Practise a security interview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>ai</category>
      <category>interview</category>
      <category>security</category>
    </item>
    <item>
      <title>AI engineering interview questions: retrieval, evaluation and everything that breaks</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Fri, 14 Aug 2026 08:37:00 +0000</pubDate>
      <link>https://dev.to/moputa/ai-engineering-interview-questions-retrieval-evaluation-and-everything-that-breaks-62n</link>
      <guid>https://dev.to/moputa/ai-engineering-interview-questions-retrieval-evaluation-and-everything-that-breaks-62n</guid>
      <description>&lt;p&gt;The field is young enough that nobody has ten years of experience, so interviewers weight engineering judgement heavily over tool familiarity. The questions are less about what you have used and more about how you would know it was working.&lt;/p&gt;

&lt;p&gt;Almost every candidate has now built something with a language model. That means the demo no longer differentiates anyone, and interviews have moved to the parts that are hard: making retrieval actually relevant, knowing whether a change made things worse, and handling text you did not write.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieval, past the diagram
&lt;/h2&gt;

&lt;p&gt;Everyone can draw the pipeline: embed the documents, embed the question, find the nearest, put them in the context. Interviewers have seen it. The questions are all about what makes it work badly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chunking.&lt;/strong&gt; Too small and a chunk loses the context that made it meaningful. Too large and the relevant sentence is diluted by everything around it. There is no universal size, and saying so with a reason is better than naming one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic search missing exact matches.&lt;/strong&gt; Nearest-neighbour search is poor at identifiers, error codes and product names, which is why hybrid retrieval with a keyword component so often beats pure embeddings in practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieving the wrong thing confidently.&lt;/strong&gt; Similarity is not relevance. Something always comes back, whether or not anything useful exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale content.&lt;/strong&gt; The index is a copy. What happens when the source changes is an operational question people forget to answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Users say the answers are often wrong. How would you debug that?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Junior answer:&lt;/strong&gt; I would improve the instructions to the model, try a larger model, and adjust the temperature to make it more accurate and less prone to making things up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Senior answer:&lt;/strong&gt; First I would find out whether retrieval or generation is failing, because they need opposite fixes and guessing wastes a week. I would take a sample of the bad answers and look at what was actually retrieved. If the right passage was never retrieved, no amount of instruction tuning will help and the work is in chunking or hybrid search. If the right passage was retrieved and the answer still contradicted it, that is a generation and grounding problem. Before changing anything I would build a small set of question and expected-answer pairs, so I can tell whether the next change helped or just moved the failures somewhere I was not looking.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Splitting retrieval failure from generation failure is the single most useful diagnostic in this area, and most candidates skip straight to the model.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluation, which is the real interview
&lt;/h2&gt;

&lt;p&gt;If there is one topic that separates people who have shipped this from people who have prototyped it, this is it. Non-deterministic output means you cannot rely on the usual assertion-based safety net, and the temptation is to test by trying a few things and feeling good about them.&lt;/p&gt;

&lt;p&gt;What a strong answer describes is a fixed evaluation set built from real failures, run on every change, with results compared rather than eyeballed. Then the honest caveats: automated judging is itself unreliable and needs spot-checking, and an evaluation set drifts away from real usage unless you keep feeding production failures back into it.&lt;/p&gt;

&lt;p&gt;Say that you would add every reported bad answer to the set as a regression case and you have described the practice that actually keeps these systems from decaying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting structured output reliably
&lt;/h2&gt;

&lt;p&gt;A practical question because it is what most production integrations need. The model produces text; your code needs a shape it can rely on.&lt;/p&gt;

&lt;p&gt;The expected answer has three parts: constrain the output where the platform supports it, validate against a schema on the way in rather than trusting it, and have a defined path when validation fails. That last part is where the follow-up goes. Retrying forever is a cost and rate-limit incident waiting to happen, so a bounded retry and then a fallback is the answer that shows you have run this in production.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The framing that lands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Treat model output as untrusted input, exactly like a request body from the internet. Validate it, bound it, and never let it reach anything consequential unchecked. That single sentence answers a surprising number of questions in this area.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Untrusted text and injection
&lt;/h2&gt;

&lt;p&gt;Increasingly asked, and the answers separate people quickly.&lt;/p&gt;

&lt;p&gt;The core problem is that instructions and data arrive through the same channel, so text from a document, a web page or a user message can contain something that reads as an instruction. There is no complete fix through wording alone, and a candidate who claims otherwise has not thought about it hard.&lt;/p&gt;

&lt;p&gt;What actually reduces risk is architectural: keep untrusted content clearly separated from your own instructions, do not grant the system capabilities it does not need, require confirmation before anything consequential or irreversible, and validate any tool call before executing it. The mindset here is the same one a &lt;a href="https://practicedepth.com/blog/security-interview-questions-senior-engineers" rel="noopener noreferrer"&gt;security round&lt;/a&gt; tests, applied to a component that is non-deterministic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost and latency
&lt;/h2&gt;

&lt;p&gt;Frequently the most practical section, and the one where product-minded engineers do well.&lt;/p&gt;

&lt;p&gt;Cost scales with tokens, so long context is a per-request bill, not a one-off design choice. Latency scales with output length in particular, which is why streaming exists and why it changes perceived speed far more than raw model speed. The senior framing is routing: not every request needs the most capable option, and using a cheaper path for the easy majority is usually the largest available win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fine-tuning versus retrieval versus better instructions
&lt;/h2&gt;

&lt;p&gt;A judgement question, and the wrong answer is reaching for fine-tuning first.&lt;/p&gt;

&lt;p&gt;The distinction to draw is between knowledge and behaviour. If the system needs facts it does not have, that is retrieval, and fine-tuning is an expensive way to bake in something that will be stale next month. If it needs a consistent format or style, that is a case for tuning, once you have exhausted cheaper options. Being explicit that you would start with the cheapest reversible change is the signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to know specific frameworks?
&lt;/h3&gt;

&lt;p&gt;Much less than you would think. The field moves fast enough that interviewers weight reasoning about retrieval quality, evaluation and failure handling well above tool familiarity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is machine learning theory expected?
&lt;/h3&gt;

&lt;p&gt;For an AI engineering role, usually not. Embeddings and why similarity is not relevance, yes. Gradient descent, rarely, unless the role is actually a modelling role.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I show experience if my project was small?
&lt;/h3&gt;

&lt;p&gt;Talk about what went wrong and what you measured. A small project with a real evaluation set and a documented failure mode is stronger evidence than a large one with neither.&lt;/p&gt;




&lt;p&gt;This area is almost entirely judgement under follow-up, which is exactly what a focused practice round exposes. &lt;a href="https://practicedepth.com/app/practice/interview?focus=ai-engineering&amp;amp;mode=senior&amp;amp;utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=organic" rel="noopener noreferrer"&gt;Practise an AI engineering interview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>career</category>
      <category>interview</category>
    </item>
    <item>
      <title>How to actually practise for an AI technical screen</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Tue, 11 Aug 2026 12:23:33 +0000</pubDate>
      <link>https://dev.to/moputa/how-to-actually-practise-for-an-ai-technical-screen-56lh</link>
      <guid>https://dev.to/moputa/how-to-actually-practise-for-an-ai-technical-screen-56lh</guid>
      <description>&lt;p&gt;You can be good at technical interviews and still do badly at this one. In recent times, most software engineers have applied for a job only to receive a link that takes them to a platform such as Micro1 or Karat for an initial AI technical screen.&lt;/p&gt;

&lt;p&gt;That is the part people underestimate. An automated screen is not a harder version of a phone screen; it is a different measurement, and most of the preparation that carries you through a human interview quietly stops working. Rapport does nothing. Reading the room does nothing. The recovery move where you notice a flicker of doubt and add "well, depending on the write pattern" does nothing, because nothing flickered.&lt;/p&gt;

&lt;p&gt;What is left is the text of what you said.&lt;/p&gt;

&lt;p&gt;This is a practice routine for that. Not a list of questions to memorise, because memorising questions is the failure mode, but a way to rehearse that matches what the round actually measures.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, the one thing that changes everything
&lt;/h2&gt;

&lt;p&gt;Your answers are scored as a transcript.&lt;/p&gt;

&lt;p&gt;Every piece of advice below follows from that single fact. If you internalise nothing else, internalise that a human who likes you fills in your gaps and an automated score does not. A human hears half an idea, recognises the shape of the rest, and moves on. Automated scoring reads what is there.&lt;/p&gt;

&lt;p&gt;So the bar moves from "did you know it" to "did you say it". Those feel like the same thing from the inside. They are not.&lt;/p&gt;

&lt;p&gt;If you want the full breakdown of what happens minute by minute, we wrote that up separately in &lt;a href="https://practicedepth.com/blog/ai-technical-screens-explained/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch" rel="noopener noreferrer"&gt;what actually happens in an AI technical screen&lt;/a&gt;. This post assumes you know the format and want to get better at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practise out loud. Yes, really out loud
&lt;/h2&gt;

&lt;p&gt;This is the whole routine in one line, and it is the step everyone skips.&lt;/p&gt;

&lt;p&gt;Reading a question and thinking "yeah, I know that" is not practice. It is recognition, and recognition is a much lower bar than production. The gap between knowing something and saying it cleanly, in order, in about ninety seconds, under a timer, is enormous. It is also the entire thing being measured.&lt;/p&gt;

&lt;p&gt;Say your answers to an empty room. Record them on your phone and play them back once, which is unpleasant and worth more than an hour of reading. You will hear three things immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You start sentences you do not finish.&lt;/li&gt;
&lt;li&gt;You say "basically" and "kind of" before every technical claim.&lt;/li&gt;
&lt;li&gt;The strongest part of your answer arrived at the very end, after the listener had already scored the beginning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those are knowledge problems. They are all production problems, and they only fix with reps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Say the mechanism, do not imply it
&lt;/h2&gt;

&lt;p&gt;Experienced engineers imply mechanisms constantly, because with another engineer it is faster and implying it is a status signal. In this round it reads as a gap.&lt;/p&gt;

&lt;p&gt;Compare:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yeah, you would not want to do that synchronously, it would block.&lt;/p&gt;

&lt;p&gt;That call is synchronous, so it blocks the event loop. Node handles requests on a single thread, so while that runs, every other request on that process is waiting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first is what you would say to a colleague. The second is what gets credit. It feels like over-explaining. It is not over-explaining, it is the answer.&lt;/p&gt;

&lt;p&gt;A useful test while rehearsing: after each sentence, ask whether a reader with no context could tell you understood the cause, or only that you knew the conclusion. Conclusions are cheap. Causes are the thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare four projects, not twenty answers
&lt;/h2&gt;

&lt;p&gt;Most people prepare topics. Prepare projects instead.&lt;/p&gt;

&lt;p&gt;Pick four things you genuinely worked on. For each one, be able to state three things without hesitating:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The constraint.&lt;/strong&gt; What actually forced the decision. Not "we chose Postgres because it is reliable", but what property you could not do without.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The alternative you rejected.&lt;/strong&gt; And why it lost. An engineer who cannot name the road not taken usually did not make a decision, they inherited one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One number.&lt;/strong&gt; Request volume, dataset size, latency before and after, team size. One real number does more for your credibility than a paragraph of adjectives.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those three things answer the overwhelming majority of follow-ups, because follow-ups are mostly just "why" pointed at whatever you said last. If you have the constraint, the alternative and the number ready, you have somewhere to go every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drill the why-chain
&lt;/h2&gt;

&lt;p&gt;Here is the drill that changes scores fastest.&lt;/p&gt;

&lt;p&gt;Take one of your four projects. Say your answer out loud. Then ask yourself why, and answer. Then ask why again. Then a third time.&lt;/p&gt;

&lt;p&gt;Almost everyone hits a wall on the third why. That wall is exactly where an automated follow-up will find you, and finding it in your kitchen is considerably better than finding it in the round. You do not need to eliminate the wall, you need to know where it is, so you can say "I do not know, here is how I would find out" deliberately instead of discovering it live.&lt;/p&gt;

&lt;p&gt;Do this on four projects and you have covered most of what a thirty minute screen can reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure every answer the same way
&lt;/h2&gt;

&lt;p&gt;Under pressure, structure is what stops you rambling. Use the same shape every time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The answer, first.&lt;/strong&gt; One sentence. Not a preamble, not a restatement of the question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The mechanism.&lt;/strong&gt; Why that is the answer, stated causally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The trade-off or the limit.&lt;/strong&gt; When it stops being true, or what it costs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That third part is the one that separates candidates, and it is the one people leave out because it feels like undermining yourself. It is the opposite. Naming the limit of your own answer is the clearest available signal that you have actually used the thing rather than read about it.&lt;/p&gt;

&lt;p&gt;Here is the same question with and without it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q: How would you speed up a slow endpoint?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without:&lt;/strong&gt; I would add caching. Probably Redis in front of the database, with a sensible TTL, and that usually takes most of the load off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With:&lt;/strong&gt; First I would find out where the time goes, because caching a slow query hides it rather than fixing it. If it is one N+1 query then an index or a join is the fix and caching is premature. If it is genuinely expensive and read-heavy, then a cache in front, and the real question becomes invalidation: a TTL is fine for data that can be stale for a minute, and not fine for anything a user just edited, because they will refresh and see their own write missing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same knowledge. The second one names a constraint, rejects an alternative, and admits a limit. It also cannot be produced by memorising an answer, which is precisely why it scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  A realistic week
&lt;/h2&gt;

&lt;p&gt;If you have a screen coming up and limited time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Day 1.&lt;/strong&gt; Pick your four projects. Write the constraint, the rejected alternative and the number for each. Twenty minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Days 2 to 4.&lt;/strong&gt; One project per day. Say it out loud, run the three-why drill, record one answer and listen back once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 5.&lt;/strong&gt; Rehearse the answer shape on unfamiliar questions, so structure survives surprise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 6.&lt;/strong&gt; Rest. Cramming the day before produces stiff, scripted answers, which collapse on the first follow-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is roughly two hours total, distributed. Distribution matters more than volume here, because you are building a speaking habit rather than loading facts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What not to bother with
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memorising answers.&lt;/strong&gt; They survive the first question and fall apart on the second, which is the one that counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rehearsing your confidence.&lt;/strong&gt; Confidence with nothing underneath is the single easiest thing for a follow-up to expose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breadth.&lt;/strong&gt; A screen samples a few areas. Being genuinely deep in the four things you actually work with beats being shallow across twelve, every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Practise out loud, say the mechanism instead of implying it, know the constraint and the rejected alternative and one number for four real projects, and find your third-why wall before the round does.&lt;/p&gt;

&lt;p&gt;None of that is about being smarter. It is about making what you already know legible to something that will not meet you halfway.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build &lt;a href="https://practicedepth.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch" rel="noopener noreferrer"&gt;PracticeDepth&lt;/a&gt;, an AI interviewer that asks senior-level questions across sixteen topics, then drills into whatever you just said until it finds your ceiling, and tells you where you actually stand. A practice run is free and takes about twenty minutes. It is a lot less comfortable than reading a list, which is the entire point.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>interview</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Every interview question has a second question behind it. Here are 7</title>
      <dc:creator>Martin</dc:creator>
      <pubDate>Mon, 10 Aug 2026 16:00:42 +0000</pubDate>
      <link>https://dev.to/moputa/every-interview-question-has-a-second-question-behind-it-here-are-7-5fg8</link>
      <guid>https://dev.to/moputa/every-interview-question-has-a-second-question-behind-it-here-are-7-5fg8</guid>
      <description>&lt;p&gt;Interviewers rarely ask for the thing they want to know. They ask for something adjacent, cheap to answer, and easy to score, and then they listen to how you got there.&lt;/p&gt;

&lt;p&gt;That is not a trick. It is the only practical way to run the interview. The stated question has an answer that is freely available and takes an evening to memorise. The question behind it does not, because answering that one requires having been on the wrong side of it at some point.&lt;/p&gt;

&lt;p&gt;Below are seven snippets. Nothing exotic, nothing from a puzzle site. For each one: what gets asked, what is actually being measured, and the follow-up that arrives if you answer only the surface.&lt;/p&gt;

&lt;p&gt;Read the code before you read the commentary. It is more useful that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The method that loses its object
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rates&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Rates&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;EUR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;USD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.09&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// TypeError: Cannot read properties of undefined (reading 'EUR')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Asked:&lt;/strong&gt; why does this throw?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually asked:&lt;/strong&gt; do you know that &lt;code&gt;this&lt;/code&gt; is decided by the call, not by where the function was written?&lt;/p&gt;

&lt;p&gt;The answer that scores says the binding is resolved at call time from the receiver, and &lt;code&gt;map&lt;/code&gt; calls the function with no receiver, so inside a class body (which is strict) &lt;code&gt;this&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt; rather than the global object. Then it says what to do: &lt;code&gt;.map((c) =&amp;gt; rates.lookup(c))&lt;/code&gt;, or bind it, or make &lt;code&gt;lookup&lt;/code&gt; an arrow-valued field so it closes over the instance instead.&lt;/p&gt;

&lt;p&gt;The version that only says "you lost the &lt;code&gt;this&lt;/code&gt; context" is correct and stops one layer short of where the question was aimed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The follow-up:&lt;/strong&gt; and what would the arrow field cost you? (One function per instance rather than one on the prototype, which is fine at almost any scale and worth being able to say out loud.)&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The loop that is slow on purpose, or by accident
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Asked:&lt;/strong&gt; what is wrong with this?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually asked:&lt;/strong&gt; can you tell the difference between sequential-because-it-has-to-be and sequential-by-accident?&lt;/p&gt;

&lt;p&gt;Most people say "use &lt;code&gt;Promise.all&lt;/code&gt;" and that is where it ends. The stronger answer notices this only matters when the calls are independent, then volunteers the cases where the loop is right: each call depends on the last one's result, or the endpoint is rate limited and firing two hundred requests at once gets you a 429 and a slower total, or you are writing rows that have to land in order.&lt;/p&gt;

&lt;p&gt;If you do fan it out, &lt;code&gt;ids.map((id) =&amp;gt; fetchUser(id))&lt;/code&gt; and not &lt;code&gt;ids.map(fetchUser)&lt;/code&gt;, because &lt;code&gt;map&lt;/code&gt; passes the index as a second argument and plenty of functions quietly accept one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The follow-up:&lt;/strong&gt; two hundred ids. Still &lt;code&gt;Promise.all&lt;/code&gt;? (No. That is a concurrency limit, and being able to say so is most of the point of the question.)&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The &lt;code&gt;Promise.all&lt;/code&gt; that leaves money on the floor
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reservation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nf"&gt;chargeCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;reserveStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;sendReceipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Asked:&lt;/strong&gt; what happens if &lt;code&gt;reserveStock&lt;/code&gt; rejects?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually asked:&lt;/strong&gt; do you know what happens to the other two?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Promise.all&lt;/code&gt; rejects on the first rejection, but it does not cancel anything. The card charge is still in flight and will still succeed, and you have just thrown away the only reference to its result. You now have a customer who has paid for stock you never reserved, and no handle on the charge id to refund it.&lt;/p&gt;

&lt;p&gt;The answer that lands names the state you are left in, not just the control flow. Then it says what you would do instead: &lt;code&gt;allSettled&lt;/code&gt; when you need every outcome, sequential when a later step depends on an earlier one committing, and a compensating action for the leg that already succeeded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The follow-up:&lt;/strong&gt; what shows up in your logs when the other two reject afterwards? (Unhandled rejections, because &lt;code&gt;Promise.all&lt;/code&gt; already settled and nothing is attached to them any more.)&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The &lt;code&gt;try&lt;/code&gt; block that catches nothing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;processQueue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;queue failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Asked:&lt;/strong&gt; why is nothing being logged?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually asked:&lt;/strong&gt; do you understand that &lt;code&gt;catch&lt;/code&gt; is about a stack frame, not about a block of text?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;processQueue&lt;/code&gt; is async and is not awaited, so it returns a promise and the &lt;code&gt;try&lt;/code&gt; block completes normally. The rejection arrives later, on a frame that no longer has this handler on it. Adding &lt;code&gt;await&lt;/code&gt; fixes it. So does &lt;code&gt;.catch()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is worth being able to say precisely, because it is the same mechanism behind a &lt;code&gt;catch&lt;/code&gt; that misses a &lt;code&gt;setTimeout&lt;/code&gt; callback, a rejection inside an event listener, and half the "we have logging and we still cannot see the error" incidents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The follow-up:&lt;/strong&gt; what does Node do with that rejection now? (Crashes the process by default since v15, which surprises people who remember the warning.)&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The floats holding money
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;amountPaid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;markAsPaid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Asked:&lt;/strong&gt; what is the bug?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually asked:&lt;/strong&gt; do you know why it happens, and do you know where it stops being theoretical?&lt;/p&gt;

&lt;p&gt;Everyone can recite &lt;code&gt;0.1 + 0.2 !== 0.3&lt;/code&gt;. The interesting part is that binary floating point cannot represent tenths exactly, the error is tiny per operation, and it accumulates over a &lt;code&gt;reduce&lt;/code&gt;. The equality check is where it surfaces, but the corruption happened earlier, which is why a tolerance comparison is a patch and not a fix.&lt;/p&gt;

&lt;p&gt;Store minor units as integers, or use a decimal type your database actually understands, and do the arithmetic there. Say which one you would pick and why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The follow-up:&lt;/strong&gt; so where do you do currency conversion, and at what precision? (There is no clean answer, which is exactly why it gets asked. They want to hear you reason about rounding direction and who absorbs the difference.)&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The timestamp with no timezone
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// schema&lt;/span&gt;
&lt;span class="nx"&gt;scheduledAt&lt;/span&gt;  &lt;span class="nx"&gt;DateTime&lt;/span&gt;

&lt;span class="c1"&gt;// write&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reminder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;scheduledAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-03-29T02:30:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Asked:&lt;/strong&gt; what could go wrong here?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually asked:&lt;/strong&gt; do you know that an instant and a wall-clock time are different types of thing?&lt;/p&gt;

&lt;p&gt;That string has no offset, so it is parsed as local time on whichever machine ran it. Your laptop and the container in Frankfurt disagree. And 02:30 on 29 March 2026 does not exist in most of Europe, because the clocks go from 02:00 straight to 03:00 that morning.&lt;/p&gt;

&lt;p&gt;"Always store UTC" is the memorised answer and it is not always right. An instant, like when a payment settled, is UTC and converted for display. A future wall-clock commitment, like a 09:00 recurring standup, has to be stored as local time plus a zone id, because if the zone changes its rules between now and then, the UTC instant you computed is wrong and the 09:00 is still 09:00.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The follow-up:&lt;/strong&gt; the meeting was booked before a country changed its DST rules. What is in your database now? (This is the whole question. It is why the answer is a zone id and not an offset.)&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The check that loses a race
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existing&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&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;existing&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Asked:&lt;/strong&gt; is this safe?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actually asked:&lt;/strong&gt; do you know that a read and a write are two separate moments?&lt;/p&gt;

&lt;p&gt;Two requests arrive together. Both read, both find nothing, both insert. You now have duplicate users, and everything downstream that assumed one row per email is quietly wrong. It will not reproduce locally, and it will happen the first time a client retries on a timeout.&lt;/p&gt;

&lt;p&gt;The answer that scores does not propose a longer &lt;code&gt;if&lt;/code&gt;. It puts a unique constraint on the column and lets the database be the thing that arbitrates, then either catches the violation and re-reads, or uses an upsert. The database is the only place with a serialisation point. Application code cannot manufacture one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The follow-up:&lt;/strong&gt; what does your API return to the request that lost? (The existing row and a 200, most likely. Which is the same shape as an idempotency key, and that is usually where they take it next.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern, so you can do this yourself
&lt;/h2&gt;

&lt;p&gt;Read those seven again and the second question is always one of a small number of shapes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One level down.&lt;/strong&gt; You said what happens. Now say what makes it happen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you were left holding.&lt;/strong&gt; Not the control flow, the state after the failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When your answer stops being true.&lt;/strong&gt; Every rule has a boundary, and naming yours unprompted is the strongest single move available to you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What it cost.&lt;/strong&gt; You picked something. Something else got worse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can run all four against any answer you would give, on any topic, without knowing what you will be asked. Wherever you run out of road on the second or third pass, that is a gap, and finding it tonight is much cheaper than finding it on a call.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that reading cannot fix
&lt;/h2&gt;

&lt;p&gt;Here is the uncomfortable bit, and the reason lists like this one are less useful than they look.&lt;/p&gt;

&lt;p&gt;Recognising the second question on a page you are reading calmly is not the same skill as producing the answer out loud, in order, in one breath, eleven minutes into an interview, immediately after someone has picked one phrase out of what you just said and asked you about that specifically.&lt;/p&gt;

&lt;p&gt;Those are different skills. Only one of them gets tested. And the gap between them is not closed by reading more, because you cannot find the edge of your own knowledge by consulting it. You find it by being asked, and then being asked again about whatever you just said.&lt;/p&gt;

&lt;p&gt;So: pick one topic you would claim on your CV. Say your answer out loud, to the wall if necessary. Then ask yourself the four shapes above, in order, and keep going until you run out. The place you stop is your interview.&lt;/p&gt;




&lt;p&gt;I build &lt;a href="https://practicedepth.com/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=second-question&amp;amp;utm_content=outro" rel="noopener noreferrer"&gt;PracticeDepth&lt;/a&gt;, an AI interviewer that asks senior-level questions across sixteen topics, then drills into whatever you just said until it finds your ceiling, and tells you where you actually stand. A practice run is free and takes about twenty minutes. It is a lot less comfortable than reading a list, which is the entire point.&lt;/p&gt;

&lt;p&gt;If any of the seven above went a way you did not expect, I would genuinely like to hear it in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>interview</category>
    </item>
  </channel>
</rss>
