Web Developer

$85K–$150K
US Salary Range
30+
Interview Q&As
50+
Code Snippets

Table of Contents

HTML & CSS Fundamentals

Semantic HTML5 Elements

Semantic elements describe their meaning to the browser and developer. They improve accessibility and SEO:

Element Purpose Use Case
<header> Page or section header Logo, nav, introductory content
<nav> Navigation links Main menu, breadcrumbs
<main> Main content (only one per page) Page's primary content
<section> Thematic grouping Chapters, numbered sections
<article> Self-contained content Blog post, forum post, news item
<aside> Sidebar content Related links, ads, callouts
<footer> Page or section footer Copyright, contact, links

CSS Box Model

Every element consists of content, padding, border, and margin (outside in):

/* Content → Padding → Border → Margin */
.box {
  width: 200px;           /* Content width */
  padding: 20px;          /* Space inside border */
  border: 2px solid #ccc; /* Border line */
  margin: 10px;           /* Space outside border */
}

/* Total width = 200 + 40 (padding) + 4 (border) + 20 (margin) = 264px */

CSS Specificity

Selector Type Points Example
Inline style 1000 style="color: red;"
ID selector 100 #header
Class/Pseudo-class 10 .btn, :hover
Element selector 1 div, p, a
!important overrides everything (use sparingly): color: red !important;

Flexbox Complete Guide

.flex-container {
  display: flex;
  flex-direction: row;        /* row | column | row-reverse | column-reverse */
  justify-content: center;   /* Main axis alignment */
  align-items: center;       /* Cross axis alignment */
  flex-wrap: wrap;           /* nowrap | wrap | wrap-reverse */
  gap: 15px;              /* Space between items */
}

.flex-item {
  flex: 1;                  /* shorthand: grow shrink basis */
  flex-grow: 1;             /* Growth factor */
  flex-shrink: 1;           /* Shrink factor */
  flex-basis: 200px;        /* Base size before grow/shrink */
}

CSS Grid

.grid-container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr; /* 3 columns, middle is 2x wider */
  grid-template-rows: auto 200px auto;
  grid-gap: 20px;
  grid-auto-fit: auto-fit;   /* auto-fill | auto-fit */
}

/* Responsive: auto-fit shrinks empty tracks, auto-fill keeps them */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

Responsive Design

/* Mobile-first approach: start with mobile styles */
.container {
  width: 100%;
  padding: 15px;
}

/* Tablet and up */
@media (min-width: 768px) {
  .container { width: 750px; }
}

/* Desktop and up */
@media (min-width: 1024px) {
  .container { width: 960px; }
}

/* Viewport meta tag in <head> */
<meta name="viewport" content="width=device-width, initial-scale=1.0">

CSS Variables & BEM Naming

/* CSS Variables */
:root {
  --color-primary: #6c63ff;
  --color-secondary: #00d4aa;
  --spacing-unit: 8px;
}

.button {
  background: var(--color-primary);
  padding: calc(var(--spacing-unit) * 2);
}

/* BEM: Block__Element--Modifier */
.card { }                    /* Block */
.card__header { }           /* Element */
.card__header--active { }  /* Modifier */

Pseudo-classes vs Pseudo-elements

Type Example Purpose
Pseudo-class (:) :hover, :focus, :nth-child(n) Element state/position
Pseudo-element (::) ::before, ::after Style part of element

CSS Animations

