15. __dirname in an ES module
A Node.js script was converted to ESM ("type": "module"). It now fails with "__dirname is not defined". What is the modern fix?
- A.Call require("path").dirname() with no arguments
- B.Use import.meta.dirname (or derive it from import.meta.url)
- C.Declare var __dirname = "." at the top of the file
- D.Rename the file to .ts so Node adds __dirname
Show answer
Answer: B. Use import.meta.dirname (or derive it from import.meta.url)
__dirname and __filename are CommonJS wrapper variables and do not exist in ES modules. Node provides import.meta.dirname and import.meta.filename (Node 20.11+), or you can use fileURLToPath(new URL(".", import.meta.url)).
16. Two copies of one library
The bundle analyzer shows two copies of date-fns, versions 2.x and 3.x. What is the likely cause?
- A.The bundler copies every package twice
- B.The lockfile was committed to Git
- C.date-fns is listed in devDependencies
- D.Different packages depend on incompatible ranges
Show answer
Answer: D. Different packages depend on incompatible ranges
If your app needs date-fns 3 and another dependency needs ^2, npm installs both, and both may be bundled. You can upgrade the other package, use overrides if compatible, or check with npm ls date-fns.
17. Finding why a package is installed
Which command shows which dependency pulled a package into node_modules?
- A.npm audit lodash
- B.npm explain lodash
- C.npm outdated lodash
- D.npm pack lodash
Show answer
Answer: B. npm explain lodash
npm explain (or npm why) prints the dependency path from your package.json down to that package. npm ls lodash shows a tree as well. This is the first step before removing or deduping a heavy package.
18. Does devDependencies keep code out?
A library is in devDependencies but is imported in a frontend component. Will it be in the production bundle?
- A.No; devDependencies are always left out
- B.Only if NODE_ENV is set to development
- C.Only if the library has no default export
- D.Yes; the bundler includes whatever the code imports
Show answer
Answer: D. Yes; the bundler includes whatever the code imports
For frontend apps, bundlers follow import statements, not dependency categories. The split mainly matters for what npm installs (for example with --omit=dev) and for published packages.
19. A 64-bit id changes after parsing
An upstream service sends a 64-bit numeric id. What does this Node.js code print?
const body = '{"id": 9007199254740993}'
console.log(JSON.parse(body).id)- A.9007199254740993
- B.9007199254740992
- C."9007199254740993"
- D.SyntaxError: number too large
Show answer
Answer: B. 9007199254740992
JavaScript numbers are 64-bit floats and can represent integers exactly only up to 2^53 - 1 (9007199254740991). Larger values are rounded to the nearest representable number, so the id silently becomes ...992 and may point to the wrong record. APIs should send large ids, such as Snowflake or bigint keys, as strings.
20. Webhook signature never matches
You verify a payment provider's webhook signature like this, using the correct secret, but verification always fails. Why?
app.post('/webhooks/pay', express.json(), (req, res) => {
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest('hex')
if (expected !== req.get('X-Signature')) return res.sendStatus(400)
// ...
})- A.The signature covers the exact raw request bytes, and re-serialising the parsed JSON changes them
- B.HMAC-SHA256 cannot be computed with Node.js crypto
- C.The secret must be base64-decoded twice before use
- D.The HMAC must be computed over the object with sorted keys, so the code should sort req.body keys before stringifying
Show answer
Answer: A. The signature covers the exact raw request bytes, and re-serialising the parsed JSON changes them
JSON.stringify of the parsed body rarely reproduces the original bytes, so the HMAC differs. Capture the raw body (for example express.raw() on that route) and sign that. Also compare with crypto.timingSafeEqual and check the timestamp to block replays.
21. Pool runs dry after some errors
After a few failed queries, every request hangs waiting for a database connection. What is the bug?
const client = await pool.connect()
const res = await client.query(sql, params)
client.release()
return res.rows
- A.The pool must be created inside each request
- B.client.release must be called before the query
- C.If query throws, release never runs; put it in finally
- D.pool.connect returns a new database every time
Show answer
Answer: C. If query throws, release never runs; put it in finally
When client.query throws, execution skips client.release(), and that connection is never returned. After a few errors the pool is empty and new requests wait forever. Use try/finally, or pool.query for single statements, which releases automatically.
22. Algorithm behind Python and V8 sort
Which algorithm do CPython's list.sort() and the V8 engine's Array.prototype.sort (Chrome, Node.js) use?
- A.Timsort, a stable merge and insertion sort hybrid
- B.Introsort, an unstable quick and heap sort hybrid
- C.Plain heap sort, for its O(1) memory
- D.Randomised quick sort with three-way partitioning
Show answer
Answer: A. Timsort, a stable merge and insertion sort hybrid
CPython has used Timsort for many years, and V8 switched to Timsort in 2018. Timsort finds existing sorted runs and merges them, so it is very fast on partly sorted real data and it is stable.
23. Promise callback versus setTimeout order
In Node.js or a browser, what does this code print?
console.log(1)
setTimeout(() => console.log(2), 0)
Promise.resolve().then(() => console.log(3))
console.log(4)
- A.1 4 3 2
- B.1 2 3 4
- C.1 4 2 3
- D.1 3 4 2
Show answer
Answer: A. 1 4 3 2
The synchronous code prints 1 and 4 first. Then the microtask queue, which holds promise callbacks, is fully drained before the next task, so 3 prints. The setTimeout callback waits in the task (macrotask) queue and prints last.
24. Webhook signature always fails
Stripe webhook verification fails for every event, even though the signing secret is correct. The app registers app.use(express.json()) before all routes. What is the cause?
- A.Stripe webhooks must be received over plain HTTP, because TLS changes the signed payload
- B.The signature covers the raw bytes, which parsing and re-serializing change
- C.Webhook secrets expire every hour
- D.Express cannot read headers on POST requests
Show answer
Answer: B. The signature covers the raw bytes, which parsing and re-serializing change
Signature checks hash the exact bytes that were sent. Once express.json() parses the body, you no longer have those bytes, and JSON.stringify will not reproduce them exactly. Register the webhook route with express.raw({ type: "application/json" }) before the JSON parser and pass that buffer to the verify function.
25. One report slows every request
An Express API has a route that builds a large report in a synchronous loop taking 3 seconds. While it runs, even the health check times out. Why?
- A.Express handles only one route per file
- B.The database connection pool is too small
- C.Node runs JavaScript on one main thread, so the loop blocks the event loop for everyone
- D.The health check route is missing async
Show answer
Answer: C. Node runs JavaScript on one main thread, so the loop blocks the event loop for everyone
While synchronous code runs, Node cannot pick up any other callback, so all requests on that process wait. Move CPU-heavy work to worker_threads or a background job queue and return a job ID or stream the result. This is also why you should use the async versions of functions like bcrypt.hash.
26. Matrix jobs cancelled early
A workflow tests on Node 20, 22 and 24 using a matrix. When the Node 20 job fails, the other two are cancelled, hiding whether they pass. Why?
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [20, 22, 24]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "${{ matrix.node }}" }
- run: npm ci && npm test- A.All matrix jobs share one runner, so one failure stops the whole runner.
- B.actions/setup-node cancels the other Node versions when one of them fails.
- C.fail-fast defaults to true, cancelling the other matrix jobs; set fail-fast: false.
- D.You must add needs: between matrix entries so they run independently.
Show answer
Answer: C. fail-fast defaults to true, cancelling the other matrix jobs; set fail-fast: false.
GitHub Actions cancels in-progress and queued matrix jobs when any one fails, because fail-fast is true by default. Setting fail-fast: false lets every combination finish, which is useful for seeing the full compatibility picture. Each matrix entry runs as a separate job on its own runner.
27. Merge conflict in package-lock.json
Two branches both added npm packages, and merging them gives a large conflict in package-lock.json. What is the cleanest way to resolve it?
- A.Delete the lockfile and stop committing it to Git
- B.Fix package.json, then rerun npm install to rebuild it
- C.Pick all of one side's lockfile changes by hand
- D.Mark the lockfile as a binary file in .gitattributes
Show answer
Answer: B. Fix package.json, then rerun npm install to rebuild it
The lockfile is generated, so hand-merging thousands of lines is error-prone. Fix package.json so it contains both sets of dependencies, then let npm rebuild the lockfile and commit it. Removing the lockfile would make builds non-reproducible.
28. Focusing audit on shipped code
`npm audit` lists 80 issues, most in build and test tools that never ship. You want a gate that focuses on code that runs in production. What helps?
- A.Delete the lockfile so npm audit finds fewer packages
- B.Run npm audit --omit=dev for the gate, and track dev issues apart
- C.Turn off npm audit in CI
- D.Move all dev dependencies into dependencies
Show answer
Answer: B. Run npm audit --omit=dev for the gate, and track dev issues apart
--omit=dev limits the report to dependencies installed in production, which is where runtime risk lives. Dev tools still matter, since a compromised build tool can tamper with the build, so track them with a lower priority. Deleting the lockfile breaks reproducibility.