Dev.to WebDev 🛠 Dev 👁 0 📖 9 min read

Full Stack Interview Questions for Senior Developers in 2026 — 55 Deep Answers Across Frontend, Backend, APIs & Databases

Junior full-stack interviews ask "what's the difference between GET and POST." Senior full-stack interviews ask how a single click travels from the browser through your CDN, API, cache, and database and back, where you v

Junior full-stack interviews ask "what's the difference between GET and POST." Senior full-stack interviews ask how a single click travels from the browser through your CDN, API, cache, and database and back, where you validate the same rule on the client and the server and why both, and what breaks when the network drops mid-request.

A senior full-stack engineer isn't someone who knows a little of everything — it's someone who understands the seams: where the client ends and the server begins, where trust boundaries live, and where latency and failure hide.

This is 55 questions across client-side, server-side, the client-server seam, data & persistence, auth & security, and delivery & architecture. Each has a short answer, a deep dive, and the senior signal the interviewer is listening for. Every example is from Mattrx — a real multi-tenant platform: React (TanStack Query) frontend, .NET 9 / ASP.NET Core (Clean Architecture + CQRS) API, Azure SQL, Redis, Kafka, SignalR.

Block A — client-side (frontend)

1. Structure a large frontend app → by feature, not file type. Group everything a feature needs together; "one change touches five folders" is a structure smell.

2. Client vs server state → different problems. Server state (API data) → a query library (TanStack Query) that caches/refetches/invalidates. Client state (UI toggles) → local state/context. Don't dump server data into Redux.

3. Caching, invalidation, optimistic updates → the library caches by key; after a mutation you invalidate the keys or optimistically update and roll back on error.

const archive = useMutation({
  mutationFn: (id) => api.archiveCampaign(id),
  onMutate: async (id) => {                       // optimistic: update UI first
    await qc.cancelQueries({ queryKey: ['campaigns'] });
    const prev = qc.getQueryData(['campaigns']);
    qc.setQueryData(['campaigns'], (c) => c?.filter((x) => x.id !== id));
    return { prev };                              // snapshot for rollback
  },
  onError: (_e, _id, ctx) => qc.setQueryData(['campaigns'], ctx?.prev),
  onSettled: () => qc.invalidateQueries({ queryKey: ['campaigns'] }),   // reconcile
});

4. CSR vs SSR vs SSG vs ISR → CSR for app-like behind-login screens; SSR for personalized/SEO/always-fresh; SSG for static; ISR for mostly-static with periodic revalidation. Match to freshness + SEO.

5. Avoid prop drilling without over-using context → composition first; context for stable global values (theme, user); a store only for complex shared state. Context re-renders all consumers — wrong for hot state.

6. Make a React app fast → ship less JS (code-split), render less (memoize, virtualize long lists), fetch smart (cache + dedup). Measure before optimizing.

7. Forms & validation → a form library (React Hook Form) + schema (Zod) for UX — but never trust it; the server re-validates. Client validation is UX; server validation is security.

8. Loading/error/empty states → every async view has four states — loading, error, empty, data — and a senior handles all four, not just the happy path.

9. Accessibility & responsiveness → semantic HTML (real buttons/labels), keyboard nav, contrast, aria only when semantics fall short, mobile-first layout. A <div onClick> isn't keyboard-accessible; a <button> is.

10. Where to store auth tokens → an httpOnly, Secure, SameSite cookie. JS can't read it, so an XSS can't steal it. localStorage is readable by any script. The trade-off shifts to CSRF, which SameSite + anti-forgery handles.

Block B — server-side (API & backend)

11. Clean REST API → model resources (nouns), HTTP verbs for actions, meaningful status codes, stateless. POST /campaigns, not POST /createCampaign.

12. REST vs GraphQL vs gRPC → REST for public CRUD; GraphQL for flexible aggregated client queries; gRPC for fast typed internal service-to-service. Match to the consumer.

13. Version an API → only on a breaking change (URL/header/media type). Prefer additive, non-breaking changes so you rarely need a new version.

14. Server validates input → re-validate every input at the boundary — the client can be bypassed (curl, tampered request). Reject with 400 + details.