@keyframes slideIn {
  0% {
    transform: translateX(-100%);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

.element {
  animation: slideIn 0.5s ease-in-out 0.2s forwards;
  /* name duration easing delay fill-mode */
}

JavaScript (ES6+) — Deep Dive

var vs let vs const

Keyword Scope Hoisting Reassign Redeclare
var Function Hoisted (undefined) Yes Yes
let Block Hoisted (TDZ) Yes No
const Block Hoisted (TDZ) No No

Closures - Deep Dive

/* Closure: inner function accessing outer function's variables */
function makeCounter() {
  let count = 0; /* Outer scope variable */

  return function() {
    count++; /* Inner function accesses count */
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2

/* Classic loop problem - solved by closure */
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // prints 3,3,3 (var is function-scoped)
}

/* Solution 1: Use let (block-scoped) */
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // prints 0,1,2 ✓
}

/* Solution 2: IIFE closure */
for (var i = 0; i < 3; i++) {
  ((j) => {
    setTimeout(() => console.log(j), 100); // prints 0,1,2 ✓
  })(i);
}

Arrow Functions

/* Arrow functions have lexical 'this' - no arguments object */
const greet = (name) => `Hello, ${name}`;

/* No own 'this' - inherits from parent */
const obj = {
  name: 'John',
  getName: function() {
    const arrow = () => this.name; // 'this' from obj
    return arrow();
  }
};
obj.getName(); // 'John'

Event Loop Explained

/* Call Stack → Microtask Queue → Macrotask Queue */
console.log('1: Start');

setTimeout(() => console.log('4: setTimeout'), 0); // Macrotask

Promise.resolve()
  .then(() => console.log('3: Promise.then')); // Microtask

queueMicrotask(() => console.log('2: queueMicrotask')); // Microtask

console.log('1: End');

/* Output:
   1: Start
   1: End
   2: queueMicrotask
   3: Promise.then
   4: setTimeout
*/

Prototypal Inheritance

/* Prototype chain: instance → Constructor.prototype → Object.prototype */
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  return `${this.name} makes sound`;
};

function Dog(name, breed) {
  Animal.call(this, name); /* Borrow constructor */
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype); /* Set up chain */
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() {
  return `${this.name} barks`;
};

const dog = new Dog('Rex', 'Labrador');
console.log(dog.speak()); // 'Rex barks'

Destructuring & Spread Operator

/* Array destructuring */
const [a, b, ...rest] = [1, 2, 3, 4];
// a=1, b=2, rest=[3,4]

/* Object destructuring */
const { name, age = 18 } = { name: 'John', age: 25 };

/* Spread in array */
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4]; // [1,2,3,4]

/* Spread in object */
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // {a:1, b:2, c:3}

Array Methods Deep Dive

/* map: transform each element */
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2); // [2,4,6]

/* filter: keep elements matching condition */
const evens = numbers.filter(n => n % 2 === 0); // [2]

/* reduce: accumulate into single value */
const sum = numbers.reduce((acc, n) => acc + n, 0); // 6

/* find: first element matching condition */
const found = numbers.find(n => n > 1); // 2

/* every: all elements match condition */
const allPositive = numbers.every(n => n > 0); // true

/* some: at least one matches */
const hasEven = numbers.some(n => n % 2 === 0); // true

Promises & async/await

/* Promise states: pending → fulfilled/rejected */
const fetchData = new Promise((resolve, reject) => {
  setTimeout(() => resolve('data'), 1000);
});

/* Promise chaining */
fetchData
  .then(data => console.log(data))
  .catch(err => console.error(err))
  .finally(() => console.log('done'));

/* async/await is syntactic sugar for Promises */
async function getData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(error);
  }
}

/* Promise utilities */
Promise.all([p1, p2, p3]); /* Wait all, reject if one fails */
Promise.race([p1, p2]); /* Return first settled */
Promise.allSettled([p1, p2]); /* Wait all, return all results */

Deep Clone Approaches

/* Shallow copy - only copies top level */
const original = { a: 1, b: { c: 2 } };
const shallow = { ...original };
shallow.b.c = 3; // Affects original!

/* JSON approach - loses functions, dates, undefined */
const deepClone = JSON.parse(JSON.stringify(obj));

/* structuredClone - handles most types, no functions */
const deepClone2 = structuredClone(obj);

/* Recursive function - handles all types */
function deepClone(obj, map = new WeakMap()) {
  if (map.has(obj)) return map.get(obj);

  if (typeof obj !== 'object' || obj === null) return obj;

  const cloned = Array.isArray(obj) ? [] : {};
  map.set(obj, cloned);

  for (const key in obj) {
    cloned[key] = deepClone(obj[key], map);
  }
  return cloned;
}

React — Complete Reference

JSX to React.createElement

/* JSX syntax */
const element = <div className="container">
  <h1>Hello</h1>
</div>;

/* Compiles to */
const element = React.createElement(
  'div',
  { className: 'container' },
  React.createElement('h1', null, 'Hello')
);

Functional Component with useState

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  /* Functional update pattern - safe for async */
  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </>
  );
}

