Skip to main content

Engineering practices

How to Interview a Senior Node.js and TypeScript Engineer

A practical interview process for senior Node.js and TypeScript engineers, with a scorecard, realistic exercises, strong signals, and red flags.

Written by Aminul Islam Alvi15 min read

A senior Node.js and TypeScript interview should answer one question: can this engineer make a production backend safer and easier to change?

It should not reward the person who remembers the most utility types, event loop phase names, or framework decorators. Those details can be useful, but recall is not the job. The job is understanding an uncertain problem, designing a clear boundary, handling failure, delivering a change, and helping other engineers work with it afterward.

I would build the interview around evidence from that work. Give the candidate a small but believable service problem. Let them ask questions, read code, identify risk, and improve part of the design. Change one constraint and see whether their reasoning changes with it.

This guide describes the interview I would run. It complements my broader guide to hiring a Senior Software Engineer, but focuses on the places where Node.js and TypeScript experience should become visible.

Define the evidence before the questions

Start with the service this person will actually own. A senior engineer joining a high-volume API needs different depth from one joining a small internal platform, even when both job descriptions say Node.js and TypeScript.

Write down three outcomes expected during the first six months. They might be:

  1. Make a webhook-backed billing workflow safe under retries and partial failure.
  2. Reduce API latency without hiding correctness problems behind a cache.
  3. Move a loosely typed service to stricter TypeScript without stopping product delivery.
  4. Improve tests and observability around a queue that is difficult to support.
  5. Help a team split an oversized module without creating unnecessary services.

Now turn those outcomes into interview evidence. If the job involves webhook reliability, the candidate should reason about idempotency, concurrency, transactions, and recovery. If the job involves modernising TypeScript, they should show how types move uncertainty to system boundaries instead of covering it with assertions. If the job includes on-call ownership, they should be able to investigate a production symptom with incomplete information.

This step prevents an interview panel from testing whatever each interviewer happens to enjoy. It also makes the process fairer. The U.S. Office of Personnel Management's guidance on structured interviews recommends predetermined, job-related questions and a common rating scale so candidates have the same opportunity to provide evidence.

Do not require exact framework knowledge unless the framework itself is a genuine constraint. A strong Fastify engineer can learn NestJS. Experience making an external side effect idempotent, finding event loop delay, or evolving a database contract transfers across frameworks.

Build a Node.js and TypeScript scorecard

Use the same scorecard for every candidate. Five areas are usually enough:

AreaEvidence to look for
TypeScript and API boundariesModels domain states clearly, treats external input as untrusted, and knows where static types stop helping
Node.js runtime judgementUnderstands asynchronous I/O, event loop blocking, bounded concurrency, cancellation, and process lifecycle
Data and reliabilityReasons about transactions, duplicate delivery, retries, partial failure, migrations, and recovery
Testing and operationsChooses tests by risk and includes logs, metrics, traces, rollout, and rollback in the design
Senior ownershipClarifies the problem, explains tradeoffs, controls scope, communicates uncertainty, and improves team understanding

Define the scale before the interview. I prefer four ratings because they make interviewers choose a side:

  1. No evidence: The answer did not demonstrate the skill, even after a follow-up.
  2. Below the role: The candidate recognised part of the issue but needed substantial direction or proposed unsafe defaults.
  3. Meets the role: The candidate found the important risks, made a workable decision, and explained how they would verify it.
  4. Strong evidence: The candidate connected multiple constraints, adapted when facts changed, and improved the problem beyond the immediate fix without losing scope.

Write examples for scores two and four in advance. For runtime judgement, a two might identify a blocking operation but only suggest adding more application instances. A four might first confirm event loop delay, isolate the responsible workload, distinguish CPU work from asynchronous I/O, propose a bounded alternative, and define a measurement for the change.

Avoid combining everything into an intuitive "senior" score. A charming architecture discussion should not erase weak evidence around data safety. Record what the candidate said and did, then rate it.

Use an interview loop that resembles the job

A useful process can fit into two focused sessions after an initial conversation.

