This is the multi-page printable view of this section. Click here to print.
Tech Interviews
- 1: FE
- 1.1: React Interview
- 2: BE
- 2.1: Node.js Interview
- 3: System Design
1 - FE
What you need for a Frontend (FE) tech interview:
Core Skills
- Strong understanding of HTML, CSS, and JavaScript (ES6+)
- Deep knowledge of at least one modern frontend framework (React, Vue, Angular)
- Familiarity with state management (Redux, Context API, Vuex, etc.)
- Experience with REST APIs and/or GraphQL
- Responsive and accessible web design principles
- Component-based architecture and modular code
Coding & Problem Solving
- Ability to solve algorithm and data structure problems in JavaScript
- Comfort with live coding and whiteboard exercises
- Debugging and performance optimization skills
Tooling & Workflow
- Experience with build tools (Webpack, Vite, etc.) and package managers (npm, yarn)
- Version control with Git
- Testing (unit, integration, E2E with Jest, Testing Library, Cypress, etc.)
Soft Skills
- Clear communication and ability to explain technical concepts
- Code review and collaboration experience
Interview Preparation
- Practice common FE interview questions (see the Q&A section)
- Build and review small projects or coding challenges
- Be ready to discuss trade-offs, best practices, and recent trends in frontend development
1.1 - React Interview
Most Probable React Interview Questions (2025) — with thorough answers
Curated and ordered by likelihood based on common interview patterns for React engineers in 2024–2025. Answers are practical, detailed, and include pitfalls, examples, and “when to use” guidance.
1) What is the difference between functional and class components?
- Class components use ES6 classes, have lifecycle methods
(componentDidMount, etc.), and use
this.stateandthis.setState. - Functional components are plain functions. With hooks (since React 16.8),
they can use state (
useState), effects (useEffect), and more. They are now the standard for new React code.
Example:
// Class component
class MyComponent extends React.Component {
state = { count: 0 };
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
{this.state.count}
</button>
);
}
}
// Functional component
function MyComponent() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
2) What’s the difference between props and state?
| Aspect | Props | State |
|---|---|---|
| Definition | Data passed from parent to child | Data managed within the component |
| Mutability | Immutable (read-only in child) | Mutable (via setState/useState) |
| Ownership | Owned by parent component | Owned by the component itself |
| Updates | Parent re-renders to pass new props | Component calls setState to update |
| Use case | Configuration, data flow down | Interactive data, component memory |
Key principles:
- Props flow down (unidirectional data flow) - parent controls child behavior
- State is local - encapsulated within component unless lifted up
- Derive from props when possible - don’t copy props to state unnecessarily
// ❌ Bad: Copying prop to state (gets out of sync)
function User({ name }) {
const [userName, setUserName] = useState(name);
// userName won't update if parent changes name prop
}
// ✅ Good: Use prop directly or with useMemo for derivation
function User({ name }) {
const displayName = name.toUpperCase();
return <div>{displayName}</div>;
}
3) Why shouldn’t you mutate state directly?
Directly mutating state (e.g., state.value = 123) in React is a common
mistake. React relies on detecting changes to state objects to know when to
re-render components. If you mutate state directly, React may not detect the
change, leading to bugs and UI not updating as expected. Instead, always use the
state updater function (e.g., setValue(newValue)) or return a new object/array
when updating state.
Example:
// ❌ Bad: direct mutation
state.items.push(newItem);
setState(state);
// ✅ Good: create new array
setState({ ...state, items: [...state.items, newItem] });
4) How does React rendering work? What triggers re-renders and how do you avoid unnecessary ones?
- A component re-renders when its state changes or when its parent re-renders and passes new props (new identities).
- React compares element trees (reconciliation) and updates the DOM minimally.
- To avoid unnecessary re-renders:
- Use React.memo for pure child components.
- Stabilize function and object props via useCallback/useMemo.
- Split large contexts; avoid putting high-churn data in a single provider.
- Derive UI from state instead of duplicating state.
- Virtualize long lists (react-window).
5) What is the Virtual DOM and how does React use it?
- The Virtual DOM (VDOM) is a lightweight JavaScript representation of the actual DOM. It’s an in-memory tree of React elements.
- How React uses it:
- When state/props change, React creates a new VDOM tree
- React compares (diffs) the new tree with the previous one (reconciliation)
- React calculates the minimal set of DOM changes needed
- React batches and applies only those changes to the real DOM
Benefits:
- Performance: Batch updates and minimize expensive DOM operations
- Abstraction: Write declarative code without manual DOM manipulation
- Cross-platform: Same reconciliation algorithm works for React Native, React Three Fiber, etc.
Common misconception: VDOM isn’t always faster than direct DOM manipulation. For simple updates, direct DOM can be faster. VDOM’s value is in developer productivity and handling complex UIs declaratively.
// You write declarative code:
<button onClick={() => setCount(count + 1)}>{count}</button>
// React handles the imperative DOM updates:
// button.textContent = newCount;
6) What are keys in lists and why are they critical?
- Keys give list items stable identities between renders, allowing React to efficiently determine which items were added, removed, or reordered during reconciliation.
- React uses keys to match old vs new children efficiently. Without keys (or with duplicate keys), React may incorrectly reuse components, leading to state bugs and performance issues.
- Best practices:
- Use stable, unique IDs from your data (e.g., database IDs)
- Avoid array indexes when items can be reordered, filtered, or inserted/removed
- Never use random values (Math.random()) - keys must be stable across renders
- Common bugs with wrong keys:
- Input values appearing in wrong items after reordering
- Component state not properly resetting when items are replaced
- Animations/transitions breaking
// ❌ Bad: using index as key in dynamic list
{
items.map((item, i) => <Item key={i} {...item} />);
}
// ✅ Good: using stable unique ID
{
items.map((item) => <Item key={item.id} {...item} />);
}
7) How do you debug a React component that renders incorrectly?
- Check props and state: Use React DevTools to inspect the component tree and see current props/state values.
- Add console.log: Log props, state, and key variables inside the render or effect functions.
- Simplify the component: Temporarily remove logic or children to isolate the problem.
- Check dependencies: Ensure useEffect/useMemo/useCallback dependencies are correct.
- Check keys: For lists, make sure keys are unique and stable.
- Look for warnings: Read the browser console for React warnings or errors.
8) What is the difference between server-side rendering (SSR) and client-side rendering (CSR)?
- SSR: The HTML is generated on the server for each request, so the user sees content faster and SEO is better. After loading, React hydrates the page to make it interactive.
- CSR: The browser loads a mostly empty HTML file and JavaScript, then React renders everything on the client. First paint is slower, but navigation can be faster after the initial load.
When to use:
- SSR: For SEO, fast first paint, sharing links, or dynamic content.
- CSR: For apps where SEO is not important, or for highly interactive dashboards.
9) What is Context? When to use it vs Redux Toolkit, Zustand, or React Query?
- Context removes prop drilling for relatively stable global values (theme, locale, auth user, feature flags).
- Avoid using one giant context for rapidly changing values (causes wide re-renders). Split contexts or use a state library.
- Redux Toolkit: predictable, centralized state with good devtools; best for complex client state and cross-cutting concerns.
- Zustand/Jotai/Recoil: minimal APIs and fine-grained subscriptions; good for simpler or performance-focused stores.
- React Query/SWR: manages server state (caching, refetching, retries). Don’t put server data in Redux by default.
10) Controlled vs uncontrolled inputs. When to choose each?
- Controlled: value is driven by state and updated via onChange. Best for validation, conditional UI, and single source of truth.
- Uncontrolled: DOM holds value (ref). Simpler for basic forms, large forms where full control adds overhead, or file inputs.
// Controlled
const [v, setV] = useState('');
<input value={v} onChange={(e) => setV(e.target.value)} />;
// Uncontrolled
const r = useRef(null);
<input ref={r} defaultValue="hi" />;
11) What are custom hooks and why would you create one?
A custom hook is a JavaScript function whose name starts with use and that can
call other hooks. Custom hooks let you extract and reuse stateful logic between
components (e.g., form handling, data fetching, subscriptions) without
duplicating code or using HOCs.
Example:
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
return width;
}
12) What is React.memo and when would you use it?
React.memo is a higher-order component that memoizes a functional component.
It prevents unnecessary re-renders by shallowly comparing props. Use it for pure
components that render the same output for the same props, especially in large
lists or performance-sensitive trees.
Example:
const MyComponent = React.memo(function MyComponent(props) {
// ...
});
13) How do you optimize performance in React applications?
- Use
React.memofor pure components. - Use
useMemoanduseCallbackto avoid unnecessary recalculations and re-renders. - Virtualize long lists (e.g., with
react-window). - Code-split with
React.lazyandSuspense. - Debounce or throttle expensive operations.
- Avoid unnecessary state in high-level components.
- Profile with React DevTools and browser performance tools.
14) How and when to use useMemo and useCallback? What can go wrong?
- useMemo memoizes expensive computations; useCallback memoizes function identities passed to children.
- Use them when profiling shows unnecessary re-renders or costly calculations.
- Pitfalls: overuse (adds complexity), wrong dependencies (stale results), and assuming they always improve performance. Measure first.
15) Data fetching in React: useEffect vs React Query/SWR. Which is better and why?
- Raw useEffect is fine for simple fetches but you must handle caching, stale data, retries, deduplication, background revalidation, and error states manually.
- React Query/SWR handle these concerns out of the box, improving UX and reliability. They also support optimistic updates and infinite queries.
16) What are React Fragments and why use them?
- Fragments let you group multiple children without adding extra DOM nodes.
Use
<React.Fragment>or the shorthand<>...</>.
Why use them:
- Avoid unnecessary wrapper divs that can break CSS layouts (flexbox/grid)
- Keep DOM cleaner and improve performance
- Required when returning multiple elements from a component
// ❌ Bad: extra div in DOM
return (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);
// ✅ Good: no wrapper element
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);
// With keys (use full syntax)
{
items.map((item) => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.desc}</dd>
</React.Fragment>
));
}
17) useRef — what is it and when do you use it?
useRef is a React Hook that returns a persistent, mutable object with a
.current property. This object stays the same between renders. Updating
ref.current does not cause a re-render, so refs are ideal for storing
values that need to survive across renders but shouldn’t trigger UI updates.
When to use useRef:
- To access and interact with DOM elements directly (e.g., focusing an input, measuring size, scrolling).
- To keep mutable values like timers, interval IDs, or third-party library instances.
- To store the latest value of a prop or callback, avoiding stale closures in event handlers or effects.
Examples:
// Example 1: Keep previous value
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Example 2: Timer ref in TypeScript
function Timer(): JSX.Element {
const intervalRef = (useRef < number) | (null > null);
useEffect(() => {
intervalRef.current = window.setInterval(() => console.log('tick'), 1000);
return () => {
if (intervalRef.current) window.clearInterval(intervalRef.current);
};
}, []);
return <div>Timer running</div>;
}
Key pitfalls:
- Don’t use refs for values that should trigger a UI update—use state for that.
ref.currentis only set after the component mounts (undefined on the server).
Common interview follow-up: If asked how to avoid stale closures in event
listeners, explain that you can store the latest callback in a ref and always
call ref.current from your event handler.
18) What are Higher-Order Components (HOCs)? When to use them vs hooks?
- HOC is a function that takes a component and returns a new component with additional props or behavior. It’s a pattern for reusing component logic.
// Example HOC
function withAuth(Component) {
return function AuthComponent(props) {
const { user, loading } = useAuth();
if (loading) return <Spinner />;
if (!user) return <Redirect to="/login" />;
return <Component {...props} user={user} />;
};
}
// Usage
const ProtectedProfile = withAuth(Profile);
When to use HOCs:
- Legacy codebases already using them
- Need to wrap third-party components
- Enhancing components from libraries you don’t control
When to use hooks instead:
- Hooks are preferred in modern React - more composable, less nesting
- Better TypeScript support
- Easier to understand data flow
// ✅ Better: custom hook
function useAuth() {
const { user, loading } = useAuth();
return { user, loading };
}
function Profile() {
const { user, loading } = useAuth();
if (loading) return <Spinner />;
if (!user) return <Redirect to="/login" />;
return <div>Welcome {user.name}</div>;
}
2 - BE
What you need for a Backend (BE) tech interview:
Core Skills
- Strong understanding of at least one backend language (Node.js, Python, Java, Go, etc.)
- Knowledge of RESTful API design and/or GraphQL
- Database fundamentals (SQL and/or NoSQL)
- Authentication, authorization, and security best practices
- Understanding of networking, HTTP, and web protocols
- Familiarity with cloud platforms or deployment (AWS, GCP, Azure, Docker, etc.)
Coding & Problem Solving
- Ability to solve algorithm and data structure problems
- Comfort with live coding and whiteboard exercises
- Debugging and performance optimization skills
Tooling & Workflow
- Experience with version control (Git)
- Familiarity with testing frameworks (unit, integration, E2E)
- CI/CD basics and deployment pipelines
Soft Skills
- Clear communication and ability to explain technical concepts
- Code review and collaboration experience
Interview Preparation
- Practice common BE interview questions (see the Q&A section)
- Build and review small projects or coding challenges
- Be ready to discuss trade-offs, best practices, and recent trends in backend development
2.1 - Node.js Interview
Most Probable Node.js Interview Questions (2025) — with thorough answers
Curated and ordered by likelihood based on common interview patterns for React engineers in 2024–2025. Answers are practical, detailed, and include pitfalls, examples, and “when to use” guidance.
1) Explain Node.js’s event-driven, non-blocking I/O architecture
Node.js uses an event-driven, non-blocking I/O model. This means operations like reading files, network requests, or database queries do not block the main thread. Instead, Node.js delegates these tasks to the system and continues executing other code. When the operation completes, a callback is triggered.
Benefits:
- Handles many concurrent connections efficiently
- Ideal for I/O-heavy applications (APIs, real-time apps)
Pitfall:
- Not suitable for CPU-bound tasks (see Q5)
Example:
fs.readFile('file.txt', (err, data) => {
if (err) throw err;
console.log(data);
});
// Code below runs immediately, not after file is read
2) What is the Node.js event loop and how does it work?
The event loop is the core mechanism that allows Node.js to perform non-blocking I/O. It continuously checks for pending events (timers, I/O, callbacks) and processes them in phases. When your code schedules async work (e.g., setTimeout, fs.readFile), the event loop ensures callbacks run when the work is done.
Phases:
- Timers (setTimeout, setInterval)
- I/O callbacks
- Idle/prepare
- Poll (new I/O events)
- Check (setImmediate)
- Close callbacks
Example:
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
console.log('sync');
// Output order: sync, timeout OR immediate (order not guaranteed)
3) What is the difference between asynchronous and synchronous programming?
- Synchronous: Code executes line by line; each operation waits for the previous one to finish. Blocking.
- Asynchronous: Operations can start and finish independently; code continues running while waiting for results. Non-blocking.
Example:
// Synchronous
const data = fs.readFileSync('file.txt');
console.log(data);
// Asynchronous
fs.readFile('file.txt', (err, data) => {
console.log(data);
});
console.log('This runs before file is read');
4) How does Node.js handle multiple requests if it’s single-threaded?
Node.js runs JavaScript in a single thread, but uses background threads (via libuv) for I/O. When a request comes in, Node.js delegates I/O work to the system, freeing the main thread to handle more requests. Callbacks are queued and processed by the event loop.
Key point:
- Node.js is single-threaded for JS, but multi-threaded for I/O under the hood.
5) What happens if you run CPU-heavy code in Node.js?
CPU-heavy code (e.g., image processing, large calculations) blocks the event loop, preventing Node.js from handling other requests. This leads to poor performance and unresponsive servers.
Solution:
- Offload CPU work to child processes, worker threads, or external services.
Example:
// Bad: blocks event loop
while (true) {
/* infinite loop */
}
// Good: use worker_threads
const { Worker } = require('worker_threads');
new Worker('./heavy-task.js');
6) What is the EventEmitter class?
EventEmitter is a core Node.js class that enables objects to emit and listen for named events. It’s the foundation for many Node.js APIs (e.g., streams, HTTP server).
Example:
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('greet', (name) => console.log(`Hello, ${name}`));
emitter.emit('greet', 'Alice');
7) Explain middleware in Express.js
Middleware are functions that run during the request-response cycle in Express. They can modify the request/response, end the request, or pass control to the next middleware.
Types:
- Application-level, router-level, error-handling, built-in, third-party
Example:
app.use((req, res, next) => {
console.log('Request received');
next();
});
8) How do you scale Node.js applications?
- Use the cluster module to spawn multiple processes (one per CPU core)
- Use load balancers (e.g., Nginx, PM2)
- Use stateless design and externalize session/state
- Use microservices for large systems
Example:
const cluster = require('cluster');
if (cluster.isMaster) {
for (let i = 0; i < require('os').cpus().length; i++) {
cluster.fork();
}
} else {
// Worker process
require('./server');
}
9) What is the V8 engine?
V8 is Google’s open-source JavaScript engine, written in C++. It compiles JavaScript to machine code for fast execution. Node.js uses V8 to run JavaScript on the server.
Key features:
- Just-In-Time (JIT) compilation
- Used by Chrome and Node.js
10) What are callbacks and what is callback hell?
- Callback: A function passed as an argument to be executed later (e.g., after async work).
- Callback hell: Code becomes deeply nested and hard to read/maintain due to many callbacks.
Solution:
- Use Promises or async/await for cleaner async code.
Example:
// Callback hell
fs.readFile('a.txt', (err, a) => {
fs.readFile('b.txt', (err, b) => {
fs.readFile('c.txt', (err, c) => {
// ...
});
});
});
// Promises
fs.promises
.readFile('a.txt')
.then((a) => fs.promises.readFile('b.txt'))
.then((b) => fs.promises.readFile('c.txt'));
11) What are Streams in Node.js?
Streams are objects for reading or writing data piece by piece (chunks), rather than all at once. Useful for large files, network sockets, etc.
Types:
- Readable, Writable, Duplex, Transform
Example:
const fs = require('fs');
const readStream = fs.createReadStream('file.txt');
readStream.on('data', (chunk) => console.log(chunk));
12) What is the difference between process.nextTick() and setImmediate()?
process.nextTick()queues a callback to run after the current operation, before the event loop continues.setImmediate()queues a callback to run on the next event loop iteration, after I/O events.
Example:
process.nextTick(() => console.log('nextTick'));
setImmediate(() => console.log('immediate'));
console.log('sync');
// Output: sync, nextTick, immediate
13) What are global objects in Node.js?
Global objects are available in all Node.js modules without requiring imports.
Examples: global, process, __dirname, __filename, Buffer,
setTimeout, etc.
Pitfall:
- Avoid polluting the global namespace; prefer module scope.
14) What is the cluster module and how does it enable multi-threading?
The cluster module allows Node.js to create child processes (workers) that share the same server port. Each worker runs in its own process, enabling multi-core CPU usage. Communication is via IPC (inter-process communication).
Example:
const cluster = require('cluster');
if (cluster.isMaster) {
cluster.fork();
} else {
// Worker code
}
15) How do you prevent memory leaks in Node.js?
- Remove event listeners when no longer needed
- Avoid global variables for large objects
- Monitor memory usage (process.memoryUsage())
- Use tools like heapdump, clinic.js, or Chrome DevTools
Example:
emitter.removeAllListeners();
global.bigObject = null;
16) What are the security best practices for Node.js applications?
- Validate and sanitize user input
- Use HTTPS
- Avoid eval and insecure deserialization
- Use environment variables for secrets
- Keep dependencies up to date
- Use security linters (npm audit, snyk)
17) How do you debug Node.js applications?
- Use
console.logfor simple debugging - Use the built-in Node.js debugger (
node inspect) - Use Chrome DevTools (
node --inspect) - Use logging libraries (winston, pino)
- Add breakpoints and step through code
Example:
// Start with inspect
node --inspect-brk app.js
// Open chrome://inspect in Chrome
3 - System Design
This is a placeholder page that shows you how to use this template site.
Information in this section helps your user try your project themselves.
What do your users need to do to start using your project? This could include downloading/installation instructions, including any prerequisites or system requirements.
Introductory “Hello World” example, if appropriate. More complex tutorials should live in the Tutorials section.
Consider using the headings below for your getting started page. You can delete any that are not applicable to your project.
Prerequisites
Are there any system requirements for using your project? What languages are supported (if any)? Do users need to already have any software or tools installed?
Installation
Where can your user find your project code? How can they install it (binaries, installable package, build from source)? Are there multiple options/versions they can install and how should they choose the right one for them?
Setup
Is there any initial setup users need to do after installation to try your project?
Try it out!
Can your users test their installation, for example by running a command or deploying a Hello World example?