useEffect Hook

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch('/api/data');
      const result = await response.json();
      setData(result);
      setLoading(false);
    };

    fetchData();

    /* Cleanup function - runs before unmount or re-run */
    return () => {
      /* Cancel request, cleanup listeners */
    };
  }, []); /* Empty deps = runs once on mount */

  if (loading) return <div>Loading...</div>;
  return <div>{JSON.stringify(data)}</div>;
}

Custom Hook Example

function useFetch(url) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let isMounted = true;

    fetch(url)
      .then(res => res.json())
      .then(data => {
        if (isMounted) setData(data);
      })
      .catch(err => {
        if (isMounted) setError(err);
      })
      .finally(() => {
        if (isMounted) setLoading(false);
      });

    return () => {
      isMounted = false; /* Prevents state update on unmount */
    };
  }, [url]);

  return { data, error, loading };
}

useReducer for Complex State

const initialState = { count: 0, error: null };

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    case 'ERROR':
      return { ...state, error: action.payload };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })>+</button>
    </div>
  );
}

useContext & Context API

const ThemeContext = React.createContext('light');

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Header />
      <Content />
    </ThemeContext.Provider>
  );
}

function Header() {
  const { theme, setTheme } = useContext(ThemeContext);

  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Toggle Theme
    </button>
  );
}

useMemo vs useCallback

/* useMemo memoizes computed value */
function ExpensiveComponent({ data }) {
  const filtered = useMemo(() => {
    /* Only runs if 'data' changes */
    return data.filter(item => item.active);
  }, [data]);

  return <List items={filtered} />;
}

/* useCallback memoizes function reference */
function Parent() {
  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []); /* Function reference stays same */

  return <Button onClick={handleClick} />;
}

React.memo & Key Prop

/* React.memo prevents re-render if props unchanged */
const UserCard = React.memo(({ user }) => {
  return <div>{user.name}</div>;
});

/* Key prop helps React identify which items have changed */
function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}

/* DON'T use index as key - breaks component state */
/* BAD: key={index} - causes issues when list reorders */
/* GOOD: key={item.id} - stable across re-renders */

Controlled vs Uncontrolled Components

/* Controlled: React manages input value */
function ControlledInput() {
  const [value, setValue] = useState('');

  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

/* Uncontrolled: DOM manages value, use ref to access */
function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleSubmit}>Submit</button>
    </>
  );
}

React Router v6

import { BrowserRouter, Routes, Route, Link, useNavigate, useParams } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/user/:id" element={<UserDetail />} />
      </Routes>
    </BrowserRouter>
  );
}

function UserDetail() {
  const { id } = useParams();
  const navigate = useNavigate();

  return (
    <div>
      <p>User ID: {id}</p>
      <button onClick={() => navigate('/')}>Back</button>
    </div>
  );
}

Node.js & Express

Express Basics

const express = require('express');
const app = express();

/* Middleware - executes before route handlers */
app.use(express.json()); /* Parse JSON bodies */

/* Basic route handlers */
app.get('/api/users', (req, res) => {
  res.json({ users: [] });
});

app.post('/api/users', (req, res) => {
  const { name, email } = req.body;
  /* Create user */
  res.status(201).json({ id: 1, name, email });
});

app.put('/api/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ id, updated: true });
});

app.delete('/api/users/:id', (req, res) => {
  res.status(204).send();
});

/* Error handling middleware - must have 4 params */
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Internal Server Error' });
});

app.listen(3000, () => console.log('Server running'));

JWT Authentication

const jwt = require('jsonwebtoken');
const SECRET = 'your-secret-key';

/* Generate JWT */
const token = jwt.sign(
  { userId: 123, email: 'user@example.com' },
  SECRET,
  { expiresIn: '24h' }
);

/* Verify JWT middleware */
function authenticateToken(req, res, next) {
  const token = req.headers['authorization']?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'No token' });
  }

  try {
    const decoded = jwt.verify(token, SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    res.status(403).json({ error: 'Invalid token' });
  }
}

/* Protected route */
app.get('/api/protected', authenticateToken, (req, res) => {
  res.json({ user: req.user });
});

REST API Status Codes

Code Status When to Use
200 OK Request successful, returning data
201 Created Resource created successfully
204 No Content Request successful, no response body (DELETE)
400 Bad Request Invalid request data
401 Unauthorized Authentication required/failed
403 Forbidden Authenticated but no permission
404 Not Found Resource doesn't exist
409 Conflict Request conflicts (duplicate email)
500 Server Error Unhandled server error

