From fad1f298c2718c1217fdfc23fe62dfa326031dab Mon Sep 17 00:00:00 2001 From: Andrei Smirnov Date: Sat, 26 Sep 2026 11:08:40 +0300 Subject: [PATCH] Fix factual errors found in topic brief review Correct or qualify claims across all 19 briefs flagged by an external review, after re-verifying each finding against primary docs or by running code. Includes the follow-up verification pass (SSTORE cost rules, 403 semantics, allSettled, bcrypt limit, PgBouncer default, set-cover bound, correlated availability). Co-Authored-By: Claude Opus 5.5 --- docs/topics/ai.md | 8 ++++---- docs/topics/algorithms.md | 11 ++++++----- docs/topics/aws.md | 14 ++++++++------ docs/topics/backend.md | 9 +++++---- docs/topics/database.md | 28 ++++++++++++++++------------ docs/topics/devops.md | 18 +++++++++++------- docs/topics/distributed.md | 26 ++++++++++++++------------ docs/topics/ethereum.md | 8 ++++---- docs/topics/frontend.md | 8 ++++---- docs/topics/golang.md | 18 +++++++++--------- docs/topics/javascript.md | 7 ++++--- docs/topics/networking.md | 12 ++++++------ docs/topics/os.md | 12 ++++++------ docs/topics/python.md | 14 +++++++------- docs/topics/rust.md | 17 +++++++++-------- docs/topics/security.md | 7 ++++--- docs/topics/solidity.md | 10 +++++----- docs/topics/system.md | 21 ++++++++++++--------- docs/topics/typescript.md | 8 ++++---- 19 files changed, 138 insertions(+), 118 deletions(-) diff --git a/docs/topics/ai.md b/docs/topics/ai.md index b8fd725..8765391 100644 --- a/docs/topics/ai.md +++ b/docs/topics/ai.md @@ -18,7 +18,7 @@ What experienced AI engineers forget before an interview, grouped by subtopic. F ### Mixture of experts - An **MoE** layer routes each token to a few of many expert feed-forward networks, so **active parameters** per token are a fraction of the total. -- Compute follows active parameters, but GPUs must hold **all** weights; uneven routing complicates serving. +- Compute follows active parameters, but memory follows **total** parameters (offloading experts to CPU saves GPU memory, at a speed cost); uneven routing complicates serving. ### Reasoning models @@ -38,7 +38,7 @@ What experienced AI engineers forget before an interview, grouped by subtopic. F ### Structured output -- **Constrained decoding** masks tokens so output always matches a JSON schema or grammar. +- **Constrained decoding** masks tokens so output matches a JSON schema or grammar — except when cut off by the token limit or replaced by a refusal. - It guarantees **shape, not correctness** — still validate values in code. ### Tool calling @@ -84,7 +84,7 @@ What experienced AI engineers forget before an interview, grouped by subtopic. F ### Embeddings and similarity - An embedding maps text to a vector; similarity is usually **cosine** — on normalized vectors that's just the dot product. -- Query and documents must use the **same embedding model**; changing models means re-embedding the corpus. +- Query and documents must share one **embedding space** (same model, or jointly trained query/passage encoders as in DPR); changing models means re-embedding the corpus. ### Vector index types @@ -165,7 +165,7 @@ What experienced AI engineers forget before an interview, grouped by subtopic. F ### Model Context Protocol - **MCP** standardizes how apps discover and call tools, read resources, and fetch prompts — JSON-RPC 2.0 over stdio (local) or **Streamable HTTP** (remote, replaced HTTP+SSE in 2025). -- Remote servers authorize with **OAuth 2.1**; MCP doesn't replace per-action authorization or business validation. +- Authorization is optional; HTTP servers that support it use **OAuth 2.1**. MCP doesn't replace per-action authorization or business validation. ### Browser automation as a fallback diff --git a/docs/topics/algorithms.md b/docs/topics/algorithms.md index 8207d93..9624560 100644 --- a/docs/topics/algorithms.md +++ b/docs/topics/algorithms.md @@ -42,7 +42,7 @@ What experienced engineers forget before an algorithms interview, grouped by sub ### Monotonic stacks -- **Next greater/smaller element**: pop indices the new value makes obsolete; the remaining top is the answer. +- **Next greater/smaller element**, scanning left to right: the new value is the answer for every index it pops. - Each index is pushed and popped once, so the scan is **O(n)**; pick strict vs non-strict comparison for duplicates. ### Monotonic deque @@ -70,7 +70,7 @@ What experienced engineers forget before an algorithms interview, grouped by sub ### Rolling hash (Rabin-Karp) - A **polynomial rolling hash** updates a window's hash in O(1) as it slides, so search is O(n + m) expected. -- Verify matches (or use two moduli) because collisions happen; also finds duplicate substrings via binary search on length. +- Collisions happen: **verify each match** for exact results — two moduli only make collisions rarer; also finds duplicate substrings via binary search on length. ## Sorting and selection @@ -149,7 +149,7 @@ Standard array implementations; variants differ in stability and space. | Edge weights | Algorithm | Time with adjacency lists | Caveat | |---|---|---|---| -| all equal | BFS | O(V+E) | counts edges | +| all equal, positive | BFS | O(V+E) | counts edges | | nonnegative | Dijkstra + binary heap | O((V+E) log V) | negative edges invalidate greedy settlement | | negative allowed | Bellman-Ford | O(VE) | detects reachable negative cycles | | DAG, any weights | topological relaxation | O(V+E) | needs acyclic graph | @@ -201,10 +201,11 @@ Standard array implementations; variants differ in stability and space. ### Bit manipulation -- `x & (x − 1)` clears the lowest set bit (zero result = power of two); **`x & −x`** isolates it. +- `x & (x − 1)` clears the lowest set bit, so `x > 0 && (x & (x − 1)) == 0` tests a power of two; **`x & −x`** isolates it. - XOR of all elements cancels pairs — finds the single unpaired number in O(1) space. ### Recognizing NP-hard problems - Traveling salesman, subset sum, knapsack, graph coloring, SAT, Hamiltonian path, and set cover are **NP-hard** in general. -- Exact answers then need exponential search or pseudopolynomial DP on small numbers; otherwise use a greedy or **approximation** algorithm and say so. +- No polynomial algorithm is known (none exists unless P = NP); exact answers use exponential search with pruning, or pseudopolynomial DP when numbers are small (subset sum, knapsack only). +- Otherwise use heuristics or an **approximation** algorithm where a guarantee exists (greedy set cover: H(n) ≤ ln n + 1); general TSP and coloring have no good ratio unless P = NP. diff --git a/docs/topics/aws.md b/docs/topics/aws.md index e001d7e..00b6184 100644 --- a/docs/topics/aws.md +++ b/docs/topics/aws.md @@ -43,7 +43,7 @@ What experienced AWS developers and DevOps engineers forget before an interview, ### Route 53 routing - Policies: simple, **weighted** (canary), latency, **failover** (with health checks), geolocation, geoproximity, multivalue. -- **Alias records** work at the zone apex (`example.com` → ALB/CloudFront), where a CNAME isn't allowed, and are free to query. +- **Alias records** work at the zone apex (`example.com` → ALB/CloudFront), where a CNAME isn't allowed; queries are free when the target is an AWS resource, but billed when it is a plain record in the same zone. ## Compute @@ -70,8 +70,9 @@ What experienced AWS developers and DevOps engineers forget before an interview, ### API Gateway vs ALB -- **API Gateway**: auth, throttling, usage plans, request validation; integration timeout **29 s** by default (raisable for Regional REST APIs). -- **ALB** in front of Lambda or containers is cheaper at high request volume but has none of the API-management features. +- API Gateway REST API: auth, throttling, usage plans, request validation; integration timeout **29 s** by default (raisable for Regional and private APIs). +- **HTTP API**: cheaper, JWT/Lambda/IAM authorizers and throttling, but no usage plans or request validation; integration timeout max **30 s**. +- ALB in front of Lambda or containers can authenticate via OIDC/Cognito but has no throttling, usage plans, or validation; LCU pricing is usually cheaper at sustained high volume. ### ECS service mechanics @@ -104,7 +105,8 @@ What experienced AWS developers and DevOps engineers forget before an interview, ### Aurora storage -- The storage volume keeps **6 copies across 3 AZs**; a write needs **4 of 6**, a read **3 of 6** — it survives losing an AZ plus one more copy for reads. +- The storage volume keeps **6 copies across 3 AZs**; a write needs **4 of 6**, so writes survive losing an AZ; the **3-of-6** read quorum survives an AZ plus one more copy. +- Normal reads go to one storage node known to be current; quorum reads are only needed during recovery. - Up to 15 replicas share the same storage volume, so replica lag is usually well under 100 ms and failover doesn't copy data. ### DynamoDB partitions and indexes @@ -122,8 +124,8 @@ What experienced AWS developers and DevOps engineers forget before an interview, ### Lambda with SQS -- Lambda **polls** SQS and invokes the function with a batch; by default, one failed record makes the whole batch visible again after the visibility timeout. Enable **partial batch responses** to retry only failed records. -- Set the queue visibility timeout at least as long as the function timeout; configure the **DLQ on the SQS queue**, not the Lambda asynchronous-failure destination. +- Lambda polls SQS and invokes the function with a batch; by default, one failed record makes the whole batch visible again after the visibility timeout. Enable **partial batch responses** to retry only failed records. +- Set the queue visibility timeout to **6× the function timeout** plus the batching window (Lambda rejects anything below 1×); configure the **DLQ on the SQS queue**, not the Lambda asynchronous-failure destination. ### Routing and orchestration diff --git a/docs/topics/backend.md b/docs/topics/backend.md index 8c0e9cf..2a198b8 100644 --- a/docs/topics/backend.md +++ b/docs/topics/backend.md @@ -13,7 +13,7 @@ What experienced backend engineers forget before an interview, grouped by subtop | Codes | Meaning | |---|---| -| **401** vs **403** | not authenticated vs authenticated but not allowed (or 404 to hide that the resource exists) | +| **401** vs **403** | missing or invalid credentials (re-auth may help) vs refused — credentials, if sent, are insufficient; others might succeed (or 404 to hide that the resource exists) | | **409** vs **422** | conflicts with current state vs well-formed but fails validation | | 202 / 204 | accepted for async processing / success with no body | | 412 / 429 | precondition (`If-Match`) failed / rate limited | @@ -37,13 +37,14 @@ What experienced backend engineers forget before an interview, grouped by subtop | | REST | gRPC | GraphQL | |---|---|---|---| | Contract | OpenAPI (optional) | **Protobuf**, strict | schema, strict | -| Transport | HTTP/1.1 or 2 | **HTTP/2** only | usually HTTP POST | +| Transport | any HTTP version | **HTTP/2** only | usually HTTP POST | | Strength | HTTP caching, simplicity | streaming, speed, codegen | client picks fields, one round trip | | Weakness | over/under-fetching | browsers need gRPC-Web | hard to cache, N+1, query-cost limits | ### Pagination -- **Cursor (keyset)**: `WHERE (created_at, id) > (:c, :id) ORDER BY ... LIMIT n` — fast and stable at any depth; return an opaque cursor. +- **Keyset**: `WHERE (created_at, id) > (:c, :id) ORDER BY ... LIMIT n` — fast at any depth and unaffected by inserts; needs a unique, immutable sort key, or updated rows skip or repeat. +- Expose the position as an opaque **cursor**; a cursor is just a token and can also encode an offset. - **Offset**: simple and jumps to page N, but slows with depth and skips or repeats rows as data changes. ### Versioning @@ -72,7 +73,7 @@ What experienced backend engineers forget before an interview, grouped by subtop ### Transactional outbox - Write the business row and an outbox row in the **same DB transaction**; a relay (poller or CDC) publishes outbox rows and marks them sent. -- Fixes the dual-write problem: committing and publishing can't both be atomic otherwise. Delivery is at-least-once. +- Fixes the dual-write problem without 2PC, which most brokers don't support. Delivery is at-least-once. ### Inbox deduplication diff --git a/docs/topics/database.md b/docs/topics/database.md index 13e9238..9056be4 100644 --- a/docs/topics/database.md +++ b/docs/topics/database.md @@ -11,8 +11,9 @@ What experienced database engineers forget before an interview, grouped by subto ### Composite indexes -- **Leftmost-prefix rule**: `(a, b, c)` serves `a`, `a,b`, `a,b,c` — not `b` alone. -- Put **equality columns first, range columns last**; the column after a range condition can't be used for seeking. +- **Leftmost-prefix rule**: `(a, b, c)` seeks efficiently on `a`, `a,b`, `a,b,c` — not on `b` alone. +- **Skip scan** (MySQL 8.0.13+, PostgreSQL 18+) serves `b` alone by probing each distinct `a`; cheap only when `a` has few values. +- Put **equality columns first, range columns last**; columns after a range condition only filter inside the scanned range (unless skip scan applies). ### Covering indexes @@ -68,7 +69,7 @@ What experienced database engineers forget before an interview, grouped by subto ### PostgreSQL MVCC -- Readers read a snapshot and never block writers, or vice versa; writers still lock rows against each other. +- Plain `SELECT`s read a snapshot and neither block nor wait for row writes; writers (and `FOR UPDATE`) still lock rows against each other. - Old row versions stay in the table, tagged **`xmin`/`xmax`**; **VACUUM** reclaims them, and a long-running transaction blocks it, causing bloat. - **XID wraparound** (32-bit transaction IDs) forces aggressive anti-wraparound vacuuming. - HOT updates skip index maintenance when no indexed column changed and the page has room. @@ -84,7 +85,7 @@ What experienced database engineers forget before an interview, grouped by subto ### Optimistic locking -- Add a `version` column and `UPDATE ... WHERE id = ? AND version = ?`; zero rows updated means someone else won — reload and retry. +- Add a `version` column: `UPDATE ... SET version = version + 1 WHERE id = ? AND version = ?`; zero rows updated means someone else won — reload and retry. - Best when conflicts are rare; pessimistic locks when they're frequent. ### Deadlocks @@ -112,8 +113,9 @@ What experienced database engineers forget before an interview, grouped by subto ### Write-ahead log -- Commits append to the **WAL** and `fsync` it before data pages change; pages are written lazily at **checkpoints**. -- Crash recovery replays the log (ARIES-style redo/undo); **group commit** batches many commits into one fsync. +- A commit is durable once its **WAL** record is fsynced; dirty data pages are flushed later (**checkpoints**, background writer), never before their WAL. +- Crash recovery replays WAL from the last checkpoint: PostgreSQL only redoes (uncommitted rows stay invisible by commit status); InnoDB redoes, then rolls back with undo. +- **Group commit** batches many commits into one fsync. ### Backups and PITR @@ -148,7 +150,7 @@ What experienced database engineers forget before an interview, grouped by subto - Any comparison with NULL — even **`NULL = NULL`** — is UNKNOWN, and `WHERE` drops it; use `IS [NOT] NULL` or `IS [NOT] DISTINCT FROM`. - **`NOT IN` with a NULL in the list returns no rows** — use `NOT EXISTS`. -- `COUNT(*)` counts rows; `COUNT(col)` and other aggregates skip NULLs. +- `COUNT(*)` counts rows; `COUNT(col)`, `SUM`, `AVG`, `MIN`/`MAX` skip NULLs, but `array_agg`/`json_agg` keep them. ### Window functions @@ -181,7 +183,7 @@ What experienced database engineers forget before an interview, grouped by subto ### Wide-column key design (Cassandra, DynamoDB) -- The **partition key** picks the node; the sort/clustering key orders rows inside the partition — a query must supply the partition key. +- The **partition key** picks the node; the sort/clustering key orders rows inside the partition — an efficient query must supply the partition key; otherwise it's a full scan (DynamoDB `Scan`, Cassandra `ALLOW FILTERING`) or a secondary index. - Model **tables per query** (denormalized), not per entity; unbounded partitions (all events of a popular user) become hot and huge — add a time bucket to the key. - Deletes write **tombstones**, which slow reads until compaction purges them. @@ -195,7 +197,8 @@ What experienced database engineers forget before an interview, grouped by subto ### Normalization -- 1NF atomic values → 2NF no partial-key dependency → **3NF/BCNF** no dependency on non-key columns: each fact stored once. +- 1NF atomic values → 2NF no partial-key dependency → **3NF** no transitive dependency through non-key columns: each fact stored once. +- **BCNF** is stricter: every nontrivial dependency's determinant is a superkey; 3NF still allows it when the dependent column is part of a candidate key. - **Denormalize** measured hot paths only (copied columns, counters, materialized views), accepting harder writes. ### Surrogate keys @@ -206,7 +209,7 @@ What experienced database engineers forget before an interview, grouped by subto ### Constraints and soft deletes - `FOREIGN KEY`, `CHECK`, and `UNIQUE` are the last line of defense — application checks alone race. -- **Soft deletes** (`deleted_at`) plus a partial unique index keep history and undo. +- **Soft deletes** (`deleted_at`) keep deleted rows for undo and audit; a partial unique index (`WHERE deleted_at IS NULL`) frees the value for reuse, so an undelete can then conflict. ## Scaling @@ -219,12 +222,13 @@ What experienced database engineers forget before an interview, grouped by subto ### Table partitioning - **Range** (most common, by date), list, or hash; **partition pruning** skips irrelevant partitions. -- Dropping an old partition is instant, unlike a bloating bulk `DELETE`; PostgreSQL unique constraints must include the partition key. +- Dropping an old partition is a cheap metadata change, unlike a bloating bulk `DELETE`, but takes an `ACCESS EXCLUSIVE` lock on the parent — `DETACH PARTITION CONCURRENTLY` (PostgreSQL 14+) first avoids that. +- PostgreSQL unique constraints must include the partition key. ### Connection pooling - Each PostgreSQL connection is a **process** (a few MB), so thousands of direct connections hurt — pool them. -- PgBouncer session mode keeps `SET` and advisory locks; **transaction mode** scales further but loses session state; prepared statements work in it **since PgBouncer 1.21**. +- PgBouncer session mode keeps `SET` and advisory locks; **transaction mode** scales further but loses session state; protocol-level prepared statements work in it **since PgBouncer 1.21** when `max_prepared_statements` > 0 (default 0 before 1.24, now 200); SQL `PREPARE` still doesn't. ### OLTP vs OLAP diff --git a/docs/topics/devops.md b/docs/topics/devops.md index f089604..efbe81a 100644 --- a/docs/topics/devops.md +++ b/docs/topics/devops.md @@ -63,7 +63,8 @@ What experienced DevOps engineers forget before an interview, grouped by subtopi ### Node components - **kubelet** runs the node's Pods through the container runtime (CRI) and reports status. -- **kube-proxy** (iptables/IPVS) or an **eBPF** CNI such as Cilium implements Service virtual IPs. +- **kube-proxy** or an **eBPF** CNI such as Cilium implements Service virtual IPs. +- kube-proxy modes: iptables (default), nftables (GA in 1.33), IPVS (deprecated since 1.35). ## Kubernetes workloads @@ -90,7 +91,8 @@ What experienced DevOps engineers forget before an interview, grouped by subtopi ### PodDisruptionBudgets -- A **PDB** (`minAvailable` or `maxUnavailable`) limits **voluntary** disruptions — node drains, upgrades, autoscaler scale-down — not crashes. +- A **PDB** (`minAvailable` or `maxUnavailable`) limits only disruptions that go through the **Eviction API** — node drains, autoscaler scale-down. +- It doesn't stop crashes, direct Pod deletes, or Deployment rolling updates (those follow `maxUnavailable`). - A PDB that allows zero disruptions blocks node drains forever. ### Persistent volumes @@ -127,7 +129,8 @@ What experienced DevOps engineers forget before an interview, grouped by subtopi ### Requests, limits, QoS -- Requests drive scheduling; limits cap usage — over the CPU limit the container is **throttled**, over the memory limit it's **OOMKilled**. +- Requests drive scheduling; limits cap usage — over the CPU limit the container is **throttled**. +- At the memory limit the kernel reclaims (page cache) first and **OOMKills** only if usage still can't stay under it. - Requests vs limits set the QoS class (Guaranteed / Burstable / BestEffort), which decides eviction order under node pressure. - In-place Pod resize (GA in **1.35**) changes CPU/memory requests without restarting the Pod. @@ -138,8 +141,9 @@ What experienced DevOps engineers forget before an interview, grouped by subtopi ### Graceful Pod shutdown -- On deletion the Pod is removed from endpoints and gets `SIGTERM` at the same time, so it may still receive traffic briefly — a short **`preStop`** sleep covers that race. -- After **`terminationGracePeriodSeconds`** (default **30 s**) the kubelet sends `SIGKILL`; app-side steps are in [backend.md](backend.md). +- On deletion, endpoint updates (EndpointSlice marks the Pod terminating, not ready) and the kubelet's shutdown run concurrently, so the Pod may still receive traffic briefly. +- The kubelet runs **`preStop`** first, then sends `SIGTERM` — a short `preStop` sleep covers that race. +- **`terminationGracePeriodSeconds`** (default **30 s**) covers `preStop` plus shutdown; then the kubelet sends `SIGKILL`. App-side steps are in [backend.md](backend.md). ### ConfigMaps and Secrets @@ -178,7 +182,7 @@ What experienced DevOps engineers forget before an interview, grouped by subtopi - **`moved`** blocks record a renamed address, so the plan doesn't destroy and recreate it. - **`import`** blocks (1.5) adopt existing resources into state. -- **`removed`** blocks (1.7) forget a resource without destroying it. +- **`removed`** blocks (1.7) drop a resource from config; the default destroys it — only `lifecycle { destroy = false }` keeps the real object. ### Modules, workspaces, licensing @@ -197,4 +201,4 @@ What experienced DevOps engineers forget before an interview, grouped by subtopi ### Pull-based reconciliation - A controller (**Argo CD**, **Flux**) watches a Git repo of desired state and reconciles the cluster — the cluster pulls, so CI needs no cluster credentials. -- Manual `kubectl` changes are drift and get reverted on the next sync; emergency fixes must land in Git too. +- Manual `kubectl` changes are drift: Flux reapplies every interval, Argo CD reverts them only with **`selfHeal`** on; emergency fixes must land in Git too. diff --git a/docs/topics/distributed.md b/docs/topics/distributed.md index 221a753..591cff9 100644 --- a/docs/topics/distributed.md +++ b/docs/topics/distributed.md @@ -17,15 +17,16 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Byzantine faults -- Tolerating `f` nodes that lie needs **`3f + 1`** nodes, vs `2f + 1` for crash faults; the classic algorithm is **PBFT**. +- Under partial synchrony, tolerating `f` nodes that lie needs **`3f + 1`** nodes, vs `2f + 1` for crash faults; the classic algorithm is **PBFT**. +- The bound depends on the model: with signed messages and a synchronous network, fewer nodes suffice. - Most internal systems assume crash faults only; blockchains must assume Byzantine ones. ## Consistency models ### Consistency hierarchy -- From strongest: strict serializable > linearizable > sequential > causal > eventual. -- **Linearizability**: single-object recency — once a write completes, every later read (in real time) sees it. +- Single-object models, strongest first: linearizable > sequential > causal; eventual only promises convergence, which causal alone doesn't guarantee. +- **Linearizability**: single-object recency — once a write completes, every later read (in real time) sees it or a newer value. - **Serializability**: multi-object transaction isolation — equivalent to *some* serial order, not necessarily real-time order. - **Strict serializability** adds real-time order (Spanner, FoundationDB); CockroachDB is serializable but linearizable only per key. @@ -72,7 +73,7 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Hot keys and secondary indexes - A single hot key isn't fixed by more partitions — **salt** it into sub-keys and merge on read. -- **Local** secondary indexes (per partition) need scatter-gather reads; **global** ones are partitioned separately and updated asynchronously. +- **Local** secondary indexes (per partition) need scatter-gather unless the query fixes the partition key; **global** ones are partitioned by the indexed value, so a write touches several partitions — asynchronous in DynamoDB, transactional in Spanner. ## Consensus and coordination @@ -84,7 +85,7 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Raft - **Leader election**: randomized timeouts avoid split votes; one vote per node per term. -- Log replication: an entry commits once a **majority** stores it. +- Log replication: a leader commits an entry once a **majority** stores it — only for current-term entries; older ones commit indirectly with them. - **Election restriction**: only a candidate with an up-to-date log can win, so committed entries survive. ### Raft reads and membership @@ -112,7 +113,7 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Logical clocks -- **Lamport clock**: increment per event; on receive, `max(local, received) + 1`. A total order consistent with causality, but can't detect concurrency. +- **Lamport clock**: increment per event; on receive, `max(local, received) + 1`. With node ID as tie-breaker, a total order consistent with causality, but can't detect concurrency. - **Vector clock**: one counter per node; comparing two vectors tells **happened-before** from concurrent. - Hybrid logical clock (HLC): physical time + logical counter — causal and close to wall time (CockroachDB). @@ -139,7 +140,7 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Effectively-once processing -- **Exactly-once = at-least-once delivery + idempotent processing**. +- **Effectively-once = at-least-once delivery + idempotent processing**: handlers may run repeatedly, but the effect lands once. - Publish events atomically with the local write via the **transactional outbox**; dedupe on the consumer via an inbox — both in [backend.md](backend.md). ## Conflict resolution @@ -151,7 +152,8 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### CRDTs -- Merges are **commutative, associative, idempotent**, so replicas converge without coordination. +- State-based: merges are **commutative, associative, idempotent**, so replicas converge without coordination. +- Op-based: replicas broadcast operations that must commute; delivery must be exactly-once and usually causal. - **G-Counter** (per-node counts, summed), PN-Counter (two G-Counters), **OR-Set** (add wins over concurrent remove), LWW-Register. ### OT vs CRDT for collaborative editing @@ -179,7 +181,7 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Tail latency and hedging -- With fan-out to `n` servers each slow with probability `p`, a request is slow with probability **`1 − (1 − p)ⁿ`** — at 100 servers, a 1% tail hits 63% of requests. +- Waiting on all `n` servers, each independently slow with probability `p`, a request is slow with probability **`1 − (1 − p)ⁿ`** — at 100 servers, a 1% tail hits 63% of requests. - **Hedged requests**: send a second copy after ~p95 latency and take the first reply. ### Failure detection @@ -211,8 +213,8 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Kafka durability -- Each partition has a leader and followers; the **ISR** (in-sync replicas) are followers caught up within `replica.lag.time.max.ms`. -- **`acks=all`** + **`min.insync.replicas=2`** (with replication factor 3) acknowledges a write only once 2 replicas have it — survives one broker loss without losing acknowledged data. +- Each partition has a leader and followers; the **ISR** (in-sync replicas) is the leader plus followers caught up within `replica.lag.time.max.ms`. +- **`acks=all`** waits for every current ISR member; **`min.insync.replicas=2`** (with replication factor 3) rejects writes when the ISR shrinks below 2 — survives one broker loss without losing acknowledged data. - `unclean.leader.election.enable=false` (default) refuses to elect an out-of-sync replica, choosing unavailability over data loss. ### Kafka rebalancing, compaction, KRaft @@ -224,5 +226,5 @@ Key distributed-systems building blocks and trade-offs, grouped by subtopic. ### Event sourcing and CQRS - **Event sourcing** stores every change as an immutable event and rebuilds state by replay, with snapshots for speed. -- **CQRS** splits the write model from read models updated asynchronously — reads are eventually consistent. +- **CQRS** splits the write model from read models; if read models update asynchronously (often from events), reads are eventually consistent. - Event schemas must stay readable forever: only add fields, or upcast old events. diff --git a/docs/topics/ethereum.md b/docs/topics/ethereum.md index aa80c91..81c29be 100644 --- a/docs/topics/ethereum.md +++ b/docs/topics/ethereum.md @@ -7,7 +7,7 @@ What experienced Ethereum engineers forget before an interview, grouped by subto ### Account model - **EOA** (controlled by a secp256k1 key) vs **contract account** (controlled by its code); only EOAs originate transactions. -- The **nonce** counts an EOA's sent transactions — it prevents replay and orders them. +- The **nonce** orders an EOA's transactions and prevents replay; an EIP-7702 authorization also increments it, even in someone else's transaction. - A stuck transaction blocks every later nonce until replaced by one with the same nonce and a higher fee. ### Address derivation @@ -58,7 +58,7 @@ What experienced Ethereum engineers forget before an interview, grouped by subto ### Execution model -- Stack-based, **256-bit words**, stack depth 1024 — only the top **16** items are reachable, the source of "stack too deep". +- Stack-based, **256-bit words**, stack depth 1024 — `DUP` reaches only the top **16** items (`SWAP16` the 17th), the source of "stack too deep". - Memory expansion cost is **quadratic** in size; storage is the most expensive resource, priced per 32-byte slot. - `DELEGATECALL` runs another contract's code on the caller's storage; any call forwards at most 63/64 of the remaining gas (EIP-150) — see [solidity.md](solidity.md). @@ -86,9 +86,9 @@ What experienced Ethereum engineers forget before an interview, grouped by subto ### EIP-712 and permits -- **EIP-712** signs typed data with a domain separator (name, version, chainId, contract) — readable in wallets, not replayable across contracts or chains. +- **EIP-712** signs typed data with a domain separator (name, version, chainId, contract) — readable in wallets; stops cross-chain/cross-contract replay only if the verifier checks the domain, and same-contract replay still needs a nonce. - **ERC-1271** lets contract wallets "sign" via `isValidSignature`. -- **EIP-2612 `permit`** and Permit2 grant ERC-20 approvals by signature instead of a transaction. +- **EIP-2612 `permit`** grants an ERC-20 approval by signature, so the owner skips the `approve` transaction (anyone can submit it); Permit2 needs a one-time `approve` to its contract first. ### Token standards diff --git a/docs/topics/frontend.md b/docs/topics/frontend.md index 5a89226..59e3265 100644 --- a/docs/topics/frontend.md +++ b/docs/topics/frontend.md @@ -11,12 +11,12 @@ What experienced frontend engineers forget before an interview, grouped by subto | CSR | in the browser after JS loads | slow first paint and SEO, fast later navigation | | SSR | on the server, per request | fresh and SEO-friendly, higher server load | | SSG | at build time | fastest and cheapest, stale until rebuild | -| ISR | like SSG, regenerated on a schedule or on demand | near-CDN speed, bounded staleness | +| ISR | like SSG, regenerated in the background after a revalidate interval or on demand | near-CDN speed; serves stale until regeneration succeeds | ### Streaming SSR and hydration - **Streaming SSR** flushes HTML in chunks, so the shell paints before slow data-dependent sections resolve. -- **Hydration** attaches listeners and state to server HTML instead of re-rendering it. +- **Hydration** runs the render on the client and attaches listeners to the server HTML, reusing the DOM instead of recreating it. - A **hydration mismatch** comes from render output that differs between server and client — `Date.now()`, `Math.random()`, `window` checks during render. ### Islands and Server Components @@ -93,7 +93,7 @@ What experienced frontend engineers forget before an interview, grouped by subto ### Bundle size - **Code splitting** by route or with dynamic `import()` keeps the first bundle to what the first screen needs. -- **Tree shaking** needs ES modules and side-effect-free code (`"sideEffects": false`). +- **Tree shaking** needs ES modules; `"sideEffects": false` also lets the bundler drop unused modules whole — list CSS and polyfill files as exceptions. ### Images @@ -160,7 +160,7 @@ What experienced frontend engineers forget before an interview, grouped by subto ### Strict Mode double invocation - In development, **Strict Mode** double-calls render functions to expose impure rendering. -- It also mounts → unmounts → remounts each component to expose effects with **missing cleanup**; production runs once. +- It also runs an extra effect setup → cleanup → setup cycle on mount (state kept) to expose **missing cleanup**; production skips it. ## React hooks pitfalls diff --git a/docs/topics/golang.md b/docs/topics/golang.md index 0a9515f..cad31a3 100644 --- a/docs/topics/golang.md +++ b/docs/topics/golang.md @@ -6,8 +6,8 @@ What experienced Go engineers forget before an interview, grouped by subtopic. ### Scheduler (GMP) -- Goroutines (G) run on OS threads (M) through logical processors (P); **`GOMAXPROCS`** = number of Ps, defaults to CPU count. -- **Since Go 1.25**, the default `GOMAXPROCS` respects the cgroup CPU limit and updates when it changes; before that it used the host CPU count, hence `automaxprocs`. +- Goroutines (G) run on OS threads (M) through logical processors (P); **`GOMAXPROCS`** = number of Ps. +- Default = min(logical CPUs, affinity mask); **since Go 1.25** also capped by the cgroup CPU limit (rounded up, floor 2) and updated when it changes — before that, containers needed `automaxprocs`. - Blocking syscall → the M is parked and the P moves to another M; network I/O goes through the netpoller and doesn't hold a thread. - Preemption is asynchronous (signal-based) since Go 1.14, so tight loops no longer starve the scheduler. - Goroutines start with a **2 KB** stack that grows and is copied as needed — this is why launching hundreds of thousands is normal. @@ -17,10 +17,10 @@ What experienced Go engineers forget before an interview, grouped by subtopic. | Operation | nil channel | closed channel | |---|---|---| | send | blocks forever | **panics** | -| receive | blocks forever | zero value, `ok == false` | +| receive | blocks forever | buffered values first, then zero value, `ok == false` | | close | panics | **panics** | -Only the sender should close a channel; `for range ch` ends when it closes. +Only the sender should close a channel; `for range ch` ends once it's closed and drained. ### select @@ -83,7 +83,7 @@ Only the sender should close a channel; `for range ch` ends when it closes. ### Struct layout and padding -- Fields are aligned to their size, so `struct{ a bool; b int64; c bool }` takes **24 bytes** while `{b int64; a, c bool}` takes **16** — order fields largest first in hot, numerous structs. +- Fields are aligned to their type's alignment, so on 64-bit `struct{ a bool; b int64; c bool }` takes **24 bytes** while `{b int64; a, c bool}` takes **16** — order fields largest first in hot, numerous structs. - An empty struct (`struct{}`) is **zero bytes** — used for sets (`map[K]struct{}`) and signal channels. ### Cleanups, weak pointers, interning @@ -104,7 +104,7 @@ Only the sender should close a channel; `for range ch` ends when it closes. ### Maps - Hash table; implemented as **Swiss tables since Go 1.24** (faster, but iteration order was already randomized and remains so). -- Concurrent write (or write + read) is a **fatal, unrecoverable** error, not a panic you can `recover`. +- Concurrent write (or write + read) is a race; the runtime detects it best-effort, and then it's a **fatal, unrecoverable** error, not a panic you can `recover`. - Maps never shrink after deletions; `clear(m)` (**1.21**) empties one without reallocating the header. - Map values aren't addressable: `&m[k]` and `m[k].field = x` don't compile for struct values. @@ -119,7 +119,7 @@ Only the sender should close a channel; `for range ch` ends when it closes. ### Interface internals - An interface value is a **(type, value)** pair; it is `nil` only when both are nil — a nil `*T` stored in an `error` is **not** `== nil`. -- Comparing two interface values **panics** if their dynamic type is not comparable (e.g. holds a slice). +- Comparing two interface values **panics** if both hold the same non-comparable dynamic type (e.g. `[]int`); different dynamic types just compare unequal. - Compile-time satisfaction check: `var _ io.Reader = (*MyReader)(nil)`. ### Method sets and embedding @@ -137,7 +137,7 @@ Only the sender should close a channel; `for range ch` ends when it closes. ### Iterators and loop variables - **Range-over-func** iterators (`iter.Seq`, `iter.Seq2`, **1.23**) let `range` work over a function, powering the `slices`/`maps` iterator helpers. -- **Since Go 1.22**, each loop iteration gets its own variable — the classic closure-capture-in-loop bug requires `go 1.21` or earlier semantics to reproduce. +- **Since Go 1.22**, each iteration gets its own copy of variables declared by the loop (`:=`) — the classic closure-capture bug needs `go 1.21` semantics or a reused outer variable (`for i = 0; ...`). ### Newer language features @@ -193,7 +193,7 @@ Only the sender should close a channel; `for range ch` ends when it closes. ### Compatibility and GODEBUG -- Behavior changes ship behind **`GODEBUG`** settings whose defaults follow the main module's **`go` line** — upgrading the toolchain alone doesn't change semantics. +- Potentially breaking changes ship behind **`GODEBUG`** settings whose defaults follow the main module's **`go` line**, so a toolchain upgrade alone keeps their old behavior (other runtime and performance changes still apply). - Override per program with `//go:debug` directives or the `GODEBUG` environment variable. ## Testing and profiling diff --git a/docs/topics/javascript.md b/docs/topics/javascript.md index 1a8198f..cc071a9 100644 --- a/docs/topics/javascript.md +++ b/docs/topics/javascript.md @@ -59,7 +59,7 @@ What experienced JavaScript engineers forget before an interview, grouped by sub - Every `number` is a 64-bit double: `0.1 + 0.2 !== 0.3`; compare with a tolerance or use integers (cents). - Integers are exact only up to **`Number.MAX_SAFE_INTEGER` = 2⁵³ − 1**; IDs beyond that (Snowflake, tweet IDs) must travel as strings or `BigInt`. -- Bitwise operators convert to **32-bit** signed integers. +- Bitwise operators on numbers truncate to **32-bit** signed integers (`>>>` returns unsigned); on `BigInt` they are arbitrary-precision. - `Math.sumPrecise(iterable)` (ES2026) adds floats without accumulating rounding error, unlike a naive `reduce`. ### Typed arrays @@ -74,13 +74,13 @@ What experienced JavaScript engineers forget before an interview, grouped by sub | Combinator | Settles when | Use when | |---|---|---| | `Promise.all` | any rejects, or all fulfill | need every result, fail fast | -| `Promise.allSettled` | always, tagged fulfilled/rejected | need every outcome | +| `Promise.allSettled` | all settle (input rejections don't reject it), tagged fulfilled/rejected | need every outcome | | `Promise.race` | first settles (either way) | timeouts, first response wins | | `Promise.any` | first fulfills, or all reject (`AggregateError`) | first success wins | ### Sequential vs parallel await -- `await` inside a loop runs **serially**; start the operations first, then `await Promise.all([...])` so their times overlap. +- `for (const x of xs) await f(x)` runs **serially**: each call starts after the previous settles; start all calls first, then `await Promise.all([...])` so they overlap. - An unhandled rejection doesn't throw synchronously; Node **crashes the process on it by default since Node 15**. ### Cancellation and async iteration @@ -140,6 +140,7 @@ What experienced JavaScript engineers forget before an interview, grouped by sub ### Node event loop phases - Phases: **timers → pending callbacks → poll (I/O) → check (`setImmediate`) → close**. +- **Since Node 20 (libuv 1.45)**, timers run after poll in each iteration instead of before it (plus once when the loop starts); this can shift timer vs `setImmediate` ordering. - After each callback, the `process.nextTick` queue drains first, then promise microtasks. - `setTimeout(f, 0)` vs `setImmediate(f)` order is **nondeterministic** in the main module; inside an I/O callback `setImmediate` always runs first. diff --git a/docs/topics/networking.md b/docs/topics/networking.md index bb967ea..e04b5b5 100644 --- a/docs/topics/networking.md +++ b/docs/topics/networking.md @@ -50,7 +50,7 @@ What experienced engineers forget about networking before an interview, grouped ### Head-of-line blocking - TCP delivers bytes in order, so one lost packet stalls everything behind it — even data for unrelated HTTP/2 streams. -- HTTP/1.1 blocks at the request level, HTTP/2 fixes that but not TCP's, and **QUIC** fixes both with per-stream loss recovery. +- HTTP/1.1 blocks at the request level, HTTP/2 fixes that but not TCP's, and **QUIC** fixes both: streams are delivered independently, so a lost packet stalls only the streams whose data it carried. ### UDP @@ -72,7 +72,7 @@ What experienced engineers forget about networking before an interview, grouped ### HTTP/3 and QUIC -- QUIC runs over UDP with TLS 1.3 built in: **1-RTT** setup (0-RTT on resumption) and loss recovery **per stream**, so no cross-stream head-of-line blocking. +- QUIC runs over UDP with TLS 1.3 built in: **1-RTT** setup (0-RTT on resumption) and independent stream delivery, so no cross-stream head-of-line blocking; loss detection and congestion control stay per connection. - **Connection migration**: a connection ID survives an IP change (Wi-Fi → cellular). - Clients discover it via the `Alt-Svc` header or a DNS HTTPS record; many networks block UDP, so browsers fall back to TCP. @@ -100,7 +100,7 @@ What experienced engineers forget about networking before an interview, grouped ### Transport and privacy -- UDP port 53 by default, TCP for large responses (answers above ~**1232 bytes** with EDNS get truncated and retried over TCP). +- UDP port 53 by default, TCP for large responses: an answer bigger than the client's advertised EDNS buffer size (commonly **1232 bytes**, to avoid fragmentation) is truncated and retried over TCP. - DoT/DoH encrypt queries to the resolver; **DNSSEC** signs records for authenticity but doesn't encrypt them. ## IP and routing @@ -112,18 +112,18 @@ What experienced engineers forget about networking before an interview, grouped ### NAT -- NAT rewrites source IP and port and tracks each flow in a **connection-tracking table** — inbound connections can't be initiated from outside. +- NAT rewrites source IP and port and tracks each flow in a **connection-tracking table** — unsolicited inbound connections are dropped unless a port forward or static mapping exists. - One public IP offers at most ~64k source ports **per destination IP:port** (AWS NAT gateway: **55k**), so heavy traffic to one endpoint exhausts it; a full conntrack table drops new connections. ### MTU and fragmentation -- Ethernet **MTU 1500** → TCP MSS 1460; tunnels (VPN, VXLAN) shrink it. +- Ethernet **MTU 1500** → TCP MSS 1460 over IPv4 (1440 over IPv6); tunnels (VPN, VXLAN) shrink it. - **Path MTU discovery** needs ICMP "fragmentation needed" messages; firewalls that drop ICMP cause **black holes** — small requests work, large ones hang. ### Anycast and BGP - **BGP** exchanges routes between autonomous systems; a bad announcement or withdrawal can take a whole company offline (Facebook, October 2021). -- **Anycast** announces one IP from many locations and BGP delivers each client to the nearest — CDNs, public DNS resolvers, DDoS absorption. +- **Anycast** announces one IP from many locations and BGP delivers each client to the nearest by routing policy (usually, not always, the lowest-latency) — CDNs, public DNS resolvers, DDoS absorption. ### IPv6 diff --git a/docs/topics/os.md b/docs/topics/os.md index 710ea22..8ca9aa2 100644 --- a/docs/topics/os.md +++ b/docs/topics/os.md @@ -46,7 +46,7 @@ What experienced engineers forget about Linux and OS internals before an intervi ### CPU quotas and throttling -- A cgroup CPU limit is a **quota per 100 ms period**: a 1-CPU limit lets 4 threads use 25 ms each, then **throttles** the whole group for the rest of the period — latency spikes while average CPU looks low. +- A cgroup CPU limit is a **quota per period** (`cpu.max`, default 100 ms): a 1-CPU limit lets 4 threads use 25 ms each, then **throttles** the whole group for the rest of the period — latency spikes while average CPU looks low. - Watch `nr_throttled` in `cpu.stat`; match thread-pool sizes (`GOMAXPROCS`, JVM) to the quota. ### Load average @@ -58,7 +58,7 @@ What experienced engineers forget about Linux and OS internals before an intervi ### Virtual memory and the TLB -- Each process sees a private virtual address space mapped to physical frames in **4 KB pages** through page tables. +- Each process sees a private virtual address space mapped to physical frames in pages (**4 KB** on x86-64; arm64 can use 16 or 64 KB) through page tables. - The **TLB** caches translations; **huge pages** (2 MB, 1 GB) cut TLB misses for large heaps — transparent huge pages can also cause latency spikes during compaction, so databases often disable them. ### Page faults @@ -69,11 +69,11 @@ What experienced engineers forget about Linux and OS internals before an intervi ### Overcommit and the OOM killer - `malloc` usually succeeds without backing memory; physical pages are allocated **on first touch** (overcommit). -- When memory runs out, the **OOM killer** kills the process with the highest `oom_score` (tuned by `oom_score_adj`); in a container, hitting the **cgroup limit** triggers an OOM kill inside that cgroup only. +- When memory runs out, the **OOM killer** kills the process with the highest `oom_score` (tuned by `oom_score_adj`); in a container, a **cgroup limit** that reclaim can't satisfy triggers an OOM kill inside that cgroup only. ### cgroup memory limits -- cgroup v2 **`memory.max`** is the hard limit: exceeding it OOM-kills inside the cgroup (a Kubernetes memory limit maps here). +- cgroup v2 **`memory.max`** is the hard limit: at it the kernel reclaims, and OOM-kills inside the cgroup only if reclaim fails (a Kubernetes memory limit maps here). - **`memory.high`** is a soft limit: above it the kernel throttles and reclaims aggressively instead of killing. - Page cache counts toward the cgroup but is reclaimed first, so watch the **working set** (usage minus inactive file pages), which the kubelet uses for eviction. @@ -103,7 +103,7 @@ What experienced engineers forget about Linux and OS internals before an intervi ### File descriptors and inodes - Everything open — files, sockets, pipes — is a file descriptor; the soft limit is often **1024** (`ulimit -n`), causing "too many open files" on busy servers. -- A filename points to an inode; deleting an open file frees its space only when the **last descriptor closes** — why `df` and `du` can disagree. +- A filename points to an inode; deleting a file frees its space only when no hard links, **open descriptors**, or mappings remain — why `df` and `du` can disagree. - **Hard links** are extra names for the same inode (same filesystem only); symlinks store a path and can dangle. ### I/O multiplexing: select, poll, epoll @@ -147,7 +147,7 @@ What experienced engineers forget about Linux and OS internals before an intervi ### False sharing -- Cores transfer memory in **64-byte cache lines**; two threads writing different variables on the same line keep invalidating each other's caches. +- Cores transfer memory in **cache lines** (64 bytes on x86-64, 128 on Apple M-series); two threads writing different variables on the same line keep invalidating each other's caches. - Pad or align hot per-thread counters to separate lines. ## Performance analysis diff --git a/docs/topics/python.md b/docs/topics/python.md index 443218d..148c6ae 100644 --- a/docs/topics/python.md +++ b/docs/topics/python.md @@ -9,7 +9,7 @@ What experienced Python engineers forget before an interview, grouped by subtopi - CPython compiles to bytecode run by a stack-based VM; the **GIL** lets only one thread execute bytecode at a time, keeping refcounting safe without per-object locks. - The GIL is released during blocking I/O and by some C extensions, so threads still help I/O-bound work; **CPU-bound** work needs processes or a GIL-releasing extension. - A running thread is asked to drop the GIL every **5 ms** (`sys.getswitchinterval()`), which is why CPU-bound threads also slow I/O threads. -- The GIL makes single bytecodes atomic, not whole statements — `x += 1` on a shared value still races. +- The GIL doesn't make statements atomic — `x += 1` on a shared value is several bytecodes and still races; use a `Lock`. ### Free threading and subinterpreters @@ -37,7 +37,7 @@ What experienced Python engineers forget before an interview, grouped by subtopi - **`__slots__`** replaces the per-instance `__dict__` with fixed storage — less memory per instance, no arbitrary new attributes. - **`weakref`** holds a reference that doesn't keep the object alive — for caches and observers. -- **`__del__`** runs at refcount zero, or only at the next GC pass if the object is in a cycle (collectable since 3.4, PEP 442); use `with` or `weakref.finalize` for deterministic cleanup. +- **`__del__`** runs at refcount zero, or only at the next GC pass if the object is in a cycle (collectable since 3.4, PEP 442); use `with` for deterministic cleanup (`weakref.finalize` is a safer `__del__`, not more deterministic). ## Built-in data structures @@ -76,7 +76,7 @@ What experienced Python engineers forget before an interview, grouped by subtopi ### asyncio event loop -- One thread runs the loop: it polls ready I/O (via `selectors` — epoll/kqueue), then runs ready callbacks; a coroutine only yields control at an **`await`** on something not yet ready. +- One thread runs the loop: it polls ready I/O (epoll/kqueue via `selectors` on Unix; IOCP proactor on Windows since 3.8), then runs ready callbacks; a coroutine only yields control at an **`await`** on something not yet ready. - `asyncio.create_task` schedules a coroutine; calling a coroutine function without awaiting or scheduling it **does nothing**. - Keep a reference to created tasks — the loop holds only weak references, so an unreferenced task can be **garbage-collected mid-run**. @@ -84,7 +84,7 @@ What experienced Python engineers forget before an interview, grouped by subtopi - **`TaskGroup`** (3.11) is structured concurrency: one failure cancels the siblings and waits for them; `gather` doesn't cancel the others by default. - A blocking call in a coroutine (`time.sleep`, sync file I/O) **stalls the whole event loop** — use async libraries or `asyncio.to_thread`. -- Cancellation raises **`CancelledError`** at the next `await`; cleanup in `finally` must re-raise it, never swallow it. +- Cancellation raises **`CancelledError`** at the next `await`; `finally` cleanup propagates it automatically, but an `except` that catches it must re-raise. ## Gotchas @@ -127,7 +127,7 @@ What experienced Python engineers forget before an interview, grouped by subtopi ### Descriptors - An object with `__get__`/`__set__`/`__delete__` on a class attribute is a **descriptor**; `property`, bound methods, `classmethod`, and `staticmethod` are all built on it. -- **Data descriptors** (define `__set__`) beat the instance `__dict__`; non-data descriptors (only `__get__`, e.g. functions) lose to it. +- **Data descriptors** (define `__set__` or `__delete__`) beat the instance `__dict__`; non-data descriptors (only `__get__`, e.g. functions) lose to it. ### Class creation hooks @@ -137,7 +137,7 @@ What experienced Python engineers forget before an interview, grouped by subtopi ### Equality and hashing - Defining **`__eq__`** without `__hash__` sets `__hash__ = None`, making instances unhashable. -- `@dataclass(frozen=True)` makes instances immutable and hashable; `slots=True` (**3.10**) adds `__slots__`. +- `@dataclass(frozen=True)` blocks field reassignment (shallow — a list field stays mutable) and generates `__hash__`, which raises `TypeError` on an unhashable field; `slots=True` (**3.10**) adds `__slots__`. ## Functions and iteration @@ -153,7 +153,7 @@ What experienced Python engineers forget before an interview, grouped by subtopi ### functools.cache pitfalls -- **`@cache`** (3.9) is `lru_cache(maxsize=None)` — unbounded, so it grows forever on unbounded inputs; set a `maxsize`. +- **`@cache`** (3.9) is `lru_cache(maxsize=None)` — unbounded, so it grows forever on unbounded inputs; switch to `lru_cache(maxsize=N)` to bound it. - On a method it keys on `self` and **keeps every instance alive**; use `cached_property` or a per-instance cache. - Arguments must be hashable, and a returned mutable object is shared by every caller. diff --git a/docs/topics/rust.md b/docs/topics/rust.md index 175e01f..e76d685 100644 --- a/docs/topics/rust.md +++ b/docs/topics/rust.md @@ -19,7 +19,7 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. | Type | Checked | Thread-safe | Note | |---|---|---|---| -| `Cell` | none needed | no | copy/replace in and out, never hands out references | +| `Cell` | none needed | no | copy/replace in and out; no references through `&Cell` | | `RefCell` | runtime | no | a conflicting borrow **panics** | | `OnceCell`/`OnceLock` | write once | `OnceLock` yes | lazy initialization | | `Mutex`/`RwLock` | runtime (lock) | yes | can be poisoned | @@ -32,7 +32,7 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. ### `'static` and HRTBs -- **`T: 'static`** means "holds no non-static borrows" — **owned data satisfies it**; it doesn't mean "lives forever". `&'static T` is the forever reference. +- **`T: 'static`** means "holds no non-static borrows" — **`String`, `Vec` satisfy it**, `Box<&'a str>` doesn't; it doesn't mean "lives forever". `&'static T` is the forever reference. - **HRTB** (`for<'a> Fn(&'a T)`) says "works for every lifetime", needed when the reference is only created inside the callee. ### "Does not live long enough" @@ -45,7 +45,7 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. ### Static vs dynamic dispatch - **Generics** are monomorphized: one copy per type, zero-cost calls, bigger binary. -- **`dyn Trait`** is a fat pointer (data + vtable): one copy, an indirect call, heterogeneous collections. +- **`dyn Trait`** is unsized; `&dyn`/`Box` is a fat pointer (data + vtable): one copy, an indirect call, heterogeneous collections. - **Dyn compatibility** (formerly "object safety"): roughly, no methods returning `Self` by value and no generic methods. ### Coherence: orphan rule and blanket impls @@ -74,8 +74,8 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. | Trait | Captured state | Callable | |---|---|---| -| `Fn` | borrowed immutably | many times, concurrently | -| `FnMut` | borrowed mutably | many times, not concurrently | +| `Fn` | borrowed immutably | many times, via `&self` (concurrently only if `Sync`) | +| `FnMut` | borrowed mutably | many times, via `&mut self` | | `FnOnce` | moved out / consumed | once | - Every `Fn` is also `FnMut` and `FnOnce`; take the **loosest** bound your code allows (`FnOnce` if you call it once), so callers can pass more kinds of closures. @@ -133,7 +133,7 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. ### Send and Sync - **`Send`**: ownership can move to another thread. **`Sync`**: `&T` can be shared (`T: Sync` ⇔ `&T: Send`). -- `Rc` is neither; `RefCell` is `Send` but not `Sync`; **`MutexGuard` is not `Send`** — it must be released on the locking thread. +- `Rc` is neither; `RefCell` is `Send` if `T: Send`, never `Sync`; **`MutexGuard` is not `Send`** — it must be released on the locking thread. ### Mutex poisoning and scoped threads @@ -147,7 +147,7 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. ### Atomics and memory ordering -- Relaxed: atomicity only. **Release** store + **Acquire** load: happens-before between the two threads. **SeqCst**: one global order, strongest and slowest. +- Relaxed: atomicity only. **Release** store + an **Acquire** load that reads its value: happens-before between the two threads. **SeqCst**: one global order, strongest and slowest. - x86 is strongly ordered, so a too-weak ordering often works there and fails only on ARM. ## Async @@ -166,6 +166,7 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. ### Cancellation is drop - Dropping a future cancels it at its current `.await`, with no signal and no chance to run async cleanup. +- Dropping a Tokio `JoinHandle` **detaches** the spawned task instead — cancel with `abort()`. - **Cancel safety**: in `select!`, a losing branch is dropped mid-await — `read_line` into a buffer can lose data, `recv()` on a channel can't. ### Async closures @@ -189,7 +190,7 @@ What experienced Rust engineers forget before an interview, grouped by subtopic. ### Macro kinds and hygiene - **`macro_rules!`** pattern-matches token trees; **procedural macros** (derive, attribute, function-like) run code on a `TokenStream` at compile time. -- **Hygiene**: identifiers created inside a macro don't collide with the caller's, unlike C textual macros. +- **Hygiene**: `macro_rules!` locals and labels don't collide with the caller's (mixed-site); items and proc-macro output are unhygienic. ### Features and workspaces diff --git a/docs/topics/security.md b/docs/topics/security.md index b26ba33..2322010 100644 --- a/docs/topics/security.md +++ b/docs/topics/security.md @@ -73,7 +73,8 @@ What experienced engineers forget about application security before an interview ### Password storage -- Use a slow, memory-hard hash: **argon2id**, scrypt, or bcrypt — never a fast hash like SHA-256; store algorithm and parameters with the hash. +- Use a slow, memory-hard hash — **argon2id**, else scrypt — never a fast hash like SHA-256; store algorithm and parameters with the hash. +- bcrypt is for legacy systems only: not memory-hard, and input is capped at 72 bytes (some libraries truncate silently, others reject — Python `bcrypt` 5.0+). - A unique **salt** per password defeats rainbow tables; a **pepper** (server-side secret stored elsewhere) helps if only the database leaks. ### Password policy (NIST SP 800-63B) @@ -89,7 +90,7 @@ What experienced engineers forget about application security before an interview ### MFA strength - Phishing resistance: **WebAuthn/passkeys** > TOTP app > SMS (SIM swap). -- A passkey's private key never leaves the device and its signature covers the **origin**, so a lookalike domain gets nothing usable. +- A passkey's private key never reaches the server (synced passkeys copy it end-to-end encrypted between the user's devices), and its signature covers the **origin**, so a lookalike domain gets nothing usable. ### Cookie flags @@ -192,7 +193,7 @@ What experienced engineers forget about application security before an interview ### TLS 1.3 handshake - **1-RTT** full handshake: key shares go in the first flight. -- Optional **0-RTT** resumption data can be **replayed** — only safe for idempotent requests. +- Optional **0-RTT** resumption data can be **replayed** — accept it only for requests safe to repeat (idempotent and cheap); HTTP servers can answer `425 Too Early`. ### Post-quantum key exchange diff --git a/docs/topics/solidity.md b/docs/topics/solidity.md index 3029b97..60ccda9 100644 --- a/docs/topics/solidity.md +++ b/docs/topics/solidity.md @@ -14,13 +14,13 @@ What experienced Solidity engineers forget before an interview, grouped by subto - Bases are listed **most base-like first**: `contract D is B, C` (both `is A`) linearizes to D → C → B → A (C3, like Python's MRO). - **`super`** calls the next contract in the linearization, not the declared parent: in D, `super.f()` inside C calls B even though C only inherits A. -- Overridable functions need `virtual`; overriding needs `override`, and `override(B, C)` when several bases define it. +- Overridable functions need `virtual`; overriding needs `override`, and `override(B, C)` when several bases define it; implementing a single interface's function needs no `override` since 0.8.8. ### Modifiers - `_` marks where the function body runs; a `return` in the body only leaves the body, so code after `_` still runs **after the return**. - Modifiers apply left to right; `_` may run several times, and if it never runs the body is skipped and return values stay at their defaults. -- Modifier code is **inlined** at every use — move the logic into an internal function to shrink bytecode. +- Legacy codegen **inlines** modifier code at every use — move the logic into an internal function to shrink bytecode; via-IR emits modifiers as functions. - `virtual` modifiers are deprecated since 0.8.31, ahead of 0.9. ### Libraries and user-defined types @@ -84,7 +84,7 @@ What experienced Solidity engineers forget before an interview, grouped by subto ### ABI encoding pitfalls -- A low-level call to an address with **no code succeeds** — check `code.length` first; high-level calls revert instead. +- A low-level call to an address with **no code succeeds** — check `code.length` first when you expect a contract (precompiles have no code either); high-level calls revert instead. - **`abi.encodePacked`** of several dynamic values can collide (`("a","bc")` vs `("ab","c")`) — hash `abi.encode` output for signatures. - **`abi.encodeCall`** (0.8.11) type-checks the function and arguments; `abi.encodeWithSignature` silently accepts a typo in the signature string. @@ -152,7 +152,7 @@ What experienced Solidity engineers forget before an interview, grouped by subto ### Gas optimization patterns -- An `SSTORE` costs **20,000** gas to set a zero slot and 2,900 to change a non-zero one (+2,100 if cold) — see [ethereum.md](ethereum.md). +- `SSTORE` cost compares the slot's value at transaction start, now, and new: a clean slot (now == start) costs **20,000** gas from zero or 2,900 from non-zero; a dirty slot or a no-op costs 100; +2,100 if cold — see [ethereum.md](ethereum.md). - **Pack** small variables that are read and written together into one slot. - **`constant`/`immutable`** values are embedded in bytecode — no `SLOAD`. - Cache storage reads in local variables inside loops; use `calldata` parameters. @@ -209,7 +209,7 @@ What experienced Solidity engineers forget before an interview, grouped by subto ### Revert data bubbling -- A low-level `call` returns `(bool success, bytes memory data)` and **doesn't revert** — always check `success`. +- A low-level `call` returns `(bool success, bytes memory data)`: a callee revert gives **`success == false`** instead of bubbling up — always check it. - Re-throw the callee's error unchanged with assembly: `revert(add(data, 32), mload(data))`. - OpenZeppelin's `Address.functionCall` does both. diff --git a/docs/topics/system.md b/docs/topics/system.md index c1f359b..7b00174 100644 --- a/docs/topics/system.md +++ b/docs/topics/system.md @@ -12,9 +12,9 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, ### Back-of-envelope conversions -- 1 day ≈ 86,400 s ≈ 10⁵ s, so **1M requests/day ≈ 12 QPS**; peak ≈ 2–3× average. +- 1 day ≈ 86,400 s ≈ 10⁵ s, so **1M requests/day ≈ 12 QPS**; assume peak ≈ 2–3× average unless told otherwise. - 1 KB × 1M items = 1 GB; 1 KB × 1B = 1 TB. -- A well-indexed PostgreSQL box handles roughly **10k+ simple queries/s**; Redis ~100k ops/s per core. +- Ballpark capacity (varies with workload): a well-indexed PostgreSQL box handles **10k+ simple queries/s**; Redis ~100k ops/s per core. ## Traffic and edge @@ -34,7 +34,7 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, - **Reverse proxy** (Nginx, Envoy): TLS termination, load balancing, caching in front of servers. - **API gateway**: auth, rate limiting, routing, aggregation for north–south traffic; a BFF is one gateway per client type. -- **Service mesh** (Istio, Linkerd): sidecar proxies handle east–west traffic — mTLS, retries, telemetry without app changes. +- **Service mesh** (Istio, Linkerd): sidecar proxies (or per-node proxies in Istio ambient mode) handle east–west traffic — mTLS, retries, telemetry without app changes. ### Real-time delivery to clients @@ -43,7 +43,7 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, | Short polling | client pulls | simple, wasteful, latency = interval | | Long polling | client pulls, server holds | works everywhere, one request per message | | **SSE** | server → client | plain HTTP, auto-reconnect with `Last-Event-ID`, text only | -| **WebSocket** | both ways | stateful connections — needs sticky routing and a connection registry | +| **WebSocket** | both ways | stateful connections — needs a connection registry to find the server holding a user | - Push to many clients means **stateful** servers: plan for reconnect storms after a deploy (jittered reconnects). @@ -117,7 +117,7 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, ### Distributed rate limiting - Enforce at the gateway, keyed by user, API key, or IP; reject with **429** and `Retry-After`. -- Share counters in **Redis** (atomic `INCR` + expiry, or a Lua script) so every server sees one limit. +- Share counters in **Redis** so every server sees one limit; wrap `INCR` + `EXPIRE` in `MULTI` or a Lua script, or a crash between them leaves a counter with no TTL. - A local per-instance limiter skips the round trip but is only approximate (limit ÷ instances). ## Reliability and operations @@ -125,7 +125,8 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, ### Availability math - Components in **series** multiply: 99.9% × 99.9% ≈ 99.8%. -- Redundant components in **parallel** fail only together: 1 − 0.001² ≈ 99.9999%, if failures are independent. +- Redundant components in **parallel** fail only together: 1 − 0.001² ≈ 99.9999%. +- Both formulas assume **independent failures**; a shared zone or dependency wipes out most of the parallel redundancy gain. - RPO (acceptable data loss) and RTO (acceptable recovery time) drive backup and failover design; active-passive vs active-active. ### SLIs, SLOs, error budgets @@ -167,7 +168,8 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, ### Geospatial indexes -- **Geohash** encodes lat/lon as a string where nearby points share a prefix; query a cell and its **8 neighbors**, since close points can straddle a boundary. +- **Geohash** encodes lat/lon as a string: a shared prefix means nearby, but close points can straddle a cell boundary and share none. +- Pick a precision whose cell is at least the search radius, then query the cell and its **8 neighbors**. - A **quadtree** subdivides dense areas further, adapting to uneven density; Google S2 and Uber H3 use hierarchical cells the same way. ## Classic designs: user-facing @@ -176,7 +178,7 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, - Read-heavy (~100:1); **base62** in 7 characters gives ~3.5 trillion keys. - Keys from hash + collision check, a counter encoded in base62 (predictable unless shuffled), or a pre-generated key pool. -- **301** is cached by browsers (less load); 302 hits you on every click (analytics). Collect analytics asynchronously. +- **301** is cached by browsers by default (less load, lost clicks); 302 isn't cached without explicit freshness headers, so every click hits you (analytics). Collect analytics asynchronously. ### News feed @@ -239,7 +241,8 @@ Key system design building blocks and trade-offs, grouped by subtopic. For CAP, ### Leaderboard -- A Redis **sorted set**: `ZINCRBY` updates a score, `ZREVRANGE` reads the top N, `ZREVRANK` a user's rank — all O(log n). +- A Redis **sorted set**: `ZINCRBY` updates a score and `ZREVRANK` returns a user's rank in O(log n); `ZRANGE ... REV` reads the top k in O(log n + k). +- `ZRANGE ... REV` replaces `ZREVRANGE`, deprecated since Redis 6.2. - Beyond one node, shard by score range or keep per-shard top-k and merge. ### Booking and inventory contention diff --git a/docs/topics/typescript.md b/docs/topics/typescript.md index beb2906..ce8cb4c 100644 --- a/docs/topics/typescript.md +++ b/docs/topics/typescript.md @@ -87,7 +87,7 @@ What experienced TypeScript engineers forget before an interview, grouped by sub ### Type erasure - Types, interfaces, and type-only imports are erased — **zero runtime footprint**. -- Exceptions that emit runtime code: **all `enum`s**, `namespace`s with values, parameter properties, and legacy decorators. +- Exceptions that emit runtime code: **`enum`s** (a `const enum` is inlined instead), `namespace`s with values, parameter properties, and legacy decorators. ### Transpile-only builds @@ -97,7 +97,7 @@ What experienced TypeScript engineers forget before an interview, grouped by sub ### Node type stripping - Node runs `.ts` files by stripping types, on by default **since Node 22.18 / 23.6**. -- **`erasableSyntaxOnly`** (**5.8**) forbids syntax that can't simply be erased (enums, parameter properties, value namespaces), guaranteeing a file runs there. +- **`erasableSyntaxOnly`** (**5.8**) forbids syntax that can't simply be erased (enums, parameter properties, value namespaces); Node still ignores tsconfig (`paths`, JSX) and needs explicit `.ts` import extensions. ### Module resolution @@ -120,7 +120,7 @@ What experienced TypeScript engineers forget before an interview, grouped by sub ### The `strict` family - **`strict`** enables `strictNullChecks`, `noImplicitAny`, `strictFunctionTypes`, `strictPropertyInitialization`, and more — on by default since 6.0. -- Without **`strictNullChecks`**, `null`/`undefined` are assignable to every type. +- Without **`strictNullChecks`**, `null`/`undefined` are assignable to every type except `never`. ### Flags outside `strict` @@ -153,5 +153,5 @@ What experienced TypeScript engineers forget before an interview, grouped by sub ### Readonly and `as const` -- `readonly T[]` blocks `push`/`splice` at compile time; **`as const`** narrows a literal to its most specific type and makes it deeply `readonly`. +- `readonly T[]` blocks `push`/`splice` at compile time; **`as const`** narrows a literal to its most specific type and makes it deeply `readonly` — but not arrays/objects it only references. - Both are compile-time only — nothing is frozen at runtime.