The first session can cover code and debugging in about 75 minutes:

  1. Ten minutes to explain the repository and let the candidate explore.
  2. Twenty minutes to review a small TypeScript workflow.
  3. Twenty-five minutes to change or test one part of it.
  4. Fifteen minutes to investigate a production symptom.
  5. Five minutes for the candidate to summarise what they would do next.

The second session can cover system design and collaboration in about 60 minutes. Use one service problem from your domain. Spend the final part on a real project the candidate has delivered and ask how their decisions worked in production.

Share the format ahead of time. Let candidates use their normal editor, documentation, and ordinary development tools. If engineers on your team use AI assistance, define whether candidates may use it and how you will assess verification. Tool restrictions create noise unless operating without those tools is genuinely part of the role.

Use a small repository that starts successfully and has a fast test command. Do not waste interview time on installation problems. The exercise should contain enough existing code to reveal reading and change habits, but not so much that success depends on discovering a hidden file.

Watch the process as well as the result. Does the candidate run the failing test? Do they inspect callers before changing a public type? Do they ask which behavior is contractual? Do they make the smallest safe change, or rebuild the surrounding architecture to display patterns?

Test TypeScript at the boundaries

TypeScript expertise is not the ability to produce the most elaborate generic. It is the ability to make invalid states and uncertain data visible without making ordinary code painful to use.

Ask the candidate to follow one value from an untrusted boundary into the domain. Good boundaries include an HTTP body, environment variable, database result, queue message, webhook payload, or third-party API response.

External data should not become trusted merely because someone wrote as PaymentEvent. TypeScript describes what the program believes at compile time; it does not validate a JSON payload at runtime. The TypeScript handbook presents unknown as the appropriate type for a value whose shape is not yet known and shows how control-flow checks narrow it to a usable type.

Useful questions include:

  1. Where should this value be unknown, and where should it become a domain type?
  2. What must be checked at runtime?
  3. Would a discriminated union make an impossible state harder to represent?
  4. Which type assertions are justified, and which ones hide missing validation?
  5. How would this API evolve without forcing a large unsafe migration?
  6. Does a generic improve the caller's experience, or only move complexity into the type system?

A strong candidate will not insist that every value needs a branded type or a schema library. They will match precision to risk. A payment state deserves more care than the sort direction for an internal table. They should also recognise that a clean domain type does not remove the need to handle an old database row or a newer event producer.

Test how they respond to compiler friction. Senior engineers do not reach for any at the first difficult integration. They also do not spend hours defeating the compiler to preserve an abstraction that the team no longer understands. They find the source of uncertainty, contain it, and leave the common path readable.

Test Node.js as a production runtime

Move beyond the phrase "Node.js is single-threaded." Ask what the runtime means for a real service under load.

One useful scenario is this:

API latency rises from 80 milliseconds to four seconds whenever several customers import large files. Database time looks normal, CPU is high, and adding await did not help. What would you investigate?

A strong answer begins with evidence. The candidate may ask for event loop delay, CPU profiles, request sizes, memory behavior, and the exact work performed during import. They should understand that async does not make CPU-heavy JavaScript non-blocking. Node's own guidance explains that long callbacks on the event loop delay other clients and can harm both throughput and security; the goal is to keep work for each turn small or move appropriate work away from the event loop.

The eventual solution depends on the workload. It could be streaming instead of buffering, bounded queue workers, worker threads for CPU-heavy JavaScript, a child process, a native asynchronous API, smaller batches, or a product limit. The senior signal is not naming every option. It is measuring first and choosing an option that matches the failure.

Use follow-up scenarios to expose adjacent judgement:

  1. A route calls 5,000 external URLs with Promise.all. How would you protect the service and the provider?
  2. A request is cancelled, but its database and API work continues. Where should cancellation propagate?
  3. A deploy stops the process while jobs are running. What does graceful shutdown require?
  4. Memory grows slowly and falls after a restart. How would you separate a leak from legitimate cache or traffic growth?
  5. A promise rejects outside the request path. How will the team learn about it, and what state may now be uncertain?

Good answers discuss limits, timeouts, cleanup, backpressure, and ownership of asynchronous work. Weak answers add retries everywhere, create unbounded parallelism, or assume more instances will correct a broken per-process behavior.