Input Validation with Zod

const { z } = require('zod');

const userSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
});

app.post('/api/users', (req, res) => {
  try {
    const validated = userSchema.parse(req.body);
    /* Create user with validated data */
    res.status(201).json(validated);
  } catch (err) {
    res.status(400).json({ errors: err.errors });
  }
});

Databases for Web Developers

SQL Basics

/* CREATE TABLE */
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  name VARCHAR(255) NOT NULL,
  email VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

/* INSERT */
INSERT INTO users (name, email) VALUES ('John', 'john@example.com');

/* SELECT */
SELECT * FROM users WHERE email = 'john@example.com';

/* UPDATE */
UPDATE users SET name = 'Jane' WHERE id = 1;

/* DELETE */
DELETE FROM users WHERE id = 1;

/* JOIN */
SELECT u.name, p.title
FROM users u
INNER JOIN posts p ON u.id = p.user_id;

/* GROUP BY */
SELECT u.name, COUNT(p.id)
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id;

Prisma ORM

/* schema.prisma */
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]  /* Relation */
}

model Post {
  id     Int     @id @default(autoincrement())
  title  String
  userId Int
  user   User    @relation("fields: [userId]", "references: [id]")
}

/* Prisma Client usage */
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

/* Create */
const user = await prisma.user.create({
  data: { email: 'john@example.com', name: 'John' },
});

/* Read */
const users = await prisma.user.findMany({
  where: { email: 'john@example.com' },
  include: { posts: true },
});

/* Update */
const updated = await prisma.user.update({
  where: { id: 1 },
  data: { name: 'Jane' },
});

/* Upsert - update or create */
const user = await prisma.user.upsert({
  where: { email: 'john@example.com' },
  update: { name: 'John Updated' },
  create: { email: 'john@example.com', name: 'John' },
});

PostgreSQL Features

Redis for Caching

/* String operations */
SET user:1:name "John" EX 3600 /* Expires in 1 hour */
GET user:1:name

/* Hash operations */
HSET user:1 name "John" email "john@example.com"
HGET user:1 name
HGETALL user:1

/* List operations */
LPUSH notifications:1 "New message"
LRANGE notifications:1 0 10

/* Sorted set - leaderboards, rate limiting */
ZADD leaderboard 100 "user1" 95 "user2"
ZRANGE leaderboard 0 10 WITHSCORES

N+1 Query Problem

/* BAD - N+1 queries */
const users = await prisma.user.findMany();
for (const user of users) {
  const posts = await prisma.post.findMany({
    where: { userId: user.id }
  }); /* 1 + N queries! */
}

/* GOOD - Single query with relations */
const users = await prisma.user.findMany({
  include: { posts: true } /* 1 query with JOIN */
});

Performance & Security

Core Web Vitals

Metric Good Needs Improvement What It Measures
LCP <2.5s >4s Largest content paint
FID/INP <100ms >300ms First input delay / Interaction to Next Paint
CLS <0.1 >0.25 Cumulative Layout Shift

Security: XSS Prevention

/* BAD - XSS vulnerability */
function render(userInput) {
  return <div>{dangerouslySetInnerHTML: { __html: userInput }}</div>;
  /* If userInput = "<img src=x onerror='alert(1)'>", executes JS */
}

/* GOOD - Always escape output */
function render(userInput) {
  return <div>{userInput}</div>;
  /* React automatically escapes - safe */
}

/* Use Content-Security-Policy header */
/* Blocks inline scripts, restricts sources */

Security: SQL Injection Prevention

/* BAD - SQL injection vulnerable */
const query = `SELECT * FROM users WHERE email = '${email}'`;
/* If email = "' OR '1'='1", returns all users */

/* GOOD - Use parameterized queries */
const query = 'SELECT * FROM users WHERE email = $1';
const result = await db.query(query, [email]);

/* Or use ORM which handles escaping */
const user = await prisma.user.findUnique({
  where: { email: email }
});

Security: CSRF Prevention

/* CSRF token middleware */
const csrf = require('csurf');
const cookieParser = require('cookie-parser');

app.use(cookieParser());
app.use(csrf());

