Full-Stack / DevOps
Top Full-Stack & DevOps Engineer Interview Questions 2026
Prep tips for Full-Stack / DevOps interviews
- For frontend questions, always connect your answer to performance and user experience
- CI/CD answers should mention: pipeline stages, testing gates, rollback strategy, and observability
- Docker and Kubernetes questions are near-universal in DevOps interviews — know the difference between a pod, deployment, and service
- System design rounds expect you to cover both the frontend and backend: API contracts, state management, caching, and deployment
- The virtual DOM is an in-memory JavaScript representation of the real DOM. When state or props change, React re-renders the component tree to a new virtual DOM tree, then performs reconciliation (diffing) to determine the minimal set of real DOM mutations needed. React's diffing algorithm is O(n) — it compares trees level by level and uses keys to match list items efficiently. Reconciliation is triggered by
setState,useStatesetters, context changes, or parent re-renders. UseReact.memo,useMemo, anduseCallbackto prevent unnecessary re-renders in performance-critical components. - REST uses fixed endpoints (one URL per resource) returning a predetermined shape — it can over-fetch (more data than needed) or under-fetch (requiring multiple requests). GraphQL exposes a single endpoint where clients specify exactly the fields they need in a query, eliminating over/under-fetching. Choose REST for: simple CRUD APIs, public APIs where you want predictable caching, or teams unfamiliar with GraphQL. Choose GraphQL for: complex, interconnected data graphs, mobile apps where bandwidth matters, or when many different clients (web, mobile, partner) need different data shapes from the same backend.
- A JWT (JSON Web Token) consists of three Base64-encoded parts: header (algorithm), payload (claims like user_id, roles, expiry), and signature. The server signs the token with a secret (HMAC) or private key (RSA/EC); the client stores it and sends it in the Authorization header. The server validates the signature on each request — no database lookup needed, making JWTs stateless. Security considerations: (1) store in httpOnly cookies, not localStorage (prevents XSS access); (2) keep expiry short (15 min for access tokens) and use refresh tokens; (3) use RS256 over HS256 in distributed systems (servers only need the public key to verify); (4) include a JTI claim and maintain a small blocklist for logout/revocation; (5) validate the algorithm in the header — never accept "alg: none".
- Pipeline stages per service: (1) Trigger on PR — lint, unit tests, build Docker image; (2) Merge to main — integration tests, SAST (security scan), build and push image to registry with immutable tag (git SHA); (3) Deploy to staging — Helm chart update via ArgoCD (GitOps), automated smoke tests and contract tests; (4) Deploy to production — canary release (10% traffic via Argo Rollouts or feature flag), monitor error rate and latency for 10 min, progressive rollout to 100% or automatic rollback. With 20 services: use a mono-repo with affected-only builds (Nx, Turborepo) to avoid rebuilding all services on every commit. Enforce semantic versioning and changelogs. Each service owns its deployment manifest in a separate GitOps repo. Observability gates: if p99 latency or error rate breaches SLO during canary, pipeline auto-rolls back.
- A Docker image is a read-only, layered template built from a Dockerfile — it packages application code, runtime, libraries, and configuration. It is immutable and shareable via a registry (Docker Hub, ECR). A container is a running instance of an image — it adds a writable layer on top, runs as an isolated process with its own filesystem and network namespace, and is ephemeral (changes to the writable layer are lost when the container is removed). One image can run as many containers simultaneously. Think of an image as a class definition and a container as an object instance.
- Node.js is single-threaded but non-blocking: the event loop continuously checks the call stack and the event queue. When an async operation (I/O, timers, network) is initiated, it is handed off to libuv (which uses OS-level async I/O or a thread pool); the callback is queued in the event loop when complete. Phases in order: timers (setTimeout/setInterval), pending callbacks, idle/prepare, poll (I/O callbacks), check (setImmediate), close callbacks. This model handles high concurrency for I/O-bound workloads without threads, but CPU-bound tasks block the event loop — offload those to worker threads (
worker_threadsmodule) or child processes. - Frontend: WebSocket connection (or Server-Sent Events for unidirectional) to receive push updates; React with virtualised rendering (react-window) for large metric grids; debounce rapid updates to 1-2 fps to avoid unnecessary renders. Backend: WebSocket servers cannot maintain 100k connections per instance — horizontally scale with sticky sessions or switch to pub/sub. Architecture: metric producers write to Kafka → stream processor (Flink/Spark Streaming) aggregates per-second → results pushed to Redis Pub/Sub → WebSocket server layer subscribes and fans out to connected clients. Scale: each Node.js WebSocket server handles ~5-10k concurrent connections; deploy 15-20 instances behind a load balancer (sticky or consistent hash by user_id). For dashboards where all users see the same data (global metrics), broadcast efficiently — one Kafka consumer pushes to all WebSocket servers, each pushes to all their connected clients.
- CORS (Cross-Origin Resource Sharing) is a browser security mechanism that blocks JavaScript from making requests to a different origin (domain, protocol, or port) than the page was loaded from. The server opts in by returning CORS headers:
Access-Control-Allow-Origin(specific origin or * for public APIs),Access-Control-Allow-Methods,Access-Control-Allow-Headers. For requests with credentials (cookies, auth headers), you must setAccess-Control-Allow-Credentials: trueand cannot use wildcard for origin. Preflight: non-simple requests trigger an OPTIONS preflight — the server must respond to OPTIONS with the appropriate CORS headers. Never setAccess-Control-Allow-Origin: *on authenticated APIs. - Start by profiling — use React DevTools Profiler to identify which components are re-rendering unnecessarily. Common fixes: (1) Memoisation —
React.memofor components,useMemofor expensive computations,useCallbackfor stable function references; (2) Code splitting —React.lazyandSuspenseto split by route; (3) Virtualise long lists — react-window or react-virtual; (4) State colocation — move state down to prevent top-level re-renders; (5) Avoid inline object/array literals in JSX (new reference every render); (6) Bundle analysis — webpack-bundle-analyzer to find large dependencies. For initial load: server-side rendering (Next.js) or static generation, image optimisation, and caching with proper Cache-Control headers. - Immediate (first 5 minutes): (1) Confirm the spike is real and deployment-correlated via dashboards (Datadog, Grafana); (2) Assess blast radius — which endpoints, which user segments, is data being corrupted or just erroring? (3) Mitigate first — trigger a rollback immediately; do not investigate root cause before stopping the bleeding. In Kubernetes:
kubectl rollout undo deployment/api; in CI/CD: re-deploy the previous image tag. Confirm error rate recovers. Then investigate: diff the deployment, check logs for the error messages, check if a DB migration ran, check if a feature flag was toggled. After resolution: write a blameless post-mortem covering timeline, root cause, contributing factors, and action items (better testing, canary deployment, automated rollback trigger on SLO breach). - Blue-green deployment maintains two identical environments — blue (live) and green (new version). Traffic is switched from blue to green all at once via load balancer or DNS; rollback is instant by switching back. It requires double the infrastructure. Canary deployment gradually shifts a small percentage of traffic (e.g. 5% → 20% → 50% → 100%) to the new version, monitoring for errors at each stage. It uses less infrastructure than blue-green and reduces risk by limiting blast radius. Use blue-green for: applications where any partial state is dangerous; canary for: gradual validation with real user traffic and automated rollback gates.
- An index is a separate data structure (typically a B-tree) that maps column values to row locations, enabling the database to find rows without a full table scan — O(log n) vs O(n). Index a column when it appears frequently in WHERE, JOIN ON, or ORDER BY clauses. Indexes hurt performance when: (1) Write-heavy tables — every INSERT, UPDATE, DELETE must also update all indexes; (2) Low-cardinality columns — an index on a boolean column with 50/50 split is often slower than a full scan; (3) Too many indexes — query planner can choose the wrong one; (4) Unused indexes — they consume storage and slow writes with no benefit. Use
EXPLAIN ANALYZEto verify the query planner is using your index. - Use STAR. Cover: (1) the nature and severity of the incident (user impact, duration, revenue affected); (2) how it was detected (monitoring alert, user report — and what that revealed about your observability gaps); (3) your specific role in triage and resolution; (4) how you communicated with stakeholders during the outage; (5) the root cause; (6) what you changed afterward (monitoring improvement, architectural fix, process change). Interviewers are assessing calm under pressure, structured thinking, communication skills, and follow-through on systemic fixes — not just heroics.
- 100M req/day ≈ 1,150 req/sec (average), with peaks perhaps 5-10x. Write path (shorten): encode a base62 short code from an auto-incrementing ID (use a distributed ID generator like Snowflake to avoid coordination); store long URL → short code in a primary DB (PostgreSQL or DynamoDB). Read path (redirect): the hot path — 99% of traffic. Cache aggressively: Redis with short code → long URL, TTL matching the URL's expiry. Cache hit rate for popular URLs will be >95%; for misses, fetch from DB. CDN (Cloudflare) at the edge for the most-popular URLs — 301 redirect gets cached by browsers and CDN; use 302 for analytics tracking. Database: DynamoDB (key-value pattern is perfect) or sharded PostgreSQL. Estimated data: 100M new URLs/year × ~100 bytes each ≈ 10GB/year — not a storage challenge. Custom domains, expiry, and click analytics are additive features.
- A rolling update incrementally replaces old pods with new ones, maintaining availability throughout. Controlled by two parameters in the Deployment spec:
maxUnavailable(how many old pods can be taken down at once) andmaxSurge(how many extra new pods can run above the desired count). Kubernetes creates new pods, waits for them to become Ready (pass readiness probes), then removes old ones. If the new pods fail readiness checks, the rollout stalls — preventing a bad deploy from fully replacing the old version. Roll back withkubectl rollout undo deployment/nameor to a specific revision:kubectl rollout undo deployment/name --to-revision=2. Rollout history is stored in ReplicaSets. - Horizontal Pod Autoscaler (HPA) scales the number of pod replicas up or down based on metrics (CPU utilisation, memory, or custom metrics). It is the primary autoscaler for stateless workloads — add more pods under load, remove them when idle. Vertical Pod Autoscaler (VPA) adjusts the CPU and memory requests/limits of existing pods based on observed usage. Currently, VPA requires a pod restart to apply changes (limitation). Use HPA for stateless services under variable load; use VPA in recommendation mode to right-size pod requests, then apply the suggestions to your Deployment manifests. Do not use HPA and VPA on CPU/memory simultaneously on the same Deployment — they will conflict.
useStatemanages local component state — triggers a re-render when updated.useEffecthandles side effects (data fetching, subscriptions, DOM mutations) — runs after render; specify dependencies to control when it re-runs; return a cleanup function for subscriptions.useCallbackmemoises a function reference — use it when passing a callback to a memoised child component (React.memo) to prevent it from re-rendering due to a new function reference every render.useMemomemoises an expensive computed value — use it when a computation is genuinely expensive (large array transform, complex calculation) and the inputs are stable. Avoid premature optimisation:useCallbackanduseMemohave their own overhead; only add them when profiling reveals a problem.- Three pillars — metrics, logs, traces: (1) Metrics — Prometheus scrapes metrics from services; Grafana provides dashboards and alerting; AlertManager routes alerts to PagerDuty/Slack by severity. Define SLOs (e.g. 99.9% availability, p99 latency <500ms) and alert on SLO burn rate (Google's approach), not just raw thresholds — reduces alert fatigue. (2) Logs — structured JSON logs from all services; ship via Fluentd/Fluent Bit to Elasticsearch (ELK) or Loki (lighter, better Grafana integration); correlate logs by request_id/trace_id. (3) Traces — OpenTelemetry instrumentation in all services; export to Jaeger or Tempo for distributed tracing. Tie all three together: from a Grafana alert, drill into logs, then traces to find the exact failing request. Runbooks linked in every alert reduce MTTR.
- An operation is idempotent if making the same request multiple times produces the same result as making it once. GET, PUT, and DELETE are idempotent by HTTP convention; POST is not. Idempotency matters because networks are unreliable — clients retry requests on timeout or network failure. Without idempotency, a retry could create duplicate records, double-charge a customer, or send multiple emails. Implement idempotency keys in non-idempotent operations: the client generates a unique key per request; the server stores the result keyed by this ID and returns the cached result on duplicate requests (Stripe's payment API is a canonical example). Always make payment and notification endpoints idempotent.
- Frame your answer around business context and technical debt as risk. Ship if: the code works correctly, the tech debt is bounded and documented, and the business deadline is real. Refactor if: the debt is actively slowing down delivery, causing bugs, or creating a security/scalability risk. Use the "Boy Scout Rule" — leave code slightly better than you found it as part of normal delivery rather than batching all refactoring into big-bang projects. Make debt visible: add it to the backlog with estimated impact. For larger refactors, negotiate time as part of sprint planning by quantifying the cost — "this component causes 30% of our bug reports; one sprint now saves three sprints over the next quarter."
Practice these questions with Cogniv
Get real-time AI answers during live mock interviews. Free to start.
Start practicing →