Interview Prep Hub

Question Bank (496)

Filter by topic and difficulty. Click a question to expand the answer. Mark as reviewed to track progress.

Difficulty:

496 of 496 questions shown • 0 reviewed

GoeasyWhat is a goroutine and how is it different from an OS thread?

A goroutine is a lightweight thread managed by the Go runtime. It starts with about 2KB stack vs ~1MB for an OS thread, and the runtime multiplexes goroutines onto OS threads using the GMP scheduler. You can run hundreds of thousands of goroutines on a single machine.

GoeasyHow do channels work in Go?

Channels are typed conduits for communication between goroutines. Sending: ch <- value. Receiving: v := <-ch. Unbuffered channels block until both sides are ready; buffered channels block when full/empty. They are the primary synchronization mechanism in Go and are usually preferred over shared memory with locks.

GomedExplain Go interfaces and how they differ from Java/C# interfaces

Go interfaces are satisfied implicitly — there is no 'implements' keyword. Any type that has the required methods automatically satisfies the interface. This enables duck typing and makes mocking easy. The empty interface (any) holds any value. Interfaces are usually small (1–3 methods) — Rob Pike: 'the bigger the interface, the weaker the abstraction.'

GomedWhat is context.Context and when do you use it?

context.Context carries deadlines, cancellation signals, and request-scoped values across API boundaries. Pass ctx as the first argument to functions that do I/O. When the parent context is cancelled (request closed, timeout), all downstream operations can also cancel — preventing wasted work. Always derive children with WithCancel, WithTimeout, WithDeadline, or WithValue.

GomedHow does defer work in Go?

defer schedules a function call to run when the surrounding function returns. Multiple defers run in LIFO order. Use for cleanup: defer file.Close(), defer mu.Unlock(). Gotcha: deferred function args are evaluated immediately, but the function runs at return. Also: avoid deferring in tight loops — defers accumulate until function exit.

GohardExplain the GMP scheduler model

G = goroutine, M = OS thread (machine), P = logical processor (context). GOMAXPROCS sets the number of Ps. Each P has a local run queue; M binds to a P to execute Gs. When a G blocks on syscall, the M detaches and the P picks up another M. Work stealing balances queues. This gives Go cheap concurrency without per-goroutine OS threads.

GomedWhat is a nil interface value vs a nil pointer wrapped in an interface?

An interface in Go has two fields: type and value. A nil interface has both nil. An interface holding a nil pointer has type set but value nil — it is NOT == nil. This is a classic bug: returning a typed nil from a function that returns an interface gives the caller a non-nil interface that fails nil checks.

GomedHow do you handle errors idiomatically in Go?

Functions return (T, error). Caller checks err != nil immediately. Wrap with context using fmt.Errorf('loading user %s: %w', id, err) — the %w verb keeps the original error for errors.Is and errors.As inspection. Define sentinel errors (var ErrNotFound = errors.New('not found')) or typed errors for richer handling.

GomedWhat is the difference between sync.Mutex and sync.RWMutex?

Mutex allows one goroutine at a time. RWMutex allows many readers OR one writer. Use RWMutex when reads vastly outnumber writes. Always defer mu.Unlock() right after Lock(). Be careful of copying mutexes — they become useless. Use go vet -copylocks to catch this.

GohardHow does the Go garbage collector work?

Go uses a concurrent tri-color mark-and-sweep collector with very short stop-the-world pauses (sub-millisecond for most heaps). It runs concurrently with the program. Allocations that escape the stack go to the heap. Use sync.Pool to reuse objects and reduce GC pressure on hot paths. Tune with GOGC (default 100 means GC triggers when heap doubles).

GoeasyWhat is the zero value of various types in Go?

Numbers: 0. Strings: ''. Bool: false. Pointers, interfaces, slices, maps, channels, functions: nil. Structs: each field at its zero. This is why Go has no NullPointerException — but you do need to nil-check pointers and check map/slice length before access.

GomedSlice vs Array — what is the difference?

Array has fixed size (part of its type: [3]int and [4]int are different types). Slice is a header (pointer, length, capacity) referencing a backing array. Slices are passed cheaply by value (just the header). Appending past capacity allocates a new backing array.

GomedWhat does the select statement do?

select waits on multiple channel operations and runs whichever is ready first. If multiple are ready, one is chosen randomly. Add a default case for non-blocking behavior. Common pattern: select with time.After for timeouts.

GohardWhat is the loop variable capture issue?

Pre-Go 1.22, the loop variable was a single variable shared across all iterations. Closures or goroutines launched in the loop all saw the final value. Fix: pass as parameter or shadow inside the loop. Go 1.22 changed this — each iteration gets a fresh variable.

GomedHow do you prevent goroutine leaks?

Every goroutine must have a clear exit path. Common leaks: forgetting to close channels, blocking on channels with no sender, infinite loops without context cancellation. Use context.WithCancel for cleanup. Use go test with -race and inspect goroutine count in production with pprof.

GomedWhat does iota do?

iota is the index inside a const block, starting at 0 and incrementing by 1 for each ConstSpec. Used to define enums: const (Red = iota; Green; Blue) gives 0, 1, 2. Reset with each const block. You can express bitmasks: 1 << iota.

GoeasyHow do you read JSON in Go?

Define a struct with json: tags, then json.Unmarshal([]byte, &v) or json.NewDecoder(r).Decode(&v). For unknown shapes, use map[string]interface{} or json.RawMessage. For streaming large bodies, use Decoder.

GohardExplain GoFiber vs net/http

Fiber is built on fasthttp, not net/http. fasthttp avoids per-request allocations using object pools — faster but with caveats: not 100% HTTP/2 compliant, Request/Response objects are reused (mind reference lifetimes), and most net/http middleware does not work directly. Trade speed for ecosystem compatibility.

GomedWhat is the difference between make and new?

new(T) allocates zeroed memory and returns a pointer *T. make(T, args) initializes slices, maps, and channels — these need internal structures set up. So: ptr := new(int); s := make([]int, 10).

GomedWhat is a struct tag?

A backtick-quoted string after a struct field that provides metadata to libraries via reflection. Common: json:'name', db:'col', validate:'required'. Multiple tags space-separated. Used by encoding/json, GORM, validator, etc.

GohardHow do generics work in Go?

Added in Go 1.18. Use type parameters with constraints: func Map[T any, U any](s []T, f func(T) U) []U. Constraints define the operations allowed: comparable, constraints.Ordered, or custom interfaces with type sets. Use sparingly — Go style still prefers interfaces for most polymorphism.

GomedHow would you implement a graceful shutdown for a Fiber server?

Listen for SIGINT/SIGTERM, call app.ShutdownWithTimeout(30 * time.Second), drain in-flight requests, close DB connections, flush logs, then exit. Use a sync.WaitGroup for background workers. Critical for zero-downtime deploys.

GohardWhat is the rationale behind Go choosing composition over inheritance?

Go embeds types instead of inheriting. struct Foo { Logger; ... } makes Foo have all Logger methods. This avoids the fragile-base-class problem, ambiguity in multiple inheritance, and tightly coupled hierarchies. You combine behaviors instead of forming a single chain.

GomedWhen would you use sync.WaitGroup vs errgroup?

WaitGroup waits for N goroutines. errgroup (from x/sync) does the same but cancels all if any errors. Use errgroup when goroutines share a context and you want fail-fast semantics. WaitGroup is fine for fire-and-forget batches where failures are isolated.

GohardHow do you profile a Go service in production?