/* Return token to client */
app.get('/form', (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

/* Validate token on POST */
app.post('/submit', (req, res) => {
  /* Middleware validates token automatically */
  res.send('Form accepted');
});

/* Or use Same-Site cookies */
/* Set-Cookie: sessionid=...; SameSite=Strict; */

System Design for Web

Full-Stack Architecture

/* Typical modern stack */
Client Layer (React/Next.js)
  ↓ HTTPS
API Gateway (Node.js/Express)
  ↓
Business Logic
  ↓
Database Layer (PostgreSQL)
  ↓
Cache Layer (Redis)
  ↓
File Storage (S3)

/* Scalability patterns */
- Load Balancer (nginx, HAProxy)
- Multiple API instances
- Database replication (master-slave)
- Redis cluster
- CDN for static assets

Real-time Features

Method Latency Server Load Complexity Use Case
Polling High (seconds) High Simple Updates every few seconds
Long Polling Medium Medium Medium Near-real-time updates
SSE Low Low-Medium Medium Server → Client updates
WebSockets Very Low Medium-High High Bidirectional real-time

File Upload Architecture

/* Client generates presigned URL for S3 upload */
1. Client requests upload permission from API
2. API generates presigned URL (15 min expiry)
3. Client uploads directly to S3 (no server involved)
4. S3 calls webhook to API (file processed)
5. API stores file metadata in database

/* Benefits: */
- Server not bottleneck
- No large file in memory
- Secure: presigned URL expires
- Parallel uploads

Top 30 Interview Questions & Answers

1. What is the event loop in JavaScript?
The event loop is a mechanism that allows JavaScript to handle asynchronous operations despite being single-threaded. It continuously checks the call stack and executes callbacks from the task queue when the stack is empty. Order: call stack → microtask queue (Promises) → macrotask queue (setTimeout). This is crucial for understanding async behavior in web apps.
2. Explain closures with a real example
A closure is a function that has access to variables from its outer function's scope. Example: A makeCounter function returns an inner function that accesses the count variable. Each call creates a new closure with its own count variable. Used in data privacy, callbacks, and event handlers where you need to "remember" state.
3. What is the difference between == and ===?
== is loose equality (type coercion): "5" == 5 is true. === is strict equality (no coercion): "5" === 5 is false. Always use === in production code to avoid subtle bugs. Type coercion can lead to unexpected behavior like 0 == false being true.
4. How does React reconciliation work?
React uses a diffing algorithm (fiber architecture) to compare the new virtual DOM with the previous one. It identifies changed elements and updates only those in the real DOM. Key factors: component type (same?), keys in lists (stable identity), and props/state changes. This makes React efficient by minimizing DOM operations.
5. What is a Promise and how is it different from async/await?
A Promise is an object representing an eventual outcome (fulfilled or rejected). async/await is syntactic sugar built on Promises, making async code look synchronous and easier to read. async function returns a Promise; await pauses execution until the Promise settles. Both enable handling asynchronous operations like API calls.
6. Explain CSS specificity calculation
Specificity determines which CSS rule applies when there are conflicts. Inline styles (1000) > IDs (100) > classes/pseudo-classes (10) > elements (1). For example, .class (10) + element (1) = 11, which beats just element (1). !important overrides everything. Understanding specificity prevents unexpected styling.
7. What are React hooks and why were they introduced?
Hooks are functions that let you use React features (state, effects) in functional components. Introduced in React 16.8 to replace class components which were verbose and had complex lifecycle management. Common hooks: useState (state), useEffect (side effects), useContext (context), useReducer (complex state), useMemo (optimization). They enable code reuse through custom hooks.
8. How does JWT authentication work?
JWT (JSON Web Token) has three parts: header (algorithm), payload (user data), signature (server secret). Flow: (1) Client sends credentials, (2) Server creates JWT and returns it, (3) Client includes JWT in Authorization header for future requests, (4) Server verifies signature with secret key. Stateless - server doesn't store session info. Tokens expire (exp claim) for security.
9. What is CORS and how do you fix it?
CORS (Cross-Origin Resource Sharing) is a browser security feature preventing cross-origin requests by default. To fix: (1) Add cors middleware to Express: app.use(cors()), (2) Set headers: Access-Control-Allow-Origin, Access-Control-Allow-Methods, (3) Handle preflight OPTIONS requests. CORS protects users from malicious sites stealing data.
10. Explain the CSS box model
The box model describes how space is allocated around elements: content (actual size) → padding (inside space) → border (outline) → margin (outside space). Total width = content width + padding + border + margin. box-sizing: border-box makes width include padding/border. Understanding this prevents layout surprises.
11. What is the difference between null and undefined?
null is an intentional absence of value (assigned by programmer). undefined is unintentional - variables not initialized, functions with no return, missing function parameters. typeof null is "object" (historical bug), typeof undefined is "undefined". Both are falsy in conditionals but === null checks specifically.
12. How does prototypal inheritance work?
Objects inherit from other objects via the prototype chain. When accessing a property, JavaScript looks in the object, then its prototype, then the prototype's prototype, etc. Constructor functions (function Animal() {}) have a prototype property where methods are stored. To set up inheritance: ChildConstructor.prototype = Object.create(ParentConstructor.prototype). Modern approach uses class syntax which is syntactic sugar.
13. What is memoization in React and when should you use it?
Memoization caches results to avoid recalculation. React.memo prevents component re-render if props unchanged. useMemo caches computed values. useCallback caches function references. Use when: (1) expensive calculations, (2) passing functions to memoized children, (3) preventing infinite loops. Overuse hurts performance - only optimize proven bottlenecks.
14. Explain REST principles
REST (Representational State Transfer) uses HTTP methods for CRUD: GET (read), POST (create), PUT (full update), PATCH (partial update), DELETE (remove). Resources are identified by URLs (/users, /users/123). Stateless - each request is independent. Responses use standard status codes (200, 201, 400, 404, 500). Makes APIs predictable and cacheable.
15. What is the virtual DOM?
The virtual DOM is an in-memory representation of the real DOM. When state changes, React creates a new virtual DOM, diffs it against the old one, and updates only the changed parts in the real DOM. This batching improves performance since DOM updates are expensive. Helps React provide a declarative API - you describe what the UI should be, React handles updates.
16. What are HTTP status codes and when do you use them?
1xx (informational), 2xx (success: 200 OK, 201 Created, 204 No Content), 3xx (redirect), 4xx (client error: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found), 5xx (server error: 500, 503 Service Unavailable). Choose appropriate status to communicate request outcome to client. Proper status codes improve debugging and API usability.
17. How do you handle errors in Express?
Use a dedicated error-handling middleware with 4 parameters (err, req, res, next). Must be defined after all other app.use() and route handlers. Catches errors thrown or passed to next(err). Should log errors, set appropriate status code, and send error response to client. Also wrap async/await in try-catch in route handlers.
18. What is the N+1 query problem?
When fetching a list of items, then for each item fetching related data in a loop. Example: fetch 100 users, then for each user fetch their posts = 101 queries instead of 1. Solution: Use JOINs in SQL or eager loading in ORMs (include: { posts: true } in Prisma). Causes major performance issues at scale.
19. How does XSS attack work and how do you prevent it?
XSS (Cross-Site Scripting) injects malicious JavaScript into web pages. Example: User submits "<img src=x onerror='stealCookie()'>" and it gets stored/displayed. Prevention: (1) Always escape user input in output, (2) Use frameworks that auto-escape (React escapes by default), (3) Content-Security-Policy header restricts script sources, (4) Never use dangerouslySetInnerHTML with user input, (5) Input validation.
20. What is middleware in Express?
Middleware functions process requests and responses. Have access to request object (req), response object (res), and next function. Execute in order they're defined. Can modify requests/responses, end response, or call next() to pass control to next middleware. Examples: express.json() (parse JSON), cors() (handle CORS), authentication (verify tokens), error handling.
21. Explain controlled components in React
Controlled component: React state controls the input value. You set value={state} and update it with onChange handler. Gives React full control - can validate, format, or restrict input. Uncontrolled: DOM controls value, use ref to access it. Controlled is recommended - easier to validate and sync with other form elements.
22. What is hoisting in JavaScript?
JavaScript hoisting moves declarations to top of scope. var declarations are hoisted and initialized to undefined. let/const are hoisted but not initialized (Temporal Dead Zone - accessing throws ReferenceError). Function declarations are fully hoisted. This explains why var x; console.log(x); outputs undefined rather than error. Use const by default to avoid hoisting confusion.
23. How do you optimize React performance?
(1) Code splitting with React.lazy and Suspense, (2) Memoization: React.memo, useMemo, useCallback for expensive operations, (3) Key prop in lists (use unique IDs, not index), (4) useReducer instead of multiple useState for related state, (5) Avoid inline objects/functions as props, (6) Use production builds, (7) Profiler API to identify bottlenecks, (8) Measure with Lighthouse.
24. What is the purpose of useRef in React?
useRef returns a mutable object that persists across re-renders. Use for: (1) Accessing DOM directly (focus input, measure size), (2) Storing mutable value that doesn't trigger re-render (timer IDs, previous props), (3) Storing state you don't need in UI. Ref changes don't re-render. Don't overuse - prefer state for values that affect UI.
25. Explain the difference between SQL and NoSQL
SQL (PostgreSQL): Structured schema, ACID transactions, relations/JOINs, scales vertically. Best for structured data with relationships. NoSQL (MongoDB): Schema-less/flexible, eventual consistency, documents/collections, scales horizontally. Best for unstructured data, high write volume. Choose SQL for financial systems (need ACID); NoSQL for real-time analytics (high throughput). Can use both (polyglot persistence).
26. What is a database index and when should you use it?
Index creates fast lookup for columns (like a book's index). Without index, database scans all rows (O(n)). With index, lookup is O(log n). Index trades space for speed. Index on columns you filter/sort (WHERE, ORDER BY). Avoid indexes on low-cardinality columns (gender: M/F) or columns you rarely query. Monitor: indexes slow down writes and use disk space.
27. How do you implement authentication in a web app?
(1) User submits credentials (email/password), (2) Server hashes password with bcrypt and compares with stored hash, (3) If match, generate JWT or session token, (4) Return token to client, (5) Client includes token in Authorization header for future requests, (6) Server validates token before processing request. Use HTTPS to protect credentials in transit. Never store passwords in plain text.
28. What is the difference between let and const?
Both let and const are block-scoped (unlike var which is function-scoped). const cannot be reassigned after initialization. Both are hoisted but not initialized (Temporal Dead Zone). Use const by default (prevents accidental reassignment), let when you need to reassign. const for objects/arrays doesn't mean immutable - properties can change, just can't reassign the reference.
29. Explain Flexbox vs CSS Grid
Flexbox: 1D layout (row or column), content-first, distributes space around items. Best for navigation bars, button groups, alignment. CSS Grid: 2D layout (rows and columns), design-first, explicit grid areas. Best for page layouts, complex designs. Can use both together - Grid for overall layout, Flexbox for components within grid cells.
30. What is GraphQL and how does it differ from REST?
GraphQL: query language for APIs. Client specifies exactly what data it needs (no over/under fetching). Single endpoint. Strongly typed schema. Complex queries with relationships in one request. REST: multiple endpoints (GET /users, GET /users/1/posts), inflexible data (over/under fetching), simpler to cache. GraphQL better for complex data requirements; REST simpler for simple CRUD APIs. GraphQL has steeper learning curve.

Glossary

API (Application Programming Interface)
Contract for how software components communicate. REST API uses HTTP for web services.
CORS (Cross-Origin Resource Sharing)
Browser security mechanism allowing/restricting requests between different origins (domains).
JWT (JSON Web Token)
Stateless authentication token with header.payload.signature structure, verified using server secret.
Virtual DOM
In-memory copy of real DOM used by React to efficiently detect and apply changes.
Hoisting
JavaScript behavior where var/function declarations move to top of scope during compilation.
Closure
Function retaining access to outer function's variables even after outer function completes.
Promise
Object representing eventual completion or failure of async operation, with then/catch methods.
ORM (Object-Relational Mapping)
Tool (Prisma, Mongoose) translating database records to objects, providing type-safe queries.
N+1 Query Problem
Performance issue where fetching list triggers N additional queries for related data (1 + N total).
XSS (Cross-Site Scripting)
Security vulnerability where attacker injects malicious JavaScript into web pages.
CSRF (Cross-Site Request Forgery)
Attack where authenticated user unknowingly makes request to another site, prevented by tokens.
Memoization
Caching technique storing computation results to avoid recalculation with same inputs.
Middleware
Function in Express that processes request/response, can modify them or call next handler.
REST (Representational State Transfer)
API architectural style using HTTP methods (GET, POST, PUT, DELETE) for CRUD operations.
React Hook
Function allowing functional components to use state and side effects (useState, useEffect, etc).