Do not turn the discussion into event loop trivia. Knowing the exact ordering of every queue is valuable for a narrow class of bugs. The broader hiring signal is whether the candidate can connect runtime behavior to latency, correctness, and recovery.

Use one small code review exercise

Code review gives a better senior signal than asking someone to build an API from an empty file. A real senior engineer will spend a great deal of time understanding and improving code that already exists.

Give the candidate this simplified webhook handler and explain that duplicate delivery is normal:

type PaymentEvent = {
  id: string
  orderId: string
  type: 'payment.succeeded'
}

type Dependencies = {
  events: {
    has(id: string): Promise<boolean>
    markProcessed(id: string): Promise<void>
  }
  orders: {
    markPaid(orderId: string): Promise<void>
  }
  email: {
    sendReceipt(orderId: string): Promise<void>
  }
}

export async function handlePaymentWebhook(body: unknown, deps: Dependencies) {
  const event = body as PaymentEvent

  if (await deps.events.has(event.id)) return

  await deps.orders.markPaid(event.orderId)
  await deps.email.sendReceipt(event.orderId)
  await deps.events.markProcessed(event.id)
}

The task is not "find every mistake." Ask the candidate to identify the most important production risks, choose one improvement, and describe the tests they would add.

There is plenty to discuss:

  1. The assertion accepts malformed external data without validation.
  2. The separate has and markProcessed calls create a check-then-act race.
  3. Failure after markPaid can cause the whole sequence to run again.
  4. A database transaction cannot make the external email send atomic.
  5. The code does not show which errors should trigger provider retry or permanent rejection.
  6. There is no visible context for logs, metrics, or support investigation.

Do not demand one perfect redesign. The available data store, provider delivery contract, email guarantees, and existing order model all affect the answer. A strong candidate will ask about those constraints before choosing between a unique event insert, a transactional state change, an outbox, a separate receipt job, or another design.

Change one fact after the candidate proposes a solution. Say that two application instances can receive the same event concurrently, or that the email provider sometimes times out after accepting a message. See whether the design changes. This is much closer to senior work than grading the code against a hidden answer key.

If you ask the candidate to edit the code, keep the expected change small. Runtime validation plus focused tests may be enough. So may replacing the check-then-act interface with one atomic operation. Ask what remains unsafe afterward. Senior engineers should be able to ship an incremental improvement without pretending it solves everything.

Ask about data, failure, and operations

Framework fluency can make a demo look complete while the dangerous parts remain unanswered. Use the system design discussion to follow data through failure.

For example:

Design an endpoint that accepts a CSV import of customer records. Files can be large, validation errors must be downloadable, users should see progress, and retrying an upload must not create duplicates.

Let the candidate drive. Useful areas to explore include:

  1. The synchronous boundary: what the request accepts, stores, and returns.
  2. Work scheduling: queue semantics, concurrency limits, and backpressure.
  3. Data identity: how rows are matched and what "duplicate" means.
  4. Failure: partial batches, poison messages, retry limits, and manual recovery.
  5. Security: file limits, content handling, tenant isolation, permissions, and sensitive values.
  6. Operation: progress, structured logs, metrics, tracing, alerts, and support tools.
  7. Delivery: schema migration, compatibility, staged rollout, and rollback.

Ask the candidate to choose where consistency matters. "Use a transaction" is not a complete answer when the workflow spans a database, object storage, a queue, and notifications. You want to hear which local state transition can be atomic, how the rest of the workflow resumes, and which operations must be idempotent.

Then reduce the scale. Tell them the product has 200 customers and two backend engineers. A senior candidate should be willing to simplify. A modular service with a database-backed job may be a better choice than a new event platform. Architecture maturity includes knowing what the team can operate.

Ask how they would test the design. Look for a deliberate mix: focused unit tests for parsing rules, integration tests for database constraints and transactions, contract tests around external messages, and a small number of end-to-end tests for the critical workflow. The candidate should be able to connect every expensive test to a failure worth catching.

Score senior engineering behaviour

Technical correctness is necessary, but seniority becomes clearest in how the candidate handles ambiguity and other people.