public sealed class CreateCampaignValidator : AbstractValidator<CreateCampaign>
{
    public CreateCampaignValidator()
    {
        RuleFor(x => x.Name).NotEmpty().MaximumLength(120);
        RuleFor(x => x.Budget).GreaterThanOrEqualTo(0);
    }
}

15. Offset vs cursor pagination → offset is simple but slow/inconsistent on deep pages; cursor/keyset is stable and fast at any depth. Use cursor for large, changing datasets.

16. Consistent API errors → one contract — ProblemDetails (RFC 7807) with status, title, detail, trace id — handled centrally. Never leak stack traces.

17. DTOs vs entities → never return entities (leaks the DB shape, over-shares, risks cycles). DTOs are the deliberate, versioned API contract decoupled from persistence.

18. Layer a backend → thin controllers (HTTP only), business logic in an application/service layer, data access in repositories. Dependencies point inward. Logic in controllers is the classic smell.

19. "Async all the way" → async I/O frees the thread during waits, so the server handles far more concurrent requests. Blocking on async (.Result) defeats it and can deadlock or exhaust the thread pool.

20. Rate-limit & throttle → per client/tenant/IP in a shared store (Redis) so it works across instances; return 429 + Retry-After.

21. Cache on the server → cache-aside with Redis for hot, expensive reads: check cache, on miss load + populate, set a TTL, invalidate on write.

22. Slow work in a request → don't. Offload to a queue + worker; the API returns 202 Accepted + a status URL.

await _bus.SendAsync(new GenerateReport(tenantId, spec), ct);
return Accepted($"/reports/{jobId}");

Block C — the client-server seam

23. Walk a request end-to-end → Browser → DNS → CDN/edge → load balancer → API instance → middleware (auth, rate-limit) → handler → cache/DB → serialized response → back through LB/CDN → render. A senior names where latency, caching, and failure live at each hop.

24. Keep the API contract in sync → generate from one source: OpenAPI on the server → a typed client for the frontend. Contract drift becomes a build error, not a prod bug.

25. CORS → the browser's rule that origin A can't call API origin B unless B opts in with Access-Control-Allow-Origin. Allowlist specific origins — never * with credentials. Preflight OPTIONS handles non-simple requests.

