Full-Stack Developer interview prep
A spaced-repetition deck of 812+ Full-Stack Developer interview questions — organised by topic and difficulty, and scheduled for review based on your ratings. Preview a few cards below, then choose access to study the whole track on an Anki-style SM-2 schedule.
Eligible new monthly or yearly subscribers get 7 days free. Checkout confirms eligibility and charges.
Before you start
Best after building a small web application. You should be able to read basic Python, JavaScript, SQL, and HTML/CSS; these cards review concepts rather than teach programming from scratch.
First pass: Python → Databases. Begin with the foundational questions in these modules, then follow the outline. Revisit advanced and senior questions as the job requires; all cards remain available from the start.
Shared topics can appear in several tracks and keep one review history. The outline labels shared foundations and role-specific modules; choose your depth for the job you are preparing for.
What's covered
Every topic in this track, grouped the way you'd study it.
Python
133 cardsRole-specific module
Databases
108 cardsRole-specific module
Backend
176 cardsRole-specific module
Frontend
149 cardsRole-specific module
Web Security
8 cardsShared foundations
CS Fundamentals
101 cardsRole-specific module
DevOps & Infra
47 cardsRole-specific module
System Design
52 cardsRole-specific module
Behavioral
38 cardsRole-specific module
Sample questions
A few cards from the deck — reveal each answer, then choose access to study the full set on a schedule.
What's the difference between mutable and immutable types?
What's the difference between mutable and immutable types?
Short answer: Mutable objects can change in place (list, dict, set); immutable objects cannot (int, str, tuple). Rebinding a variable is different from mutating its object.
items = [1, 2]
alias = items
items.append(3)
assert alias is items and alias == [1, 2, 3]
text = "hello"
original = text
text += " world"
assert original == "hello" # the original string did not change
In depth:
id()identifies an object and stays constant during its lifetime. Treating it as a memory address is a CPython implementation detail.- A tuple cannot replace its elements, but a list stored inside it can still change. Immutability is not automatically deep immutability or thread safety.
- Dictionary keys and set elements must be hashable: their hash stays stable, and equal objects have equal hashes. A tuple containing a list is unhashable. A user-defined mutable instance can be hashable by identity, so hashability is not synonymous with immutability.
- Functions receive references to objects. Mutating an argument can affect the caller; rebinding a local parameter does not rebind the caller's variable.
Sources: Python hashability, object identity.
What does SELECT do, and in what order do the parts of a query execute?
What does SELECT do, and in what order do the parts of a query execute?
Short answer: SELECT retrieves rows from tables. The logical execution order does NOT match the written order: first FROM, then WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY, LIMIT.
In depth:
Written (syntactic) order:
SELECT DISTINCT col1, agg(col2)
FROM t
WHERE cond
GROUP BY col1
HAVING agg_cond
ORDER BY col1
LIMIT 10 OFFSET 20;
Logical execution order:
1. FROM / JOIN -- which tables, how to join
2. WHERE -- row filter BEFORE grouping
3. GROUP BY -- grouping
4. HAVING -- group filter AFTER aggregation
5. SELECT -- expressions, aliases, window functions
6. DISTINCT -- remove duplicates
7. ORDER BY -- sorting
8. LIMIT / OFFSET -- slice
⚠️ Gotcha: an alias from SELECT cannot be used in WHERE (since WHERE runs before SELECT), but it can be used in ORDER BY and often in GROUP BY (depends on the DBMS). Example of the error:
SELECT salary * 12 AS annual FROM emp WHERE annual > 100000; -- ERROR
SELECT salary * 12 AS annual FROM emp ORDER BY annual; -- OK
What is HTTP and how does the request-response cycle work?
What is HTTP and how does the request-response cycle work?
Short answer: HTTP (HyperText Transfer Protocol) is a text-based application-layer client-server protocol on top of TCP (in HTTP/3 — on top of QUIC/UDP). The client sends a request, the server returns a response; the connection holds no state between requests (stateless).
In depth:
A request consists of a start line (method + path + version), headers, and an optional body:
POST /api/v1/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
Content-Length: 38
{"name": "Anna", "email": "a@ex.com"}
A response consists of a status line (version + code + reason phrase), headers, and a body:
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/v1/users/42
{"id": 42, "name": "Anna", "email": "a@ex.com"}
⚠️ Gotcha: "HTTP only works over TCP" is incorrect for HTTP/3, which uses QUIC over UDP. And the reason phrase ("OK", "Created") is purely informational — clients should rely on the numeric code, not the text.
Data types: primitives vs objects?
Data types: primitives vs objects?
Short answer: JS has 7 primitive types (string, number, boolean, null, undefined, symbol, bigint) and one reference type — the object (including arrays, functions, dates). Primitives are immutable and copied by value; objects are copied by reference.
In depth:
// Primitives — copied by value
let a = 10;
let b = a;
b = 20;
console.log(a); // 10 — a is unchanged
// Objects — copied by reference
let obj1 = { x: 1 };
let obj2 = obj1;
obj2.x = 99;
console.log(obj1.x); // 99 — both variables point to the same object
// Primitives are immutable: you can't "change" a string, only create a new one
let str = "hello";
str[0] = "H";
console.log(str); // "hello" — unchanged
// bigint — for integers larger than 2^53 - 1
const big = 9007199254740993n;
console.log(big + 1n); // 9007199254740994n
// symbol — a unique identifier
const id = Symbol("id");
Primitives store the value directly. They "have methods" thanks to autoboxing: "abc".toUpperCase() temporarily wraps the string in a String wrapper object.
⚠️ Gotcha: typeof function(){} returns 'function', even though a function is an object. And typeof [] is 'object', so for arrays use Array.isArray([]).
Why does the browser block your fetch with a CORS error, what is a preflight request, and what must the server send back?
Why does the browser block your fetch with a CORS error, what is a preflight request, and what must the server send back?
Short answer: The same-origin policy lets page scripts read responses only from their own origin (scheme + host + port). CORS is the server's opt-in: response headers such as Access-Control-Allow-Origin tell the browser which other origins may read the response. The browser does the enforcing, so the fix belongs on the server (or a same-origin proxy), not in frontend code.
In depth:
- Simple requests —
GET,HEADorPOSTwith only safelisted headers and aContent-Typeoftext/plain,multipart/form-dataorapplication/x-www-form-urlencoded. They are sent immediately, and the browser then hides the response unlessAccess-Control-Allow-Originmatches. - Preflighted requests — everything else:
PUT/PATCH/DELETE,Content-Type: application/json, anAuthorizationor custom header. The browser first sendsOPTIONSand sends the real request only if the answer allows it.Access-Control-Max-Agecaches the permission (Chromium caps it at 2 hours). - Credentials — with
fetch(url, { credentials: "include" })the server must return the exact origin (not*) plusAccess-Control-Allow-Credentials: true, and cross-site cookies needSameSite=None; Secure. - Reading headers — beyond the safelisted response headers, JS sees only those listed in
Access-Control-Expose-Headers.
OPTIONS /orders/42 HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, PUT
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 600
Vary: Origin
⚠️ Common mistake: "fixing" CORS on the client — adding Access-Control-Allow-Origin to the request or switching to mode: "no-cors", which only returns an opaque response you cannot read.
What is Big O notation?
What is Big O notation?
Short answer: Big O describes how an algorithm's running time or memory usage grows as the input size n increases, dropping constants and lower-order terms. It's an upper bound on the growth rate.
In depth:
Big O answers the question "what happens when n becomes very large?". We're not interested in the exact number of operations, only the nature of the growth. That's why O(2n + 100) simplifies to O(n), and O(3n² + n) — to O(n²).
The main growth classes (from best to worst):
# O(1) — constant: doesn't depend on n
def first(arr):
return arr[0] if arr else None
# O(log n) — logarithmic: each step halves the problem (binary search)
def binary_search(arr, target):
lo, hi = 0, len(arr) - 1
while lo <= hi:
mid = (lo + hi) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
# O(n) — linear: a single pass
def total(arr):
s = 0
for x in arr: # n iterations
s += x
return s
# O(n log n) — efficient sorts (merge, quick on average, Timsort)
def sort_it(arr):
return sorted(arr)
# O(n^2) — quadratic: a nested loop
def has_dup_naive(arr):
for i in range(len(arr)):
for j in range(i + 1, len(arr)):
if arr[i] == arr[j]:
return True
return False
# O(2^n) — exponential: naive Fibonacci, enumerating subsets
def fib_naive(n):
if n < 2:
return n
return fib_naive(n - 1) + fib_naive(n - 2)
How to estimate code: sequential steps add up and the dominant term remains (O(n) + O(n log n) = O(n log n)); nested loops multiply (O(n·m), or O(n²) when m = n); discarding half the problem with constant work per step gives O(log n). With two inputs, keep both variables: O(n + m) can't be reduced to O(n) unless you know that m ≤ n.
⚠️ Gotcha: Big O is about asymptotics (behavior at large n), not about actual time. An O(n) algorithm can be slower than an O(n²) one on small data because of large constants. People also confuse: Big O (upper bound), Ω (lower bound), Θ (tight bound); in interviews "O" usually means Θ.
Ready to make it stick?
Explore a sample, choose a track, and build a review routine that fits your preparation.
Questions about this track
How should I prepare for a Full-Stack Developer interview?
Study the concepts you'll be asked to explain, not just the ones you can code. RecallDeck's Full-Stack Developer track gives you 812+ curated interview questions and resurfaces each one with an Anki-style SM-2 schedule based on your ratings — to practise recalling and explaining them. Combine reviews with coding and mock interviews.
What topics does the Full-Stack Developer track cover?
The Full-Stack Developer track is organised into the core areas Full-Stack Developer interviews actually test, grouped by topic and by difficulty (Concept, Junior, Middle, Senior). You can preview the full outline and sample questions above before signing in.
Is spaced repetition effective for Full-Stack Developer interview prep?
Yes. Active recall and spaced practice can help retain what you study. Grade yourself honestly and check your understanding with practical work. RecallDeck schedules each Full-Stack Developer card using your ratings, so familiar cards return less often and difficult ones get more practice.
Can I try the Full-Stack Developer track before paying?
Eligible new monthly or yearly subscribers can try the complete Full-Stack Developer track and every feature for 7 days. Checkout confirms trial eligibility and the amount due; returning subscribers may be charged immediately. Cancel online before the trial ends to avoid its first charge. Lifetime access has no trial.