Ask for one past project that went wrong in production. Follow the story closely:

  1. What did you believe before the incident?
  2. Which evidence changed your diagnosis?
  3. What did you do to reduce customer harm first?
  4. Which decision was yours?
  5. How did the system or team change afterward?
  6. What would you do differently now?

Strong candidates can separate facts from assumptions. They name their contribution without claiming the whole team's work. They discuss the mistake without making a colleague the villain. Most importantly, their story includes a changed safeguard, test, alert, runbook, design, or working agreement. Experience is useful when it changes future behavior.

Look for these patterns throughout the interview:

  1. They ask about users, load, failure cost, and team constraints before choosing tools.
  2. They state assumptions and revise them when you add information.
  3. They distinguish a safe first change from a more complete future design.
  4. They explain technical ideas at the level the listener needs.
  5. They consider the engineer who will debug the system six months later.
  6. They know when to ask security, product, data, or platform specialists for help.

Treat red flags as missing or negative job evidence, not personality differences. Concern is justified when a candidate repeatedly trusts type assertions at external boundaries, ignores duplicate delivery, proposes unlimited concurrency, treats tests as a coverage number, or cannot explain how a release was observed. A quiet candidate who pauses before answering is not a red flag.

Give one follow-up before scoring a concern. The candidate may have misunderstood the constraint. Record the answer, not the interviewer's impression of confidence.

Make the decision from evidence

Have every interviewer submit notes and scores before the group discussion. This protects the panel from anchoring on the first confident opinion.

During the review, examine each scorecard area separately. Ask interviewers to cite a specific answer, code change, or decision. "They felt senior" is not evidence. "They found the idempotency race, asked about the database constraint, and changed their design when two consumers were introduced" is evidence.

Set the threshold before interviewing. A candidate does not need the highest score everywhere. They do need to meet the areas that protect your product's critical risks. For a billing platform, weak reliability judgement may be disqualifying even when TypeScript skill is excellent. For a platform migration, the team may accept limited experience with one queue library if the candidate shows strong transferable reasoning.

End with three questions:

  1. Can this person own the six-month outcomes in the role brief with reasonable support?
  2. What is the strongest evidence for that conclusion?
  3. What risk would remain if we hired them, and can the team responsibly support it?

The purpose of the process is not to prove that your panel is clever. It is to give a capable engineer a fair chance to demonstrate how they work and to give your team enough evidence to make a responsible decision.

If you need someone to lead production Node.js and TypeScript work, you can review my TypeScript and Node.js engineering experience or discuss the role with me.

Frequently asked questions

Common questions about interviewing senior Node.js and TypeScript engineers

What questions should I ask a senior Node.js engineer?

Use scenarios about production behavior: event loop delay, bounded concurrency, duplicate messages, partial failure, process shutdown, memory growth, and slow dependencies. Ask how the candidate would investigate before asking for a solution. Their measurements and tradeoffs are more useful than memorised runtime facts.

What questions should I ask a senior TypeScript engineer?

Ask the candidate to model a real domain state and trace untrusted data into it. Discuss runtime validation, narrowing, assertions, API evolution, and whether a generic helps callers. Look for types that clarify the program without turning the type system into a second application.

Should a senior candidate complete a live coding exercise?

A short code review or debugging exercise is useful when it resembles the role. Let the candidate use a normal editor and explain the scoring criteria. Avoid puzzle problems and large take-home projects. Offer reasonable adjustments for candidates who need another format.

Does the candidate need experience with our exact Node.js framework?

Usually not. Exact framework experience matters when the team has an urgent or specialised constraint. Otherwise, assess transferable skills around HTTP boundaries, asynchronous work, data, failure, testing, and operation. A strong engineer can learn familiar framework conventions.

How do I tell a senior engineer from a mid-level engineer in this interview?

A senior engineer usually handles a wider problem without losing the critical details. They clarify constraints, make failure visible, choose a proportionate design, plan delivery and observation, and leave the team better able to maintain the result. Judge those behaviours against your role, not years of experience.

How long should the technical interview take?

Two focused sessions of about 60 to 75 minutes each are usually enough when the questions are structured. One can cover code and debugging; the other can cover system design and past work. A longer process rarely compensates for unclear scoring.

Node.jsTypeScriptTechnical interviewsSenior software engineer