26. Serialization gotchas → casing (camelCase JS vs PascalCase C#), dates/timezones (ISO-8601 UTC), nulls vs missing, number precision, enums (string vs int). Standardize in the serializer config.

27. Idempotent mutations → the network can drop after the server processed but before the client got the response → the retry double-charges. An idempotency key makes the retry return the stored result.

if (await _idem.TryGetAsync(key) is {} prior) return prior;
var result = await Process(cmd, ct);
await _idem.StoreAsync(key, result);

28. Optimistic UI + reconciliation → update the UI immediately, send the mutation, commit on success / roll back on error / refetch to sync. The server is always the source of truth.

29. File uploads end-to-end → don't stream files through your API. Issue a presigned URL, let the client upload directly to blob storage, then notify the API.

30. Partial failures & network errors → assume the network fails. Client: timeouts, retries with backoff (idempotent calls), clear error UX. Server: idempotency, transactions, graceful degradation (serve stale/cached).

31. Real-time updates → WebSockets/SignalR for bidirectional low-latency; SSE for one-way streams; polling as fallback. Scaling needs a backplane (Redis) across instances.

Block D — data & persistence

32. SQL vs NoSQL → SQL for relational data needing transactions/joins/consistency; NoSQL for flexible schemas, massive scale, specific access patterns. Choose by data shape + access pattern; polyglot is fine.

33. Schema migrations across a team → version in source control, apply as a reviewed gated deploy step (not on startup), make changes backward-compatible (expand/contract) for zero-downtime.

34. N+1 full-stack → loading a list then querying per item. Fix by loading related data in one query (join/include) or projecting exactly what the screen needs.

var list = await _db.Campaigns
    .Select(c => new CampaignCard(c.Id, c.Name, c.Events.Count()))
    .ToListAsync(ct);

35. Consistent writes → a transaction within one DB (all or nothing); the outbox pattern + eventual consistency across services. 2PC doesn't scale.

36. Caching layers → browser → CDN → API/response cache → Redis → DB. Invalidation is the hard part: TTLs, keys tied to data, explicit purges on write.

37. Connection pooling → opening a DB connection is expensive; a pool reuses a fixed set. Keep connections short-lived; exhaustion is a classic under-load failure.

38. Indexing basics → index columns you filter/join/sort on; a query without a supporting index scans the table. Composite order matters (most-selective leading). Verify with the plan (seek vs scan).

39. Large datasets end-to-end → never load it all. Paginate (cursor), stream exports, aggregate on the DB (GROUP BY / rollups), cache hot aggregates.

40. Validate at every layer → defense in depth: client (UX), API (security boundary), domain (invariants), DB constraints (last line — NOT NULL, unique, FK). The DB constraint catches the race the app check missed.

Block E — auth, security & cross-cutting

41. Sessions vs JWT → sessions (id in a cookie, state server-side) are easy to revoke but need shared storage; JWT (self-contained, signed) is stateless/scalable but hard to revoke. Use short-lived JWTs + refresh tokens, or sessions when instant revocation matters.

42. Authorization vs authentication → authn = who you are; authz = what you can do. Enforce authz on the server via roles/claims (RBAC) and resource-based checks (does this user own this record?).

43. OAuth2 / OIDC → OAuth2 delegates authorization without sharing passwords; OIDC adds identity on top. Auth-code flow with PKCE is the modern default for web/mobile.

44. Multi-tenant isolation → every query scoped by TenantId, enforced centrally (global query filter / RLS / per-tenant DB) so a dev can't forget it. The tenant comes from the token, never a request parameter.

modelBuilder.Entity<Campaign>().HasQueryFilter(c => c.TenantId == _tenant.Id);

45. Baseline security controls → HTTPS/HSTS, secrets in a vault, security headers (CSP, X-Content-Type-Options, X-Frame-Options), input validation + output encoding, least-privilege DB accounts, dependency scanning.

46. OWASP you design against → injection (parameterized queries), XSS (encoding + CSP), CSRF (SameSite + anti-forgery), broken access control / IDOR (resource-based authz), SSRF, known-vulnerable components. IDOR is the most common real-world hole.

47. Trace a request across the stack → a correlation/trace id generated at the edge and propagated through every hop, with structured logs carrying it. OpenTelemetry automates it.

48. Observability → the three pillars — logs, metrics (RED: rate/errors/duration), traces — plus health checks and SLO alerting. Know something's wrong before the user reports it.

Block F — delivery & architecture

49. CI/CD → on every push: build, lint, test; on merge: deploy through environments with automated gates. No manual production deploys.

50. Config & secrets → 12-factor: config in the environment, secrets in a vault injected at runtime, feature flags for rollout. Promote one artifact across envs — only config differs.

51. Monorepo vs polyrepo → monorepo eases sharing + atomic cross-stack changes; polyrepo gives independence at coordination cost. Generate client types from the API schema so the contract can't drift.

52. Testing strategy → a pyramid: many unit, fewer integration (API + DB via test containers), few E2E (Playwright on critical journeys). E2E-heavy suites become the flaky bottleneck.

53. Scale a full-stack app → keep services stateless → scale horizontally; push state to shared stores; cache; offload slow work to queues; scale the DB (replicas, partitioning, caching) since it's usually the bottleneck.

54. Deployment strategies → rolling, blue-green (instant rollback), canary (small % → watch → ramp). Pair with backward-compatible migrations for zero-downtime.

55. Design a feature end-to-end → "scheduled report emails": validated form + schedule list (server state) → POST /reports/schedules (validate, authz, persist) → a worker enqueues due jobs, another renders + emails → a tenant-scoped schedules table indexed on next-run → idempotency on send, tracing, metrics, retries + DLQ. Name the seams: where you validate, where trust is enforced, what runs async, how it fails safely.

The through-line: full-stack seniority is owning the path from a database column to a pixel and back. The client is for UX and is never trusted; the server is the authority and validates everything; the network will fail so design for retries and idempotency; caching and async make it fast but add staleness and complexity; security and observability are woven through every layer. Answer each question with the seam, the trade-off, and the failure it prevents.

This post was originally published on PrepStack. The full original has all 55 with complete code on both sides.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.