RecallDeck
Interview track

Full-Stack Developer interview prep

A spaced-repetition deck of 716+ Full-Stack Developer interview questions — organised by topic and difficulty, and resurfaced right before you'd forget. Preview a few cards below, then choose access to study the whole track on an Anki-style SM-2 schedule.

716 cards21 topics
See plans and start trial

7 days free on monthly or yearly · every feature included.

What's covered

Every topic in this track, grouped the way you'd study it.

Python

122 cards
Core LanguageData Model & InternalsConcurrency & AsyncStdlib, Typing & Testing

Databases

104 cards
SQL FundamentalsPostgres Internals & OptimizationNoSQL & Redis

Backend

169 cards
HTTP & REST APIArchitecture & ScalingAuth & SecurityDjango & FastAPIORM & SQLAlchemy

Frontend

146 cards
JavaScript CoreReactTypeScriptCSS & Browser Rendering

CS Fundamentals

76 cards
Data Structures & AlgorithmsNetworks

DevOps & Infra

35 cards
Docker, CI/CD & Linux

System Design

29 cards
System Design

Behavioral

35 cards
Behavioral

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?

Short answer: Mutable objects can be changed in place without creating a new object (list, dict, set, bytearray); immutable ones cannot (int, float, str, bytes, tuple, frozenset, bool, None). Any "change" to an immutable object creates a new object.

In depth: Mutability is about whether an object can change its contents while keeping the same id() (its address in memory).

# Immutable: the operation creates a NEW object
s = "hello"
print(id(s))
s += " world"      # a new string is created
print(id(s))       # id changed — it's a different object

# Mutable: the same object is changed
lst = [1, 2, 3]
print(id(lst))
lst.append(4)      # change in place
print(id(lst))     # same id

Why it matters:

  • Hashability. Only objects that are immutable (by content) can be used as dict keys or set elements. If a key could change, its hash would drift and it couldn't be found.
  • Safety when sharing. An immutable object can be safely shared across threads/functions — no one can corrupt it.
  • Function arguments. Python passes a reference to the same object: a function can mutate a mutable argument's contents, but rebinding the local parameter does not rebind the caller's variable.

⚠️ Gotcha: A tuple is immutable, but if it holds a list, that list can still be changed:

t = ([1, 2], 3)
t[0].append(99)   # OK! the list inside is mutable
print(t)          # ([1, 2, 99], 3)
# t[0] = [...]    # THIS is a TypeError — you can't reassign an element

Also, hash(([1,2], 3)) will fail — a tuple is unhashable if it contains an unhashable element.

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           -- evaluate expressions, aliases
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
  1. 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.

  1. 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([]).

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 detail:

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, 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)

A rough guide to growth at n = 1,000,000: O(1) — 1 operation, O(log n) — ~20, O(n) — a million, O(n log n) — ~20 million, O(n²) — a trillion (already too much), O(2^n) — infeasible even at n = 50.

⚠️ 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 Θ.

What is Docker and what problem does it solve?

Short answer: Docker is a containerization platform that packages an application together with all its dependencies, libraries, and configuration into an isolated, portable image. It solves the "works on my machine, but not on the server" problem.

In depth:

The classic pain: a developer wrote code on their laptop (Python 3.11, a specific version of libpq, environment variables), but the server has a different OS version, doesn't have the other libraries, and the variables aren't set — the application crashes. Docker pins the whole environment into an image, and that image runs identically locally, in CI, and in production.

What Docker gives you:

  • Reproducibility — the same image everywhere. The environment is described as code (a Dockerfile) and can be versioned in git.
  • Isolation — containers don't interfere with each other (dependencies, ports, processes).
  • Portability — the image runs on any machine with Docker, regardless of the host OS.
  • Fast startup — a container comes up in seconds (unlike a VM).
# Build an image from the Dockerfile in the current directory
docker build -t myapp:1.0 .

# Run a container from the image, publish a port, in the background
docker run -d -p 8000:8000 --name myapp myapp:1.0

# List running containers
docker ps

# Get a shell inside a running container
docker exec -it myapp bash

# Container logs
docker logs -f myapp

⚠️ Gotcha: Docker doesn't "virtualize hardware" and doesn't run a full OS. Containers use the host's kernel. So a Linux image won't run natively on the Windows kernel — on Windows/macOS a lightweight Linux VM runs under the hood (via WSL2 or a hypervisor), and the containers run inside it.

Ready to make it stick?

Start your first session in under a minute. Your future self, mid-interview, will thank you.

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 716+ curated interview questions and resurfaces each one with an Anki-style SM-2 schedule right before you'd forget it — so the answers are still there under pressure on interview day.

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. Actively recalling an answer and grading yourself honestly builds far more durable memory than re-reading notes. RecallDeck schedules each Full-Stack Developer card to reappear at the moment you're about to forget it, so your daily reviews shrink while your recall holds.

Can I try the Full-Stack Developer track before paying?

Yes. Monthly and yearly access include a seven-day trial of the complete Full-Stack Developer track, the full SM-2 scheduler, statistics, flexible pacing, and cram mode. You can cancel online before the first charge.

Other interview tracks

RecallDeckSpaced-repetition interview prep