In this article
Node.js interview questions that expose the wrong hire before it costs you $240,000
Tech & Infrastructure
Apr 23, 2026
12 min read
Most Node.js interview questions test memorization. The developer reads the answer on the first Google result and you never know. This guide shows you what to actually test, what wrong answers look like, and why most companies discover the problem only after the damage is done.
An engineer wrapped a full database table scan inside a transaction block. The transaction demanded a read/write lock. Because Node.js is single-threaded, the lock paralyzed the primary production database for every user on the platform. The engineer had passed a standard technical interview. They had never understood how the event loop interacts with blocking I/O. Gusto Engineering documented this incident publicly. The cost was not the salary. The cost was the damage done before anyone realized the hire was wrong.
According to SHRM, replacing a specialized technical employee costs between 50% and 200% of their annual salary. For a senior Node.js developer at $120,000 a year, that is up to $240,000. Not counting what they built while they were there. This guide is for the person who needs to catch that before it happens.
Key takeaways
| What you are testing | What the wrong hire does | What the right hire does |
|---|---|---|
| Event loop ordering | Guesses. Gets it wrong confidently. | Explains the phase order without prompting. |
| Stream memory handling | Adds more RAM to the server. | Rewrites using createReadStream with error handling. |
| Async error handling | Adds try/catch and moves on. | Knows the Express version and Node.js version behavior. |
| Idempotency | Has not heard the word. | Implements it and spots the race condition. |
| TypeScript type safety | Uses any everywhere. | Knows why enums break tree-shaking. |
The problem with every Node.js interview guide you have read
Search “Node.js interview questions.” You get GeeksForGeeks. roadmap.sh. InterviewBit. Every one of them is a study guide written for candidates, not for the person running the interview.
“What is the event loop?” lives on the first Google result. So does “What is a Buffer?” and “What is REPL?” A candidate who spent one evening on YouTube can answer all 50 questions on those lists without having written a line of production Node.js in their life.
The result is documented. A hiring manager on r/ExperiencedDevs described watching a candidate with years of stated TypeScript experience fail to locate “the TypeScript option” in their own IDE before the technical portion even began. Not fail the question. Fail to start. This is not rare.
Heavy reliance on AI coding tools like GitHub Copilot has created a cohort of developers who generate functional-looking code but cannot debug a runtime failure, navigate a local environment without assistance, or explain why their own implementation works. They pass standard interviews because standard interviews test the wrong things.
A Gusto Engineering blog post documented what this costs in production. An engineer wrapped a full table scan inside an ActiveRecord transaction block. The transaction demanded a read/write lock on the entire database. Because Node.js is single-threaded, the lock paralyzed the primary production database for every user on the platform. The engineer had passed a standard technical interview. They had never understood how Node.js’s event loop interacts with blocking I/O.
The Node.js interview questions below cannot be answered from a study guide. They require either production experience or the ability to reason correctly about a broken system in real-time.
If you cannot evaluate the answers yourself
Not everyone running this interview is a senior Node.js engineer. If you are a founder, a product manager, or a non-technical hiring manager, you need different signals.
Three proxy questions that require no technical knowledge to evaluate:
“Tell me about a system you built that broke in production. What exactly broke and what did you change?”
You are not evaluating the technical content. You are evaluating whether a real answer exists. Developers who have shipped production systems have specific, sometimes embarrassing stories. Developers who have only built demos say “I haven’t really had major issues” or describe a bug in a local test environment.
According to engineering leadership frameworks documented by First Round Review, the production failure story is one of the highest-signal interview techniques available because it bypasses all theoretical posturing. A senior engineer who has never broken a production system has either never worked at scale or lacks the honesty to admit fault. Both are disqualifying.
“How do you onboard a junior developer joining your team?”
Camille Fournier defines this in The Manager’s Path: senior engineers provide multiplicative value, elevating everyone around them through documentation, mentorship, and tooling. Their output is measured in what the team ships, not what they write personally. A developer whose entire answer focuses on writing their own code is functioning at mid-level regardless of their stated years of experience.
“I need X built. What questions do you have before you start?”
Describe a vague technical requirement and watch what happens. A junior developer agrees to build it. A senior Node.js developer immediately asks about data payload sizes, expected concurrent traffic, geographic distribution of users, and existing infrastructure constraints. The presence of boundary-setting before writing a single line of code is a documented high-confidence signal for senior experience.
Task 1: event loop ordering
Present this code and ask for the exact output in order:
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
process.nextTick(() => console.log('4'));
console.log('5');
The correct output:
1
5
4
3
2
Why: 1 and 5 print first because they are synchronous. The main thread runs to completion before any callback fires. 4 prints before 3 because Node.js drains the process.nextTick queue before processing Promise microtasks. This ordering is documented in the Node.js official event loop specification and has been consistent since v11. 2 prints last because setTimeout, even with 0ms delay, queues in the macrotask phase, which runs after all microtask queues are exhausted.
What the wrong hire says: “1, 2, 3, 4, 5 because setTimeout(0) runs immediately.” Confident. Wrong. They have never shipped asynchronous Node.js at scale.
What the right hire adds unprompted: The V8 behavior changed between Node.js v10 and v11. Production applications that relied on the old ordering broke silently on upgrade. They know this because it happened to them.
Follow-up that separates senior from mid-level: “What happens if you schedule a new process.nextTick call inside a nextTick callback?” Correct answer: it executes in the current tick, not the next one. This can starve the event loop if done recursively. Mid-level developers do not know this. Senior engineers have encountered it in production.
Task 2: stream memory behavior
Present this code:
const fs = require('fs');
const data = fs.readFileSync('10gb-log.txt', 'utf8');
const lines = data.split('\n');
lines.forEach(line => {
if (line.includes('ERROR')) console.log(line);
});
Ask: “This runs on a server processing a 10GB log file. The server crashes in production. What is wrong and how do you fix it?”
The correct diagnosis: readFileSync loads the entire file into V8’s heap before processing starts. 10GB exceeds available RAM. Node.js crashes with an out-of-memory error. Even with enough RAM, the synchronous read blocks the event loop for the entire duration, preventing the server from handling any other requests.
The correct fix:
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('10gb-log.txt'),
crlfDelay: Infinity
});
rl.on('line', (line) => {
if (line.includes('ERROR')) console.log(line);
});
rl.on('error', (err) => {
console.error('Stream error:', err);
process.exit(1);
});
rl.on('close', () => {
console.log('Processing complete');
});
What the wrong hire says: “Add more RAM to the server.” Or: switches to readFile (asynchronous) without understanding that the problem is loading the entire file into memory at all, not whether it blocks.
What the right hire adds unprompted: Mentions backpressure. Knows the default highWaterMark for streams is 64KB. Explains what happens if downstream processing is slower than the stream source. The error handler is included without being asked. A developer who ships a stream without an error handler has not shipped streams in production.
Task 3: async error handling across Express versions
Present this:
const express = require('express');
const app = express();
app.get('/user/:id', async (req, res) => {
const user = await getUserFromDatabase(req.params.id);
res.json(user);
});
app.listen(3000);
Ask: “What happens when getUserFromDatabase throws? Be specific about what happens to the server process.”
What actually happens:
In Express 4 on Node.js 14 or earlier: the rejected promise becomes an unhandled rejection. Express 4 does not catch it. The request hangs. The client times out.
In Express 4 on Node.js 15 or later: Node.js changed the default behavior. Unhandled promise rejections crash the entire process. One bad database call takes down every concurrent user. This is controlled by the --unhandled-rejections flag, which defaults to throw in Node.js 15 and above.
In Express 5, released in 2024: Express natively handles rejected promises from async route handlers. The original code works correctly without any wrapper.
The correct fix for Express 4:
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get('/user/:id', asyncHandler(async (req, res) => {
const user = await getUserFromDatabase(req.params.id);
res.json(user);
}));
What the wrong hire says: Adds try/catch inside the route handler. This is not wrong, but it is not scalable. A developer who proposes wrapping every route manually without mentioning the utility pattern has not shipped Express at scale.
What the right hire adds unprompted: Knows the Express version. Knows the Node.js version behavior change. Mentions that express-async-errors, a real npm package with millions of downloads, solves this by monkey-patching Express. Knows Express 5 makes all of this unnecessary. If they are still running Express 4 in production in 2026, there is a reason.
Task 4: idempotency key implementation
No code given. Verbal only.
“You are building a payment endpoint. Network timeouts mean clients retry the same request. How do you guarantee the same payment is never processed twice? Show me the implementation.”
This is one of the Node.js interview questions with no answer in any study guide because it requires production judgment. Stripe, Adyen, and PayPal all implement idempotency keys as a documented requirement for payment APIs.
The correct implementation:
const redis = require('redis');
const client = redis.createClient();
app.post('/payment', asyncHandler(async (req, res) => {
const idempotencyKey = req.headers['idempotency-key'];
if (!idempotencyKey) {
return res.status(400).json({ error: 'Idempotency-Key header required' });
}
const existing = await client.get(`payment:${idempotencyKey}`);
if (existing) {
return res.status(200).json(JSON.parse(existing));
}
const result = await processPayment(req.body);
await client.setEx(`payment:${idempotencyKey}`, 86400, JSON.stringify(result));
res.status(201).json(result);
}));
The race condition most developers miss: Two requests with the same key arrive simultaneously. Both hit the Redis check before either completes. Both see no record. Both process the payment. Both store the result. The payment runs twice.
The fix is an atomic check-and-set using Redis SET NX with a short-lived lock, or a database-level unique constraint on the idempotency key inside a transaction. According to Stripe’s engineering documentation, the standard TTL for idempotency keys in production payment systems is 24 hours.
What the wrong hire says: “The payment provider handles that.” Incorrect. This has caused documented double-charges in production.
What the right hire adds unprompted: Identifies the race condition without being asked. Proposes the atomic resolution. Discusses what happens if the payment succeeds but the Redis write fails, which is the distributed transaction problem that junior developers do not know exists.
Task 5: TypeScript code review
Present this:
enum UserRole {
Admin = 'ADMIN',
Editor = 'EDITOR',
Viewer = 'VIEWER'
}
function getPermissions(role: UserRole): string[] {
const user: any = getCurrentUser();
return permissionsMap[role];
}
Ask: “What are the production problems with this TypeScript code?”
Why this matters in 2026: According to the State of JavaScript 2025 survey, 40% of developers now write exclusively in TypeScript, up from 28% in 2022. TypeScript proficiency is now a baseline expectation. But TypeScript proficiency has a failure mode: developers who add types without understanding what the compiler actually enforces.
Two production problems:
First: TypeScript enums emit actual runtime code, specifically an Immediately Invoked Function Expression. This violates TypeScript’s foundational design principle of type erasure. Enums inflate bundle sizes, disrupt tree-shaking in bundlers, and are incompatible with Node.js v22’s --experimental-strip-types flag, which explicitly rejects enums unless a second flag is added. The correct replacement is a const object with as const assertion.
Second: any on getCurrentUser() defeats the entire type system for everything downstream. It is the TypeScript equivalent of turning off your seatbelt. Every downstream function inherits the any type silently. This is an immediate code review failure.
What the wrong hire says: Defends enums as the correct TypeScript pattern for role constants. Has not read the TypeScript specification or the Node.js v22 documentation.
What the right hire adds unprompted: Knows that enums generate IIFEs at runtime. Knows that the Node.js v22 --experimental-strip-types flag rejects them. Proposes the correct replacement immediately:
const UserRole = {
Admin: 'ADMIN',
Editor: 'EDITOR',
Viewer: 'VIEWER',
} as const;
type UserRole = typeof UserRole[keyof typeof UserRole];
What you are really looking at
Five tasks. Sixty to seventy-five minutes. What the results tell you:
A developer who gets all five at senior level has shipped production Node.js. They have caused a production failure, fixed it, and built systems around preventing the next one.
A developer who gets three at mid-level and fails two is probably honest about their experience. Good candidate for a mid-level role. Not the person you want owning the architecture.
A developer who fails three or more but answers confidently throughout is the expensive one. They will be in your codebase for months before the damage is visible. Then you spend $240,000 finding out.
Now here is the part of this guide that most companies skip.
To run these Node.js interview questions correctly, you need a senior Node.js engineer who already knows all five answers. Someone who can recognize the difference between a confident wrong answer and a correct one. Someone who can probe the follow-ups. Someone who can score the idempotency race condition explanation and tell you whether the candidate actually understood it or just guessed correctly.
Most companies do not have that person. They are trying to hire that person.
The bottleneck nobody talks about
This is the actual problem. You cannot evaluate a senior Node.js developer without a senior Node.js developer. And you need a senior Node.js developer. So you run the interview blind, rely on gut feel and portfolio, and find out three months later whether you made the right call.
The Standish Group’s CHAOS Report documents that 31% of software projects are cancelled before completion and 52% are delivered late, over budget, or with reduced functionality. The average cost overrun for challenged projects is 189% of the original budget. Skill mismatch is a primary driver.
You already know this. You have felt it.
There is a different path. Not a shortcut. A structure that removes the bottleneck entirely.
Meduzzen’s Node.js developers are not candidates you evaluate. They are engineers who have already been evaluated. Already tested on exactly these scenarios. Event loop mechanics, stream handling, async error containment, idempotency patterns, TypeScript depth. Working with Meduzzen for one, two, five years. Some for longer.
The developer who arrives at your project has shipped production Node.js. Has caused a production failure. Has fixed it. Has the failure story.
$35 an hour through an EU-registered entity. Matched in 48 hours. No recruitment cycle. No 42-day average time-to-hire. No $240,000 risk on a candidate who passed the interview and cannot debug the system.
See how Meduzzen vets and places Node.js developers.
Resume red flags to catch before the technical screen
GitHub contribution graphs with pixel art or perfect uniform density. Tools like gitfiti and ghdecoy generate thousands of fake backdated commits by setting the GIT_AUTHOR_DATE environment variable. If the graph spells a word, shows recognizable pixel art, or displays a perfectly uniform dense grid that stops abruptly, it is scripted. Open the underlying repositories. If they contain only automation scripts and no readable application code, the history is fake.
Five or more years of Node.js experience with no framework opinion. Senior engineers have strong opinions about Express versus Fastify versus NestJS because they have operated under real production constraints that forced a choice. See our Node.js framework comparison for what those opinions should sound like. No opinion means no real constraints. No real constraints means no production experience at meaningful scale.
Claims TypeScript expertise, portfolio uses any throughout. Ask to see a repository before the screen. More than three uses of any in a production codebase is a consistent code review failure signal, regardless of the justification.
No production failure story. If “Tell me about a system you broke” produces a vague answer or a reference to a bug found in local testing, the candidate has not shipped production systems. This is the single most reliable non-technical signal available.
What you need to know about Node.js in 2026 that the study guides miss
Three skill areas missing from standard Node.js interview questions three years ago that are now baseline requirements:
LLM API integration and streaming. Dice Insights data from late 2025 shows that 50% of US tech job postings now require AI skills, an increase of 98% year-over-year. Senior Node.js engineers must know how to implement Server-Sent Events for streaming LLM responses: setting Content-Type: text/event-stream, Connection: keep-alive, and an AbortController on the underlying LLM request that fires when the client disconnects via req.on('close'). Missing the abort controller causes LLM generation to continue silently after the user navigates away. The API tokens keep billing. This is a documented production cost overrun.
npm supply chain awareness. In September 2025, a coordinated attack injected self-replicating worms into over 500 npm packages via compromised maintainer accounts, documented by GitHub’s security blog. PCI DSS v4.0.1 Requirement 6.3.2, enforceable from March 2025, now requires a complete inventory of all third-party software components in production, effectively mandating SBOM generation via npm sbom. Senior engineers know the difference between npm install and npm ci, understand lockfile poisoning, and know why a clean npm audit does not mean a secure dependency tree.
Bun and Deno awareness. Bun has reached a 16% market adoption rate according to Socket.dev community data. Any Node.js candidate in 2026 should have an opinion. A junior candidate advocates for rewriting production services in Bun based on benchmarks. A senior engineer knows that Bun passes approximately 90% of Node.js’s compatibility test suite, and that the remaining 10% includes C++ native addons (certain image processing libraries, specific database drivers) that cause silent runtime crashes. The correct senior answer: use Bun for local development toolchains to replace nodemon and ts-node, keep Node.js for production until ecosystem parity is confirmed.
Frequently asked questions
Five production-relevant tasks: event loop output ordering with nextTick and Promise microtask priority, stream memory management for files that exceed available RAM, async error handling behavior differences across Express 4 and 5 and Node.js 14 through 22, idempotency key implementation with race condition identification, and TypeScript type safety including enum runtime behavior and the any type problem. None of these can be answered from a study guide. All of them require production experience or production-level reasoning.
Three proxy questions that require no technical knowledge: ask for a specific production failure story and evaluate whether a real answer exists, ask how they onboard junior developers to measure whether they provide multiplicative or additive value, and describe a vague technical requirement to see whether they set boundaries before agreeing to build. These signals separate developers who have shipped from developers who have demoed. For everything beyond that, working with a staff augmentation partner who pre-vets means you never have to run the technical screen blind.
The U.S. Department of Labor estimates a bad hire costs at minimum 30% of first-year earnings. SHRM estimates total replacement cost at 50% to 200% of annual salary. For a senior Node.js developer at $120,000, that is $60,000 to $240,000. Standish Group CHAOS Report data shows challenged software projects average 189% of their original budget in cost overruns. The developer who fails the idempotency task but interviews well is not a $120,000 risk. They are a $240,000 risk.
60 to 75 minutes for a thorough screen covering the five tasks and 2026-specific questions. Anything under 45 minutes does not generate enough signal to distinguish senior from mid-level. The idempotency and TypeScript tasks each require at least 15 minutes to probe past the surface answer into real architectural judgment.
LLM API streaming patterns with abort controller handling on client disconnect, npm supply chain security including the difference between npm audit and actual supply chain risk detection, SBOM generation for PCI DSS compliance, and Bun and Deno runtime awareness including documented production incompatibilities. A senior engineer who cannot speak to any of these is operating on a 2022 knowledge baseline.
That is the honest situation for most non-technical founders and product managers. You cannot evaluate a senior Node.js developer without a senior Node.js developer to run the screen. Meduzzen solves this by pre-vetting every developer through this framework before any client sees a profile. Senior Node.js engineers at $35/hr, matched in 48 hours, through an EU-registered entity with EU employment law compliance built in. See how it works.