Import _ 'net/http/pprof' to expose /debug/pprof/* on your server. Capture profiles: go tool pprof -http=:8080 https://prod/debug/pprof/heap. Profiles: cpu, heap, goroutine, mutex, block. Continuous profiling tools: Pyroscope, Datadog. Always sample, not 100%, in prod.

NodeeasyExplain the Node.js event loop

Single-threaded loop with phases: timers (setTimeout/setInterval), pending callbacks, idle/prepare, poll (I/O), check (setImmediate), close callbacks. process.nextTick runs before any phase. Microtasks (Promises) run between phases. Long-running synchronous code blocks the loop.

Nodemedprocess.nextTick vs setImmediate vs setTimeout(0)

nextTick runs immediately after the current operation, before any I/O. setImmediate runs in the check phase, after I/O callbacks. setTimeout(0) is throttled to ~1ms minimum and runs in the timers phase. Order: sync code → nextTick → microtasks → next phase.

NodemedHow do you handle CPU-intensive work in Node?

Offload to worker_threads (true threads in V8 instances), child_process for separate processes, or external services. Never run heavy CPU in the main event loop — it stalls all requests. For CPU-bound microservices, Go is often a better fit.

NodeeasyWhat are Promises? Promise.all vs Promise.allSettled

Promise is a placeholder for an async result with states: pending, fulfilled, rejected. Promise.all([..]) rejects fast on any failure. allSettled returns array of {status, value/reason} for every input — useful when you want all results regardless of failures.

NodemedExplain Streams in Node

Streams process data in chunks: Readable (read from source), Writable (write to sink), Duplex (both), Transform (modify). Use pipe() to chain. Support backpressure — slow consumer pauses the producer. Critical for large files and proxies.

NodemedWhat is NestJS dependency injection?

Nest uses a DI container. Providers (@Injectable() classes) are registered in modules. Constructors receive dependencies. Scope: default (singleton), REQUEST (per-request), TRANSIENT (new each inject). Token-based injection with @Inject('TOKEN') allows non-class deps.

NodemedHow do you handle uncaught exceptions in Node?

process.on('uncaughtException') and process.on('unhandledRejection') catch top-level errors. After logging, exit the process — state may be corrupt. Use a process manager (PM2, systemd) to restart. Better: wrap async code in try/catch and use global error middleware in your framework.

NodehardHow do you scale a Node app on a multi-core machine?

Use the cluster module or PM2 cluster mode to fork one process per core. Each worker has its own event loop. Share nothing — keep state in Redis/DB. For more, use multiple machines behind a load balancer. Node 21+ has built-in cluster improvements.

NodeeasyWhat does package-lock.json do?

Locks the exact version of every transitive dependency. Without it, npm install on different machines could install different versions. Always commit it. Use npm ci in CI for deterministic installs from lock.

NodemedWhat is the difference between cookies and JWT for auth?

Cookies are stored by the browser and sent automatically with each request. JWT is a stateless token usually sent in Authorization header. Cookies need CSRF protection; JWT in localStorage needs XSS protection. Best practice: JWT as httpOnly cookie — combines benefits.

NodemedHow would you implement rate limiting in an Express/Nest app?

Use express-rate-limit or nestjs/throttler. Backed by Redis for multi-instance correctness — use INCR with EXPIRE or sorted sets for sliding windows. Key by IP, user, or API key. Return 429 with Retry-After header.

NodemedNestJS Guards vs Interceptors vs Pipes vs Filters

Pipes transform/validate inputs before the handler. Guards make auth decisions (allow/deny). Interceptors wrap the handler — pre/post logic, transform response. Filters catch exceptions and format error responses. Execution order: Guards → Pipes → Handler → Interceptors → Filters.

NodemedWhat are decorators in NestJS?

TypeScript decorators (@Decorator) attach metadata. Built-in: @Module, @Controller, @Get, @Inject, @UseGuards. Custom decorators wrap createParamDecorator or composition. They are syntactic sugar over reflect-metadata.

NodehardHow does Node handle file I/O internally?

libuv uses a thread pool (default size 4, configurable via UV_THREADPOOL_SIZE) for fs operations, DNS lookups, and some crypto. Network I/O is handled by the OS event mechanism (epoll/kqueue/IOCP). So Node is single-threaded for JS but multi-threaded for I/O.

NodeeasyHow do you debug a Node app?

node --inspect or --inspect-brk to open V8 debugger on port 9229. Connect with Chrome DevTools (chrome://inspect) or VS Code. For prod: use heap snapshots, profiling via pprof equivalents, structured logging. Always have request_id propagation.

SQLeasyWhat is the difference between INNER JOIN and LEFT JOIN?

INNER JOIN returns only rows present in both tables based on the join condition. LEFT JOIN returns all rows from the left table, with NULL for unmatched right-side columns. Useful for 'users and their optional profile' scenarios.

SQLmedWhat is an index and what types does PostgreSQL support?

An index is a separate data structure that speeds up lookups. Types: B-tree (default, equality + range), Hash (equality only), GIN (full-text, JSONB, arrays), GiST (geospatial, ranges), BRIN (huge sequential tables), SP-GiST. Partial indexes only index rows matching a condition. Multi-column composite indexes follow leftmost-prefix rule.

SQLmedExplain the N+1 query problem

Loading a list, then issuing one query per item. Example: fetching 100 users, then 100 queries for orders. Fix: single query with JOIN, or two queries — one for users, one for orders WHERE user_id IN (...). ORMs often hide this — watch for it in code review.

SQLmedWhat does EXPLAIN ANALYZE tell you?

EXPLAIN shows the planned query plan. ANALYZE actually runs it and shows actual times, row counts, and buffers. Look for Seq Scan on large tables (missing index), discrepancies between estimated and actual rows (stale stats), and high disk reads (low cache hit).

SQLmedWhat is a transaction and what are ACID properties?

A transaction is a unit of work that succeeds or fails atomically. ACID: Atomicity (all-or-nothing), Consistency (DB invariants preserved), Isolation (concurrent txns appear serial), Durability (committed data survives crash). BEGIN; ... COMMIT; or ROLLBACK;

SQLhardExplain isolation levels with phantom reads

Read Uncommitted: dirty reads possible (not in PG). Read Committed (PG default): no dirty, but non-repeatable and phantoms possible. Repeatable Read: no non-repeatable, in PG also no phantoms (snapshot isolation). Serializable: full serial behavior, may abort with serialization errors.

SQLmedWhen should you denormalize?

When read patterns dominate and JOINs are expensive at scale. Trade extra storage and write complexity for read speed. Common: materialized views, computed columns, read-time tables for dashboards. Always have a script to rebuild from source of truth.

SQLmedWhat is a CTE and when do you use it?

Common Table Expression — a named subquery defined with WITH. Improves readability. RECURSIVE CTEs handle hierarchies (org charts, comment trees). Note: in PG <12, CTEs were optimization fences; from PG 12 they can be inlined.

SQLhardHow do window functions differ from GROUP BY?

GROUP BY collapses rows into aggregates. Window functions compute over a window of rows but keep the original rows. Example: ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date) gives each row a rank without losing data. Useful for top-N-per-group, running totals, lag/lead.

SQLmedWhen would you use a partial index?

When most queries target a specific subset. Example: CREATE INDEX idx_active_users ON users(email) WHERE status = 'active'. Smaller, faster to scan, faster to maintain. Common with soft-delete flags.

SQLmedWhat is connection pooling and why use PgBouncer?

Postgres allocates ~10MB per connection. With many app servers and short queries, connection overhead dominates. PgBouncer multiplexes thousands of client connections onto a small number of Postgres connections. Use transaction-pooling mode for most web apps. Note: prepared statements and some session features behave differently.

SQLhardHow would you handle a high-write workload in Postgres?

Batch inserts (one statement with many VALUES), use COPY for bulk loads, partition by time, raise checkpoint timeouts, tune wal_buffers and synchronous_commit (off for non-critical writes), use UNLOGGED tables for ephemeral data, add read replicas, consider partitioning or sharding.

SQLmedWhat is the difference between TRUNCATE and DELETE?

DELETE removes rows one by one, triggers fire, MVCC creates dead tuples (need vacuum). TRUNCATE drops the entire table content nearly instantly, resets sequences (with RESTART IDENTITY), but cannot be rolled back inside a transaction with snapshot isolation in some setups. Use TRUNCATE for fast empties of staging tables.

SQLhardHow does Postgres MVCC work?

Each row version has xmin (created) and xmax (deleted) transaction IDs. Readers see only the versions valid at their snapshot. No read locks needed. Cost: dead tuples accumulate; VACUUM cleans them up. Long-running txns block vacuum and inflate bloat.

SQLmedWhen would you use JSONB vs JSON in Postgres?

JSONB stores parsed binary form — faster to query, supports indexing with GIN, slightly slower to insert. JSON stores the original text. Use JSONB 99% of the time. Use JSON only if you need to preserve formatting exactly.

SQLmedWhat is UPSERT and how do you do it in Postgres?

INSERT ... ON CONFLICT (col) DO UPDATE SET ... WHERE ...; or DO NOTHING. Atomically insert or update based on a unique constraint. Common for sync workflows. Watch for serialization conflicts under high concurrency.

SQLhardExplain the difference between SERIAL and IDENTITY columns

SERIAL is the legacy approach using a sequence behind the scenes. IDENTITY (SQL standard, PG 10+) is cleaner: GENERATED ALWAYS AS IDENTITY. Both auto-increment. Prefer IDENTITY for new schemas. For distributed IDs, use UUIDs or snowflake-style.

SQLmedWhat is a deadlock and how does Postgres handle it?

Two transactions hold locks the other needs, waiting forever. Postgres detects deadlocks automatically (default 1s timeout) and aborts the cheaper transaction with a serialization failure. App should retry. Prevent by acquiring locks in a consistent order across code paths.

SQLmedHow do you implement soft delete?

Add a deleted_at TIMESTAMPTZ NULL column. Filter WHERE deleted_at IS NULL on reads. Create a partial unique index excluding deleted rows to allow reusing emails after deletion. Add a scheduled job to hard-delete old soft-deleted rows for compliance.

SQLhardWhen would you reach for a different DB instead of Postgres?

Postgres handles 90% of use cases. Reach for: ClickHouse/BigQuery for OLAP at billion-row scale, Cassandra/DynamoDB for global write-heavy KV with low latency, Elasticsearch/OpenSearch for full-text + faceted search, Redis for sub-ms hot data, Neo4j for deep graph traversals.

RediseasyWhat is Redis primarily used for?

In-memory key-value data store. Common uses: cache, session store, queues (List/Stream), pub/sub, rate limiting, leaderboards (Sorted Set), distributed locks. Single-threaded for commands (predictable, no race in single op), persistence optional.

RedismedExplain cache-aside, write-through, and write-behind

Cache-aside: app reads cache; on miss, reads DB and writes back. Most common. Write-through: app writes both cache and DB synchronously — consistent, slower writes. Write-behind: write to cache, async to DB — fast writes, risk of data loss.

RedismedWhat is a thundering herd and how do you prevent it?

Many requests hit a cold cache simultaneously and all stampede the DB. Prevent with: single-flight (only one regenerates while others wait), refresh-ahead (refresh before expiry), or jittered TTLs so expirations are not clustered.

RedismedWhat eviction policies does Redis offer?

noeviction (default, reject writes), allkeys-lru, allkeys-lfu, allkeys-random, volatile-lru (only keys with TTL), volatile-lfu, volatile-random, volatile-ttl (closest to expiry first). For caches use allkeys-lru or allkeys-lfu. For queues, noeviction with monitoring.

RedismedHow would you implement a rate limiter with Redis?

Token bucket: INCR key with EXPIRE on first set, check if count > limit, return 429. For sliding window: store request timestamps in a Sorted Set, ZREMRANGEBYSCORE to drop old, ZCARD to count. Use Lua for atomicity across multiple commands.

RediseasyWhat data structures does Redis support?

String, List, Hash, Set, Sorted Set, Stream, HyperLogLog, Bitmap, Geo, Bitfield. Each maps to common use cases: Hash for objects, Sorted Set for leaderboards, Stream for event logs with consumer groups.

RedishardHow does Redis persistence work?

RDB: periodic point-in-time snapshots, fast restart, may lose data since last snapshot. AOF: append-only log of every write, configurable fsync (always/everysec/no), more durable, larger files. Most production setups enable both. Tune save and appendfsync to your durability needs.

RedishardHow would you use Redis for distributed locking?

Simple: SET key uniqueval NX EX 30 to acquire. Release with a Lua script that DELs only if value matches. For higher correctness, Redlock algorithm across N independent masters — but read Martin Kleppmann critique before relying on it for safety-critical things.

RedismedHow is Redis Cluster different from Redis Sentinel?

Sentinel: high availability for single primary + replicas. Auto-failover, no sharding. Cluster: shards data across multiple primaries with automatic key partitioning (16384 hash slots) and replicas — both HA and horizontal scale. Cluster has some constraints: multi-key ops only within the same slot (use hash tags).

RedismedWhat is a Redis pipeline and when do you use it?

Send multiple commands without waiting for individual responses, then read all replies. Cuts network round-trips dramatically. Use for bulk operations. Not the same as MULTI/EXEC (transaction) — pipelines are not atomic, just batched.

DockereasyWhat is the difference between an image and a container?

An image is a read-only template — a layered filesystem with metadata. A container is a running instance of an image — image plus a writable layer. Many containers can run from one image.

DockermedWhy use multi-stage builds?

Build stages have build tools (compilers, dev deps), but the final image only needs the artifact. Multi-stage lets you copy only the binary into a tiny base image (scratch, alpine, distroless). Drastically reduces image size and attack surface — a 1GB Go build can become 15MB.

DockermedHow do you reduce Docker image size?

Use slim/alpine/distroless base, multi-stage builds, .dockerignore to skip cruft, combine RUN steps to reduce layers, remove apt cache (rm -rf /var/lib/apt/lists/*), use static binaries (CGO_ENABLED=0 for Go).

DockermedWhat is the difference between CMD and ENTRYPOINT?

ENTRYPOINT is the command always run. CMD provides default arguments. Together: ENTRYPOINT ['./app'] + CMD ['--prod'] runs ./app --prod, but docker run img --dev runs ./app --dev. Use ENTRYPOINT for the binary, CMD for default flags.

DockermedHow does Docker networking work?

Bridge (default): internal isolated network with NAT to host. Host: container shares host network. Overlay: multi-host (Swarm/k8s). Containers on the same user-defined network resolve each other by container name via embedded DNS.

DockereasyWhat is a volume vs a bind mount?

Volume: managed by Docker (under /var/lib/docker/volumes/), best for databases. Bind mount: a host directory mapped into the container, good for local dev or sharing host files. tmpfs: in-memory only, for secrets.

DockerhardHow would you structure a CI/CD pipeline?

On PR: lint → unit tests → build image (cache from previous) → integration tests → vulnerability scan. On merge to main: tag image, push to registry, deploy to staging, run smoke tests, then promote to prod (manual or auto). Use feature flags for risky changes; blue-green or canary deploys.

DockermedWhat is a sidecar container?

A helper container in the same Pod (k8s) sharing network and volumes — for logging, proxying (Envoy), config reload, secret rotation. Sidecar should not own primary business logic, just augment the main container.

DockermedHow do you handle secrets in Docker?

Never bake into the image. Options: env vars (visible in inspect), Docker secrets (Swarm), bind-mount files, or external KMS — AWS Secrets Manager / Vault. App reads at startup. For k8s use Secret resources backed by encrypted store, ideally with Workload Identity / IRSA so the pod fetches from cloud KMS.

DockerhardWhat is the difference between Docker Swarm and Kubernetes?

Swarm: simpler, built into Docker, fine for small clusters. K8s: industry standard, much richer (CRDs, operators, autoscaling, service mesh), much steeper learning curve. Most companies past 10 services pick k8s.

APIeasyWhat is REST and what are its principles?

Representational State Transfer. Principles: stateless server, resource-based URLs (nouns), uniform interface (HTTP verbs), client-server separation, cacheable responses, layered system. HATEOAS is the strictest level, rarely seen in practice.

APIeasyWhat are common HTTP status codes you should know cold?

200 OK, 201 Created, 204 No Content. 301/302 redirects. 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable, 429 Rate Limited. 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.

APImedHow does JWT work?

Token format: header.payload.signature, base64url encoded. Server signs payload (claims) with secret (HS256) or private key (RS256). Client sends in Authorization: Bearer <token>. Server verifies signature and expiry on each request. No DB lookup needed — stateless.

APImedWhen should you use JWT in a cookie vs Authorization header?

Cookie (httpOnly, SameSite=Lax/Strict, Secure) defends XSS theft but needs CSRF protection. Authorization header (token in JS memory) avoids CSRF but exposes to XSS. Best: short-lived access token in cookie, refresh token stored encrypted server-side or in a separate httpOnly cookie.

APImedWhat is idempotency and why does it matter for APIs?

An operation is idempotent if applying it multiple times has the same effect as once. GET, PUT, DELETE are idempotent; POST is not. For unsafe operations like payments, require an Idempotency-Key header so retries do not duplicate. Server stores the first response keyed by the idempotency key.

APImedHow do you version a REST API?

URL versioning (/v1/...) is most common and visible. Header versioning (Accept: application/vnd.api+json;version=1) is cleaner but less discoverable. Add new fields without bumping version; bump for breaking changes. Have a deprecation policy: announce, sunset header, then remove.

APImedWhen would you choose gRPC over REST?

gRPC: lower latency, strong contracts (protobuf), bi-directional streaming, smaller payloads. Use for internal service-to-service. REST/JSON: better for public APIs, browser-friendly, easier debugging. GraphQL: client-driven queries, good for varied client needs.

APImedHow do you handle pagination?

Offset/limit: simple but slow for deep pages (DB still scans skipped rows). Cursor-based: pass an opaque cursor (e.g., last ID or timestamp) — O(log n) regardless of depth. Keyset pagination is the technical term. Use cursor for infinite scroll, offset for fixed page UI.

APIhardDesign a webhook system

Producer: emit event to queue. Worker: load subscriber config, sign payload (HMAC SHA256), POST with retry (exponential backoff up to e.g. 24h), log delivery state. Subscriber must respond 2xx within timeout (~5s). Provide replay endpoint and signing secret rotation. Always idempotent.

APImedWhat is CORS and how do you handle it?

Cross-Origin Resource Sharing — browser security that blocks JS from reading responses across origins unless server opts in via Access-Control-Allow-Origin headers. Preflight OPTIONS request for non-simple methods. Configure in middleware: allow specific origins (not *), credentials only with explicit origin.

APIhardMicroservices vs Modular Monolith — when to pick what?

Monolith: fewer ops costs, ACID transactions across modules, easier debugging, fine until team coordination becomes the bottleneck. Microservices: independent deploys, polyglot, scale parts independently, but require investment in tracing/CI/contracts. Most teams should start modular monolith and split when justified.

APImedWhat is a circuit breaker?

Resilience pattern: after N failures, 'open' the circuit and fail fast for some duration without calling the downstream. Periodically half-open to test. Prevents cascading failures and gives the dependency time to recover. Implementations: Hystrix (deprecated), resilience4j (Java), gobreaker (Go).

APImedHow do you implement health checks?

/healthz returns 200 if process alive (liveness). /readyz returns 200 only when all critical deps (DB, cache) are reachable (readiness). K8s and load balancers use these to route traffic. Deep health checks separately — they should NOT fail readyz unless truly required for serving traffic.

APIhardSaga pattern for distributed transactions

Long-running multi-service operation broken into local transactions with compensating actions on failure. Choreography: each service emits events the next listens to. Orchestration: a central coordinator drives each step. Used for payments, order fulfillment, account opening.

APImedWhat is the outbox pattern?

Atomically write business state and an event row to the same DB transaction. A separate publisher process reads the outbox and publishes to Kafka/queue, then marks delivered. Solves the dual-write problem where DB write succeeds but message publish fails (or vice versa).

Next/ReacteasyWhat is the difference between SSR, SSG, and ISR in Next.js?

SSR: HTML rendered on every request — fresh data, slower TTFB. SSG: HTML built at deploy — fastest, rebuild for updates. ISR: SSG + revalidate window — server regenerates page in the background after TTL. Pick per page based on freshness needs.

Next/ReactmedWhat are React Server Components?

Components that run only on the server, no client JS bundle. Can fetch data directly (await fetch). Cannot use state or browser APIs. Mark child components needing interactivity with 'use client'. Reduces bundle size and centralizes data fetching.

Next/ReacteasyWhat is the virtual DOM?

A lightweight in-memory tree representation of the UI. React diffs new vs old vDOM and applies minimal real DOM changes. Avoids expensive direct DOM manipulation. Note: vDOM is not always faster than well-written direct DOM code — its main value is the programming model.

Next/ReactmedExplain useEffect dependencies

Dependency array tells React when to re-run the effect. [] = once on mount. [x] = re-run when x changes. Missing deps cause stale closures (effect uses old values). Use the react-hooks/exhaustive-deps lint to catch this. For functions that should not retrigger, wrap in useCallback or move outside.

Next/ReactmedWhen would you use useMemo vs useCallback?

useMemo memoizes a computed value. useCallback memoizes a function reference. Both are about avoiding work — useMemo for expensive computations, useCallback to keep prop references stable so memoized children do not re-render. Do not wrap everything; profile first.

Next/ReactmedWhat are React keys and why do they matter?

Keys help React identify list items across renders. With stable, unique keys (NOT array index for reorderable lists), React preserves component state correctly. Using index keys on mutable lists causes weird state bugs — focus jumps, inputs hold wrong values.

Next/ReacteasyWhat is hydration?

Process where React attaches event listeners and reconciles server-rendered HTML with client React tree. Hydration errors happen when server and client render different HTML (Date.now, Math.random, conditional on window). Fix with useEffect for client-only logic.

Next/ReactmedHow does the Next.js App Router differ from Pages Router?

App Router (13+): Server Components by default, nested layouts, route groups, streaming via Suspense, Server Actions. Pages Router: getServerSideProps/getStaticProps, _app and _document. App Router is the future, but Pages Router still receives critical updates.

Next/ReactmedHow would you handle global state in React?

Tiers: useState (component-local), useContext (small global state, app-wide settings), Zustand or Jotai (medium, simple stores), Redux Toolkit (large, with devtools and middleware), Server state — React Query or SWR (cache, revalidate, mutations). Avoid putting server data in context.

Next/ReacthardHow would you optimize a slow React page?

Profile with React DevTools first. Then: memoize expensive children, virtualize long lists (react-window), code split with next/dynamic, use Server Components if possible, lazy-load images and below-the-fold, debounce inputs, batch state updates (React 18 auto-batches), preload data in route loaders.

Next/ReactmedWhat are Server Actions in Next.js?

Functions marked with 'use server' that execute on the server but can be called from client components — typically from form actions or onClick. No need to write API endpoints for mutations. Returns serialized data. Built on RPC over POST.

Next/ReactmedWhen should you use SWR vs React Query?

Both are excellent. SWR is lighter, simpler API, made by Vercel (Next.js team). React Query is richer — mutations, optimistic updates, query invalidation, devtools. For complex apps with lots of mutations, React Query. For simple read-heavy, SWR.

Next/ReacthardExplain reconciliation and the diffing algorithm

React compares two trees and computes the minimum operations to convert old to new. Heuristics: different element types unmount the subtree, same type reuses. Keys disambiguate list items. The fiber architecture (React 16+) lets it pause/resume work for concurrency.

Next/ReactmedWhat is hydration mismatch and how do you debug it?

Server-rendered HTML differs from client first render. React logs a warning (or error in newer versions). Causes: random/date values, browser-only checks, conditional render on viewport. Fix: useEffect to set client-only state, or use Suspense with a server-safe fallback.

Next/ReactmedWhat are accessibility (a11y) basics in React?

Use semantic HTML (button not div), aria-* attributes only when necessary, focus management on route change, keyboard navigation (tab order, escape to close), alt text on images, sufficient color contrast (WCAG AA 4.5:1). Test with axe DevTools and screen readers.

SysDesignmedHow would you design a URL shortener?

POST /shorten returns short code. Strategies: base62 encode auto-increment ID, or generate random 7-char and check collisions. Store in PG/Dynamo. Cache hot lookups in Redis. CDN for redirects. Analytics: Kafka → ClickHouse. Custom domains, expiry, abuse detection.

SysDesignmedDesign a rate limiter

Token bucket per user+endpoint in Redis. INCR counter with EXPIRE, reject when over limit. For sliding window: sorted set of timestamps. Distribute via consistent hashing if scale demands. Provide Retry-After header. Allow burst with bucket size > rate.

SysDesignhardDesign a notification system (your strong story!)

Producers write to Kafka. Channel-specific consumers (email, SMS, push, WhatsApp) handle delivery, retries, idempotency keys, and provider rate limits. Template service merges user data. DLQ for permanent failures. User prefs in DB. Scheduling support. Audit log.

SysDesignmedHow would you design a leaderboard?

Redis Sorted Set keyed by leaderboard ID. ZADD to update score, ZREVRANGE for top N, ZRANK for user rank. Sharding for global leaderboards by region or time. Persist final scores to DB on snapshot. Cache user position separately for fast personal view.

SysDesignhardDesign a chat application like WhatsApp

WebSocket gateways (stateful, sticky LB). Message store: Cassandra for write-heavy. Presence in Redis with heartbeat TTL. Fan-out via Redis Pub/Sub for online users; push notifications for offline. Media via S3 + CDN. E2EE handled client-side. Group chats with fan-out lists.

SysDesignmedHow do you design for high read availability?

Read replicas behind a read endpoint, app routes reads to replica pool. Cache aggressively at multiple layers (CDN, app, DB). Stale-while-revalidate for soft freshness. Multi-region with eventual consistency for global. Health-check based failover.

SysDesignmedHow do you handle a sudden 10x traffic spike?

Short term: scale stateless services horizontally (auto-scaling group, k8s HPA), enable rate limiting, shed non-critical work (queue lower priority items, disable heavy features). Cache more aggressively. Long term: capacity planning, load testing, async fallbacks for non-critical paths.

SysDesignhardDesign a payment system at high level

API layer (idempotency), ledger service (double-entry, immutable append), provider adapters (Stripe, payment gateway), webhook handler (HMAC verify, async process), reconciliation worker (nightly match), refund flow (reverse entries), audit log. Money never in floats. PCI-DSS via tokenization vault.

SysDesignmedHow would you store and query time-series data?

Postgres with partitioning by time is fine to moderate scale. Past that: TimescaleDB (PG extension), InfluxDB, or ClickHouse for analytics. Use time-bucketed compaction, downsample old data, drop oldest partitions for retention.

SysDesignhardDesign a feed system (Twitter/Instagram)

Pull model: compute on read by joining followed users events — easy but slow for active users. Push model: fan-out on write — fast read, expensive for celebrities. Hybrid: push for normal users, pull for high-followers. Cache user feed in Redis (sorted set), backfill on access.

SysDesignmedHow do you ensure exactly-once delivery in a queue?

Generally impossible at the protocol level. Achieved via at-least-once + idempotent consumers — use idempotency keys, dedup table, or check-then-act inside a transaction. Kafka has effectively-once with transactions across topics/consumers.

SysDesignmedWhat is consistent hashing and where is it used?

Distributes keys across a ring of nodes. Adding/removing a node only remaps ~1/N of keys (vs full reshuffle with mod-N). Virtual nodes per physical node balance load. Used in Cassandra, DynamoDB partitioning, Memcached client libraries, CDN edge selection.

SysDesignhardHow would you build a search service?

Source of truth in primary DB. Indexer service consumes change events (CDC, Debezium) and writes to Elasticsearch/OpenSearch with proper analyzers (lowercase, ngrams, stemming). Application calls search service which queries ES with filters/facets. Reindex pipeline for schema changes. Result re-ranking via business rules or ML.

SysDesignmedWhat is CDN and what should you put on it?

Content Delivery Network — edge caches close to users. Static assets (images, JS, CSS), generated HTML for SSG/ISR pages, public API responses with proper Cache-Control. Use Cache-Control: public, max-age=..., stale-while-revalidate. Purge on deploys.

SysDesignhardHow do you design a multi-tenant SaaS?

Tenancy levels: shared schema + tenant_id column (cheapest, isolation only logical), schema-per-tenant (medium), DB-per-tenant (strongest isolation, ops cost). Use row-level security if shared schema. Encrypt per-tenant keys for sensitive data. Background jobs and metrics must include tenant context.

BehavioraleasyTell me about yourself

Use the 60s script: name, current role, 1–2 highlights with metrics (40% latency reduction), 1 thing you are looking for next. End warmly, invite questions.

BehavioraleasyWalk me through your resume

Reverse chronological. Spend 70% on most recent role (1Finance). Group earlier stints briefly. Highlight one technical win per role.

BehavioralmedWhy are you looking to switch?

Positive framing: growth, scope, tech stack. 'I have learned a lot at 1Finance, especially on technical depth. I am looking for a role with more Go-native engineering culture and the chance to own larger systems end-to-end with stronger frontend scope.' Never criticize current team.

BehavioralmedTell me about a time you optimized a system

Use Story 1 — the 40% latency reduction. Be specific about the diagnosis (EXPLAIN ANALYZE), action (composite indexes + Redis), and result (latency, throughput, zero downtime).

BehavioralmedDescribe a difficult bug you fixed

Pick the Redis TTL miss postmortem or the WordPress migration recovery. Show diagnosis process, the fix, and what you put in place to prevent it.

BehavioralmedTell me about a time you disagreed with a teammate

Use Story 5 — scoring engine as stored procedure. Show that you brought data to the disagreement (prototyped both) rather than just opinion. Always end with what you learned from the other side.

BehavioralmedHow do you handle ambiguous requirements?

Use Story 7 — the SEO audit tool. Steps you took: interviewed users, researched standards, distilled into concrete rules, validated incrementally.

BehavioralmedTell me about your biggest failure

Use Story 8 — Redis TTL miss in production. Be specific. Do NOT pick a fake failure. End with what you put in place (CI check, code review checklist) so it never recurs.

BehavioralmedHow do you handle code review feedback?

Treat it as data, not judgment. Ask for the reasoning when not obvious. Push back with rationale when I disagree, but commit to the team decision once made. I keep a personal note of common feedback patterns to internalize.

BehavioralmedHow do you mentor juniors?

Use Story 6 — goroutine leaks. Pair on the live problem first (showing tools), then co-write checklist for repeatability, then push into team-wide CI rule for prevention. Multiply learning, do not just unblock.

BehavioralmedHow do you balance speed vs quality?

Quality is non-negotiable for security, money, and data correctness paths. For everything else, ship MVP fast and iterate based on signal. I tend to bias toward shipping with strong observability so I can detect issues quickly even if I missed edge cases.

BehavioralmedWhere do you see yourself in 3 years?

Senior engineer owning a core system end-to-end, mentoring 1–2 juniors, contributing to architectural direction. Ideally at a place where strong engineering culture lets me keep learning across both technical depth and frontend scope.

BehavioralmedHow do you keep learning?

Mix of: deep reads (DDIA, Effective Go), engineering blogs (Stripe, Cred, Discord), side projects (real-time chat, India map), occasional courses (System Design Interview by ByteByteGo), and most importantly applying things in real work.

BehavioralmedDescribe a time you took initiative

Use Story 3 — the blog platform PR/FAQ and Azure DevOps structured stories. Show you saw an unowned gap and stepped in with a concrete plan, not just a complaint.

BehavioralmedWhat questions do you have for me?

Always prepared 3–5. Examples: architecture overview, how technical decisions get made, what is a project they are proud of and what made it hard, success metrics for this role, on-call rotation, 30/60/90 day expectations.

FintechmedWhy should money never be stored as a float?

Floating point cannot represent decimals like 0.1 exactly. Sums and comparisons accumulate error. Use integer smallest-unit (paise/cents), or a Decimal type backed by arbitrary precision. Postgres NUMERIC(18,2). In Go: shopspring/decimal.

FintechmedWhat is double-entry accounting in software?

Every transaction has two entries: a debit on one account and a credit on another. The two must balance. Ledger is immutable append-only — corrections are reversing entries. Balance is derived. This makes audits possible and prevents lost money.

FintechmedHow do you handle idempotency for payments?

Client sends Idempotency-Key header. Server stores key + first response. Subsequent same-key requests return cached response. TTL of 24h+. Prevents double-charging on retries, network blips, browser refreshes.

FintechmedHow do you verify a payment webhook is authentic?

Provider signs the body with a shared secret (HMAC-SHA256 typically). Recompute the signature on receipt and compare in constant time. Also check timestamp to prevent replay (reject if > 5 min old). Process idempotently — providers retry.

FintechhardHow would you reconcile an internal ledger with bank statements?

Nightly job: ingest bank statement, match each line against internal txn by amount + reference. Discrepancies (timing, missing on either side) go to an exception queue for ops to resolve. Track aging — old breaks indicate process problems.

FintechmedWhat is PCI-DSS and when does it apply?

Payment Card Industry Data Security Standard — required if you touch raw card numbers (PAN). Most teams stay out of scope by tokenizing through a vault (Stripe, payment gateway) — you store only opaque tokens. If you do touch PANs, expect heavy audit, network segmentation, encryption everywhere.

FintechmedHow do you compute EMI?

EMI = P × r × (1+r)^n / ((1+r)^n − 1) where P=principal, r=monthly rate (annual/12/100), n=months. Amortization schedule iterates: interest = balance × r, principal = EMI − interest, balance reduces. Always test with edge cases (r=0, very small r).

FintechmedHow would you secure PII in your DB?

Encrypt at rest (column-level for sensitive fields like Aadhaar/PAN, full-disk for everything). Mask in logs (never log full PII). Role-based access. Audit who reads what. Pseudonymize in non-prod (synthetic test data). Hash + salt for any deterministic lookup.

FintechmedWhat is KYC and AML?

KYC = Know Your Customer — verify identity (PAN, Aadhaar, address proof) before onboarding. AML = Anti-Money Laundering — monitor transactions for suspicious patterns, report to regulators. In India, governed by RBI and FIU-IND. Engineering side: integrate with bureaus (Hyperverge, IDfy) and risk engines.

FintechhardHow would you design a wallet with frozen/locked balance?

Per-account: total = available + frozen. Lock operation: move amount from available to frozen, return a lock_id. Settle: charge against the lock (frozen decreases, total decreases). Release: unfreeze back to available. All operations inside a serializable transaction or with row-level locks to prevent race.

PHPeasyWhat is PHP? How is it executed?

PHP is a server-side scripting language primarily used for web development. It is executed on the server, generating HTML which is then sent to the client. Common execution methods include PHP-FPM (FastCGI Process Manager) and Apache mod_php.

PHPeasyWhat is the difference between == and === in PHP?

== is loose equality (checks value after type coercion), while === is strict equality (checks both value and type without coercion). Always use === to avoid unexpected bugs, e.g., 0 == '0' is true, 0 === '0' is false.

PHPmedWhat are magic methods in PHP? Give examples.

Magic methods in PHP are special methods that override PHP's default action when certain actions are performed on an object. They start with __. Examples: __construct() (called on instantiation), __destruct() (called on destruction), __get(), __set() (overloading properties), __call() (overloading methods), __toString() (how the object reacts when treated as a string).

PHPmedHow does PHP handle sessions?

PHP uses session_start() to initialize or resume a session. It generates a unique Session ID, sends it to the client as a cookie (PHPSESSID), and creates a file on the server (or uses Redis/Memcached) to store the data linked to that ID. Access data via the $_SESSION superglobal array.

PHPmedExplain Traits in PHP.

Traits are a mechanism for code reuse in single inheritance languages like PHP. A trait is intended to reduce the limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

PHPhardWhat are some new features introduced in PHP 8?

JIT (Just-In-Time) compilation for performance, Union Types (e.g., int|float), Named Arguments, Match Expression (safer and cleaner alternative to switch), Nullsafe operator (?->), and Attributes (metadata for classes/methods instead of PHPDoc annotations).

LaraveleasyWhat is Laravel and what is artisan?

Laravel is a free, open-source PHP web framework based on the MVC pattern. Artisan is the command-line interface included with Laravel, providing helpful commands to generate boilerplate code (controllers, models, migrations), run tasks, and manage database migrations.

LaravelmedExplain Laravel's Service Container and Service Providers.

The Service Container is an IoC (Inversion of Control) container for managing class dependencies and performing dependency injection. Service Providers are the central place of all Laravel application bootstrapping. They register bindings into the container, event listeners, middleware, and routes.

LaravelmedEloquent ORM vs Query Builder in Laravel?

Eloquent is Laravel's Active Record implementation where each database table has a corresponding Model used to interact with that table. It is intuitive but can have an overhead. Query Builder provides a convenient, fluent interface to creating and running database queries, generally faster and better for complex/raw queries.

LaravelhardHow do you handle queues and jobs in Laravel?

Laravel provides a unified API across a variety of queue backends (Redis, Beanstalkd, SQS). You create Job classes (php artisan make:job). Jobs implement the ShouldQueue interface. You dispatch them using the dispatch() helper. Worker processes (php artisan queue:work) process the jobs in the background.

LaravelhardHow does Dependency Injection work in Laravel?

Laravel's Service Container automatically injects dependencies. If you type-hint a class in a Controller's constructor or method, Laravel uses Reflection to inspect the method signature, instantiate the required classes (and their dependencies recursively), and pass them to the method.

GoeasyHow do you check if a key exists in a map in Go?

Use the 'comma ok' idiom: val, ok := myMap["key"]. If the key exists, ok is true and val is the value. If not, ok is false and val is the zero value for the map's value type.

GoeasyHow do you write a while loop in Go?

Go only has the 'for' keyword. A while loop is just a for loop with a single condition: for i < 10 { // do something }.

GohardHow does the Go memory allocator work?

It's based on TCMalloc (Thread-Caching Malloc). It uses mcache (per-P cache for lock-free small allocations), mcentral (global cache of spans), and mheap (manages all memory). It organizes memory into size classes (spans) to reduce fragmentation and allocation overhead.

GohardExplain sync.Pool and when to use it.

sync.Pool is a cache of allocated but unused items for later reuse, relieving pressure on the garbage collector. Items can be removed automatically during GC without notification. Great for short-lived, frequently allocated objects like byte buffers in high-throughput network servers (used extensively by fasthttp/Fiber).

SQLeasyWhat is a Primary Key?

A constraint that uniquely identifies each row in a table. It must contain UNIQUE values and cannot contain NULL values. A table can have only one primary key, which may consist of single or multiple columns (composite key).

SQLeasyWhat is the difference between WHERE and HAVING?

WHERE filters rows BEFORE grouping or aggregation takes place. HAVING filters rows AFTER grouping or aggregation. You cannot use aggregate functions (like SUM or COUNT) in a WHERE clause, but you can in a HAVING clause.

SQLhardExplain table partitioning strategies in PostgreSQL.

Partitioning splits large tables into smaller physical pieces while maintaining a single logical table. Strategies: RANGE (e.g., partitioning logs by month), LIST (e.g., by region or tenant ID), and HASH (distributing data evenly across partitions). Improves query performance (partition pruning) and maintenance (dropping old partitions is faster than DELETE).

SQLhardExplain the difference between logical and physical replication.

Physical replication (streaming replication) copies WAL files block-by-block, making an exact byte-for-byte copy (used for High Availability/Failover). Logical replication decodes WAL into DML statements (INSERT, UPDATE) and sends them to subscribers. Logical allows replicating specific tables, replicating across different PG versions, or to different databases.

PHPeasyWhat are the main error types in PHP?

Notices (non-critical, script continues), Warnings (more serious, script continues), and Fatal Errors (critical, script terminates). PHP 7+ also introduced Exceptions and Error classes that can be caught using try/catch.

PHPmedHow to handle exceptions in PHP?

Use try-catch blocks. Code that might throw an exception goes in the try block, and catch blocks handle specific Exception or Error types. A finally block can be added for cleanup code that always runs regardless of an exception.

PHPmedExplain PHP namespaces.

Namespaces are used to avoid naming collisions between classes/functions/constants created by you and third-party libraries. They also provide the ability to alias (import) a long name to a shorter one, improving readability. Defined using the 'namespace' keyword at the top of the file.

PHPeasyDifference between require, require_once, include, include_once.

include issues a Warning on failure but continues execution. require issues a Fatal Error and stops execution. The _once variants check if the file has already been included/required and prevent it from being loaded multiple times, avoiding redeclaration errors.

PHPmedWhat is the use of the static keyword in PHP?

Inside a class, it defines static properties/methods that belong to the class itself, not instances. Inside a function, a static variable retains its value between multiple calls to that function.

PHPhardHow does late static binding work?

Late static binding (using the 'static::' keyword instead of 'self::') allows inherited classes to override static methods/properties and have the parent class resolve to the called class at runtime, rather than the class where the method was defined.

PHPmedWhat are anonymous functions and closures in PHP?

Anonymous functions (closures) are functions without a specified name. They are often used as callback parameters. In PHP, closures can inherit variables from the parent scope using the 'use' keyword.

PHPeasyWhat is the spaceship operator (<=>)?

Introduced in PHP 7, it compares two expressions. Returns -1 if the left is less, 0 if equal, and 1 if the left is greater. Commonly used in custom sorting functions (usort).

PHPmedExplain autoloading and PSR-4.

Autoloading automatically includes class files when a class is instantiated, eliminating manual require statements. PSR-4 is the standard describing a specification for autoloading classes from file paths, mapping namespaces to directory structures. Handled natively by Composer.

PHPmedHow to prevent SQL injection in PHP?

Always use Prepared Statements and Parameterized Queries via PDO or MySQLi. Never concatenate user input directly into a SQL string. Prepared statements separate the query structure from the data, neutralizing injected SQL commands.

PHPhardWhat are generators in PHP and why use them?

Generators provide an easy way to implement simple iterators without the overhead or complexity of implementing a class that implements the Iterator interface. They use the 'yield' keyword and are highly memory-efficient for iterating over large datasets.

PHPmedExplain array_map vs array_filter.

array_map applies a callback function to each element of an array, returning an array of the modified values. array_filter iterates over an array and passes each value to a callback; if the callback returns true, the element is kept in the resulting array.

PHPmedHow do you implement interfaces and abstract classes in PHP?

An interface defines a contract of public methods a class must implement (using 'implements'). An abstract class can have both abstract methods (without body) and implemented methods (using 'extends'). A class can implement multiple interfaces but extend only one abstract class.

PHPeasyWhat is the $GLOBALS superglobal?

$GLOBALS is an associative array containing references to all variables which are currently defined in the global scope of the script. The variable names are the keys of the array.

LaraveleasyExplain the MVC architecture in Laravel.

Model represents data and business logic (Eloquent). View handles the presentation layer (Blade templates). Controller routes user requests, manipulates the model, and passes data to the view.

LaraveleasyWhat are Route parameters and Named Routes?

Route parameters allow capturing segments of the URI (e.g., /user/{id}). Named routes allow generating URLs or redirects to specific routes using a given name via the route('name') helper, making URL changes easier to manage.

LaravelmedExplain Route Model Binding.

Route model binding provides a convenient way to automatically inject model instances directly into your routes or controllers based on the ID or a specific field in the URI, e.g., passing User $user automatically fetches the user by the ID in the URL.

LaravelmedWhat is a Middleware and how to create one?

Middleware provides a mechanism for inspecting and filtering HTTP requests entering your application (e.g., authentication, logging). Created via 'php artisan make:middleware'. They run before or after the controller logic.

LaraveleasyExplain CSRF protection in Laravel.

Laravel automatically generates a CSRF 'token' for each active user session. This token is used to verify that the authenticated user is the one actually making the requests. Used in forms via the @csrf Blade directive.

LaravelmedHow does Validation work in Laravel (FormRequests)?

Laravel offers validation rules. FormRequests are custom request classes that encapsulate validation and authorization logic, keeping controllers clean. Created via 'php artisan make:request'. If validation fails, it automatically redirects back with errors.

LaravelhardExplain the Concept of Facades in Laravel.

Facades provide a 'static' interface to classes that are available in the application's service container. They serve as proxies to underlying classes, providing a terse, memorable syntax while maintaining more testability and flexibility than traditional static methods.

LaraveleasyWhat are Blade directives?

Blade is Laravel's templating engine. Directives are shortcuts for PHP code, starting with @. Common ones include @if, @foreach, @extends, @section, and @yield for building layouts and control structures.

LaravelmedExplain Eloquent Mutators and Accessors.

Accessors format Eloquent attribute values when you retrieve them (e.g., capitalizing a name). Mutators format the value before saving it to the database (e.g., hashing a password). In modern Laravel, defined via a single method returning an Attribute instance.

LaravelmedHow to handle Many-to-Many relationships in Eloquent?

Defined using the belongsToMany method on both models. It requires an intermediate 'pivot' table. You can retrieve pivot table data using the 'pivot' attribute on the models, and attach/detach related records easily.

LaravelmedWhat is Eager Loading in Laravel and why use it?

Eager loading alleviates the N+1 query problem. Instead of querying relationships lazily in a loop, you use the 'with()' method to load them in a couple of queries upfront (e.g., Post::with('comments')->get()).

LaraveleasyHow do you run database migrations and seeders?

Migrations are run with 'php artisan migrate' to create/modify tables. Seeders populate tables with dummy data using 'php artisan db:seed'. Both can be combined: 'php artisan migrate --seed'.

LaravelmedHow to use Events and Listeners in Laravel?

Events represent something that happened (e.g., UserRegistered). Listeners respond to the event (e.g., SendWelcomeEmail). They decouple logic. Dispatched via the event() helper. Listeners can be queued for background processing.

LaravelmedExplain Task Scheduling in Laravel.

Laravel's command scheduler allows you to fluently and expressively define your command schedule within the app/Console/Kernel.php file. It requires only a single cron entry on your server that calls 'php artisan schedule:run' every minute.

LaravelhardExplain the request lifecycle in Laravel.

Public/index.php -> HTTP Kernel -> Global Middleware -> Router -> Route Middleware -> Controller -> View. Along the way, Service Providers bootstrap core components and bindings.

GoeasyWhat is the init() function and when is it executed?

The init() function takes no arguments and returns no values. It runs automatically before the main() function, after all variable declarations in the package have evaluated their initializers. Used for setup tasks.

GomedHow do you handle race conditions in Go?

Race conditions occur when multiple goroutines access shared memory concurrently without synchronization. Detect them using the race detector during tests or builds: 'go test -race' or 'go build -race'. Fix them using Mutexes or Channels.

GomedExplain how method receivers work (value vs pointer).

A value receiver gets a copy of the struct; modifications inside the method aren't reflected in the original. A pointer receiver gets the memory address, allowing the method to modify the original struct. Pointer receivers are also used to avoid copying large structs.

GomedWhat are Go build constraints/tags?

Build tags (//go:build tagname) at the top of a file dictate under what conditions (OS, architecture, custom tags) the file should be included in the compilation. Useful for OS-specific implementations.

GomedHow do you use the embed package in Go?

The 'embed' package allows you to embed static files and folders directly into the Go binary at compile time. Use the //go:embed directive above a variable of type string, []byte, or embed.FS.

GohardWhat is a memory leak in Go, given it's garbage collected?

Memory leaks in Go happen when references to objects are unintentionally held, preventing the GC from reclaiming them. Common causes: appending to global slices indefinitely, unclosed response bodies, forgotten goroutines (goroutine leaks), and lingering references in maps.

GoeasyHow do you do cross-compilation in Go?

Go natively supports cross-compilation. Just set the GOOS and GOARCH environment variables before running the build command. E.g., GOOS=linux GOARCH=amd64 go build -o myapp.

GomedWhat are the differences between log.Fatal and panic?

log.Fatal prints a message and calls os.Exit(1), bypassing any deferred functions. panic initiates a normal panic sequence, executing deferred functions, which allows for recovery via the 'recover' function. Prefer returning errors, use panic only for unrecoverable state.

GomedExplain how type switches and type assertions work.

Type assertion extracts the underlying concrete value of an interface: val := i.(string). Type switch is a construct that permits several type assertions in series: switch v := i.(type) { case int: ... case string: ... }.

GomedHow do you mock interfaces in Go testing?

Since interfaces are satisfied implicitly, you simply create a test struct that implements the interface's methods. Frameworks like gomock or mockery can auto-generate these mock implementations for large interfaces.

GohardExplain the sync/atomic package and its use cases.

The sync/atomic package provides low-level atomic memory primitives useful for implementing synchronization algorithms. It's used for simple counters or state flags without the overhead of a full Mutex, ensuring safe concurrent access.

GomedHow do you read a large file chunk by chunk in Go?

Instead of os.ReadFile (loads entirely into memory), use os.Open to get an *os.File, then wrap it in a bufio.Reader. Read in chunks using Read() into a pre-allocated byte slice, or line-by-line using ReadString('\n') or a bufio.Scanner.

GomedWhat is the net/http ServeMux?

ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.

GomedHow does Go handle dependency injection?

Go does not have built-in DI frameworks like Spring. DI in Go is usually done manually via constructors: func NewService(db *sql.DB) *Service. Alternatively, libraries like google/wire use code generation for compile-time DI.

GomedWhat is go.work (Go workspaces)?

Introduced in Go 1.18, go.work allows developers to work with multiple modules locally simultaneously without needing to manually modify go.mod files with 'replace' directives. It sets up a workspace spanning multiple modules.

GohardExplain sync.Cond and when to use it.

sync.Cond implements a condition variable, a rendezvous point for goroutines waiting for or announcing the occurrence of an event. Use it over channels when you need to broadcast a signal to *multiple* waiting goroutines simultaneously (using cond.Broadcast()).

SQLeasyWhat is a Foreign Key constraint?

A Foreign Key is a column or group of columns in a relational database table that provides a link between data in two tables. It enforces referential integrity, ensuring the value matches a Primary Key in another table.

SQLeasyExplain the difference between UNION and UNION ALL.

Both combine the result sets of two or more SELECT statements. UNION removes duplicate rows from the combined result set, which incurs a performance cost for sorting/deduplication. UNION ALL keeps all duplicates and is significantly faster.

SQLmedWhat is a Materialized View?

Unlike a standard View which runs its underlying query every time it's accessed, a Materialized View stores the query result physically on disk. It speeds up read-heavy, complex queries but requires manual or scheduled refreshes (REFRESH MATERIALIZED VIEW) to reflect underlying data changes.

SQLmedHow do you handle pagination efficiently in PostgreSQL?

OFFSET/LIMIT becomes very slow for deep pages because the DB still scans and discards the offset rows. Cursor-based (keyset) pagination uses a WHERE clause on an indexed column (e.g., WHERE id > last_seen_id LIMIT 10), which is fast regardless of depth.

SQLmedWhat are Triggers in PostgreSQL?

A trigger is a specification that the database should automatically execute a particular function whenever a certain type of operation (INSERT, UPDATE, DELETE) is performed on a specific table or view. Useful for audit trails or complex validations.

SQLmedExplain Stored Procedures vs Functions in PostgreSQL.

Functions return a value and cannot execute transaction control commands (COMMIT, ROLLBACK) inside them. Stored Procedures (introduced in PG 11) don't return a value natively but *can* manage transactions, making them suitable for complex batch jobs.

SQLeasyWhat is a sequence in PostgreSQL?

A sequence is a special kind of database object designed for generating unique, strictly ascending or descending numeric identifiers. Behind the scenes, SERIAL and IDENTITY columns use sequences.

SQLhardHow does full-text search work in PostgreSQL?

PG provides built-in full-text search. It parses documents into tokens (tsvector), reducing them to lexemes (e.g., 'running' -> 'run'). Queries are converted to tsquery. Using GIN indexes on tsvector columns enables highly efficient text searching without needing external engines like Elasticsearch.

SQLmedWhat are dead tuples and how does VACUUM help?

Because of MVCC, UPDATEs and DELETEs don't immediately remove old row versions; they become 'dead tuples'. These bloat the table and slow down scans. The VACUUM process reclaims the storage occupied by dead tuples so it can be reused by new inserts.

SQLmedWhat is the pg_stat_statements extension?

An official extension that records execution statistics of all SQL statements executed. It's the primary tool for identifying slow queries, high-CPU queries, and most frequently run queries in a production DB.

SQLmedHow to perform a cross join and when is it useful?

A CROSS JOIN returns the Cartesian product of rows from tables in the join (every row in table A matched with every row in table B). Useful rarely, usually for generating test data matrices or pairing every user with every possible setting.

SQLeasyWhat are temporary tables in PostgreSQL?

Tables created with 'CREATE TEMP TABLE' are visible only to the current session and are automatically dropped when the session ends. Useful for storing intermediate results during complex calculations.

SQLhardExplain how to implement row-level security (RLS).

RLS restricts which rows a database user can select, insert, update, or delete. You enable it with 'ALTER TABLE t ENABLE ROW LEVEL SECURITY', then create policies using 'CREATE POLICY' that define a boolean expression (e.g., checking if tenant_id matches the current user's setting) applied to every query.

SQLeasyWhat is the RETURNING clause?

In INSERT, UPDATE, or DELETE statements, the RETURNING clause allows you to return values from the modified rows (e.g., INSERT INTO users (name) VALUES ('x') RETURNING id;). Avoids needing a subsequent SELECT.

SQLhardHow do you rename a column or table without downtime?

You cannot simply RENAME in a high-traffic app. 1. Add the new column. 2. Update code to write to both old and new, and read from old. 3. Backfill old data to new column. 4. Update code to read from new. 5. Remove writes to old. 6. Drop old column.

SQLmedWhat is a composite type in PostgreSQL?

A composite type represents the structure of a row or record; it consists of a list of field names and their data types. You can create them explicitly via 'CREATE TYPE' and use them as column types, essentially storing rows within columns.

SQLeasyWhat is the difference between a primary key and a composite primary key?

A primary key uniquely identifies each row in a table and consists of a single column. A composite primary key also uniquely identifies a row, but it is made up of two or more columns combined.

SQLeasyWhat are SQL and NoSQL?

SQL databases are relational, table-based databases with a predefined schema. NoSQL databases are non-relational and can be document-based, key-value pairs, wide-column, or graph databases, with dynamic schemas for unstructured data.

SQLmedWhat is the difference between SQL and NoSQL?

SQL databases are vertically scalable, table-based, and better for complex queries and multi-row transactions (ACID). NoSQL databases are horizontally scalable, document/key-value based, and better for unstructured data, rapid development, and large-scale distributed data.

SQLeasyTell me about LEFT OUTER JOIN in SQL.

A LEFT OUTER JOIN (or simply LEFT JOIN) returns all rows from the left table, and the matched rows from the right table. If there is no match, the result will contain NULL values for the right table's columns.

GoeasyWhat is a goroutine?

A goroutine is a lightweight thread managed by the Go runtime. They are much cheaper than OS threads (starting at ~2KB of memory) and thousands or even millions of them can run concurrently.

GoeasyHow do you use a goroutine?

You start a goroutine by simply placing the 'go' keyword before a function call. For example: `go myFunction()`. This executes the function concurrently with the rest of the program.

GoeasyWhat is defer() in Go?

The `defer` statement schedules a function call to be executed immediately before the function executing the `defer` returns. It's widely used for cleanup actions, like closing files or releasing mutexes.

GomedHow is Go different from other languages?

Go is statically typed and compiled, offering fast execution. It features a unique, built-in concurrency model (goroutines and channels), lacks classes and inheritance (uses structs and composition), has implicit interfaces, fast compilation times, and includes a built-in garbage collector.

GoeasyWhat is the syntax of Golang like?

Go's syntax is heavily influenced by C, but simplified. It omits semicolons at the ends of lines, uses short variable declarations (`:=`), has strict formatting rules (enforced by `go fmt`), and has no `while` loop (only `for`).

GoeasyWhat is a package in Golang?

A package is a way to group functions, types, and variables together. Every Go file starts with a `package` declaration. It provides encapsulation and code organization. The `main` package is special as it defines a standalone executable.

GomedWhat is the go.mod file and why is it used?

The `go.mod` file defines a Go module. It tracks the module's name, the Go version it was written for, and its dependencies (along with their specific versions). It's crucial for reproducible builds and dependency management.

GoeasyWhat are the common data types in Golang?

Go's basic types include string, bool, numeric types (int, int8-int64, uint, float32, float64, complex64, complex128), byte (alias for uint8), and rune (alias for int32). Composite types include arrays, slices, maps, structs, and channels.

GoeasyWhat is the difference between 'go build' and 'go run'?

`go build` compiles the Go source code into an executable binary file but does not run it. `go run` compiles the code into a temporary directory and immediately executes it, which is useful for development and testing.

GoeasyHow do you run Go tests?

You run tests using the `go test` command. Go looks for files ending in `_test.go` and executes functions named `TestXxx(t *testing.T)`. You can run all tests in the current package with `go test` or in all subdirectories with `go test ./...`.

BehavioraleasyDo you know about the Agile method?

Agile is an iterative approach to software development and project management. It emphasizes flexibility, continuous improvement, and rapid delivery of working software through short cycles (sprints), daily stand-ups, and regular feedback.

BehavioraleasyDo you use GitHub?

Yes, I use GitHub for version control and collaboration. It allows tracking changes, managing branches (like feature and bugfix branches), reviewing code through Pull Requests (PRs), and integrating with CI/CD pipelines.

BehavioraleasyDo you write unit test cases?

Yes, writing unit tests is a standard practice to ensure individual components or functions work correctly in isolation. It catches bugs early, documents expected behavior, and provides confidence when refactoring code.

BehavioralmedWhat is the difference between a unit test and an integration test?

A unit test isolates and tests a single component (e.g., a function or method), often mocking dependencies. An integration test verifies that different components or systems (like a database or an external API) work together correctly.

GomedWhat changed between Fiber v2 and Fiber v3 that breaks existing handlers?

Three things bite immediately. Handlers take fiber.Ctx by value rather than *fiber.Ctx. Body parsing moves from c.BodyParser(&req) to c.Bind().JSON(&req). Redirects become c.Redirect().Status(...).To(...). So v2 snippets will not compile against v3 — you cannot copy-paste examples from older tutorials. v3 also ships a secure-by-default proxy that blocks upstreams resolving to private IP ranges, which matters the moment you proxy to Docker hostnames.

GohardYou read a JWT signing key with os.Getenv at package init. What is the failure mode?

An unset variable is silently the empty string, so tokens still sign and verify — with no security at all. There is no error to catch because []byte('') is a perfectly valid HMAC key. The mitigations are a boot-time assertion that required secrets are non-empty (fail fast in init or main), or a fingerprint check that compares the loaded key against a pinned value and log.Fatals on mismatch. This is a real class of bug: the system looks healthy and is completely unauthenticated.

GomedHow do you make a background worker safe against reprocessing the same message?

Do not trust the message. Treat it as a trigger that says 'something may have happened' and re-read the authoritative row before acting. In our points worker, a task.completed event causes the worker to re-load the task and refuse the credit unless the row is actually completed and carries an approving admin's id. Then make the effect itself idempotent with a deterministic key, so even a legitimate replay writes nothing the second time. Trigger plus re-read plus idempotent effect is the whole pattern.

GohardExplain lock ordering and how two code paths can deadlock in a Go service using Postgres row locks.

If one path locks tasks then members, and another locks members then tasks, two concurrent transactions can each hold what the other needs. We hit exactly this: an admin review path locked tasks then members, while the profile-reward path locked members then tasks. The fix was to make the write sets disjoint — the reward path skips any task in a submitted state, which is precisely the set the review path owns — so the two never contend. The general rules are: always acquire locks in the same order everywhere, or make the sets provably disjoint.

GomedWhy would you count string length in runes rather than bytes for a content limit?

len() on a Go string returns bytes, not characters. A 1,000-character Devanagari or emoji-heavy draft is several thousand bytes, so a byte-based minimum would reject it while accepting a 999-character ASCII one. Use utf8.RuneCountInString (or range over the string) when the limit is meant to be about what a human typed. We hit this on a blog minimum-length gate.

GomedWhy put validation limits in a shared function instead of struct tags?

A struct tag cannot interpolate a constant — you have to write the literal. That means two services validating the same field each carry their own literal, free to drift. Ours did: one service capped a list at 10 and another at 20, under a comment claiming they matched. Moving the rule into a shared model.Validate... function called from both services makes drift impossible. Tags stay for the things that are genuinely per-endpoint, like required vs omitempty.

GomedHow do you model a field that has three states — absent, explicitly zero, and set?

A *int gives you two states, not three: nil means absent and a pointer to 0 means zero — but it cannot distinguish a client that genuinely sent 0 from one that submitted an empty text box that got coerced. We used a small struct with a Set() presence flag and a custom UnmarshalJSON that accepts both a JSON number and a numeric string, treating an empty string as unset. This mattered for years-of-experience, where 0 is a real answer (someone in their first job) so absent cannot be spelled as 0.

GomedWhen binding a request body in Fiber, why might your validation error messages disappear?

Fiber's default bind path wraps the cause in a non-wrapping *fiber.Error, so a detailed custom decode error gets flattened into a generic 'invalid body'. Binding with WithoutAutoHandling keeps the original error so you can return the real message. It is a good example of a framework convenience silently degrading your error quality.

GohardWhy is a nil UUID a problem for an audit or activity log?

If the actor column is NOT NULL, an event with a nil actor cannot be inserted and the write fails — usually inside a fire-and-forget publish where nobody sees the error. You either allow a nullable actor for system-originated events, or you skip publishing when there is no real actor, or you reserve a well-known system UUID. The important part is deciding deliberately, because the silent-failure version loses audit records exactly for the automated actions you most want recorded.

GoeasyWhy would you organise a repo as one Go module with many service binaries?

Shared internal packages — ledger, outbox, JWT, encryption — are compile-time shared with no versioning, no replace directives, and no chance of two services running different versions of the money code. The cost is that you cannot upgrade a dependency for one service alone, and it becomes easy to accidentally couple services, so you need a rule like 'services never import each other's packages, only internal/'. It suits a small team shipping together; it suits independent release trains badly.

GomedHow would you enforce that a set of enum-like values stays in sync between Go and the database?

Declare the set once in Go as a slice, and on boot re-assert the database check constraint from that slice. GORM will create a check constraint on a table that has none, but it never alters an existing one — so a new value works on a fresh database and is rejected everywhere older. An idempotent EnsureEnumCheck step that reads the current definition and only rewrites when it does not already permit every value closes the gap.

GomedWhat is the risk of doing case-insensitive matching on an admin-authored type string?

It widens the blast radius of a rename. We match blog and workshop types case-insensitively because those are just routing decisions, but the profile type — which is the one exception allowing a task to be credited with no approver — is matched exactly. Loose matching there would let more spellings self-pay. The residual risk stays either way: the string is admin-editable, so anyone who can rename a definition can change what it means. You limit the blast radius, you do not remove it.

GomedHow do you split a string safely when one of the legal values contains the separator?

Try the whole input against the known set first, and only split if it does not match. One of our expertise options is 'Diversity, Equity & Inclusion (DEI)' — splitting on comma first turns one valid value into two meaningless fragments. Matching the whole string before splitting is load-bearing, and it is the kind of detail that only shows up with real data.

GohardYour service publishes an event fire-and-forget and the publish fails. What is the correct design?

Do not publish from the request path at all. Write an outbox row in the same transaction as the state change, and have a relay poll and publish it. Fire-and-forget publishing has two failure modes you cannot fix at the call site: the transaction commits and the publish fails, so the effect never happens; or the publish succeeds and the transaction rolls back, so you announced something that did not happen. The outbox collapses both into one atomic write.

SQLmedWhat does SELECT ... FOR UPDATE do and when do you need it?

It takes a row-level exclusive lock inside the transaction, so any other transaction trying to lock the same row blocks until you commit. You need it when a decision depends on the current value of a row that you are about to change — a balance check before a debit, or a 'does a task already exist for this member' check before inserting. Without it, two concurrent requests both read the old value and both act on it. Note it locks rows, not gaps, so it does not stop a concurrent insert of a new row — for that you need a unique index.

SQLhardWhy does Postgres allow many NULLs in a unique index, and how is that useful?

In SQL, NULL is not equal to NULL, so uniqueness never fires between two NULLs. That is what lets a members table have both email and phone unique-but-nullable, so email-only and phone-only accounts coexist. The trap is the empty string: Postgres permits many NULLs but only one '', so if you clear a field by writing '' instead of NULL, the second member you clear collides. Any 'clear this field' path must write NULL explicitly.

SQLhardYou soft-delete rows with a deleted_at column and a unique index on email. What breaks?

The lookup and the constraint disagree. A default ORM query appends deleted_at IS NULL so it cannot see the soft-deleted row, but a plain unique index still holds that email — so the insert collides with a row your code believes does not exist, and the user gets a duplicate-key 500 they can never get past. Two fixes: make the index partial (WHERE deleted_at IS NULL), so soft-deleted rows release the value; or look up unscoped so you find the row whatever its state. We use the partial index for referrals and the unscoped lookup for members, because a returning member must never end up with a second account.

SQLmedHow do you make a table append-only at the database level?

A trigger on the table that raises an exception on UPDATE and DELETE. Application-level discipline is not enough — an ad-hoc psql session, a migration, or a future teammate's ORM call all bypass it. For our points ledger the trigger is the guarantee that history cannot be rewritten; corrections are new reversing rows, and those reversals are themselves idempotent per original transaction.

SQLmedWhy write balance = balance + ? rather than balance = <computed value>?

A relative update is computed by the database from the row's current value at write time, so it cannot clobber a concurrent writer's arithmetic. An absolute update writes a number your application computed from a possibly stale read. Combined with a FOR UPDATE lock on the same row inside the same transaction, the relative form makes lost updates structurally impossible.

SQLhardWhat causes 'column reference is ambiguous' and how do you avoid it in shared query scopes?

Two joined tables have a column of the same name and the predicate does not say which. It bites specifically in shared scope functions: a filter written as status = ? works fine on the plain list query, then the stats query joins another table that also has status and every filtered request 500s. The rule is to always qualify predicates in a reusable scope — referrals.status, referrals.created_at — even when it looks redundant today.

SQLmedHow do you enforce exactly-once financial writes using only the database?

A unique index on a deterministic idempotency key. Build the key from what happened — source type, source id, action — so the same real-world event always produces the same key. Concurrent retries then collide inside the database, the whole transaction rolls back, and you catch the unique-violation outside the transaction and return the original success response. You are using the database as the concurrency control rather than trying to coordinate in application code.

SQLhardWhy can a deterministically encrypted column still back a unique index and an equality lookup?

Because the same plaintext always produces the same ciphertext — we derive the AES-GCM nonce as an HMAC of the plaintext instead of using a random one. So WHERE email = <ciphertext> works, and a unique index on the column still catches duplicates. The trade-off is that equality is observable: anyone with database access can see that two rows hold the same value, and can confirm a guessed value by encrypting it and looking for the result. Randomised encryption removes that leak and removes every lookup that makes the system work.

SQLmedWhy is ordering an admin list by id different from ordering it by a timestamp column?

They agree until a row is mutated. A submission that gets sent back and resubmitted keeps its id but gets a new submitted_at — so ordering by id sorted it among items weeks older than the date printed next to it. Order by the timestamp the UI actually displays, with the id as a tiebreaker so a page boundary cannot repeat or skip a row, and use NULLS LAST in both directions so undated rows do not float to the top when you flip the sort.

SQLmedHow do you filter a date range when the user supplies a bare YYYY-MM-DD?

A bare date names a whole day, so advance the end date to the next midnight and compare with strict less-than. Comparing <= against the date alone truncates to midnight and silently drops everything written that day after 00:00 — a row at 23:59 vanishes from its own day's report. And an unparseable date should be a 400, not a silently ignored filter: a bad date changes the result set, so failing loudly is correct.

SQLmedWhat is a partial unique index and when do you need one over a plain unique index?

A unique index with a WHERE clause, so uniqueness only applies to rows matching the predicate. We use one on (member_id, referee_email) WHERE deleted_at IS NULL so a soft-deleted referral does not block a legitimate re-referral. The important catch: ON CONFLICT can only use an index whose predicate the statement repeats — so if you rely on ON CONFLICT for an upsert, a partial index forces you to restate the predicate, and sometimes a plain index is the better choice for that reason.

SQLhardHow do you reconcile a cached aggregate against its source of truth?

A background job that recomputes the aggregate per entity and reports the difference rather than silently correcting it. Ours runs every 24 hours over the points ledger, sums transactions per member, compares with the cached balance and returns any drift. Auto-correcting would hide the bug that caused the drift; reporting it means someone investigates. The log always wins if they disagree.

SQLmedWhy store money as numeric rather than a float?

Binary floating point cannot represent most decimal fractions exactly, so repeated arithmetic accumulates error and two paths that should agree stop agreeing. Use numeric with an explicit scale — ours is numeric(14,2) — and a decimal library in the application rather than float64. For a points system where 1 point is 1 rupee, a rounding error is a financial discrepancy someone has to explain.

APIhardExplain the transactional outbox pattern.

You need to change state and publish an event atomically, but the database and the broker are two systems with no shared transaction. So you write the event as a row in an outbox table inside the same transaction as the state change — one atomic commit. A separate relay polls pending rows and publishes them to the broker, marking them processed, with a bounded retry count before parking them as failed. You get at-least-once delivery, ordering per key if you set the message key, and a durable record of anything that could not be delivered.

APIhardHow do you achieve exactly-once processing in an event-driven system?

You do not. You get at-least-once delivery and you make the effect idempotent, and together those are indistinguishable from exactly-once from the outside. Concretely: the producer uses an outbox so nothing is lost, the consumer derives a deterministic key from the event, and the write collides on a unique index if it has already been applied. Anyone who claims exactly-once delivery is either wrong or is describing idempotent effects.

APImedWhy should an idempotency key be deterministic rather than client-supplied or random?

A random key makes every retry a new operation, which defeats the point. A client-supplied key works for public APIs but means a buggy or hostile client can either collide two different operations or force a duplicate. Deriving the key from what happened — source type, source id, action, e.g. task:<uuid>:completed — means the same real event always produces the same key, no matter which process, retry or replay produced it.

APIhardA webhook authenticates using a token in the request body rather than the Authorization header. Is that acceptable?

It is acceptable when the provider's contract dictates it — you do not get to choose their client. What matters is what you do with it: validate the token's signature, and take the identity from the token's claims, never from the other body fields. Our Xoxoday callbacks carry the member's token in an auth_token field; we verify it and then require its unique_id claim to match the stored member id, so the body can never assert whose points to spend. Route it outside your normal bearer middleware rather than weakening that middleware.

APImedShould a rate limiter key on IP or on user?

Depends on what you are protecting. Edge protection against floods keys on IP. But a per-member limit on an expensive or money-moving endpoint must key on the member id — many members share one office egress IP, and an IP-keyed limit would throttle a whole company because one person was active. We use IP at the gateway and member id on redemption routes.

APIhardWhy does an API gateway set X-Forwarded-For instead of appending to it?

Because on an untrusted hop an inbound XFF is attacker input. c.IP() is the most trustworthy address the gateway has — the actual TCP peer — so it replaces the header rather than appending, which would hand a downstream service a chain whose left-hand entries are forged. The other half is that downstream services must not believe the header unless the peer is a configured trusted proxy; otherwise anyone can assert any client address and win a whitelist exemption.

APImedWhen should an endpoint return 409 instead of 400?

400 means the request itself is malformed or invalid in isolation. 409 means the request is well-formed but conflicts with the current state of the resource — the item is not awaiting review, a non-repeatable task has already been attempted, a draft already exists for this definition. The practical benefit is that a client can treat 409 as 'refresh and reconsider' rather than 'fix your payload'. We also return the id of the conflicting resource in the 409 body where one exists, so the client can navigate to it instead of showing an error.

APImedHow do you paginate a merged list assembled from several backend sources?

You cannot ask each source for its own page N — the union does not line up, so rows repeat and vanish across pages. Either over-fetch page * size from every source, merge, sort and slice (correct but increasingly expensive, and you must stop offering Next once the sources' per-page cap makes the merge unsound), or push the merge into the database as a single query with a shared sort key. What you must not do is quietly truncate and present it as a complete page.

APImedHow do you count items for a UI badge without fetching them?

Ask each list endpoint for a single row and read its pagination meta.total, or expose a dedicated counts endpoint that runs the same predicates. Our approvals page originally fetched seven queues at 100 rows each — hundreds of fully-joined rows carrying decrypted PII — purely to produce seven integers, and it silently reported a queue of 101 as 100. Counting and listing are different operations and should not share a code path.

APImedWhy enumerate a permission catalog from code instead of storing roles in a table?

So the catalog cannot drift from what is actually enforced. Our RBAC service reads the same compiled grant table the middleware enforces, which means retiring a permission in one place removes it from the API with no other edit. The honest consequence is that custom roles are impossible, so the create-role endpoint returns 405 saying roles are fixed rather than pretending to accept one. Faking success on an operation you do not support is worse than refusing it.

APIhardTwo admin sessions, one account. How do you make the newest login win?

Make the session a row, put its id in the access token as a claim, and validate the claim against the row on every request. A new login revokes every live session for that admin inside one locked transaction and inserts the new one. The displaced device then fails on its next request rather than at token expiry, and refresh only mints against a live session — so a displaced device cannot refresh its way back in. Keep revoked rows rather than upserting, so the table doubles as a sign-in history with a reason for each ending.

APImedWhy return two different 401 codes for an ended session?

Because the messages differ. Being told 'your account signed in on another device' when you simply signed out is alarming and wrong. So we distinguish session_superseded — read off the revocation reason and only ever a genuine take-over — from session_ended for everything else: signed out, expired, unknown session id, or a session id belonging to a different admin. The status code is the same; the code field is what lets the client show the right sentence.

SysDesignhardDesign a points system where 1 point equals 1 rupee. What are the core invariants?

One: the log is the truth and any balance column is a cache, with the invariant balance == SUM(transactions) per member. Two: exactly one code path may move points, and it locks the member row, checks a deterministic idempotency key before checking sufficiency, refuses a debit that would go negative, and writes a signed row recording the resulting balance. Three: the transaction table is append-only, enforced by a database trigger — corrections are reversing rows. Four: a reconciliation job reports drift rather than silently fixing it. Five: no endpoint writes a balance directly, ideally none exists at all.

SysDesignhardWhy check the idempotency key before the balance-sufficiency check?

Ordering changes what a replay reports. If you check sufficiency first, a replayed debit for a member who has since spent down returns 'insufficient balance' — a confusing, wrong answer for an operation that already succeeded and is not being applied again. Checking the key first makes the replay report 'already applied'. It is one line of ordering and it is the difference between a client that retries sanely and one that raises a support ticket.

SysDesignhardA payment provider times out. Do you refund or confirm?

Neither — you record ambiguity as a first-class state. A timeout is not a failure: the provider may have issued the voucher. Auto-restoring the points double-pays; auto-confirming may hand out something that does not exist. Our saga has a pending_reconciliation state for exactly this, and a human checks the provider's records before any further ledger action. Definitive failures are safe to compensate automatically; ambiguous ones are not, and conflating them is how money leaks.

SysDesignhardWhy should a consumer re-read the database instead of trusting the event payload?

Because the event is a claim about the past and the row is the present. A replay, a stale event published before a bug fix, or one published by hand should not be able to cause a credit. So the worker re-loads the source row and refuses unless it is genuinely in the state that justifies the action. The payload is still useful for values you deliberately want frozen at the time of the decision — we credit the reward amount captured in the event, so a later edit to the reward cannot retroactively change what was paid.

SysDesignmedWhere should database migrations run in a microservices system that shares one database?

In exactly one place. Ours run in the gateway on boot and no service migrates anything, so there is no second list to keep in sync — which is how a table once went missing entirely, declared in one service's list and absent from the other. The cost is a boot-order dependency: against an empty database there is a window where services query tables that do not exist yet. Normal queries recover; the thing that does not recover is a message consumed in that window, because the consumer commits its offset on read. So on a fresh database you boot the migrator alone first.

SysDesignmedWhy is auto-creation of message-broker topics usually disabled, and what does that cost you?

Auto-created topics get default partition and replication settings, which is almost never what you want for a topic that carries money-adjacent events. The cost is a silent deploy trap: adding a topic in code now requires adding it to the broker's provisioning per environment. Miss it and every publish fails with 'unknown topic', the outbox row exhausts its retries and parks as failed, and the downstream effect simply never happens with no user-visible error. The mitigation is an operator-visible failed-events view with a manual retry.

SysDesignhardShould a credit happen inside the approval request or asynchronously?

Asynchronously, so the two fail independently. The approval is a decision that must be recorded whether or not the credit succeeds; the credit is a money movement that must eventually happen whether or not the admin's connection survived. The outbox makes the second durable and retryable without holding the first open. The honest cost: the response cannot report the new balance, so the UI shows the state change immediately and the balance catches up on the next poll — and a permanently failed event means an approved-but-unpaid item that only an operator view will surface.

SysDesignmedWhy would you deliberately not put a circuit breaker on an API gateway?

Because a single breaker at the gateway counts failures across every route, so one bad downstream opens it and blackholes every unrelated service with it. Breakers belong at each service's own front door, where the failures they count are that service's own. Ours are per-service and per-process: open after N 5xx, fast-fail for a timeout window, then half-open with a couple of trial requests before closing. Note also that 4xx must not trip a breaker — that is the caller's fault, not the service's.

SysDesignhardYour reverse proxy retries failed requests. Why is that dangerous for POST?

Because the client library usually cannot distinguish 'the connection closed before the upstream read the request' from 'the upstream read the request and then died before answering'. Both surface as the same connection error. Retrying the first is correct; retrying the second re-applies an operation that already happened. fasthttp encodes exactly this — it replays GET, HEAD and PUT and refuses POST — which is why our symptom was admins seeing intermittent 5xx on approve while every page load looked fine. The right fix was to stop handing out stale pooled connections, not to add a POST retry.

SysDesignmedHow do you decide what belongs in an event payload versus what the consumer should look up?

Put in the payload anything you want frozen at decision time, and look up anything that must reflect the present. We freeze the reward amount and the item's label — so a later rename cannot rewrite what an already-sent email says was completed — and we look up the task's status and approver, because those must be true now. Getting this backwards produces either emails that lie about the past or credits that act on stale state.

SysDesignmedHow do you prevent duplicate notification emails in an at-least-once pipeline?

Claim the event before sending. A notification log table keyed on the event's unique id, with the claim inserted first and released only if the send fails, means a redelivered message finds the claim and does nothing. The ordering matters: claim-then-send risks a lost email if the process dies between the two, send-then-claim risks a duplicate. For email we prefer the lost-email risk plus a release-on-failure path, because a duplicate 'your account was approved' is more confusing than a delayed one.

FintechhardWhat does it change about a design when points are redeemable for real money?

Everything about the write path. A duplicate write is a financial loss, so idempotency becomes structural rather than a nicety. Corrections cannot be edits, because an auditable history is the whole point — so the transaction table is append-only and corrections are reversing rows. Ambiguity has to be a state rather than a guess. And you become very reluctant to allow any manual movement path, because a single admin request that can mint currency is both a fraud vector and an operational hazard.

FintechmedWhy remove an admin points-adjustment endpoint rather than keep it audited?

Because an audited endpoint is still an endpoint that mints currency on a single request. Removing it means every credit now originates from an approved item flowing through the event pipeline and every debit from the redemption family, so no admin request path writes the ledger at all. The trade-off is real and worth stating: correcting a balance now needs a deliberate code change or a schema-level fix rather than a form. That is a defensible choice for a system this size and a bad one for a system with a large support operation.

FintechmedExplain reserve-then-confirm for a redemption flow.

Debit the points up front and leave the redemption pending, then call the provider. Reserving first means the member cannot spend the same points twice while the provider call is in flight. If the provider definitively fails, you compensate with a reversing credit keyed idempotently; if it succeeds, you mark the redemption completed; if the answer is ambiguous, you park it for reconciliation. The alternative — call first, debit after — leaves you having bought a voucher for someone who cannot pay for it.

FintechhardHow do you compute total earned and total spent so that balance equals earned minus spent?

Partition by source type, not by sign. Total earned is every non-redemption credit; total spent is the redemption family netted, so a refund cancels its original debit. The tempting version — earned = SUM(amount > 0) — counts a refund's reversing credit as fresh earnings, so the equation stops holding and the member appears to have earned points they never earned. It is a small query decision that determines whether your headline numbers are internally consistent.

FintechmedWhy enforce a minimum redemption amount?

Provider fees and operational cost per order do not scale down, so tiny redemptions cost more to fulfil than they are worth, and they multiply the number of ledger movements and support cases. Ours is 500 points. The design point is where you enforce it: the check belongs at the endpoint and the UI should short-circuit before even attempting SSO into the storefront, so a member below the threshold gets a clear message rather than a failed order.

FintechmedWhat does an append-only ledger give you that an updatable balances table does not?

Auditability and recoverability. You can answer why a balance is what it is, replay it, and detect drift by recomputing. You can prove that a correction happened rather than discovering that a number changed. And because history cannot be rewritten, a bug in one write path cannot quietly erase the evidence of its own effects. The cost is storage and a slightly more complex read path, both of which are trivially worth it once points are money.

Next/ReacthardYou refresh a short-lived API token in a Next.js auth callback and requests still 401. Why?

Because the callback writes the refreshed token onto the response — too late for a request that is already in flight in the same render or route handler. You need the rotation in the fetch layer as well: decode the session, refresh if the token has lapsed, retry once on a 401, and write the new token back where you can. Our symptom was that the first mutation attempted more than fifteen minutes after the cookie was written failed, and the user's retry succeeded only because the failed attempt had healed the cookie.

Next/ReactmedIf your fetch wrapper retries once on 401, what constraint does that put on request bodies?

The body must be replayable. A string or FormData can be sent twice; a stream cannot — it is already consumed, so the retry sends an empty body and fails in a much more confusing way. So the wrapper must accept a materialised body rather than piping a request stream straight through. This is an easy thing to get wrong when proxying an incoming request onward.

Next/ReacthardWhy does redirecting an expired session straight to the home page cause a redirect loop?

Because a render cannot clear a cookie. The middleware or proxy still reads the dead session as live, re-renders the protected page, that page's data fetch 401s, and you redirect again. The fix is to route through a handler that deletes the cookie and then redirects. Related: answer non-GET requests from a dead session with a 401 JSON body rather than a redirect, because fetch follows a 302 as a GET and a client checking res.ok will read the login page as a successful response.

Next/ReacthardWhat causes a hydration mismatch when rendering dates, and how do you fix it?

The server and the browser format in different time zones and locales. A container running in UTC and a reviewer's browser in IST disagree about the calendar date for anything after 18:30 UTC — the server writes one date, the client re-renders another, and React throws a hydration error. Fix by pinning both locale and time zone explicitly at format time, or by rendering an ISO string on the server and formatting client-side after mount. The same class of bug applies to anything read during render — Date.now, random, localStorage.

Next/ReactmedHow do you read a value from localStorage without breaking server rendering?

Do not seed state from it. The server has no localStorage, so initialising state from it hydrates differently than it rendered. Read it after mount, or subscribe with useSyncExternalStore providing a server snapshot — which is what we do for a persisted sort-order preference, so the server renders the default and the client swaps to the stored value in a way React is aware of.

Next/ReactmedWhy disable prefetch on tab and pagination links in a server-rendered dashboard?

Because each of those URLs is a full server render of a data-heavy page. Letting the router warm eight tabs on hover multiplies the cost of a single visit by eight, and in our case those requests came from the same IP through the same gateway, so they also started drawing rate-limit 429s that made the app look broken. Prefetch is a good default for cheap pages and a bad one for expensive ones.

Next/ReactmedHow do you build a theme-aware UI that does not flash on load?

Persist the choice in a cookie, apply it pre-paint with a small inline script in the document head that sets a class on the root element, and drive every colour from CSS-variable tokens rather than literal colours. The tokens are the part people skip and then regret: a stray literal white background is the classic cause of one element that refuses to flip with the theme.

DockermedIn a multi-service repo with one go.mod at the root, what is the Docker build context?

The repository root, not the service directory — the build needs go.mod and the shared internal packages, so you build with docker build -f services/<name>/Dockerfile . from the root. A Dockerfile whose context is its own directory cannot see the module file and fails to resolve imports. The trade-off is a larger context, which you manage with .dockerignore.

DockermedWhy must a Compose service name match the hostname your code dials?

Because Compose's DNS resolves service names on the shared network — that name is the hostname. We had exactly this bug: the gateway proxied to one hostname while three environment-specific compose files named the service something else, so the route worked locally and could not resolve in those environments. The lesson is that hostnames in code and service names in compose are a single contract, and drifting them produces environment-only failures.

DockermedWhat is the risk of depends_on for ordering in Compose?

It orders container starts, not readiness — the dependency is running, not necessarily migrated or serving. In our stack the gateway owns migrations and starts last, so against an empty database there is a brief window where services query tables that do not exist. Ordinary queries recover; a broker message consumed in that window is lost, because the consumer commits its offset on read. Either use healthcheck-gated conditions, or make it an explicit deploy step: run the migrator alone first.

DockermedWhy raise the HTTP read buffer size on a browser-facing Go service?

The default request-header buffer is 4 KiB and browsers can exceed it easily — cookies are shared across every port on localhost, so a dev machine running several apps accumulates enough cookie to blow it, and the server answers 431 Request Header Fields Too Large. Raising it to 16 KiB on browser-facing services fixes it. Internal services that only ever receive service-to-service calls do not need it.

BehavioralmedTell me about a bug that got past your tests.

Use your own story first if you have one — this is the fallback, and tell it as something you observed rather than something you fixed. On a system I work in, sending a submission back for rework returned a 500 in every live environment while passing the whole test suite. The ORM creates a check constraint on a table that has none but never alters an existing one, so a newly added status value existed only on freshly created databases — which is all the tests ever ran against. The fix was an idempotent constraint re-assertion on boot. What I took from watching it: a test suite that always starts from a fresh schema is structurally blind to migration bugs, and anything schema-shaped needs a test against an already-migrated database. The lesson is genuinely yours to claim; the debugging is not.

BehavioralmedHow do you approach an intermittent bug that only shows up on some requests?

Find what the failing requests have in common before assuming a flaky dependency. A worked example from a system I contribute to: admins saw intermittent 5xx on approvals but never on page loads, and a second click always worked. The shared property was the HTTP method — the proxy pool was handing out connections the upstream had already closed, and the client silently retries GET, HEAD and PUT but not POST, so only mutations surfaced it. The fix was cutting the idle connection lifetime, not adding a retry, because the client cannot tell a request that never arrived from one already applied. The transferable habit is looking for the invariant across failures before reaching for a cause.

BehavioralmedDescribe a design decision that traded convenience for safety, and how you would argue it.

A clean example from a system I work in: the admin points-adjustment endpoint was removed entirely — route, request type, and the permission guarding it. It was audited and idempotent, but it was still a path where one admin request could mint currency worth real rupees. Afterwards every credit originates from an approved item flowing through the event pipeline and every debit from the redemption family, so nothing writes the ledger on an admin request path. The argument that makes it defensible is naming the cost out loud: correcting a balance now needs a deliberate change rather than a form, which is right at this support volume and would be wrong at ten times the size.

BehavioralmedHow do you communicate a risk you have reduced but not eliminated?

Name it in writing next to the code that carries it, and describe the guard as what it actually does. An example: one task type can be credited without a reviewer, matched as an exact string — so an admin who can rename a definition to that string makes every task on it self-payable. The honest note says the narrow match limits the blast radius but does not remove the risk. The alternative — writing it up as if the guard closed the hole — means the next person discovers the gap themselves and reasonably assumes nobody thought about it. Documented residual risk is a decision; undocumented residual risk is a trap.

BehavioralmedWhen is retrying a failed request the wrong fix, and how would you argue against it?

When the failure modes are indistinguishable. On a system I work in, the tempting fix for intermittent approval failures was to make the proxy retry POST the way it retries GET. It is wrong because the HTTP client reports closed-before-reading-the-request and read-the-request-then-died as the same error — so a retry cannot tell a request that never arrived from one the upstream already applied. Replaying an approve would approve twice, and downstream that pays twice. The argument that lands is the concrete consequence, not the principle: not that retries are unsafe, but that this specific retry pays a member twice. Then fix the real cause — stale pooled connections — and pin both halves with a test so nobody re-introduces it.

RedismedWhen would you deliberately not add a cache to a system like this?

When correctness beats latency and the read volume does not justify it. Our balance reads are single-row lookups on an indexed primary key inside a system where being stale by one transaction is a money-facing bug. Caching that would add an invalidation path to the one part of the system we most want to have exactly one write path. We do keep the Redis wiring in place — commented, not deleted — so turning it on for genuinely hot read-only data like a reward catalog is uncommenting rather than rewriting. Being able to say why you did not add something is as valuable as adding it.

SysDesignhardHow do you design a rewards or gamification feature where the points convert to real money?

You stop treating it as gamification. Badges and streaks can be sloppy because the worst case is a wrong number on a profile; a redeemable point is currency, so a duplicate credit is a financial loss. That means an append-only ledger rather than an incrementable counter, deterministic idempotency keys on every movement, credits applied off the request path by a worker that re-reads the source row, and ideally no admin endpoint that can adjust a balance at all. The framing that sells it in an interview: a growth feature whose currency is real money is a fraud surface, and the incentives point the wrong way by design — you are paying people to invite more people and produce more content, which is exactly the shape of a system that attracts abuse.

SysDesignmedIn a referral programme that pays real money, when should the referrer be paid?

As late as you can defend, and behind a human check. Paying on invite pays for typing email addresses. Paying on signup pays for creating accounts. Paying when the referee is actually approved into the community means a reviewer looked at a real person before any money moved. In a product where membership is curated anyway, tying the reward to the approval that was already going to happen costs nothing and removes the obvious abuse. Corollaries worth mentioning: cap referrals per member with a runtime setting rather than a constant, verify the address is deliverable before writing the row, and let several referrers of the same person each earn, because each did the work.

SysDesignmedHow would you model 'ways to earn' so that adding a new one is not a deploy?

Make the task a row, not a feature. A definition carries its reward, card copy, call-to-action, an optional prerequisite type, a repeatable flag and an active flag; a member's attempt is a separate row referencing it. Adding 'follow our LinkedIn page' or 'attend this webinar' then becomes admin-authored data. The trade-offs to name: the type string becomes load-bearing, so a rename can silently change behaviour or break a prerequisite chain; validation that depends on the type has to be matched carefully; and you need a guard so system-raised rewards cannot also be claimed manually.

APImedHow do you gate a community so membership is curated without blocking sign-in?

Separate authentication from membership. Sign-in with an identity provider creates the account immediately and marks it active but unapproved; a second onboarding step collects the details a reviewer needs; approval is an explicit admin decision gated on that onboarding being complete. The member can sign in and see a limited surface throughout, so you never have someone stuck at a door with no feedback. Blocked states must be handled at the callback rather than after issuing a token — a rejected or suspended member should be turned away with a distinct reason each, not handed a token the next request rejects.

APImedWhy check 'rejected' before 'suspended' when refusing a sign-in?

Because the message is the product. A rejected applicant was never a member; telling them their account is suspended implies they had one and lost it. A suspended member is the opposite case. Both are a 401, so the distinction lives in a reason code the client maps to copy. It is the same discipline as distinguishing a session that was superseded by a new device from one that simply ended — the status code is the contract, the code field is what lets the UI say something true.

SysDesignmedHow do you sequence users through a product funnel without hardcoding the order?

Give each step an optional prerequisite expressed as data — a type string that must already be completed. The board then computes locked and unlocked per member, and reordering the funnel is an admin edit. The trap to know: if the prerequisite names a type rather than a specific item, then anything gated behind that type unlocks on the first completed item of that type. That is usually what you want for a funnel and is wrong the moment you need 'complete this specific one first', so decide which you mean before the data is live.

SysDesignmedWhat changes about your engineering when a platform holds real professional identities?

PII stops being a checkbox. Members join a curated network under their real name, company and designation, and their professional reputation is the reason they are there — so contact details are encrypted at rest, the admin surfaces that decrypt them sit behind a higher permission than the ones that render dashboards, and a role can be given queue access without being given member contact details. It also changes deletion: you suspend rather than delete, because a member is referenced from their content, referrals and every ledger row, and a soft delete leaves all of that pointing at an invisible row.

SysDesignmedA community feature lets members post questions and answer each other. What are the first design questions you would ask?

What the unit of moderation is, how the feed is ordered, and what notifications fan out. Moderation: can anything be posted immediately, or does it queue for review, and is the reviewer the same role that reviews other content? Ordering: recency is simplest and degrades fastest — decide early whether you need answer counts, acceptance or votes, because retrofitting a ranking signal onto a feed with history is painful. Notifications: an answer notifies the asker, which is a fan-out per post and the thing most likely to become a performance or spam problem. And read patterns: a question with its answers is a classic N+1 if you render a list naively.

APImedHow would you paginate a community feed that is being written to while users page through it?

Not with OFFSET. Offset pagination over a live feed repeats and skips rows as items are inserted above the cursor. Use keyset pagination — order by a stable sort key plus a tiebreaker id, and page with a WHERE clause against the last row seen. The tiebreaker matters: ordering by a timestamp alone means two rows written in the same instant can straddle a page boundary and one is never shown. It also makes deep pages cheap, because the database seeks rather than counting past everything before them.

FintechmedWhat is the fraud model for an engagement-rewards feature, and where do you put the controls?

The abuse paths are self-dealing (claim a reward for work not done), duplication (get paid twice for one action) and sybil (create or invite fake members to farm referrals). The controls map onto those: every paid submission goes through human review, so a reward is never self-certifying; every credit carries a deterministic idempotency key so a retry or replay pays once; and membership is approval-gated so a referral pays only after a reviewer saw a real person. Then note the residual risk honestly — anything self-completing, like a profile-completion reward, is the exception that has to be justified and narrowly scoped.

BehavioralmedHow would you explain a heavily-engineered feature to someone who thinks it is over-built?

Lead with the consequence, not the technique. Saying 'we use an append-only ledger with idempotency keys' invites the question; saying 'a point is a rupee and members redeem them for vouchers, so a duplicate write is money we cannot get back' answers it. Then show the restraint alongside the rigour — in the same system, blogs and workshops deliberately reuse the plain task pipeline rather than each growing their own state machine. Demonstrating where you did not add machinery is what makes the places you did add it read as judgement rather than habit.

Next/ReactmedHow do you keep an admin review queue's tab counts accurate without fetching every row?

Ask each list endpoint for a single row and read its pagination total, or expose a counts endpoint running the same predicates. Fetching a hundred rows per queue to render a few integers is expensive in a way that scales badly, it drags decrypted PII across the wire for no reason, and it silently misreports anything past the cap — a queue of 101 shows as 100. Counting and listing look similar and are different operations. A related rule for the same page: make the counts role-aware, so a badge never promises rows the user's permissions will not let the popup show.

GoeasyWhat is the difference between static and dynamic variable declaration in Go?

Both are statically typed at compile time — the difference is only whether you write the type. Explicit: var age int = 29. Inferred (short declaration): age := 29, where the compiler derives the type from the right-hand side. The := form only works inside a function body, so package-level variables must use var. Two more rules people trip on: := requires at least one new variable on the left, and an untyped constant like 3 infers to int while 3.0 infers to float64, so x := 3 followed by x = 3.5 will not compile.

GoeasyHow do constants work in Go, and what is an untyped constant?

const declares a compile-time value — only booleans, runes, strings and numbers can be constants, so you cannot write const t = time.Now(). The interesting part is that a constant without a declared type is untyped and carries arbitrary precision: const big = 1 << 62 is fine, and const Pi = 3.14159 can be used anywhere a float32, float64 or complex is expected because it converts at the point of use. Give a constant an explicit type (const timeout time.Duration = 30) only when you want to force that type at every call site.

GoeasyWhat is the difference between a buffered and an unbuffered channel?

An unbuffered channel (make(chan int)) is a synchronisation point — the send blocks until a receiver is ready and vice versa, so a successful send means the value was handed off. A buffered channel (make(chan int, 10)) accepts up to its capacity without a receiver; the sender blocks only when the buffer is full, and the receiver blocks only when it is empty. Use unbuffered when you need the handoff guarantee, buffered when you want to absorb bursts or decouple a producer from a slower consumer. A buffered channel does not make sends non-blocking forever — it just moves where the backpressure appears, which is usually what you want.

GoeasyWhat is the difference between a raw and an interpreted string literal?

Interpreted literals use double quotes and process escape sequences: \n is a newline, \t a tab, \" a quote. Raw literals use backticks and take the bytes exactly as written — no escapes are processed, and a newline inside the literal is a real newline. Raw literals are the right choice for regexes (so you write \d rather than \\d), Windows paths, SQL blocks and embedded JSON. The one limitation: a raw literal cannot contain a backtick, and carriage returns are stripped from it.

GoeasyHow do pointers work in Go, and what can you not do with them?

&x takes the address of x, *p dereferences the pointer, and *T is the type of a pointer to T. What Go deliberately removes is pointer arithmetic — you cannot do p++ to walk memory, which eliminates a whole class of C bugs. There is also no dangling-pointer problem: taking the address of a local is safe because escape analysis moves that variable to the heap and the GC keeps it alive as long as the pointer does. new(T) allocates a zeroed T and returns *T; most code prefers &T{...} because it can set fields at the same time.

GomedWhen should you pass a pointer versus a value?

Pass a pointer when the callee must mutate the caller's value, when the type is large enough that copying it per call is measurable, or when the type contains something that must not be copied (a sync.Mutex, a struct embedding one). Pass a value when the type is small and you want the callee to be unable to affect you — a copy is a free immutability guarantee, and small values often stay on the stack rather than escaping to the heap. The practical rule: be consistent per type. Mixing value and pointer receivers on the same type is the usual source of the surprise that only *T satisfies an interface when any method has a pointer receiver.

GomedIs map iteration order in Go guaranteed? How are maps implemented?

It is explicitly randomised. A map is a hash table of buckets holding 8 key/value pairs each, and range starts at a random bucket and a random offset within it, so two iterations over the same unmodified map can differ. This is deliberate — it stops code from accidentally depending on order that the implementation never promised. If you need deterministic output, collect the keys into a slice, sort it, and range over that. Other things to know: a map value is not addressable (you cannot do m[k].Field = v for a struct value), maps are not safe for concurrent read/write and will panic with a fatal concurrent map writes error, and you cannot take a pointer into a map because it rehashes on growth.

GomedWhat are the rules around closing a channel?

Only the sender closes, and only once — closing a closed channel or sending on a closed channel panics, and there is no way to ask a channel whether it is closed. Receiving from a closed channel returns the zero value immediately, and the two-value form v, ok := <-ch reports ok as false to distinguish that from a real zero value. Closing is a broadcast, which is why closing a done channel is the standard way to signal N goroutines at once. You do not have to close a channel to let it be garbage collected — close it only when the receiver needs to know the stream ended, for example when it is ranging with for v := range ch. With multiple senders, none of them can safely close; use a sync.WaitGroup and have a coordinating goroutine close after Wait.

GomedHow do panic and recover work, and when should you use them?

panic unwinds the stack running deferred functions as it goes; recover, called directly inside a deferred function, stops that unwinding and returns the panic value. Outside a deferred function recover returns nil and does nothing. The idiom is defer func() { if r := recover(); r != nil { ... } }(). Use it at a boundary you own — an HTTP middleware that turns a handler panic into a 500 rather than killing the process, or a library that must not panic across its public API — and convert the recovered value into an error rather than swallowing it. Do not use panic for ordinary control flow; return an error. Two caveats: recover only works for the goroutine that panicked, so a panic in a spawned goroutine still crashes the process, and some runtime failures such as a concurrent map write are fatal errors that recover cannot catch.

GoeasyWhat is the difference between a package and a module?

A package is the unit of compilation and naming — one directory of .go files sharing a package clause, where identifiers starting with a capital letter are exported. A module is the unit of versioning and distribution — a tree of packages rooted at a go.mod file that declares the module path and its dependency requirements. One module normally contains many packages. The module path plus the directory gives the import path, so module github.com/acme/api with a directory internal/store is imported as github.com/acme/api/internal/store. Directories named internal are importable only from within the module subtree that contains them, which is the standard way to keep something public within a repo but private outside it.

GomedWhat is the difference between a defined type and a type alias?

type Celsius float64 defines a new type — it has the same underlying representation as float64 but is a distinct type, so it needs an explicit conversion and can carry its own methods. type MyFloat = float64 is an alias — it is a second name for exactly the same type, they are interchangeable everywhere, and you cannot define methods on it. Defined types are the tool for making units and IDs non-interchangeable (a UserID cannot be passed where an OrderID is expected). Aliases exist mainly to move a type between packages without breaking callers during a refactor.

GomedWhat is the difference between a type conversion and a type assertion?

A conversion, T(v), is checked at compile time and works between types with compatible underlying representations — int to float64, []byte to string, a defined type to its underlying type. It never fails at runtime, though it can lose data (float64 to int truncates; an int to a narrower int wraps). An assertion, v.(T), applies only to interface values and is checked at runtime: it extracts the concrete dynamic type. The single-value form panics on a mismatch, so use the comma-ok form s, ok := v.(string) unless a failure genuinely is a bug. If you need to branch on several possible types, use a type switch rather than a chain of assertions.

GoeasyWhat is the blank identifier used for?

The underscore _ is a write-only placeholder that satisfies the compiler where a name is required but the value is not wanted. Common uses: discarding one return value (_, err := f()), ranging over indices only (for i := range xs) or values only (for _, v := range xs), importing a package purely for its init side effects (import _ 'github.com/lib/pq' to register a database driver), and the compile-time interface check var _ Storer = (*PostgresStore)(nil), which fails the build if the type stops satisfying the interface. Note that _ = someCall() also silences errcheck, so an ignored error should carry a comment saying why.

GomedHow does struct embedding work, and how is it different from inheritance?

Declaring a field with no name — type Admin struct { User; Level int } — embeds User, and its fields and methods are promoted so you can write a.Name and a.Save() directly. It is composition with syntactic sugar, not inheritance: there is no virtual dispatch, so a method on User that calls another User method always calls User's version even if Admin has redefined it. The embedded value is still reachable by its type name (a.User), an outer method of the same name shadows the promoted one, and ambiguous promotions at the same depth are a compile error only when you actually reference them. Embedding an interface in a struct is a useful trick for partial implementations — the type satisfies the interface and only the methods you override are real.

GomedWhat is a closure in Go, and what is the classic bug with them?

A function literal that references variables from the enclosing scope captures those variables by reference, not by value, and keeps them alive as long as the closure lives. That is what makes counters, middleware and option functions work. The classic bug is capturing a loop variable: before Go 1.22, for _, v := range xs { go func() { use(v) }() } shared a single v across every iteration, so the goroutines all saw the last value. Go 1.22 changed loop variables to be per-iteration, which fixes it for modules declaring go 1.22 or later — but the same trap still exists whenever you capture any mutable variable that outlives the closure's creation, so passing the value as a parameter is still the clearer habit.

GomedWhen should you use context.WithValue, and what are the pitfalls?

Only for request-scoped data that genuinely crosses API boundaries and is not a function parameter — a request ID, a trace span, an authenticated user identity extracted by middleware. Not for optional arguments or dependencies; those belong in the function signature or the struct. The pitfalls: the value is untyped (any), so retrieval is an unchecked assertion and a wrong key silently yields nil; using a plain string as the key risks collision with another package, which is why the key must be an unexported defined type (type ctxKey struct{}); lookups walk the context chain, so it is not a fast map; and because it is invisible in the signature, over-using it produces functions whose real inputs cannot be read from their declaration. Wrap it in typed helpers — UserFrom(ctx) (User, bool) — so the assertion lives in one place.

GohardWhat is the reflect package for, and what does it cost?

reflect lets code inspect and manipulate values whose types are not known at compile time — reflect.TypeOf and reflect.ValueOf are the entry points, and it is what encoding/json, database/sql row scanning, ORMs and validators are built on. The rules to remember: you can only Set a value obtained through a pointer and only if it is exported, so reflect.ValueOf(&x).Elem().SetInt(5) works while reflect.ValueOf(x).SetInt(5) panics. The costs are real: it moves errors from compile time to runtime panics, it defeats escape analysis and inlining so it allocates and is roughly an order of magnitude slower than direct field access, and it makes refactoring unsafe because a renamed field is not caught by the compiler. Rob Pike's rule applies — clear is better than clever; use generics or code generation first, and reflect only when the type genuinely cannot be known until runtime.

GomedHow do you write table-driven tests in Go?

Declare a slice of anonymous structs holding the name, inputs and expected output, then loop and run each as a subtest: for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ... }) }. The payoff is that adding a case is one line, failures name the specific case, and you can run one with go test -run TestX/case_name. Details worth mentioning: use t.Parallel() inside the subtest for independent cases, prefer t.Fatalf when a failure makes the rest of the case meaningless and t.Errorf when it does not, use t.Cleanup rather than defer for teardown that must survive a Fatal, and use t.Helper() in assertion helpers so the reported line number points at the caller. For fixture-heavy comparisons, reflect.DeepEqual or google/go-cmp gives a readable diff.

GomedWhat is the difference between errors.Is, errors.As, and wrapping with %w?

fmt.Errorf with %w wraps an error, adding context while keeping the original reachable — fmt.Errorf('fetch user %s: %w', id, err). Using %v instead formats it as a string and severs the chain. errors.Is walks the chain comparing against a sentinel value: errors.Is(err, sql.ErrNoRows) or errors.Is(err, gorm.ErrRecordNotFound), which is how you map a deep error to a 404 without string matching. errors.As walks the chain looking for a concrete type and assigns it, which is how you get at fields: var pgErr *pq.Error; if errors.As(err, &pgErr) { switch pgErr.Code ... }. Wrap when you are adding context that helps debugging; do not wrap if exposing the underlying error would leak an implementation detail your callers should not depend on — in that case return a sentinel of your own.

GomedWhat should you know about the time package?

time.Duration is an int64 count of nanoseconds, so multiply by a unit rather than passing a bare number: 5*time.Second, not 5. time.Time carries both a wall clock and a monotonic reading; use time.Since(start) or t2.Sub(t1) for elapsed time so an NTP adjustment cannot produce a negative duration, and note that the monotonic part is stripped by rounding, marshalling or a round-trip through the database. Formatting uses the reference layout 2006-01-02 15:04:05 rather than strftime codes. Always store and compare in UTC and convert to a location only for display. And time.After inside a select in a loop leaks a timer until it fires — use time.NewTimer with a defer Stop, or a context deadline.

GomedWhen must you use crypto/rand instead of math/rand?

Any time the value must be unguessable: session tokens, password reset links, API keys, OTPs, nonces, salts. math/rand is a deterministic PRNG — an attacker who observes a few outputs can recover the state and predict the rest, and seeding it with the current time makes that trivially easier. crypto/rand reads from the operating system CSPRNG (getrandom / /dev/urandom) and returns an error you must check. math/rand is fine for jitter on a retry backoff, load-test data, sampling and shuffling in tests. Since Go 1.20 the global math/rand source is seeded randomly at startup, so the old rand.Seed(time.Now().UnixNano()) line is unnecessary and deprecated.

GomedWhat is the gotcha with bufio.Scanner when reading a file line by line?

The default token buffer is 64KB, so a single line longer than that stops the scan and Scanner.Err() returns bufio.ErrTooLong. The failure is quiet if you forget to check Err() after the loop — the range simply ends early and looks like a clean end-of-file. The fix is scanner.Buffer(make([]byte, 0, 64*1024), maxSize) with a limit you choose, or bufio.Reader.ReadString('\n') when lines can be arbitrarily large. Two related habits: always check scanner.Err() after for scanner.Scan(), and on the write side a bufio.Writer must be Flushed — a deferred Close on the underlying file does not flush the buffer, so data is silently lost.

GomedWhy use strings.Builder instead of += to build a string?

Strings are immutable, so s += part allocates a new string and copies both operands every iteration — building n pieces is O(n^2) in bytes copied. strings.Builder writes into an amortised-growth byte slice and hands it to the final string without a copy, making the same work linear. Use it whenever the number of pieces is not tiny or is unbounded; call Grow(n) first if you can estimate the size. Alternatives worth naming: strings.Join for a slice of pieces you already have, bytes.Buffer when you need a general io.Writer or the bytes back rather than a string, and fmt.Sprintf when readability matters more than allocation. Note that a Builder must not be copied after first use — pass it by pointer.

GohardWhat is the most misunderstood thing about database/sql?

sql.DB is not a connection — it is a pool, safe for concurrent use, and you open it once at startup and keep it for the life of the process. Opening one per request exhausts the database's connection limit. Configure it explicitly: SetMaxOpenConns (bounded so the database, not your process, decides the ceiling), SetMaxIdleConns (usually equal to max open, or idle connections churn), and SetConnMaxLifetime (so connections rotate behind a load balancer or a failover). The other classic leak is rows: every Query must have a defer rows.Close(), and the connection is held until the rows are fully drained or closed — which is why a Query inside a loop over another open Query can deadlock once the pool is exhausted. Use QueryRow for single-row reads, Exec when there are no rows, and always pass values as ? or $1 placeholders rather than building SQL with Sprintf.

GomedWhat is the difference between text/template and html/template?

They have the same API, but html/template is context-aware: it parses the output as HTML and escapes each interpolation according to where it lands — HTML body, an attribute, inside a URL, inside a script block or a CSS rule — which is what actually prevents XSS. text/template does no escaping at all. Because the packages are drop-in compatible, changing the import from html/template to text/template silently removes your XSS protection and the code still compiles, so treat that import line as security-relevant in review. Use text/template for non-HTML output — config files, emails in plain text, SQL and code generation. The template.HTML type marks a value as pre-trusted and bypasses escaping, so it must only ever be applied to output you sanitised yourself.

GomedWhat is the difference between a byte and a rune, and what does len() return for a string?

byte is an alias for uint8, one byte of the UTF-8 encoding. rune is an alias for int32 and holds a single Unicode code point. A Go string is an immutable slice of bytes that is conventionally UTF-8, so len(s) counts bytes, not characters — len('héllo') is 6 and len('नमस्ते') is 18. Indexing s[i] gives a byte, which mid-character yields nonsense. Ranging over a string decodes UTF-8 and yields (byteIndex, rune) pairs, so the index jumps. To count characters use utf8.RuneCountInString, and to index by character convert with []rune(s), which allocates. This matters for anything user-facing — a 280-character limit enforced with len() cuts a non-ASCII post short and can split a multi-byte character.

GoeasyCan you change one character in a Go string?

No — strings are immutable, and s[0] = 'H' does not compile. You build a new string instead: convert to []byte for ASCII or []rune for arbitrary Unicode, modify, and convert back. For anything beyond one character, prefer the strings package (Replace, ReplaceAll, ToUpper, Title-casing via golang.org/x/text) or strings.Builder. Immutability is what makes strings cheap to pass around and safe to share between goroutines, and it is why a string can be a map key while a slice cannot. The conversion []byte(s) copies, which is the price — the compiler optimises away the copy for a few known patterns such as ranging or comparing, but not in general.

GohardExplain the backing array of a slice and how append can surprise you.

A slice is a three-word header — a pointer to a backing array, a length and a capacity. Slicing does not copy: b := a[1:3] shares a's array, so writing b[0] changes a[1]. append writes into the spare capacity if there is any and returns a slice over the same array; only when capacity is exhausted does it allocate a new array and copy, at which point the two slices stop aliasing. That is the surprise — whether append mutates the caller's data depends on capacity, so the behaviour can change with input size. Three consequences: always use the returned value (a = append(a, x)); use the three-index form a[1:3:3] to cap capacity when handing a sub-slice to code that may append; and remember that holding a small slice of a huge array keeps the whole array alive, so copy the bytes out if you only need a few of them.

GomedWhat are anonymous structs and anonymous fields?

An anonymous struct is a struct type declared inline with no name — payload := struct{ Name string; Age int }{'Arvind', 30}. It is the idiomatic choice for a one-off shape: table-driven test cases, a small JSON response you do not want to add to your model package, or decoding a fragment of a third-party payload. An anonymous field is a field declared by type with no name, which is embedding — the field is reachable by its type name and its methods are promoted to the outer struct. The two names sound alike and mean different things; the first is about not naming the type, the second about not naming the field. Anonymous structs cost nothing at runtime, but if the same shape appears in three places it should be a named type.

GomedWhy is concurrency not parallelism?

Concurrency is a property of the program's structure — dealing with many things at once by composing independently executing pieces. Parallelism is a property of the execution — doing many things at the same instant on multiple cores. A concurrent Go program with GOMAXPROCS=1 runs no parallelism at all and is still correct and often still faster, because the win comes from overlapping waiting rather than from extra cores. It matters in interviews because it names the real benefit: an I/O-bound service gets almost all of its throughput from concurrency, since goroutines blocked on the network cost nothing, while a CPU-bound workload needs actual parallelism and more goroutines than cores just adds scheduling overhead. Rob Pike's line is the summary — concurrency is about dealing with lots of things at once, parallelism is about doing lots of things at once.

GomedWhat is a data race, and how do you detect one?

A data race is two goroutines accessing the same memory concurrently with at least one of them writing, and no synchronisation ordering the two. The result is undefined — not just a stale value but potentially a torn read of a multi-word value such as an interface or slice header. Detect it with the built-in race detector: go test -race ./... and go run -race, ideally wired into CI. It instruments memory accesses and reports the two conflicting stacks plus where the goroutines were created. Two caveats: it only reports races that actually happen on the paths you exercise, so it is only as good as your tests and load; and an instrumented binary uses roughly 5–10x the memory and CPU, so it belongs in CI and staging rather than production. Fix races with a mutex, a channel, or sync/atomic — never by adding a sleep.

GoeasyHow do you keep Go code idiomatic — what tooling would you put in CI?

gofmt is non-negotiable and non-configurable by design, which is the point: formatting stops being a discussion. goimports extends it to add and group imports (stdlib, third-party, internal). go vet catches real mistakes the compiler allows — a Printf verb that does not match its argument, a struct tag that will not parse, a lost cancel from context.WithCancel, copying a lock by value. Beyond that, staticcheck for dead code and misuse patterns, errcheck for unchecked errors, and golangci-lint as the runner that bundles them behind one config. In CI the order that gives the fastest feedback is gofmt -l as a hard fail, then go vet, then the linter suite, then go test -race with coverage. Note that Go has no line-length rule — gofmt does not wrap lines, so long lines are broken by hand at logical boundaries.

JSeasyWhat are the data types in JavaScript?

Seven primitives — string, number, bigint, boolean, undefined, symbol, null — and one non-primitive, object (which covers arrays, functions, dates, Map, Set and everything else). Primitives are immutable and compared by value; objects are compared by reference, so { a: 1 } !== { a: 1 }. Two traps get asked constantly: typeof null returns 'object', a bug preserved since 1995 for backwards compatibility, and typeof an array is also 'object', so use Array.isArray() to test for one. number is an IEEE-754 double, which is why 0.1 + 0.2 is 0.30000000000000004 and why integers above 2^53 - 1 need bigint.

JSeasyWhat is the difference between == and ===?

=== is strict equality: if the types differ the answer is false, no conversion happens. == is loose equality: it coerces operands to a common type first, using rules that are worth knowing but not worth relying on — null == undefined is true, '' == 0 is true, '0' == false is true, but null == 0 is false. Use === everywhere. The one defensible use of == is x == null, which tests for null or undefined in a single check. Note NaN !== NaN under both operators, so test with Number.isNaN; and Object.is behaves like === except that it treats NaN as equal to itself and distinguishes +0 from -0.

JSeasyWhat is the difference between var, let, and const?

var is function-scoped, hoisted and initialised to undefined, can be redeclared, and on the global object it creates a property on window. let and const are block-scoped, cannot be redeclared in the same scope, and sit in the temporal dead zone until their declaration is evaluated. const does not make a value immutable — it makes the binding immutable, so const arr = [] still allows arr.push(1) but not arr = []. Use const by default and let when you genuinely reassign; var has no remaining use case in new code. The classic demonstration is a for loop with setTimeout: with var all callbacks log the final value, with let each iteration gets a fresh binding.

JSmedWhat is the temporal dead zone?

The span between entering a scope and the point where a let or const declaration is evaluated. The binding exists and is hoisted — it shadows any outer variable of the same name — but reading or writing it throws a ReferenceError: Cannot access before initialization. That is different from var, which is hoisted and initialised to undefined, and different from an undeclared name, which throws Cannot read / is not defined. The TDZ exists so that const can be enforced (a const read before assignment has no sensible value) and so that typeof stops silently returning 'undefined' for a misspelled block-scoped variable. class declarations are in the TDZ too, which is why a class cannot be used before it is declared even though a function declaration can.

JSeasyHow do arrow functions differ from regular functions?

An arrow function has no this of its own — it closes over this from the enclosing lexical scope, which is why it is the right choice for a callback inside a method and the wrong choice for an object method or a prototype method. It also has no arguments object (use rest parameters), no prototype property, cannot be used with new, and cannot be a generator. Syntax sugar on top: a single parameter can drop its parentheses, and a single-expression body drops the braces and returns implicitly — but to implicitly return an object literal you must wrap it, () => ({ a: 1 }), or the braces are parsed as a block. Arrow functions are also not hoisted, since they are always assigned to a variable.

JSmedHow is the value of `this` determined in JavaScript?

For a normal function it is decided at call time, by four rules checked in order. new binding: called with new, this is the newly created object. Explicit binding: call, apply or bind set it directly. Implicit binding: called as obj.method(), this is obj. Default binding: everything else — undefined in strict mode and modules, the global object otherwise. Arrow functions ignore all four and inherit this lexically. The interview trap is losing implicit binding by detaching a method: const f = obj.method; f() gets the default binding, which is also why passing a method as a callback needs .bind(obj) or an arrow wrapper. In a DOM handler registered with addEventListener, this is the element — unless the handler is an arrow function.

JSmedWhat is a closure and where would you actually use one?

A function bundled with the lexical environment it was created in — it keeps access to those variables after the outer function has returned, because the environment is captured by reference rather than copied. Real uses: private state (a counter or a module returning only the functions it wants public), function factories, memoisation caches, and every callback that reads a variable from an enclosing scope. Two consequences to name: a closure keeps its captured variables alive, so holding one that captures a large object is a genuine memory leak; and all closures created in the same scope share the same variables, which is exactly the var-in-a-loop bug. In practice closures are why React hooks work and why a stale closure over old props is such a common useEffect bug.

JSeasyWhat is hoisting?

Declarations are processed before any code in a scope runs, so the binding exists before the line that declares it. function declarations are hoisted with their body, so you can call them above their definition. var declarations are hoisted and initialised to undefined, so reading one early gives undefined rather than an error. let, const and class are hoisted but uninitialised — the temporal dead zone — so reading one early throws. Function expressions and arrow functions are not hoisted as functions, only as whatever their variable declaration is, so const f = () => {} called early throws while function f() {} does not. The practical rule: declare before use and hoisting stops being something you have to reason about.

JShardExplain prototypes and the prototype chain.

Every object has an internal link to another object, its prototype, reachable via Object.getPrototypeOf(obj). When you read a property, the engine checks the object, then its prototype, then that object's prototype, up to null — that chain is how inheritance works, and there are no classes underneath. A function's .prototype property is the object that will become the prototype of instances created with new; an instance's link is __proto__ (legacy but still ubiquitous). Consequences worth stating: methods live on the prototype so all instances share one function object rather than one per instance; writes always land on the instance and shadow rather than modify the prototype; a long chain costs lookups; and Object.create(null) gives a prototype-less object, which is the safe way to build a dictionary that cannot collide with toString or __proto__.

JSmedAre JavaScript classes real classes?

No — class is syntax over the prototype system. class Dog extends Animal sets Dog.prototype's prototype to Animal.prototype, methods are installed on the prototype, and extends plus super wire up the chain. What class adds beyond sugar: the body is always in strict mode, methods are non-enumerable, calling a class without new throws, and super() must run before this is usable in a derived constructor. Newer additions are real capabilities rather than sugar — #private fields are genuinely inaccessible from outside (unlike an underscore convention), static blocks run at definition time, and fields declared in the class body are per-instance. The important interview point is that the prototype chain is still what runs, so the earlier questions about lookup, shadowing and this all still apply.

JSeasyWhat are Promises and what states can they be in?

A Promise is an object representing the eventual result of an async operation. It is pending until it settles, at which point it is either fulfilled with a value or rejected with a reason, and settling is final — a promise cannot change state twice. .then registers a callback for fulfilment, .catch for rejection, .finally for cleanup that runs either way. The value of promises over callbacks is composition: then returns a new promise, so chains flatten instead of nesting, a return inside then feeds the next link, and a throw anywhere in the chain propagates to the first catch below it rather than being swallowed. The rules people get wrong: forgetting to return the inner promise inside a then breaks the chain's sequencing, and a rejection with no catch anywhere becomes an unhandled rejection.

JSmedHow does async/await relate to Promises, and what is the common mistake?

async/await is syntax over promises — an async function always returns a promise, and await pauses the function until the awaited promise settles, resuming with its value or throwing its reason so you can use ordinary try/catch. The common mistake is awaiting in a loop when the operations are independent: for (const id of ids) { await fetchOne(id) } runs them one after another. Use await Promise.all(ids.map(fetchOne)) to run them concurrently. The counterpart mistake is firing them all off when they genuinely depend on each other or when concurrency must be bounded, in which case a batching helper or a limit library is the right answer. Two more details: await only works at the top level inside a module, and an async function that throws returns a rejected promise, so a caller that forgets to await also loses the error.

JSmedWhat is the difference between the microtask and macrotask queue?

The event loop runs one macrotask (a timer callback, an I/O or UI event, a script) and then drains the entire microtask queue before rendering or taking the next macrotask. Promise callbacks, queueMicrotask and MutationObserver go to the microtask queue; setTimeout, setInterval, and DOM events go to the macrotask queue. That is why a promise resolved synchronously logs before a setTimeout(fn, 0) registered earlier. It also means a microtask that keeps scheduling microtasks starves the loop entirely and freezes the page, while a runaway setTimeout chain still lets rendering in between. setTimeout(fn, 0) does not mean immediately — it means at the next macrotask, and the browser clamps nested timers to about 4ms.

JSmedWhat are generators and when are they useful?

function* declares a generator, which returns an iterator rather than running immediately. Each yield hands a value out and suspends the function with its local state intact; the next call to .next() resumes it, optionally passing a value back in that becomes the result of the yield expression. That two-way pause/resume is the point. Practical uses: producing an infinite or expensive sequence lazily so consumers only pay for what they take, walking a tree without building the whole list, implementing custom iterables via Symbol.iterator, and driving cooperative task runners — Redux Saga is built on exactly this. Async generators plus for await...of are the natural way to consume a paginated API or a stream chunk by chunk. In everyday application code they are rare; interviewers ask because they test whether you understand the iterator protocol.

JSeasyWhat is the difference between rest parameters and the spread operator?

Same three dots, opposite directions. Rest collects: function f(first, ...others) gathers the remaining arguments into a real array, and it also works in destructuring — const { id, ...rest } = obj. It must be the last parameter. Spread expands: Math.max(...nums) turns an array into an argument list, [...a, ...b] concatenates, { ...a, ...b } merges with later keys winning. Two things to be precise about: rest gives you an actual array whereas the old arguments object is array-like and absent from arrow functions; and spread copies one level only, so nested objects are still shared references — a shallow copy, not a clone.

JSeasyHow does destructuring work?

It pulls values out of arrays or objects into bindings in one statement. Arrays destructure by position — const [a, b] = arr — and support holes ([, second]) and a rest element. Objects destructure by key — const { id, name } = user — with renaming (const { id: userId }), defaults that apply only when the value is undefined (const { page = 1 }), nesting, and computed keys. It works in parameter lists too, which is how you get named arguments: function f({ retries = 3, timeout = 1000 } = {}). The gotchas: destructuring null or undefined throws, so give the parameter that = {} default; a default does not fire for null; and starting a line with { on its own is parsed as a block, so a standalone object destructure into existing variables needs parentheses.

JSeasyWhat is the difference between map, forEach, filter, and reduce?

All four iterate; they differ in what they return. forEach returns undefined and exists purely for side effects — you cannot chain it and you cannot break out of it early. map returns a new array of the same length with each element transformed. filter returns a new array containing only the elements whose callback returned truthy. reduce folds the array into a single accumulated value of any shape. The review-worthy misuse is calling map for its side effects and discarding the result, which misleads the reader — use forEach or a for...of. When you need an early exit use for...of, some or find; when you need both a transform and a filter, one reduce or a filter followed by a map is clearer than a map that emits nulls.

JSmedHow does reduce work, and when is it the wrong tool?

reduce((acc, item, index, array) => next, initial) calls the reducer for each element, threading an accumulator through. Always pass the initial value — without it the first element becomes the accumulator and the callback starts at index 1, which breaks on an empty array with a TypeError. Good fits: summing, grouping into an object by key, building a lookup Map from a list, and flattening. It is the wrong tool when the accumulator is an object or array you rebuild with a spread each iteration — { ...acc, [k]: v } inside a reduce is O(n^2) and slower and less readable than a for...of that mutates a local object. The rule: reach for reduce when you are genuinely folding to one value, not as a way to write a loop as an expression.

JSeasyHow do you remove duplicates from an array?

For primitives, [...new Set(arr)] — a Set stores unique values with SameValueZero comparison, so it is one pass and treats NaN as equal to itself. For objects, a Set does not help because identity is by reference, so dedupe on a key: Array.from(new Map(items.map(i => [i.id, i])).values()), which keeps the last occurrence, or reverse the input to keep the first. The filter((v, i, a) => a.indexOf(v) === i) idiom you see in older code is correct for primitives but O(n^2) and misses NaN, since indexOf uses strict equality. If the criterion is a computed key rather than a field, build the Map with that computed key.

JSmedWhat is the difference between a shallow copy and a deep copy?

A shallow copy — { ...obj }, Object.assign({}, obj), arr.slice(), [...arr] — duplicates the top level only, so nested objects are still shared and mutating one shows up in both. A deep copy recursively duplicates everything. The modern built-in is structuredClone(obj), which handles nested objects, Dates, Maps, Sets, typed arrays and cyclic references — but throws on functions, DOM nodes and class instances (it returns a plain object, losing the prototype). The old JSON.parse(JSON.stringify(obj)) trick is easy to reach for and quietly wrong: it drops undefined values, functions and symbols, converts Dates to strings, turns Map and Set into empty objects, throws on cycles, and is slow. Reach for a shallow copy when the nested values are treated as immutable, which is the usual case in React state updates.

JSmedWhat are WeakMap and WeakSet, and why do they exist?

They hold their keys (WeakMap) or values (WeakSet) weakly, meaning the entry does not stop the garbage collector from reclaiming the object. Keys must be objects or non-registered symbols, they are not iterable, and they have no size — because exposing either would make GC timing observable. The purpose is attaching data to objects you do not own without leaking: caching a computed result per object, tagging DOM nodes with metadata, tracking which objects have already been visited, and holding private state per instance in library code. Compare that to a Map, which keeps its keys alive forever — a Map keyed by DOM nodes leaks every node that is removed from the page, and a WeakMap does not.

JSeasyWhat is the difference between null, undefined, and an undeclared variable?

undefined is the absence the language gives you: a declared variable that was never assigned, a missing function parameter, a property that does not exist, and the return value of a function with no return. null is the absence a programmer assigns deliberately to mean no value. An undeclared name was never declared at all and throws a ReferenceError on read — though typeof undeclared uniquely returns 'undefined' without throwing, which is why old code used it for feature detection. In practice: null == undefined is true but null === undefined is false; typeof null is 'object' and typeof undefined is 'undefined'; JSON.stringify keeps null and drops undefined properties entirely; and a default parameter fires for undefined but not for null.

JSeasyWhat values are falsy, and how do ?? and || differ?

Exactly eight values are falsy: false, 0, -0, 0n, '', null, undefined and NaN. Everything else is truthy — including '0', 'false', [] and {}. || returns the right operand when the left is falsy; ?? returns it only when the left is null or undefined. That distinction is the whole point: count ?? 10 keeps a legitimate 0, whereas count || 10 silently replaces it, which is the bug behind wrong pagination sizes, ignored empty-string inputs and disabled flags turning themselves back on. The same asymmetry applies to ||= versus ??=. Optional chaining pairs with it — user?.profile?.name short-circuits to undefined instead of throwing, and obj.method?.() calls only if the method exists — but do not sprinkle ?. over values that should never be missing, because it hides real bugs.

JSmedWhat does strict mode change?

'use strict' at the top of a file or function opts into a stricter dialect. It makes assigning to an undeclared variable a ReferenceError instead of quietly creating a global; makes writes to read-only or non-writable properties throw rather than fail silently; forbids duplicate parameter names and octal literals like 010; makes delete on a plain variable a syntax error; sets this to undefined in a plain function call rather than the global object; and removes arguments.callee and the link between arguments and named parameters. ES modules and class bodies are always in strict mode, so most modern code is already strict without the directive. The value is the same as any linter — errors that used to be silent become loud.

JSmedWhat is the difference between == coercion cases like 4 + 2 + '8' and '8' + 4 + 2?

4 + 2 + '8' evaluates left to right: 4 + 2 is 6, then 6 + '8' — where + with a string operand means concatenation — gives the string '68'. Reverse it and '8' + 4 + 2 gives '842', because the first concatenation makes everything after it a string. The underlying rule is that + is the only arithmetic operator that is overloaded for strings; every other operator coerces to number, which is why '8' - 4 is 4 and '8' * '2' is 16. The other coercion facts worth having ready: [] + {} is '[object Object]' while {} + [] at statement position is 0 because the braces parse as a block, and + '' is a common shorthand for String(x). None of this is a reason to rely on it — the answer in real code is to convert explicitly with Number(), String() or template literals.

JSmedHow do you handle errors in JavaScript?

try/catch/finally with throw. Any value can be thrown, but throw new Error('message') is the only sensible choice because it captures a stack trace — throwing a string loses it. finally always runs, including when the try returns, which makes it right for cleanup; a return inside finally overrides the one in try, so avoid it. Use the built-in subclasses meaningfully (TypeError, RangeError) and subclass Error for your own domain errors so callers can branch with instanceof. Since ES2022 you can chain with new Error('failed to load user', { cause: err }), which preserves the original the way error wrapping does in Go. Two async rules: a catch block does not catch a rejection from a promise you did not await, and async errors surface through .catch or try/catch around await — plus a global handler for unhandledrejection and window.onerror so nothing is silently lost. Note that Java's throws clause does not exist in JavaScript.

JSeasyWhat is the difference between for...in and for...of?

for...in iterates enumerable string keys, including inherited ones from the prototype chain — which is why it needs a hasOwnProperty guard and why it is the wrong loop for arrays: the keys come back as strings ('0', '1'), the order is not guaranteed for integer-like keys mixed with others, and anything added to Array.prototype shows up. for...of iterates values of any iterable — arrays, strings, Map, Set, arguments, NodeList, generators — and supports break, continue and await. The practical rules: for...of for arrays and iterables, Object.keys/values/entries for objects, and for...in essentially never in new code. Object.entries with destructuring, for (const [k, v] of Object.entries(obj)), covers what for...in used to be used for.

JSmedWhich array methods mutate the original array and which return a new one?

Mutating: push, pop, shift, unshift, splice, sort, reverse, fill and copyWithin. Non-mutating: slice, concat, map, filter, reduce, flat, flatMap, join, and the ES2023 additions toSorted, toReversed, toSpliced and with. The distinction matters most in React and Redux, where mutating state in place means the reference does not change and the component does not re-render — hence [...arr].sort() rather than arr.sort(), or toSorted() where it is available. Two details people get wrong: sort compares as strings by default, so [10, 9, 1].sort() gives [1, 10, 9] and numbers need sort((a, b) => a - b); and both sort and reverse also return the array they mutated, which makes a mutation easy to miss inside a chain.

JSeasyWhat is the difference between parseInt, Number, and unary plus?

parseInt reads leading numeric characters and stops at the first invalid one, so parseInt('42px') is 42 and parseInt('abc') is NaN. Always pass the radix — parseInt('08', 10) — because the string is otherwise interpreted by prefix and legacy engines treated a leading zero as octal. Number() and the unary + convert the whole string or fail: Number('42px') is NaN, and Number('') is 0 while parseInt('') is NaN. parseFloat is parseInt's decimal counterpart and ignores the radix argument. Pick by intent: parseInt when you are extracting a number from text such as a CSS value, Number when the whole input must be a valid number, and Math.trunc rather than parseInt when you already have a number and want the integer part.

JSeasyWhy use Number.isNaN instead of the global isNaN?

The global isNaN coerces its argument to a number first, so isNaN('hello') is true and isNaN(undefined) is true even though neither value is NaN — it really answers is this not coercible to a number. Number.isNaN returns true only for the actual NaN value, with no coercion. NaN is the only value in JavaScript not equal to itself, so x !== x is the primitive test underneath. Related: Number.isInteger and Number.isFinite are likewise the non-coercing counterparts of the globals, and NaN propagates through arithmetic, so one bad parse silently poisons every calculation downstream — which is why validating at the parse boundary matters more than checking at the end.

JSmedWhat is the difference between ES modules and CommonJS?

ESM uses import/export, is statically analysable, and is the standard in browsers and modern Node; CommonJS uses require/module.exports, is dynamic, and is the legacy Node format. Because ESM imports are resolved before execution, bundlers can tree-shake unused exports and circular imports behave differently. ESM bindings are live — reassigning an exported variable is visible to importers — whereas require gives you a snapshot of the value at that moment. ESM is always strict mode, always deferred, supports top-level await, and loads asynchronously; require is synchronous. Practical points: in Node the format is chosen by the .mjs/.cjs extension or the type field in package.json; you can import CJS from ESM but not require ESM; and __dirname does not exist in ESM (use import.meta.url). Prefer named exports over a default export — they are refactor-safe and tree-shake better.

JSmedWhat is the difference between an HTML attribute and a DOM property?

Attributes are what is written in the markup and are always strings; properties are fields on the DOM object and have real types. Most are synced, so setting el.id updates the id attribute, but several deliberately are not. value is the big one: the value attribute is the initial value while the .value property is the current one, so reading getAttribute('value') on an input the user has typed in returns the original. Similarly the checked attribute is the default state and the .checked property is the live one, class in markup is className in the DOM (because class is a reserved word), and href as an attribute is whatever was written while the property is the resolved absolute URL. The rule: read and write properties for state, attributes only for markup-level configuration and data-* values.

JSmedWhat are the ways to select DOM elements, and how do they differ?

getElementById is the fastest and returns one element or null. querySelector takes any CSS selector and returns the first match; querySelectorAll returns all matches as a static NodeList — a snapshot that does not update when the DOM changes, and one that supports forEach but not the other array methods until you spread it. getElementsByClassName and getElementsByTagName return live HTMLCollections that do update, which is a classic bug source: removing elements while looping over a live collection skips items, because the collection shrinks under you. Prefer querySelector for readability, cache the result rather than re-querying inside a loop, and reach for a live collection only when you actually want the live behaviour.

JSmedWhat is the difference between innerHTML, innerText, and textContent?

innerHTML gets and sets parsed HTML — assigning to it is the standard XSS vector, because any user-controlled string can carry markup and event handlers. textContent gets and sets raw text of all nodes including hidden ones, does not trigger layout, and is the fast and safe default. innerText is text as rendered — it respects CSS, so it excludes display:none elements and collapses whitespace the way the page shows it, and reading it forces a reflow, which makes it noticeably slower in a loop. The rules: use textContent unless you specifically need what the user sees on screen; never assign untrusted data to innerHTML — build nodes and set textContent, or sanitise with something like DOMPurify. Note that innerHTML does not execute an injected script tag, but it happily runs an onerror on an injected img.

JSmedExplain event bubbling, capturing, and delegation.

A dispatched event travels down from the window to the target (capture phase), fires on the target, then travels back up (bubble phase). addEventListener listens in the bubble phase by default; pass { capture: true } for the way down. Delegation exploits the bubble: attach one listener to a container and identify the origin with event.target.closest('.row'), which means dynamically added children work with no extra wiring and one listener replaces hundreds. Distinguish the two stop methods: preventDefault cancels the browser's default action (following a link, submitting a form) but the event keeps propagating; stopPropagation halts propagation but the default still happens. Also know that event.target is what was clicked while event.currentTarget is the element whose listener is running, and that focus, blur and scroll do not bubble — their focusin/focusout counterparts do.

JSmedWhat is the difference between a script tag, defer, and async?

A plain script tag blocks HTML parsing while it downloads and executes, which is why the old advice was to put scripts at the end of the body. defer downloads in parallel and executes after parsing completes but before DOMContentLoaded, and multiple deferred scripts run in document order — that is the right default for application code that touches the DOM. async downloads in parallel and executes the moment it arrives, interrupting parsing and running in whatever order the downloads finish — suitable only for independent third-party scripts such as analytics. Both attributes are ignored on inline scripts. type='module' is deferred by default, so a module script needs nothing extra, and modules are only ever fetched once even if imported many times.

JSeasyWhat is the difference between window.onload and DOMContentLoaded?

DOMContentLoaded fires as soon as the HTML is parsed and the DOM is built — deferred scripts have run, but images, stylesheets and iframes may still be loading. load fires only when every subresource has finished, which can be seconds later on a slow connection. Almost all initialisation belongs in DOMContentLoaded (or in a deferred script, which already runs before it); use load only when you genuinely need final dimensions, such as measuring an image. Two practical notes: window.onload = fn allows only one handler and the last assignment wins, whereas addEventListener('load', fn) composes — always use the listener form; and if you attach a listener after the event has already fired, it never runs, so guard with document.readyState.

JSeasyWhat is the difference between localStorage, sessionStorage, and cookies?

localStorage persists until explicitly cleared and is shared across every tab on the origin. sessionStorage is scoped to one tab and is discarded when that tab closes. Both hold roughly 5–10MB, store strings only (so objects need JSON round-tripping), are synchronous — which means a large read blocks the main thread — and are readable by any JavaScript on the page. Cookies are far smaller, around 4KB, and are attached to every matching HTTP request automatically, which is why they are the right place for session identifiers and the wrong place for anything else. The security point interviewers look for: an auth token in localStorage is readable by any XSS on the page, whereas an HttpOnly, Secure, SameSite cookie is not reachable from JavaScript at all — so store session tokens in cookies and keep Web Storage for non-sensitive UI preferences.

JSmedHow do timers work in JavaScript, and what are their limits?

setTimeout schedules a callback once, setInterval repeats it, and both return a handle you clear with clearTimeout / clearInterval. The delay is a minimum, not a guarantee — the callback is queued as a macrotask and runs only when the call stack is empty, so a long synchronous block delays it arbitrarily, and browsers clamp nested timers to about 4ms and throttle timers in background tabs heavily. The specific problem with setInterval is that it does not wait for the previous run to finish, so slow work overlaps and pile up; a self-scheduling setTimeout chain that calls itself after each run is the safer pattern for anything with variable duration such as polling. Always clear a timer in a cleanup path — a React useEffect return, a component teardown — or it keeps firing against unmounted state.

JSmedWhat are higher-order functions and first-class functions?

First-class means functions are ordinary values: they can be assigned to variables, stored in arrays and objects, passed as arguments and returned from other functions. A higher-order function is one that takes or returns a function — map, filter, reduce, addEventListener, setTimeout, and every decorator, middleware or React hook that accepts a callback. That is the foundation the rest of the language is built on: closures give the returned function private state, so a factory like withRetry(fn, n) returning a wrapped fn is idiomatic rather than exotic. Related terms worth having ready: currying turns f(a, b) into f(a)(b) so arguments can be supplied over time; composition chains single-argument functions; and a pure function that returns the same output for the same input with no side effects is what makes memoisation and React's rendering model safe.

JSeasyWhat does the delete operator do, and what does it not do?

delete removes an own property from an object and returns true — including when the property never existed, so a true return means nothing. What it does not do: it cannot delete a variable declared with var, let or const (a SyntaxError in strict mode), it cannot delete inherited properties or non-configurable ones, and it does not free memory directly — the value is collected only if nothing else references it. On an array it is a trap: delete arr[1] leaves a hole and does not change length, producing a sparse array that map and forEach skip while a plain for loop does not. Use splice to remove an element, filter to build a new array without it, and for objects prefer destructuring the key out — const { removed, ...rest } = obj — since that leaves the original untouched, which is what immutable state updates need.

SQLeasyIs NULL the same as zero or an empty string?

No. Zero is a value and an empty string is a value; NULL means unknown or not applicable, and SQL evaluates it with three-valued logic — true, false, unknown. That is why NULL = NULL is not true but unknown, so it never satisfies a WHERE clause, and why you must write IS NULL / IS NOT NULL. The consequences catch people out constantly: WHERE status <> 'active' silently drops rows where status is NULL; NOT IN (SELECT col ...) returns no rows at all if the subquery yields a single NULL, so prefer NOT EXISTS; count(col) skips NULLs while count(*) does not; and most aggregates ignore NULLs, so avg over a column with NULLs divides by the non-null count. Concatenating with || also yields NULL if any operand is NULL. Unique constraints are the one place NULLs are permissive — standard SQL treats each NULL as distinct, so a unique column can hold many of them.

SQLeasyWhat is the difference between COALESCE, NULLIF, and IS DISTINCT FROM?

COALESCE(a, b, c) returns the first non-NULL argument and is the standard way to supply a default — COALESCE(nickname, first_name, 'anonymous'). It short-circuits, so later arguments are not evaluated. NULLIF(a, b) is the inverse: it returns NULL when the two arguments are equal, which is the idiom for avoiding division by zero, total / NULLIF(count, 0). IS DISTINCT FROM is null-safe equality — a IS DISTINCT FROM b is true when exactly one side is NULL, where a <> b would be unknown; it is what you want in a change-detection trigger or an upsert that must treat NULL as a real value. Oracle's NVL and NVL2 and SQL Server's ISNULL are vendor-specific equivalents of COALESCE with fixed arity; COALESCE is standard and takes any number of arguments, so prefer it.

SQLmedIn what order are the clauses of a SELECT logically evaluated?

Not the order you write them. Logically: FROM and JOIN, then WHERE, then GROUP BY, then HAVING, then the SELECT list including window functions, then DISTINCT, then ORDER BY, then LIMIT/OFFSET. That order explains most beginner errors. You cannot reference a SELECT alias in WHERE, GROUP BY or HAVING because the SELECT list has not been evaluated yet — but you can in ORDER BY, which runs after it; wrap the expression in a subquery or CTE if you need it earlier. You cannot filter on a window function in WHERE either, since windows are computed with the SELECT list — that is why top-N-per-group needs the ROW_NUMBER in a subquery with the filter outside. And WHERE filters rows before grouping while HAVING filters groups after, so a predicate that does not involve an aggregate belongs in WHERE, where it reduces the work. This is the logical order; the planner is free to execute differently as long as the result matches.

SQLmedWhat is a correlated subquery and why can it be slow?

A scalar subquery returns exactly one row and one column and can be used anywhere a value is expected; it is evaluated once if it does not depend on the outer query. A correlated subquery references a column from the outer query, so conceptually it is re-evaluated once per outer row — SELECT ... WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id). That per-row execution is where the cost comes from on large outer sets. Modern planners often rewrite a correlated EXISTS or IN into a semi-join, so the naive per-row model is a worst case rather than a certainty — check EXPLAIN rather than assuming. When it does not get rewritten, the fixes are a JOIN with a grouped subquery, a LATERAL join when you genuinely need per-row parameterisation, or a window function. Prefer EXISTS over IN when the subquery can produce NULLs, and over COUNT(*) > 0 because EXISTS stops at the first match.

SQLeasyWhat are the sublanguages of SQL — DDL, DML, DCL, TCL, DQL?

DDL defines structure: CREATE, ALTER, DROP, TRUNCATE, RENAME. DML changes data: INSERT, UPDATE, DELETE, MERGE. DQL is the query half, SELECT, though many people fold it into DML. DCL manages permissions: GRANT and REVOKE. TCL controls transactions: COMMIT, ROLLBACK, SAVEPOINT, SET TRANSACTION. The distinction that matters in practice is transactional behaviour: in most engines DDL auto-commits and cannot be rolled back, which is why a failed migration can leave a half-changed schema. Postgres is the notable exception — its DDL is transactional, so you can wrap CREATE TABLE and ALTER TABLE in a transaction and roll the whole migration back, which is a genuine operational advantage worth naming in an interview.

SQLeasyWhat is the difference between a primary key and a unique constraint?

Both enforce uniqueness and both create a backing unique index. The differences: a table has at most one primary key but any number of unique constraints; primary key columns are implicitly NOT NULL while a unique column may contain NULLs — and because NULL is not equal to anything, standard SQL lets a unique column hold many NULL rows (Postgres 15 added NULLS NOT DISTINCT to opt out of that); and a foreign key by default references the primary key, though it may reference any unique constraint. Semantically the primary key is the row's identity, which is why the usual advice is a surrogate key such as a UUID or generated identity for the primary key, with unique constraints expressing the real business rules (one active email per user, one slug per tenant). Those business rules are also where partial unique indexes belong.

SQLeasyWhat is the difference between CHAR, VARCHAR, and TEXT?

CHAR(n) is blank-padded to a fixed length, so storing 'ab' in a CHAR(10) stores ten characters and comparisons ignore the trailing spaces — surprising behaviour that makes it a poor default. VARCHAR(n) stores only what you put in, with n as a maximum that is enforced as an error on overflow. TEXT is unbounded. In Postgres specifically there is no performance difference between the three — they share one implementation, and VARCHAR(n) is TEXT plus a length check — so the choice is purely about whether you want the constraint. The practical advice is TEXT or VARCHAR with a length that reflects a real business rule, plus a CHECK constraint if the rule is more than a maximum. Note that in Postgres, changing a VARCHAR(50) to VARCHAR(100) is a cheap catalog-only change, but narrowing it rewrites and validates the whole table.

SQLeasyWhat are the types of relationships between tables, and how do you model each?

One-to-one: put a unique foreign key on the dependent table, or merge the two tables unless you are splitting off rarely-read or differently-permissioned columns. One-to-many: the foreign key lives on the many side — an order carries customer_id, never the reverse. Many-to-many: you need a junction table holding a foreign key to each side, with a composite primary key on the pair to prevent duplicates, plus any attributes of the relationship itself such as role or joined_at. The junction table is the answer interviewers listen for, because the alternatives people reach for — a comma-separated column or an array of IDs — cannot be indexed usefully for joins, cannot carry a foreign key, and cannot enforce that the referenced row exists. Add an index on the second column of the junction table too, since the composite primary key only serves lookups leading with the first.

SQLmedWhat is a cursor and when should you use one?

A cursor is a server-side pointer into a result set that lets you fetch and process rows one at a time rather than all at once. Declare, open, fetch in a loop, close. Legitimate uses: streaming a result far larger than memory, and driving a long-running batch job in chunks. Illegitimate uses — which is most of them — are procedural loops that do row-by-row work SQL could express as a single set-based statement; that pattern is orders of magnitude slower because each iteration is a round trip through the executor, and it holds a transaction and its locks open for the whole loop. The interview answer is: prefer a set-based UPDATE ... FROM or INSERT ... SELECT, and if the volume is too large for one statement, batch it with a keyset loop over the primary key rather than a cursor. In Postgres, a client-side batching loop with LIMIT and a WHERE id > last_seen is usually the better shape.

SQLmedWhat is normalization, and what do 1NF, 2NF, and 3NF actually mean?

Normalization organises columns so that each fact is stored once, removing update anomalies. 1NF: every column holds a single atomic value — no comma-separated lists, no repeating groups like phone1, phone2, phone3. 2NF: 1NF plus every non-key column depends on the whole primary key, which only bites with composite keys — a product_name column in an order_items table keyed on (order_id, product_id) violates it. 3NF: 2NF plus no non-key column depends on another non-key column — storing city and postcode where postcode determines city, or storing a customer's name on every order row. BCNF tightens 3NF for the edge case of overlapping candidate keys. The point of naming them is the payoff: one fact in one place means an update touches one row and cannot leave two versions of the truth. Then say when you would break it — a deliberately denormalised counter or a materialised aggregate for read performance, with a stated plan for keeping it correct.

SQLmedWhat constraints does SQL give you, and why enforce rules in the database rather than the application?

NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY (with ON DELETE / ON UPDATE actions), CHECK for arbitrary boolean conditions on a row, DEFAULT, and in Postgres EXCLUDE for generalised constraints such as non-overlapping booking ranges. CHECK is the underused one: CHECK (amount > 0), CHECK (status IN ('draft','sent','paid')), CHECK (ends_at > starts_at). The argument for the database is that it is the only layer every writer passes through — a background job, a migration, a psql session and a second service will all bypass your application-layer validation, and a constraint cannot be forgotten by a new code path. Constraints are also documentation the planner can use. The counter-argument is that a constraint violation surfaces as a driver error you must map to a friendly message, and that changing one on a large table needs care — Postgres lets you ADD CONSTRAINT ... NOT VALID and VALIDATE later to avoid a long lock.

SQLmedWhat is collation and why does it matter operationally?

Collation is the rule set for comparing and sorting text — which characters sort before which, and whether comparison is case- and accent-sensitive. It determines ORDER BY output, the behaviour of =, LIKE and range predicates, and therefore the physical order of every B-tree index on a text column. Two operational consequences interviewers like: because the index is stored in collation order, upgrading the underlying libc/ICU library can silently change that order and leave existing indexes corrupt — the standard fix is REINDEX after a glibc upgrade or a distro change, and the reason many teams pin ICU collations instead. And a column with a case-insensitive collation (or Postgres's citext) can serve a case-insensitive lookup from a plain index, whereas WHERE lower(email) = ? needs an expression index on lower(email) to avoid a sequential scan. Set collation deliberately at the database or column level rather than inheriting whatever the server locale happened to be.

SQLmedHow do you find the second-highest salary, or the top N rows per group?

For a single second-highest, the simple form is ORDER BY salary DESC LIMIT 1 OFFSET 1, but it returns nothing on a one-row table and treats ties as separate rows. The correct-by-intent version uses DENSE_RANK: SELECT salary FROM (SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS r FROM employees) t WHERE r = 2 — DENSE_RANK because two people on the top salary should not push the answer to third place, whereas RANK skips numbers and ROW_NUMBER breaks ties arbitrarily. Top N per group is the same shape with a partition: ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) filtered to <= N in an outer query, since you cannot filter a window function in WHERE. In Postgres, DISTINCT ON (dept_id) ... ORDER BY dept_id, salary DESC is a faster idiom for N = 1. SQL Server's TOP N WITH TIES is the vendor shorthand for the RANK behaviour.

SQLmedWhat is a CASE expression, and what is conditional aggregation?

CASE is SQL's if/then/else and it is an expression, not a statement, so it can appear in SELECT, WHERE, ORDER BY, GROUP BY and inside an aggregate. Two forms: simple, CASE status WHEN 'paid' THEN ... END, and searched, CASE WHEN amount > 1000 THEN ... END, which is the general one. It short-circuits and returns NULL when nothing matches and there is no ELSE. The powerful use is conditional aggregation — pivoting rows into columns in a single pass: SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_total alongside SUM(CASE WHEN status = 'refunded' ...) rather than running three queries or joining three subqueries. COUNT(CASE WHEN cond THEN 1 END) works the same way because COUNT ignores NULLs, and Postgres offers the tidier FILTER clause, COUNT(*) FILTER (WHERE cond). CASE in ORDER BY is also how you implement a custom sort order for a status column.

SQLmedWhat is a view, and how does it differ from a materialized view or a table?

A plain view is a stored query with a name — nothing is stored, and every reference re-runs the underlying SQL, so it is always current and costs what the query costs. Uses: hiding join complexity behind a stable interface, and exposing a restricted column or row set as a permission boundary. A view is updatable only if it is simple enough for the engine to map a write back to one base row; otherwise you write through an INSTEAD OF trigger. A materialized view stores the result physically, so reads are fast and stale until you REFRESH — and a plain REFRESH takes an exclusive lock, which is why you want REFRESH CONCURRENTLY plus the unique index it requires. A table is storage you write to directly. Choose by read pattern: a view when correctness must be live and the query is cheap, a materialized view for an expensive aggregate that tolerates staleness, and a real table with an explicit update path when you need both freshness and speed.

SQLeasyWhat is a schema, and what does search_path do?

A schema is a namespace inside a database holding tables, views, functions, sequences and types, so two schemas can each have their own users table. It is the natural boundary for multi-tenancy (one schema per tenant), for separating an application's tables from extensions and staging tables, and for permissions — you can GRANT USAGE on a schema and grant table privileges within it as a unit. Postgres resolves unqualified names through search_path, which defaults to $user, public: it tries a schema named after the current user, then public. Two things follow. Migrations and functions should qualify names or set an explicit search_path, because a table shadowed in an earlier schema silently changes what a query means. And a SECURITY DEFINER function with a mutable search_path is a genuine privilege-escalation vector, which is why it should always be declared with SET search_path = pg_catalog, public.

SQLmedWhat is the difference between DISTINCT, GROUP BY, and DISTINCT ON?

SELECT DISTINCT deduplicates the entire output row across every selected column — a frequent surprise is adding an id to the select list and getting duplicates back, because the rows are now genuinely distinct. GROUP BY collapses rows into groups and lets you compute aggregates over them; with no aggregate in the select list it produces the same result as DISTINCT, and planners usually execute the two identically, so pick whichever states the intent. Postgres also has DISTINCT ON (expr), which keeps the first row per value of expr according to the ORDER BY — SELECT DISTINCT ON (user_id) * FROM events ORDER BY user_id, created_at DESC gives the latest event per user in one pass, and the ORDER BY must lead with the DISTINCT ON expressions. It is the concise alternative to a ROW_NUMBER subquery. Reach for DISTINCT sparingly: it often signals a join that is fanning out rows, and fixing the join beats deduplicating afterwards.

SQLmedWhat is Change Data Capture and when would you use it?

CDC turns the stream of inserts, updates and deletes on a table into an event feed other systems can consume. The naive implementations are polling an updated_at column, which misses deletes and races with clock skew, or audit triggers, which are correct but add write latency and a table to maintain. The production approach is log-based: read the database's own write-ahead log — Postgres logical replication and slots, MySQL binlog — usually via Debezium into Kafka, which captures every change exactly once in commit order with no impact on the writing transaction. Uses: keeping a search index or cache in sync, feeding a warehouse without nightly batch, and zero-downtime migrations where you dual-write by replicating rather than by changing application code. Two things to know: a logical replication slot that no consumer reads will retain WAL until the disk fills, and the transactional outbox pattern is the alternative when you want to publish domain events rather than raw row changes.

SQLmedWhat is the difference between OLTP and OLAP, and where does ETL fit?

OLTP is the transactional workload — many small, short, concurrent reads and writes against current state, normalised, indexed for point lookups, measured in latency per query. OLAP is the analytical workload — few large scans aggregating over history, denormalised into star or snowflake schemas, measured in throughput. They conflict on the same hardware: an analyst's full-table aggregate competes for cache and I/O with the checkout path, and long-running analytical transactions in Postgres hold back the xmin horizon so VACUUM cannot clean dead tuples. That is the reason for a separate warehouse, and ETL (or ELT, where you load raw and transform in the warehouse) is the pipeline that moves and reshapes the data into it. In interviews the useful add-on is the modern alternatives: a read replica for lightly analytical queries, and columnar storage — Redshift, BigQuery, ClickHouse — when scans dominate, because column stores read only the columns a query touches.

SQLhardHow do you write a recursive CTE?

WITH RECURSIVE name AS (anchor query UNION ALL recursive query referencing name) SELECT ... The anchor produces the starting rows, the recursive term joins against the rows produced by the previous iteration, and it stops when an iteration returns nothing. The canonical uses are hierarchies — walking an org chart or a category tree up or down from a starting node — plus graph traversal, exploding a bill of materials, and generating series such as every date in a range. Two things to get right or it is a production incident: use UNION rather than UNION ALL, or carry a visited-path array and exclude nodes already seen, because a cycle in the data makes it run forever; and track a depth column with a LIMIT or a depth cap so a bug cannot run away. Adding the depth also gives you the level for indenting output. Note that non-recursive CTEs in Postgres 12 and later are inlined by the planner rather than materialised, so the old trick of using a CTE as an optimisation fence now needs an explicit MATERIALIZED keyword.

SQLmedWhat is SQL injection and how do you actually prevent it?

It is any input that changes the structure of a query rather than supplying a value — the classic being a login form where the password field contains ' OR '1'='1. Consequences run from authentication bypass to reading other tables via UNION to dropping data. The only real defence is parameterised queries: send the SQL and the values separately so the driver never substitutes text into the statement — $1 in Postgres, ? in MySQL, named parameters in an ORM. Escaping by hand and blacklisting keywords both fail against encoding tricks and second-order injection, where a value stored safely is later concatenated into another query. What parameters cannot cover is identifiers — a dynamic ORDER BY column or table name cannot be bound, so validate it against a whitelist of known columns rather than interpolating user text. Defence in depth on top: least-privilege database roles so the application cannot DROP, row-level security where tenants share tables, and generic error messages so failures do not leak schema.

SQLmedWhat do INTERSECT and EXCEPT do?

They are the other two set operators alongside UNION. INTERSECT returns rows present in both result sets; EXCEPT (called MINUS in Oracle) returns rows in the first that are not in the second. Like UNION they require the same number of columns with compatible types, they compare whole rows, they remove duplicates unless you add ALL, and unlike UNION they are not commutative — EXCEPT depends on which side is first. They also treat NULLs as equal for the purpose of matching rows, which is the opposite of how = behaves and is occasionally exactly what you want. The practical use is reconciliation: (SELECT id FROM source EXCEPT SELECT id FROM target) tells you what failed to sync in one readable statement, and running it both ways gives you a full diff. For large sets a NOT EXISTS anti-join or a LEFT JOIN ... WHERE right.id IS NULL usually plans better, so use EXCEPT for clarity on modest sets and check EXPLAIN when the tables are big.

SQLeasyWhat is the difference between BETWEEN and IN, and what is the trap with BETWEEN?

IN tests membership in an explicit list or a subquery — status IN ('paid','refunded') — and is shorthand for a chain of ORs. BETWEEN tests a range and is inclusive on both ends, so amount BETWEEN 1000 AND 5000 includes both 1000 and 5000. That inclusivity is the trap, and it bites hardest on timestamps: created_at BETWEEN '2026-01-01' AND '2026-01-31' silently excludes almost all of the 31st, because the bare date becomes midnight and everything later that day falls outside the range. The correct form for a date range over a timestamp column is a half-open interval, created_at >= '2026-01-01' AND created_at < '2026-02-01', which also stays index-friendly and handles month lengths and time zones without special cases. The other IN caveat is NULL: NOT IN against a subquery that yields any NULL returns no rows at all, so use NOT EXISTS.

SQLmedWhat does TABLESAMPLE do?

TABLESAMPLE returns an approximate random subset of a table without scanning all of it — SELECT * FROM events TABLESAMPLE SYSTEM (1) reads roughly 1 percent. The two standard methods differ in accuracy and cost: SYSTEM samples whole disk pages, so it is fast but clustered — rows physically near each other are selected together, which skews results when the table is ordered by something correlated with what you are measuring. BERNOULLI evaluates every row independently, giving a genuinely random sample at the cost of a full scan. Add REPEATABLE (seed) for a reproducible sample. Use it for exploratory analysis, for estimating a distribution before writing an expensive query, and for building a realistic test dataset from production shape. Do not use it where accuracy matters — the percentage is approximate, and it applies to the base table before any WHERE clause, so a filtered sample of a rare value can easily come back empty.

SQLmedWhat does MERGE do, and how does it compare to INSERT ... ON CONFLICT?

MERGE takes a source and a target, matches rows on a condition, and applies WHEN MATCHED THEN UPDATE/DELETE and WHEN NOT MATCHED THEN INSERT in one statement — the standard SQL way to express an upsert or a full synchronisation. It shines when you are reconciling a whole staging table against a target, because one statement replaces a delete-then-insert or three separate passes. Postgres has had it since version 15; before that the idiom was INSERT ... ON CONFLICT (key) DO UPDATE SET ..., which is narrower but concurrency-safe by construction because it relies on a unique index. That difference is the interview point: MERGE as specified is not atomic against concurrent writers — under READ COMMITTED, two sessions can both find no match and both attempt an insert, giving a unique violation or a lost update, which is why the standard advice is ON CONFLICT for single-row upserts on a hot key and MERGE for bulk reconciliation where you control concurrency. MERGE also has a history of bugs in some engines, so check your version's notes.

SQLhardWalk me through how you would optimize a slow query.

Measure first — find the query with pg_stat_statements ordered by total time, not the one someone complained about, and confirm it is really the bottleneck rather than connection saturation or lock waits. Then EXPLAIN (ANALYZE, BUFFERS) it and read the plan bottom-up looking for three things: a node whose estimated rows differ wildly from actual, which means bad statistics or a correlated predicate the planner cannot model; a sequential scan on a large table under a selective filter, which means a missing or unusable index; and a nested loop over a large outer set, which means the row estimate that chose it was wrong. Fixes in the order I would try them: rewrite the predicate to be sargable, add or extend an index (composite with the equality columns first, or partial for a filtered subset), reduce the returned columns and rows so an index-only scan becomes possible, replace SELECT * and OFFSET pagination with keyset pagination, then ANALYZE or raise the statistics target. Only after that consider structural change — a materialized view, denormalisation, partitioning. And re-measure, because a plan that improves on a test dataset can regress at production cardinality.

SQLhardWhat is the difference between an index scan, an index-only scan, and a sequential scan — and what makes a predicate sargable?

A sequential scan reads every page; it is the right choice when the query touches a large fraction of the table, because random I/O per row would cost more. An index scan walks the B-tree to find matching entries then fetches each row from the heap — fast when the filter is selective, expensive when it is not. An index-only scan answers entirely from the index without touching the heap, which requires every referenced column to be in the index and the visibility map to be current, so it degrades on a recently-updated table until VACUUM runs. Postgres also has bitmap scans, which collect matches from one or more indexes and then read the heap in physical order, bridging the two extremes. Sargable means the predicate can be matched against an index: column = value and column > value and a prefix LIKE 'abc%' are; WHERE lower(email) = ?, WHERE created_at::date = ?, WHERE col + 1 = 10 and a leading-wildcard LIKE are not, because the column is wrapped in a function. The fixes are to move the transformation to the constant side, or build an expression index on exactly the expression you query.

SQLmedHow does LIKE work, and how do you index a pattern search?

LIKE matches with two wildcards — % for any sequence and _ for one character — and is case-sensitive; Postgres adds ILIKE for the case-insensitive version, and ~ for full regular expressions. The performance rule is about the leading character: a B-tree index can serve LIKE 'abc%' because a prefix is a range scan, but LIKE '%abc' or '%abc%' cannot use it at all and forces a sequential scan, because the index is ordered from the left. Note that a prefix search only uses the index if the column's collation permits it, which is why text_pattern_ops exists as an operator class for C-collation prefix matching. For genuine substring search the answer is a trigram index — CREATE EXTENSION pg_trgm, then a GIN index using gin_trgm_ops, which accelerates %abc% and ILIKE and also powers fuzzy similarity matching. For word-based search on prose, full-text search with tsvector and a GIN index is the right tool instead, since it handles stemming and ranking that LIKE cannot. Also remember to escape user-supplied % and _ or a search box becomes a full scan.

SQLmedWhat do COMMIT, ROLLBACK, and SAVEPOINT do?

COMMIT ends a transaction and makes its changes durable and visible to others; ROLLBACK discards everything since the transaction began. SAVEPOINT names a point inside a transaction so ROLLBACK TO SAVEPOINT undoes only the work after it while keeping the transaction and its earlier work alive — useful when one optional step in a batch may fail without invalidating the rest, and it is what a driver or ORM uses under the hood to implement nested transactions. RELEASE SAVEPOINT discards the marker. Things to state alongside: in Postgres, an error aborts the whole transaction and every subsequent statement fails with current transaction is aborted until you roll back — a savepoint is the way to recover mid-transaction; the durability of COMMIT depends on synchronous_commit and on replica acknowledgement, so a commit that returned is not automatically on the standby; and holding a transaction open across a network call or a user interaction is the classic mistake, because it holds locks and blocks VACUUM for its entire duration.

SQLmedWhat is a non-equi join and when is it useful?

An equi join matches with = and is what a JOIN nearly always is. A non-equi join uses any other predicate — <, >, BETWEEN, <> or an overlap operator — so a row on one side can match a range of rows on the other. The uses are genuinely common once you look for them: joining a fact to a rate or price table valid between two dates (ON f.occurred_at >= r.valid_from AND f.occurred_at < r.valid_to), bucketing values into bands by joining to a table of ranges, finding overlapping bookings or intervals, and self-joining to compare each row against all rows above it. The cost is the thing to say out loud: there is no hash join for an inequality, so the planner falls back to a nested loop or merge join and the result can approach the product of the two sides, which is why you bound one side, index the range columns, and in Postgres consider a range type with a GiST index and the && overlap operator instead. Many self-join-with-inequality patterns are better written as window functions.

SQLmedHow do you find and delete duplicate rows?

To find them, group by the columns that define a duplicate and keep the groups with more than one member: SELECT email, count(*) FROM users GROUP BY email HAVING count(*) > 1. To see the offending rows themselves, use a window: ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at) and look at everything with rn > 1. Deleting keeps that shape — DELETE FROM users WHERE id IN (SELECT id FROM (SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at) rn FROM users) t WHERE rn > 1) — where the ORDER BY encodes which copy survives, usually the earliest or the one with the most complete data. In Postgres, ctid is the fallback when the table has no unique column at all. Two things to do around it: run the SELECT first and check the count before the DELETE, and once the table is clean add the unique constraint or partial unique index that should have been there, because otherwise you will do this again next quarter.

SQLhardWhat is a covering index?

An index that contains every column a query needs, so the engine can answer entirely from the index without visiting the table — an index-only scan. Two ways to build one: put the extra columns in the key, which makes the index bigger and affects ordering and uniqueness, or use INCLUDE (Postgres 11+, SQL Server for years) to store them as payload in the leaf pages only. INCLUDE is usually right for columns you only ever return, since they do not participate in the search or in a unique constraint — CREATE INDEX ON orders (customer_id, created_at) INCLUDE (total). The payoff is largest for a query that returns many rows, because it removes one random heap fetch per row. The costs to name: a wider index means fewer entries per page and more memory and write amplification, so covering a rarely-run query is a bad trade; and in Postgres an index-only scan still consults the visibility map, so a heavily updated table falls back to heap fetches until autovacuum catches up. Check it worked by looking for Index Only Scan and a low Heap Fetches count in EXPLAIN ANALYZE.

SQLeasyHow do you concatenate strings in SQL, and what happens with NULLs?

The standard operator is ||, so first_name || ' ' || last_name. The trap is that in standard SQL and Postgres, concatenating anything with NULL yields NULL — so one missing last name turns the whole expression into NULL rather than into a partial name. Three ways out: wrap each operand in COALESCE, use CONCAT(a, b, c), which treats NULL as an empty string, or better use concat_ws(' ', first_name, last_name), which joins with a separator and skips NULL arguments entirely so you do not get a stray leading or trailing space. Note the vendor difference — in Oracle, || itself treats NULL as an empty string, and in SQL Server the + operator follows the standard NULL behaviour unless CONCAT_NULL_YIELDS_NULL is off. Related functions worth knowing: string_agg (Postgres) or GROUP_CONCAT (MySQL) to concatenate across rows within a group, and format() for building strings with placeholders — which is for display only, never for building SQL.

Next/ReacteasyWhat is JSX and what does it compile to?

JSX is syntax sugar that looks like HTML but is not — Babel or the TypeScript compiler turns each tag into a function call. Under the classic transform that call is React.createElement(type, props, ...children); since React 17 the automatic runtime emits jsx() imported from react/jsx-runtime instead, which is why modern files no longer need import React from 'react'. The consequences follow from it being a function call: a lowercase tag is treated as a DOM string while a capitalised one resolves to a variable, which is why components must be capitalised; attributes use JavaScript names (className, htmlFor) because they become object keys; a component must return a single root, hence Fragments; and expressions in braces are just arguments, so you can use ternaries and && but not if statements or for loops. Note that && renders a literal 0 when the left side is the number zero — use a ternary or a boolean coercion for counts.

Next/ReactmedWhat is the difference between a React node, an element, and a component?

A component is the function (or class) you write — a blueprint that takes props and returns a description of UI. An element is the plain immutable object that describes one instance: { type, props, key } — <Button /> does not run Button, it creates an element saying render a Button with these props, which is why React can compare cheaply and decide whether to call the function at all. A node is anything React can render in a slot: an element, a string, a number, null, undefined, a boolean, or an array of nodes — which is why the TypeScript type for children is ReactNode while the type of a single element is ReactElement. The distinction matters when an interviewer asks why calling a component as a function, Button(), differs from rendering it as <Button /> — the first inlines the output into the parent's tree with no separate fibre, so it gets no own state, no hooks isolation and no place in the DevTools tree.

Next/ReacteasyWhat are Fragments and when do you need one?

A Fragment lets a component return multiple children without introducing a wrapper DOM node — <>...</> in shorthand, or <React.Fragment> when you need to pass a key. Since JSX compiles to a single function call, a component must return one root, and before Fragments the workaround was a wrapping div that polluted the DOM. That matters more than it sounds: a stray div breaks CSS grid and flexbox layouts where the parent expects its children to be direct descendants, breaks table markup where a tbody may only contain tr, and adds depth that costs a little memory and makes the tree harder to read. The one case that needs the long form is a list — <React.Fragment key={id}> — because the shorthand cannot take props.

Next/ReactmedWhat goes wrong when you use the array index as a key?

Keys tell React which element in a list corresponds to which element in the previous render. With an index, that identity is positional rather than tied to the data, so any operation that shifts positions — inserting at the top, deleting from the middle, sorting, filtering — makes React match the wrong old element to the new one. The visible symptoms are state landing on the wrong row: text typed into one input appearing next to a different item, a checked checkbox jumping, a CSS transition animating the wrong element, and an uncontrolled input keeping a stale value. It also costs performance, because React updates the props of every element after the insertion point instead of moving one node. Use a stable id from your data. Index keys are acceptable only when the list is static, never reordered or filtered, and the items have no state — and even then a real id is cheaper than revisiting the decision later.

Next/ReacteasyWhat is the difference between props and state?

Props are inputs passed in from the parent — read-only from the child's perspective, and changing them is the parent's job. State is data the component owns, initialised by it and updated with a setter, and updating it schedules a re-render. The rules that follow: never write to props (mutating a prop object mutates the parent's data and skips the re-render); derive rather than copy, because useState(props.value) reads the prop once at mount and then silently ignores later changes, which is the classic stale-initial-state bug; and if two components need the same value, lift it to their nearest common ancestor rather than duplicating it. A useful framing for interviews: state is anything that changes over time, is not passed in, and cannot be computed from what you already have — everything else should be a prop or a derived value computed during render.

Next/ReactmedWhat is the difference between class and function components?

Class components extend React.Component, hold state in this.state, update it with setState, and hook into the lifecycle through componentDidMount, componentDidUpdate and componentWillUnmount. Function components are plain functions using hooks. Function components are the recommended default and every new React feature — hooks, Suspense integration, Server Components, the React Compiler — targets them. The mapping to know: componentDidMount plus componentDidUpdate plus componentWillUnmount collapse into one useEffect with a dependency array and a cleanup return, and shouldComponentUpdate becomes React.memo. The one thing still exclusive to classes is the error boundary, since componentDidCatch and getDerivedStateFromError have no hook equivalent — which is why most codebases keep a single class ErrorBoundary or use react-error-boundary. Also worth naming: this binding was a genuine source of bugs, and closures replaced it with a different one, the stale closure.

Next/ReacthardWhat is React Fiber?

Fiber is the reconciler rewritten in React 16 so that rendering can be interrupted. The old stack reconciler walked the tree recursively and could not be paused, so a large update blocked the main thread until it finished. Fiber re-implements the tree as a linked list of fibre nodes — each holding a unit of work, its state, and pointers to child, sibling and return — which lets React process work incrementally, pause after a unit, hand control back to the browser for a high-priority event, and resume or discard the work later. That is the machinery underneath everything called concurrent: priority lanes, useTransition, Suspense, and time slicing. The two phases matter for reasoning about bugs: the render phase (calling your components, diffing) is interruptible and may run twice or be thrown away, which is exactly why side effects in render are forbidden and why StrictMode double-invokes to surface them; the commit phase, where the DOM is mutated, is synchronous and uninterruptible.

Next/ReacteasyWhat is the difference between the Shadow DOM and the Virtual DOM?

They are unrelated despite the similar names. The Shadow DOM is a browser standard, part of Web Components — it attaches a separate, encapsulated DOM subtree to an element so that its markup and especially its CSS do not leak in or out. That is a scoping and isolation feature, and React does not use it. The Virtual DOM is React's own in-memory tree of plain JavaScript objects describing the intended UI; React diffs the new tree against the previous one and applies the minimum set of real DOM mutations. That is a rendering-strategy feature. The short answer for an interview is that Shadow DOM is about encapsulation and is implemented by the browser; Virtual DOM is about efficient updates and is implemented by the library. You can use both together — a React app can render into a shadow root — though event handling and styling need care because React attaches its listeners at the root container.

Next/ReactmedWhat is the difference between controlled and uncontrolled components?

In a controlled component React state is the single source of truth: the input gets value={state} and onChange={e => setState(e.target.value)}, so what is on screen is always what is in state. In an uncontrolled component the DOM keeps its own value and you read it when you need it, usually through a ref or from the form submit event, with defaultValue supplying the initial content. Controlled is the default choice because it makes validation-as-you-type, conditional formatting, disabling submit and resetting trivial. Uncontrolled is better for large forms where a re-render per keystroke is measurable, for file inputs (which are always uncontrolled, since their value cannot be set programmatically), and for integrating a non-React widget. The trap is the accidental switch: passing value={undefined} initially and a string later makes React warn about changing a component from uncontrolled to controlled — initialise with an empty string, not undefined or null.

Next/ReacteasyWhat does it mean to lift state up, and how is that different from prop drilling?

Lifting state up means moving a piece of state to the nearest common ancestor of the components that need it, then passing the value down as a prop and a setter down as a callback. It is the standard fix when two siblings must stay in sync — a filter input and the list it filters — because duplicating the state in both leaves you reconciling two copies. Prop drilling is what it turns into when the ancestor is far away: the value is threaded through several intermediate components that do not use it themselves, which makes those components harder to reuse and every signature churn a multi-file change. The fixes for drilling, in escalating order: restructure with composition so the consumer is passed as children and the data never has to traverse (often enough on its own), then Context for genuinely global and rarely-changing values like theme or the current user, then an external store for large or frequently-updating shared state. Reaching for Context at two levels of drilling is over-correction.

Next/ReactmedWhat is React.memo and when does it actually help?

React.memo wraps a component so it skips re-rendering when its props are shallowly equal to last time — the function-component equivalent of the old PureComponent, and roughly what shouldComponentUpdate did by hand. The catch is that shallow equality fails for anything created inline during the parent's render: an object literal, an array, an inline arrow, or a JSX element passed as a prop is a new reference every time, so the memo never hits. That is the real reason useCallback and useMemo exist — they are there to keep a memoized child's props stable, not to make the parent faster. So memo is only worth it when the component is genuinely expensive to render or renders a long list, its props are stable or you have made them stable, and you have measured the win in the Profiler. Wrapping every component in memo makes the app slower on balance, because each one now pays a props comparison on every parent render. Note that the React Compiler in React 19 automates most of this, at which point manual memo is largely legacy.

Next/ReacteasyHow should you type-check props — PropTypes or TypeScript?

TypeScript. PropTypes validated props at runtime in development only, logging a console warning on a mismatch, and were removed from React in version 19 — the prop-types package still exists but the built-in support is gone. TypeScript checks at compile time, catches the error in the editor before the code runs, covers the whole component surface rather than just props, and costs nothing at runtime. Practical notes for an interview: type props with an explicit interface rather than React.FC (which added an implicit children and had generics quirks), use React.ReactNode for children, and prefer a discriminated union over optional booleans when props are mutually exclusive — that lets the compiler reject an invalid combination rather than leaving you to check for it at runtime. The one thing TypeScript cannot do is validate data crossing a runtime boundary, which is why an API response should still be parsed with something like Zod rather than merely cast.

Next/ReactmedWhy does React tell you never to mutate state?

Because React decides whether to re-render by comparing references, not by deep-inspecting values. state.items.push(x) followed by setItems(state.items) passes the same array reference, so Object.is says nothing changed and the render is skipped — the data updated but the screen did not. Mutation also breaks React.memo, useMemo and useEffect dependency checks for the same reason, defeats time-travel debugging and the ability to compare previous and next state, and in concurrent rendering it can let a half-applied change be observed. The fix is to replace rather than edit: [...items, x], items.filter(...), items.map(...), { ...obj, field: value }, and toSorted or [...arr].sort() rather than arr.sort() which mutates in place. For deeply nested state the spread chain becomes unreadable, which is the signal to flatten the shape, move to useReducer, or use Immer — where you write mutating syntax and it produces an immutable update for you.

Next/ReactmedWhat are the rules of hooks and why do they exist?

Call hooks only at the top level — never inside a condition, loop, nested function or after an early return — and call them only from a React function component or another hook. The reason is the implementation: hooks are not named, they are stored per component as an ordered list, and React matches this render's hook calls to last render's purely by call index. Skipping a useState behind an if shifts every subsequent hook by one, so a useEffect suddenly reads the state that belonged to another hook — corrupted state rather than a clean error. The linter, eslint-plugin-react-hooks, enforces both rules, and its exhaustive-deps rule is the one people disable and should not: a missing dependency is a stale closure waiting to happen. The way to write a conditional hook is to keep the call unconditional and make the condition an argument — useQuery(key, { enabled: isReady }) — or to split the branch into two components.

Next/ReactmedWhat is the difference between useEffect and useLayoutEffect?

Both run after render, but at different moments. useEffect is deferred — React commits to the DOM, the browser paints, and then the effect runs asynchronously, so it never blocks the frame. useLayoutEffect runs synchronously after the DOM mutation but before the browser paints, so whatever it writes is visible in the same frame. Use useEffect for essentially everything — data fetching, subscriptions, logging, timers. Use useLayoutEffect only when you must read layout and change it before the user sees the intermediate state: measuring an element to position a tooltip, restoring scroll position, or synchronously adjusting a size to avoid a visible flicker. The costs to name: it blocks paint, so slow work there is jank; and it does not run on the server, so it warns during SSR — the standard workaround is a useIsomorphicLayoutEffect that falls back to useEffect when window is undefined.

Next/ReactmedWhat is useRef used for?

It returns a mutable object whose .current persists across renders and whose mutation does not trigger one. That gives it two distinct jobs. First, a handle on a DOM node — <input ref={inputRef} /> then inputRef.current.focus() — for focus management, measurement, scrolling, and driving imperative libraries such as a chart or a map. Second, an instance variable: holding a timer or interval id so cleanup can clear it, keeping the previous value of a prop to compare against, storing a mutable flag like hasSubmitted, or caching something that must survive re-renders without causing them. The rule that keeps it safe is that you must not read or write a ref during render — it is not part of the rendering data flow, and doing so makes output depend on something React does not track. If a value should appear on screen when it changes, it is state, not a ref. A callback ref (ref={node => ...}) is the escape hatch when you need to react to the node attaching or detaching.

Next/ReactmedWhen must you use the updater form of a state setter?

Whenever the next value depends on the current one: setCount(c => c + 1) rather than setCount(count + 1). The reason is that count is a value captured by the closure of this render, so calling setCount(count + 1) twice in one handler computes the same result twice and increments by one, and an async callback — a setTimeout, a fetch resolution, an event listener registered once — reads whatever count was when it was created, which may be several renders stale. The updater form receives the latest queued state instead, so it composes correctly and is immune to the stale closure. React batches state updates within an event handler, and since React 18 that batching also covers promises, timeouts and native handlers, so a batch of updater calls all apply in order. The same argument applies to the callback form of setState in class components and is why it existed there too.

Next/ReactmedWhen should you reach for useReducer instead of useState?

When the next state depends on the previous state in a non-trivial way, when several fields change together as one logical transition, or when the update logic is complex enough that you want it testable in isolation. A reducer is a pure (state, action) => newState function, so it can be unit tested with no React at all, and every way the state can change is enumerated in one switch rather than scattered across handlers. The other advantage is that dispatch is referentially stable for the life of the component, so passing it down to memoized children or into a Context needs no useCallback — unlike a setter you have wrapped. A good signal to migrate: three or four useState calls that are always updated together, or a bug caused by two of them going momentarily out of sync. Combine it with Context to get a lightweight Redux for a single feature — reducer state in one provider, dispatch in another, so consumers of dispatch do not re-render when state changes.

Next/ReacteasyWhat is useId for?

It generates an identifier that is stable across the server and client render, for linking related elements — an input to its label with htmlFor, a field to its error message with aria-describedby, a control to the element it describes. The reason it exists is hydration: generating an id with a counter or Math.random produces a different value on the server than on the client, which is a hydration mismatch, and a module-level counter breaks as soon as components mount in a different order. useId is derived from the component's position in the tree, so both renders agree. Two things it is not for: it must not be used as a list key (keys come from your data), and it is not a general unique-id generator for database records. If a component renders several related fields, call it once and suffix — const id = useId() then id + '-email' and id + '-error' — rather than calling the hook repeatedly.

Next/ReactmedWhat is a custom hook and what makes a good one?

A function whose name starts with use and which calls other hooks — that naming is not decoration, it is what tells the linter to enforce the rules of hooks inside it. Custom hooks extract stateful logic so it can be reused: useDebounce, useLocalStorage, useMediaQuery, useFetch, useOnClickOutside. The key property is that they share logic, not state — two components calling useCounter() each get their own independent state, which is exactly the difference from a Context and the thing candidates most often get wrong. What makes a good one: it returns a stable shape (a tuple for two values, an object for more), it cleans up whatever it subscribes to, its dependencies are honest so it does not go stale, and it does one thing — a useEverything hook that fetches, caches and formats is harder to test than three. Custom hooks are also the replacement for HOCs and render props, which solved the same problem with wrapper components and worse ergonomics.

Next/ReactmedWhat actually causes a component to re-render?

Four things: its own state changed (via a setter, where React bails out if the new value is Object.is-equal to the old), its parent re-rendered, a Context it consumes changed value, or a store it subscribes to notified it. The one people get wrong is the second — a parent re-render re-renders all its children by default, regardless of whether their props changed, because React has to call the function to find out what it returns. Props changing is not itself a trigger; it is a consequence of the parent rendering. Two clarifications worth stating: a re-render means React calls your function and diffs the result, which is usually cheap — it is not a DOM update, and if the output is identical no DOM is touched; and the fix for an expensive one is rarely memo first. Better options are moving state down so fewer components sit under it, passing the expensive subtree through children so it is created in the parent that is not re-rendering, and splitting the Context. Verify with the Profiler's why-did-this-render, not by guessing.

Next/ReactmedWhat is forwardRef, and what changed in React 19?

Refs were historically not passed through like other props — putting ref on a function component did nothing, because ref (like key) was reserved and stripped from props. forwardRef((props, ref) => ...) was the wrapper that let a component receive one and attach it to an inner DOM node, which is what any design-system Input or Button needs so a parent can focus or measure it. In React 19 ref is an ordinary prop for function components, so you write function Input({ ref, ...props }) and forwardRef is deprecated — it still works, and a codemod exists. Related: useImperativeHandle lets a component expose a curated API through its ref — focus and clear, say — rather than the raw node, which keeps the internals encapsulated; use it sparingly, since an imperative escape hatch bypasses the declarative data flow that makes React predictable.

Next/ReactmedWhat are error boundaries and what do they not catch?

A component that implements getDerivedStateFromError or componentDidCatch and renders a fallback instead of crashing the tree below it — since React 16, an uncaught render error unmounts the whole application rather than leaving a half-broken UI, so a boundary is what stops one broken widget taking down the page. They must be class components; there is no hook equivalent, which is why most teams keep one class or use react-error-boundary for its hooks-friendly API and resetErrorBoundary. What they do not catch is the part interviewers probe: errors inside event handlers (use try/catch there — the handler is not part of rendering), asynchronous code such as a setTimeout callback or a rejected promise, errors thrown by the boundary itself, and errors during server-side rendering. Place them deliberately — one at the root for a last-resort screen, plus narrower ones around independently-failing regions such as a dashboard widget or a route, so a failure degrades one panel instead of the page.

Next/ReactmedWhat is Suspense?

A boundary that catches a child suspending — a component that is not ready to render — and shows a fallback until it is. Originally it served React.lazy for code-split components; now it also covers data through frameworks and the use hook, and streaming SSR, where the server sends the shell immediately and flushes each boundary's HTML as its data resolves. Two properties are worth naming. It is declarative: the loading state lives in the tree next to the thing that loads, rather than in an isLoading branch inside every component, so several children can share one spinner. And boundary placement is a design decision — one boundary high in the tree means the whole page waits, while several narrow ones let independent regions appear as they are ready, which is the difference between a good and a bad perceived load. Pair it with an error boundary, since the sibling failure case is an error rather than a pending state, and note that Suspense does not catch errors itself.

Next/ReactmedWhat are Portals for?

createPortal(children, domNode) renders children into a different part of the DOM while keeping them in the same React tree. That split is the whole point: the visual position escapes the parent, but context, state and — importantly — event bubbling still follow the React tree, so a click inside a portalled modal still reaches an onClick on its React parent even though the node lives on document.body. The use case is any overlay that must escape a clipping or stacking ancestor: modals, dropdowns, tooltips and toasts, which break when an ancestor has overflow:hidden, a transform, or a z-index that traps them. Things to get right alongside it: focus must be moved into the overlay and trapped there, Escape must close it, the rest of the page should be inert or aria-hidden, and the container must exist before you portal into it — which in Next.js means guarding for the server render, since document does not exist there.

Next/ReactmedWhat does React StrictMode do?

It is a development-only wrapper that adds checks and deliberately double-invokes certain functions to surface bugs that would otherwise only appear under concurrent rendering. In development it renders components twice, runs effects twice (mount, unmount, mount), and double-calls state updater functions and reducers — none of which happens in production. The double-effect is the one that confuses people: seeing a fetch fire twice is not a bug, it is StrictMode telling you the effect is not idempotent and has no cleanup. The fix is to write the cleanup — abort the request, unsubscribe the listener, clear the timer — which is what makes the component safe when React later mounts, unmounts and remounts it for real. The double render similarly exposes impure components: if rendering twice produces different output or a visible side effect, the component was mutating something it should not. Treat every StrictMode warning as a real bug, not noise to be silenced.

Next/ReactmedWhat is code splitting and how do you do it in React?

Splitting the bundle so the browser downloads only what the current view needs, instead of one file containing the whole application. In React the primitive is React.lazy(() => import('./Heavy')), which returns a component that suspends while its chunk loads, wrapped in a Suspense boundary that supplies the fallback; Next.js wraps the same idea in next/dynamic, which adds an ssr:false option and a loading prop. The natural split points are routes first (the biggest win, since a user rarely visits every page), then heavy dependencies behind an interaction — a rich text editor, a chart library, a date picker, a PDF viewer — then anything below the fold or behind a modal. Two things to get right: prefetch on hover or on idle so the click does not wait for a network round trip, and keep the fallback the same size as the content to avoid layout shift. Measure before splitting — a bundle analyser tells you which dependency is actually large, and splitting a 4KB component achieves nothing while adding a request.

Next/ReacthardWhy does Context cause unnecessary re-renders and how do you fix it?

Every consumer of a context re-renders whenever the provider's value changes, and the comparison is by reference. Two failures follow. First, an inline object — value={{ user, setUser }} — is a new reference on every provider render, so every consumer re-renders even when nothing meaningful changed; wrap it in useMemo with honest dependencies. Second, context has no selector: a consumer that only reads theme still re-renders when user changes, because it subscribes to the whole value. The fixes, in order: split into several small contexts by change frequency so a fast-changing value does not drag slow-moving consumers with it; separate state from dispatch into two providers, since dispatch is stable and its consumers then never re-render; push state down or pass the expensive subtree as children so it is not re-created; and if you genuinely need selector-based subscriptions, use a store — Zustand, Redux or useSyncExternalStore — because that is the problem stores solve and Context does not. Context is a dependency-injection mechanism, not a state manager.

Next/ReacteasyWhat is one-way data flow, and what was Flux?

Data in React flows down: a parent passes props to a child, and a child that needs to change something calls a function the parent passed it. There is no two-way binding, so for any piece of state there is exactly one component that owns it and one place that can change it — which is what makes a wrong value traceable by walking up the tree rather than searching for whoever might have written to it. Flux was Facebook's architecture generalising the same idea to application state: an action is dispatched, the dispatcher sends it to stores, stores update and notify views, and views dispatch further actions — a cycle, never a shortcut back up. Redux is the well-known descendant, simplified to a single store and a pure reducer, and its vocabulary (action, reducer, dispatch, store) is Flux's. The reason the pattern persists is debuggability: every change is an explicit, serialisable action, which is what makes time-travel debugging and action logs possible.

Next/ReactmedWhat are some common React anti-patterns?

Mutating state directly, so the reference does not change and the re-render never happens. Copying props into state with useState(props.value), which silently ignores later prop changes — derive during render instead, or key the component to force a remount when identity genuinely changes. Storing values in state that can be computed from existing state or props, which creates two sources of truth that drift. Calling hooks conditionally. Using the array index as a key on a reorderable list. Doing side effects during render rather than in an effect or a handler. Using an effect to sync one state to another — that is a render-time derivation, and the extra render it causes is visible. Reading or writing a ref during render. Disabling exhaustive-deps to silence a warning instead of fixing the dependency. Wrapping everything in memo, useMemo and useCallback without measuring, which adds comparisons and allocation for no gain. And giant components — the sign is a file where the hook list at the top no longer fits on a screen.

Next/ReacteasyWhat are synthetic events?

React wraps native browser events in a SyntheticEvent — a cross-browser normalised object with the same interface (preventDefault, stopPropagation, target, currentTarget) regardless of engine quirks, with the underlying native event still available as e.nativeEvent. Rather than attaching a listener per element, React attaches listeners at the root container (the document before React 17, the root container since) and dispatches through its own tree — which is how a portalled child still bubbles to its React parent even though the DOM says otherwise. Details worth knowing: React 17 removed event pooling, so the old requirement to call e.persist() before using an event asynchronously is gone; onChange on an input behaves like the native input event, firing per keystroke rather than on blur, which is a deliberate difference from the DOM; and mixing addEventListener with React handlers means the two systems can see events in an order you did not expect, so stopPropagation in one may not stop the other.

Next/ReacthardWhat are React's concurrent features?

Concurrency here means rendering is interruptible: React can start rendering an update, pause it to handle something more urgent, and resume or discard the work — enabled by the Fiber architecture and by React 18's createRoot. The practical consequence is that a slow update can no longer block typing. React assigns priority lanes: discrete user input (clicks, keystrokes) is urgent and must land in the same frame, while updates you mark with startTransition are deferred and may be interrupted or thrown away if a newer one arrives. The canonical example is a search box over a large filtered list — the input update is urgent so the field stays responsive, the list re-render is a transition so React can abandon a half-finished render when the next keystroke arrives. Suspense integrates with the same machinery, which is what allows streaming SSR and lets React keep showing the previous content instead of flashing a spinner. Note it is not multi-threading — it is still one thread, cooperatively yielding.

Next/ReacthardWhat is the difference between useTransition and useDeferredValue?

Both mark work as non-urgent so it cannot block user input; they differ in where you apply them. useTransition gives you [isPending, startTransition] and wraps the state update — you call startTransition(() => setQuery(value)) at the source, which requires that you own the setter, and isPending gives you a first-class way to show a subdued loading state. useDeferredValue wraps a value you were handed — const deferred = useDeferredValue(query) — and React renders with the previous value until it has time to catch up, which is what you use when the state comes from a prop or a hook you do not control. The pattern that ties them together: keep the input controlled by urgent state so typing is instant, then feed the expensive list the deferred value. Two caveats: neither makes the render faster, they only stop it blocking, so a genuinely slow component still needs memoization or virtualisation; and deferring a value causes an extra render, so it is not free on cheap subtrees.

Next/ReactmedHow do you handle an expensive computation without blocking the UI?

First establish where the cost is. If it is a pure calculation that repeats unnecessarily, useMemo removes the repetition — but it runs synchronously during render, so it does not help if the calculation is slow the first time. If the work is genuinely heavy CPU (parsing a large file, image processing, a big sort or crypto), move it off the main thread into a Web Worker and communicate by message — that is the only option that truly stops the jank, since everything else still competes for the same thread. If it is rendering rather than computing — thousands of rows — the answer is virtualisation, rendering only the visible window with react-window or TanStack Virtual, plus pagination on the server. If it is a large but interruptible React update, mark it with startTransition or useDeferredValue so typing stays responsive. Chunking with setTimeout or requestIdleCallback is the fallback for work you can split. And measure first: the Profiler and a performance trace tell you whether you are looking at a slow function, too many renders, or layout thrash.

Next/ReactmedWhat are higher-order components and render props, and why did hooks replace them?

Both are pre-hooks patterns for sharing stateful logic. A HOC is a function taking a component and returning a wrapped one with extra props — withRouter, connect from Redux, withAuth. A render prop passes a function as a prop (often children) that receives the shared state and returns elements — the old React Router Route render prop, or a <Mouse>{pos => ...}</Mouse>. They work, and the problems they share are structural: both add wrapper components, so the tree fills with layers that exist only to inject data (wrapper hell in DevTools); prop names can collide silently when HOCs are stacked; the data flow is indirect, so where a prop comes from is not obvious; static methods and refs need explicit forwarding; and types are awkward. A custom hook shares the same logic with no wrapper, an explicit call site, no naming collisions and straightforward typing. HOCs still have a place for genuinely cross-cutting wrapping — an error boundary, an analytics wrapper — where you are adding structure rather than data.

Next/ReactmedWhat is the composition pattern in React?

Building complex UI by passing components to each other rather than by configuring one component with a growing pile of props. Instead of <Modal title showClose footerButtons={[...]} />, you write <Modal><Modal.Header/><Modal.Body>...</Modal.Body></Modal> and let the caller supply the pieces. Two concrete techniques: children for the default slot, and named props holding elements — <Layout sidebar={<Nav/>} content={<Feed/>} /> — for multiple slots. Composition is also the first and often sufficient answer to prop drilling, because a component passed as children is created in the parent that already has the data, so nothing needs threading through the middle. It has a performance side effect worth knowing: children passed from above are not re-created when the wrapper re-renders, so an expensive subtree passed as children is effectively memoized for free. The alternative it replaces is inheritance, which React explicitly does not use — the docs recommend composition for every case where you might reach for it.

Next/ReacteasyHow do you re-render when the browser window is resized?

Subscribe in an effect and store the size in state: a useEffect with an empty dependency array that adds a resize listener, sets state in the handler, and returns a cleanup that removes the listener. The cleanup is the part interviews test — without it every mount adds another listener, which leaks and eventually calls setState on an unmounted component. Two refinements make it production-quality. Debounce or throttle the handler, because resize fires continuously during a drag and a setState per event is a re-render per frame. And read the size inside the effect on mount rather than during render, because window does not exist during server rendering — reading it at module scope or in the initial state crashes SSR and causes a hydration mismatch. The modern alternatives are worth naming: useSyncExternalStore is the correct primitive for subscribing to an external value like this, ResizeObserver is better when you care about an element rather than the window, and CSS container queries or media queries handle most cases with no JavaScript at all.

Next/ReacthardWhat are the common pitfalls when fetching data in useEffect?

Race conditions: two requests in flight resolve out of order and the slower, older one overwrites the newer result — fix with an ignore flag set in the cleanup, or an AbortController whose abort you call there. Missing cleanup generally, which leaks subscriptions and updates unmounted components. Dependency mistakes: an object or a function in the array makes the effect run every render (an infinite loop when the effect sets state), while omitting a dependency gives a stale closure fetching last render's id. Ignoring loading and error states, so a failure renders as an empty list. Waterfalls, where a child fetches only after its parent resolved, when both could have started together. And the StrictMode double-fetch in development, which is not a bug but a signal that cleanup is missing. The honest interview answer is that this is why the ecosystem moved on: React Query or SWR give caching, deduplication, retries, revalidation and race handling out of the box, and in an App Router app the data belongs in a Server Component or a loader, not in an effect at all.

Next/ReacteasyWhat is React Router and how do you define dynamic routes?

React Router maps URLs to components so a single-page app can have real, linkable, back-button-friendly URLs without a server round trip. You declare routes — in v6 as <Routes> with <Route path='/users/:id' element={<User />} /> or via createBrowserRouter with a route object array — and navigate with <Link to='...'>, which intercepts the click and pushes to the History API instead of reloading. A dynamic segment is a colon-prefixed path parameter, read inside the component with the useParams hook: const { id } = useParams(). Points worth adding: v6 matches the single best route rather than every prefix, so the old exact prop is gone; a splat path='*' catches everything not matched; and the data-router API (createBrowserRouter) adds loaders and actions that fetch before rendering, which removes the fetch-in-effect waterfall. Next.js solves the same problem with file-based routing, so a Next codebase does not use React Router at all.

Next/ReactmedHow do nested routes and Outlet work in React Router?

Nesting a <Route> inside another makes the child's path relative to the parent's and, crucially, makes the child render inside the parent rather than instead of it. The parent renders <Outlet /> to mark the slot where whichever child matched should appear — which is exactly how you build a persistent shell: a layout route rendering the sidebar and header once, with the page content swapping in the outlet as the URL changes. That avoids remounting the shell on every navigation, so scroll position and any state in the sidebar survive. Related pieces: an index route (<Route index>) is what renders in the outlet at the parent's own path; a pathless layout route groups children under shared UI without adding a URL segment; useOutletContext passes data from a layout to whatever renders inside it; and relative links (to='settings' rather than to='/users/1/settings') resolve against the current route, which keeps a nested tree movable.

Next/ReacteasyWhat is the difference between BrowserRouter and HashRouter?

BrowserRouter uses the History API, so URLs are clean — /users/42 — and are real paths the server sees. That requires the server to rewrite every unknown path to index.html, otherwise a refresh or a pasted deep link 404s, which is the single most common deployment bug with SPAs. HashRouter puts the route after a fragment — /#/users/42 — which the browser never sends to the server, so any static host serves it with no configuration. Use BrowserRouter by default: clean URLs are better for SEO, analytics and sharing, and the rewrite is one line in nginx, a _redirects file, or a hosting setting. Reach for HashRouter only when you cannot control the server — a file:// build, an old CDN, or an app embedded in someone else's page. The other routers are situational: MemoryRouter keeps history in memory, which is what you use in tests and React Native, and StaticRouter is for server rendering.

Next/ReacteasyHow do you navigate programmatically, and when do you use replace instead of push?

The useNavigate hook returns a function: navigate('/dashboard') after a successful login, navigate(-1) to go back, and navigate('/path', { state: { from } }) to pass data the destination reads with useLocation. By default it pushes a new entry onto the history stack, so the back button returns to where the user came from. Pass { replace: true } to overwrite the current entry instead, which is what you want whenever going back would be wrong or broken: after a login (back should not return to the login form of an already-authenticated user), after a redirect away from a deleted resource, when replacing a temporary URL, and when a redirect would otherwise create a loop between two pages that each bounce to the other. The declarative equivalent is <Navigate to='...' replace />, which is preferred inside render — calling navigate during render rather than in an effect or a handler is a side effect in render and warns.

Next/ReactmedHow do you implement a private or protected route?

Wrap the protected element in a component that checks auth and either renders it or redirects: if the user is not authenticated, return <Navigate to='/login' replace state={{ from: location }} />, otherwise return children (or an <Outlet /> if you make it a layout route, which lets you protect a whole subtree with one wrapper). Capturing the attempted location in state is what lets the login page send the user back where they were going instead of dumping them on a generic dashboard. The details that separate a good answer: handle the third state — auth is usually loading on first paint, and treating unknown as unauthenticated flashes the login page for every returning user, so render nothing or a skeleton while it resolves; use replace so back does not return to a protected page; and say plainly that this is UX, not security — a client-side check is trivially bypassed, so every protected resource must be authorised on the server regardless of what the router does.

Next/ReacteasyHow do you read query parameters and highlight the active route?

For query strings, useSearchParams returns a URLSearchParams and a setter, so const [params, setParams] = useSearchParams() then params.get('page'). Because it writes to the URL, filters, sort order and pagination stored there become shareable and survive a refresh and the back button — which is why URL state is usually a better default than useState for anything a user might want to link to. Use the functional form of the setter to update one parameter without clobbering the others, and pass { replace: true } for high-frequency updates such as a search-as-you-type box so you do not fill the history stack. For active links, <NavLink> handles it for you — its className and style props accept a function receiving { isActive, isPending }. Doing it by hand means comparing useLocation().pathname, in which case watch for prefix matching: /users would otherwise light up while you are on /users/42/settings.

Next/ReactmedHow do you localize a React application?

Use an i18n library rather than hand-rolling a lookup object: react-i18next or react-intl (FormatJS) are the two standards, and next-intl is the common choice in Next.js. The mechanics are the same — a provider holds the active locale and a message catalogue keyed by id, and components pull strings by key, either declaratively (<FormattedMessage id='cart.total' values={{ count }} />) or through a hook (useIntl().formatMessage, or the t function from useTranslation). What separates a real answer from a superficial one is what translation is not: it is not string concatenation, because word order differs between languages — use placeholders in the message, never a + b; plurals need ICU plural rules rather than an if count === 1, since some languages have more than two forms; dates, numbers and currencies go through Intl.DateTimeFormat and Intl.NumberFormat so they follow the locale; and RTL languages need direction-aware layout. Operationally, keep the locale in the URL so pages are indexable and shareable, and lazy-load catalogues so a user does not download every language.

Next/ReactmedHow do you test React components, and what queries should you use?

Jest or Vitest as the runner, React Testing Library for rendering and querying. The guiding principle is to test what a user experiences rather than implementation details — render the component, interact with it, assert on what appears — because a test coupled to internal state or a component name breaks on every refactor while a test coupled to visible behaviour survives it. Use the query priority the library recommends: getByRole first (getByRole('button', { name: /save/i })), since it also verifies accessibility, then getByLabelText for form fields, then getByText, and data-testid only as a last resort. Know the three prefixes: getBy throws if absent, queryBy returns null and is the only correct one for asserting absence, and findBy returns a promise for something that will appear. Prefer userEvent over fireEvent — it simulates a real interaction sequence (focus, keydown, input) rather than dispatching a single synthetic event. And test behaviour at the boundary: one integration test through a feature beats ten tests asserting that a setter was called.

Next/ReactmedHow do you test asynchronous behaviour in React components?

Use the async queries and utilities rather than sleeping. findByText and its siblings return a promise that retries until the element appears or the timeout expires — await screen.findByText('Saved') is the idiomatic way to wait for something to render after a fetch. waitFor retries an arbitrary assertion, and waitForElementToBeRemoved is the correct way to wait for a spinner to disappear. Wrap state-updating interactions so React flushes them — userEvent does this for you, which is another reason to prefer it, and the act warning in the console is React telling you an update happened outside that window, usually from a promise resolving after the test finished. Things to avoid: an arbitrary setTimeout, which makes the suite slow and flaky; putting a side-effecting call inside waitFor, since it retries; and asserting on a loading state that may already be gone. Fake timers are the tool when the code itself uses timers — advance them explicitly instead of waiting in real time.

Next/ReactmedHow do you mock API calls in component tests?

Two levels. jest.mock (or vi.mock) replaces the module — your api client or fetch itself — which is quick but couples the test to how the request is made, so switching from axios to fetch breaks tests that assert nothing about behaviour. Mock Service Worker intercepts at the network layer instead, so the component runs its real client and the test defines handlers by URL and method returning realistic responses. MSW is the better default: the same handlers work in tests, in Storybook and in the browser during development, it exercises the actual request path including headers and serialisation, and it makes error and slow cases easy to express by returning a 500 or delaying a handler. Whichever you use, the discipline is the same — test the states, not the call: loading, success, empty, and error. Asserting that fetch was called with a particular URL is a weak test; asserting that the error state renders a retry button the user can click is a real one.

Next/ReactmedHow do you test a custom hook?

Use renderHook from React Testing Library, which mounts the hook inside a throwaway component and exposes result.current. Read values from result.current, and wrap anything that updates state in act() so React flushes before you assert — for async updates, await the promise or use waitFor rather than asserting immediately. Two options carry most of the real cases: the wrapper option supplies providers the hook depends on (a QueryClientProvider, a context, a router), and rerender lets you pass new props to check that the hook responds to changing arguments, which is where dependency bugs show up. unmount is how you verify cleanup — that a listener was removed or a timer cleared. The judgement call worth voicing: a hook that is a thin wrapper over another library is often better covered by a test of the component that uses it, since that tests the integration rather than the plumbing. Reserve dedicated hook tests for hooks with real logic — debouncing, state machines, derived calculations.

Next/ReacteasyWhat is snapshot testing and when is it useful?

toMatchSnapshot serialises the rendered output to a file on first run and compares against it thereafter, failing when the output changes. The appeal is coverage for free; the problem is that it asserts nothing about intent — a snapshot passes as long as nothing changed, so it cannot tell a fix from a regression. In practice large snapshots get rubber-stamped with the update flag whenever they fail, at which point they are recording history rather than testing anything, and they produce noisy diffs on unrelated refactors. Where they do earn their place: small, focused snapshots (inline ones especially, via toMatchInlineSnapshot, so the expected value sits in the test file where a reviewer sees it), serialised non-UI output such as a reducer result or a formatted string, and pinning down error messages. For component behaviour, an explicit assertion — that the button is disabled, that the error text appears — says what you meant and survives a markup change that did not alter behaviour.

Next/ReacteasyWhat is the difference between shallow rendering and full DOM rendering?

Shallow rendering renders one level deep, leaving child components as unrendered placeholders, so a test sees only the component under test. Full rendering mounts the whole subtree into a real DOM (jsdom), so children run, effects fire and interactions behave as they would in a browser. Shallow was the Enzyme-era default, justified as isolation; the reason the ecosystem abandoned it is that it tests structure rather than behaviour — the assertions are about which child components were created with which props, which is implementation detail that breaks on refactor and passes even when the rendered result is broken. React Testing Library only does full rendering, deliberately, and Enzyme has no official adapter past React 17. The other deprecations to know: react-test-renderer was deprecated in React 19, and its shallow renderer earlier — the recommended path for both is render from React Testing Library. If a subtree is genuinely too expensive or too coupled, mock that specific module rather than shallow-rendering everything.

Next/ReactmedWhat is new in React 19?

Actions, which are async functions passed to a form's action prop or run through a transition, with pending state, errors and optimistic updates handled by React rather than by hand — supported by the new hooks useActionState, useFormStatus and useOptimistic. The use hook, which reads a promise or a context during render and suspends until it resolves, and unlike other hooks may be called conditionally. Server Components and Server Actions promoted to stable in the official API. ref as an ordinary prop, deprecating forwardRef. Document metadata — a <title> or <meta> rendered anywhere is hoisted into head — plus support for stylesheet and async script hoisting and preloading. Better hydration error messages that show a diff. Context usable as <Context> rather than <Context.Provider>. Cleanup functions returned from ref callbacks. And alongside the release, the React Compiler, which memoizes automatically at build time. Removals worth knowing: PropTypes, defaultProps on function components, legacy string refs, and react-test-renderer.

Next/ReactmedWhat are Actions and useActionState in React 19?

An Action is an async function React runs inside a transition and tracks for you. The shape most people meet first is <form action={submitFn}> — on submit React calls submitFn with the FormData, keeps the form in a pending state while it runs, resets uncontrolled inputs on success, and surfaces a thrown error to the nearest error boundary. That removes the boilerplate every form used to carry: an isSubmitting state, a try/catch setting an error state, and a manual reset. useActionState is the hook that gives you the result: const [state, formAction, isPending] = useActionState(action, initialState), where the action receives the previous state plus the FormData and returns the next state — so validation errors come back as data rather than as thrown exceptions, and the whole flow works before hydration when paired with a Server Action. useFormStatus is the companion for a child component (a submit button) that needs to know its parent form is pending without prop drilling.

Next/ReactmedWhat does useOptimistic do?

It lets you render the expected result of an action immediately, before the server confirms it, and reverts automatically if the action fails. const [optimisticItems, addOptimistic] = useOptimistic(items, (current, newItem) => [...current, newItem]) — you call addOptimistic inside the action, the UI updates instantly, and when the action settles React discards the optimistic state and re-renders from the real value. The point is perceived latency: a like button, a sent message, a toggled checkbox or an added cart item should respond at the speed of the click, not the speed of the network. What React handles for you is the hard part — the revert on failure and the reconciliation when the true value arrives, which is where hand-rolled optimistic updates usually go wrong. Two constraints: it only works inside a transition or an action, since that is what defines the window the optimistic value lives in, and it is optimistic rather than a queue, so it does not by itself sequence several in-flight mutations of the same value.

Next/ReactmedWhat is the use hook and how does it differ from useEffect plus fetch?

use reads the value of a promise or a context during render. Given a promise it suspends the component until the promise resolves, so the nearest Suspense boundary shows the fallback and the component body then runs with the resolved value — no isLoading state, no useState for the data, no effect. Given a context it does what useContext does. It breaks one long-standing rule deliberately: because it is not backed by the ordered hook list in the same way, it may be called inside a condition or a loop, which is why it can read a context conditionally where useContext cannot. The critical constraint is that use does not create the promise — a promise created during render would be new on every render and loop forever, so the promise has to come from a cache, a framework loader, or a Server Component passing it down as a prop. That is the honest framing for an interview: use is the consumption primitive, and the framework or a caching library still owns fetching, deduplication and revalidation.

Next/ReactmedWhat is the React Compiler?

A build-time compiler that analyses component and hook code and inserts memoization automatically, so React skips re-rendering and recomputation without you writing useMemo, useCallback or React.memo. It works by understanding which values actually depend on which, which is something a human maintaining a dependency array does badly — the manual approach is verbose, easy to get wrong in both directions (a missing dependency causes a stale value; an over-eager memo costs more than it saves), and clutters the code. The compiler's precondition is that your components follow the rules of React: pure during render, no mutation of props or state, no side effects in the render path, hooks called unconditionally. Code that breaks those rules is where it bails out — and the eslint-plugin-react-compiler lint rule exists to flag exactly that, which makes it useful even before you enable the compiler itself. The practical effect on interviews: manual memoization is becoming a legacy skill, but you still need to understand what it does to reason about why the compiler helps.

Next/ReactmedWhat is the difference between Server Components and Client Components?

A Server Component runs only on the server. Its code is never sent to the browser, so a heavy dependency like a markdown parser or a date library adds nothing to the bundle, and it can read a database, the filesystem or a secret directly because none of that crosses the wire — only the rendered output does. What it cannot do is anything stateful or interactive: no useState, no useEffect, no event handlers, no browser APIs. A Client Component is the React you already know — marked with 'use client' at the top of the file, shipped to the browser, hydrated, interactive. The rules that follow catch people out: 'use client' marks a boundary, not a single file, so everything imported below it is also client code; a Server Component can render a Client Component but not the reverse by import — you pass one down through children instead, which is how you keep an interactive shell wrapping server-rendered content; and props crossing the boundary must be serialisable, so you cannot pass a function (other than a Server Action) or a class instance. The default in the App Router is server, and the skill is pushing 'use client' as far down the tree as possible.