Concepts
One concept at a time.
181 short notes, one idea each, across system design, databases, backend, frontend, AI, security and Git. Each one fits on a page, takes a few minutes to read, and links to the lessons that put it to work.
181 concepts
System design
Requirements & scale
Frame the problem, put numbers on it, and decide how much system it needs.
- Clarifying requirementsTurn a vague prompt into users, features, scale and constraints before drawing a single box.
- Non-functional requirementsLatency, availability, durability, consistency and cost — written down as numbers.
- Latency numbers to knowMemory, SSD, network and cross-region round trips, to the nearest order of magnitude.
- Capacity estimationFrom daily users to requests per second, storage and bandwidth in a few lines of arithmetic.
- Little's lawWork in progress equals throughput times latency — the formula behind pool, queue and fleet sizes.
- Availability and the ninesWhat 99.9% and 99.99% mean in minutes of downtime, and how serial dependencies multiply.
- SLIs, SLOs and error budgetsMeasure what users feel, set a target, and spend the slack on purpose.
- Tail latencyWhy p99 matters more than the average, how fan-out amplifies it, and hedged requests.
- Vertical vs horizontal scalingA bigger machine or more machines — and why the answer is usually "bigger, until it isn’t".
- Stateless servicesMove sessions and files out of the server so any instance can answer any request.
- Monolith vs microservicesWhat splitting a service buys you, what it costs, and the modular monolith in between.
- When not to add infrastructureThe cheapest, most reliable component is the one you never run.
Networking & APIs
How bytes and requests move between clients and services.
- TCP vs UDPReliable ordered streams versus fire-and-forget datagrams, and why HTTP/3 runs on UDP.
- HTTP/1.1, HTTP/2 and HTTP/3Connection reuse, multiplexing, head-of-line blocking and QUIC.
- REST, gRPC and GraphQLThree API styles and where each one fits: public APIs, service-to-service, and client-shaped reads.
- Polling, SSE and WebSocketsFour ways to push updates to a client, and what each costs the server.
- Content delivery networksEdge caches close to users: what they cache, how they invalidate, and what they protect.
- API gateway vs load balancerOne spreads traffic; the other also authenticates, limits and routes by API.
- Idempotency keysMake a retried POST do its work exactly once by remembering the client’s request key.
- Keyset paginationPage through large tables with a cursor on an indexed key instead of OFFSET.
Storage & databases
How databases lay bytes on disk, and how to pick one.
- B-trees vs LSM-treesUpdate in place or append and compact: the two storage-engine designs and their trade-offs.
- Write-ahead logWrite the intent to an append-only log first, so a crash can always be replayed.
- Relational vs NoSQLKey-value, document, wide-column and graph stores compared with a relational database.
- Normalization vs denormalizationStore each fact once, or copy it where it’s read — and how to keep the copies honest.
- Object storageBlobs by key at any scale: S3-style storage, presigned URLs and why files don’t go in the database.
Replication & partitioning
Copy data for availability, split it for scale.
- Leader–follower replicationOne node takes writes and streams them to read replicas; sync, async and failover.
- Replication lag anomaliesRead-your-writes, monotonic reads and how to route reads around a lagging replica.
- Quorum reads and writesWith N replicas, R + W > N makes every read overlap the latest write.
- Range vs hash partitioningTwo ways to decide which shard owns a key, and what each does to range queries and hot spots.
- Consistent hashingPlace nodes and keys on a ring so adding a node moves about 1/N of the keys, not all of them.
- Hot partitions and hot keysWhen one key gets most of the traffic: detection, key salting, splitting and caching.
- Secondary indexes on sharded dataLocal indexes mean scatter-gather reads; global indexes mean distributed writes.
Caching
Serve repeated reads from somewhere faster, and keep it correct.
- Caching patternsCache-aside, read-through, write-through and write-behind, side by side.
- Eviction policiesLRU, LFU, FIFO and TTLs: deciding what to drop when the cache is full.
- Cache invalidationDelete on write, version your keys, and know the race that leaves stale data behind.
- Cache stampedesOne expiring hot key, a thousand simultaneous misses — and three ways to stop it.
Async work & messaging
Decouple producers from consumers without losing or doubling work.
- Message queues vs event logsRabbitMQ/SQS-style queues delete what they deliver; Kafka-style logs keep it for replay.
- Delivery guaranteesAt-most-once, at-least-once and "exactly-once" — and why the last one needs your help.
- Idempotent consumersProcess a redelivered message twice and still get the effect once.
- Transactional outboxWrite the event in the same database transaction as the change, then relay it.
- Dead-letter queuesPark the message that fails every time so it stops blocking the ones behind it.
- BackpressureLet a slow consumer slow the producer down instead of drowning in a growing queue.
- SagasA multi-service transaction as a sequence of local steps with compensating actions.
- Event sourcing and CQRSStore the events, derive the state — and read from models built for reading.
Reliability
Keep working, or fail gracefully, when the parts around you don’t.
- Timeouts and deadlinesEvery network call needs a bound, and the bound should shrink as the request travels.
- Retries, backoff and jitterRetry only what is safe, wait longer each time, add randomness, and cap the total.
- Circuit breakersStop calling a dependency that keeps failing, and probe it before trusting it again.
- BulkheadsSeparate pools for separate dependencies, so one slow neighbour can’t sink the ship.
- Load sheddingReject some work early and cheaply so the rest still finishes on time.
- Graceful degradationServe a simpler page instead of an error page when a non-critical part is down.
- Rate-limiting algorithmsFixed window, sliding log, sliding counter and token bucket, compared on the same traffic.
Consistency & coordination
Agree on order, ownership and truth across machines that fail independently.
- CAP theoremDuring a network partition, choose between answering and being linearizable.
- PACELCCAP covers partitions; PACELC adds the latency-versus-consistency trade-off the rest of the time.
- Consistency modelsLinearizable, sequential, causal and eventual consistency, from strongest to weakest.
- Clocks and orderingWhy wall clocks can’t order events across machines, and what Lamport and vector clocks do instead.
- Unique ID generationUUIDv4, UUIDv7, Snowflake IDs and ticket servers: uniqueness, ordering and index locality.
- Leader electionPick exactly one node to do a job — and survive the moment two nodes both think it’s them.
- Leases and fencing tokensA lock that expires, plus a number that lets storage reject the holder whose lease already ran out.
- Consensus and RaftHow a majority of nodes agree on one ordered log, and why you rent this from etcd or ZooKeeper.
- Two-phase commitAtomic commit across databases: prepare, then commit — and the coordinator that can block everyone.
- Gossip protocolsSpread membership and state by having each node talk to a few random peers.
Data structures for scale
Compact, approximate and spatial structures that big systems lean on.
- Bloom filtersA few bits per item that answer "definitely not" or "probably yes" to set membership.
- HyperLogLogCount distinct users in 12 KB with about 1% error, and merge counts across servers.
- Count-min sketchApproximate per-key counts in fixed memory, for heavy hitters and trending topics.
- Geohashes and quadtreesTurn latitude and longitude into keys you can index, so "nearby" becomes a range scan.
- Merkle treesHash trees that find which parts of two large datasets differ by comparing a few hashes.
- Inverted indexesMap each term to the documents that contain it — the structure behind full-text search.
Databases & SQL
Writing queries
How a database reads your SQL, and the mistakes that come from reading it differently.
- SQL’s logical query orderFROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY — why an alias works in one clause and not another.
- NULL and three-valued logicNULL means unknown, so comparisons return unknown — and NOT IN with a NULL returns nothing.
- Join algorithmsNested loop, hash and merge joins: how the planner actually matches rows, and when each wins.
- The N+1 query problemOne query for the list, then one more per row — the most common reason a page makes 500 queries.
Transactions & indexes
Keep data correct under concurrency, find it fast, and change the schema without downtime.
- ACIDAtomicity, consistency, isolation and durability: what a transaction promises and who keeps each promise.
- Isolation levels and anomaliesRead committed, repeatable read and serializable — and the dirty reads, lost updates and write skew each one allows.
- MVCCKeep several versions of each row so readers never wait for writers, and clean up the old ones later.
- Optimistic vs pessimistic lockingLock the row before you change it, or check a version number when you write — and when each fits.
- Composite and covering indexesColumn order decides which queries an index can serve; including columns lets it skip the table.
- Zero-downtime migrationsExpand the schema, move the code, backfill, then contract — so old and new code both work mid-deploy.
Backend, Go & Rust
Go
The parts of Go that work differently from the languages most people come from.
- Implicit interfacesA Go type satisfies an interface just by having the methods — so define small interfaces where you use them.
- Errors as values and wrappingReturn errors, add context with %w, and inspect the chain with errors.Is and errors.As.
- Context and cancellationOne value that carries a deadline and a cancel signal through every call a request makes.
- Data racesTwo goroutines, one variable, at least one write and no synchronization — and how the race detector finds it.
Rust
Ownership, borrowing and the type-system ideas that let Rust be fast and memory-safe without a garbage collector.
- Ownership and movesEvery Rust value has exactly one owner; assigning or passing it moves ownership, and the owner’s scope decides when it’s freed.
- The borrow checkerMany shared references or one mutable reference, never both — the compile-time rule behind Rust’s memory and thread safety.
- LifetimesAnnotations like 'a describe how long references stay valid and how outputs relate to inputs; they never extend anything’s life.
- Result, Option and ?No null and no exceptions: absence is Option<T>, failure is Result<T, E>, and ? propagates both in one character.
- Static vs dynamic dispatchGenerics compile a copy per type and call directly; trait objects share one copy and call through a vtable.
- Interior mutabilityMutate through a shared reference with Cell, RefCell, Mutex or atomics — the aliasing rule still holds, checked at runtime.
- Send and SyncTwo auto traits that say which types may move to or be shared with other threads — why safe Rust has no data races.
- Lazy futures and async runtimesA Rust future does nothing until polled; a runtime such as Tokio polls it and wakes it when I/O is ready.
Web services
Handling requests, signing users in, and running a service people depend on.
- MiddlewareWrap a handler in another handler to add logging, auth or recovery to every request.
- Connection poolingReuse a bounded set of database connections — and size the bound from latency, not guesswork.
- Sessions vs tokensA random ID the server looks up, or a signed token the server verifies — and what each costs at logout.
- Password hashingStore a slow, salted hash — Argon2id or bcrypt — never the password and never a fast hash.
- Liveness and readiness checksOne probe asks “should I restart you?”, the other “should I send you traffic?” — mixing them up causes outages.
- Graceful shutdownOn SIGTERM, stop taking new work, finish what’s in flight, then exit — so deploys drop no requests.
- Logs, metrics and tracesThree kinds of telemetry and the question each answers: what happened, how much, and where the time went.
Frontend
The browser
What the browser does with your HTML, CSS and JavaScript.
- The event loopOne thread, a task queue and a microtask queue — why a promise callback runs before a setTimeout.
- How the browser renders a pageParse, style, layout, paint and composite — and which CSS changes trigger which steps.
- The cascade and specificityWhen two rules disagree: origin, layers, specificity, then order — and why !important is a smell.
- Event bubbling and delegationEvents travel up the tree, so one listener on a parent can handle every child — even ones added later.
- Same-origin policy and CORSThe browser keeps pages from reading other origins’ responses; CORS headers are how a server opts in.
- The accessibility tree and ARIAWhat a screen reader actually sees: roles, names and states — and why native elements beat ARIA.
Building apps
How React renders, where state belongs, and what ships to the user.
- Rendering and reconciliationA state change re-runs your component; React diffs the result and touches only what changed.
- Derived state vs effectsIf you can compute it during render, don’t store it and don’t sync it with an effect.
- Server state vs client stateData you fetched is a cache of someone else’s truth; treat it like one instead of copying it into useState.
- Narrowing and discriminated unionsLet TypeScript prove which case you’re in from a check you already wrote, and catch the case you forgot.
- CSR, SSR, SSG and hydrationRender in the browser, on each request or at build time — and the hydration cost of doing both.
- Tree shaking and code splittingShip only the code that’s used, and only when it’s needed.
- Core Web VitalsLCP, INP and CLS: loading, responsiveness and visual stability, measured on real users at the 75th percentile.
AI engineering
Models & prompting
How a language model reads and writes, and how to get the output you asked for.
- Tokens and tokenizationModels read and bill in sub-word tokens, not characters or words — and that explains a lot of odd behaviour.
- Context windowsEverything the model sees on one call — prompt, history, documents and its own output — has to fit.
- Temperature and samplingThe model outputs probabilities; temperature and top-p decide how adventurously the next token is picked.
- Few-shot promptingShow the model two or three worked examples instead of describing the format in words.
- Structured outputsGet JSON that matches a schema every time — and still validate it, because valid isn’t the same as correct.
- The tool-use loopThe model asks for a tool call, your code runs it and sends the result back, and the loop repeats until it answers.
Embeddings & retrieval
Find the right text to put in front of the model.
- Embeddings and cosine similarityTurn text into a vector so “similar meaning” becomes “small angle”.
- Approximate nearest-neighbour indexesHNSW and IVF trade a little recall for searching millions of vectors in milliseconds.
- Chunking documentsSplit documents into pieces small enough to match a question and big enough to answer it.
- Hybrid search and rerankingCombine keyword and vector search, then let a reranker put the best few on top.
LLMs in production
Make it cheaper, safer and measurably better.
- Prompt cachingReuse the model’s work on an unchanged prompt prefix for a fraction of the price and latency.
- Prompt injectionText the model reads can carry instructions; any tool it can call is reachable by whoever wrote that text.
- LLM-as-judge evaluationGrade open-ended outputs with a model and a rubric — after checking the grader agrees with you.
- Decision modelsJev, Clef and similar models return a probability for each allowed answer instead of text — fast routing, scoring and yes/no gates.
- QuantizationStore weights in 8 or 4 bits instead of 16, so a model fits on a smaller GPU at a small cost in quality.
Security & operations
Application & cloud security
The attacks that matter most and the controls that stop them.
- STRIDESix questions — spoofing, tampering, repudiation, disclosure, denial of service, elevation — to ask of every part of a design.
- Injection and parameterized queriesNever build a query or command by pasting in user input; pass the input as a parameter instead.
- XSS and Content Security PolicyUntrusted text rendered as HTML becomes the attacker’s script; escaping prevents it and CSP limits the damage.
- TLS and certificatesHow a client proves it’s talking to the real server and agrees a key no one else can read — and what mTLS adds.
- Least privilegeGive each person, service and job only the access it needs, for only as long as it needs it.
- Short-lived credentials with OIDCLet CI jobs and services trade a signed identity token for temporary cloud credentials — no stored keys.
- SBOMs and build provenanceA list of what’s inside an artifact, and a signed record of where and how it was built.
Running systems
Containers, controllers, deploys and recovery.
- Namespaces and cgroupsA container is a normal Linux process with a restricted view (namespaces) and a budget (cgroups).
- Desired state and reconciliationDeclare what should exist; a controller keeps comparing it with what does and closes the gap.
- Rolling, blue-green and canary deploysThree ways to replace running code, and how each limits how many users a bad release reaches.
- RPO and RTOHow much data you can afford to lose and how long you can afford to be down — the two numbers behind every backup plan.
Git
How Git works
The few data structures underneath every Git command.
- Git’s object modelBlobs, trees and commits, each named by the hash of its content — a snapshot store, not a diff store.
- Working tree, index and HEADThree copies of your files — and every “undo” command is about which of them it changes.
- Branches, refs and HEADA branch is a file holding one commit hash; HEAD says which branch you’re on.
- The reflogGit’s record of everywhere HEAD and each branch has pointed — how you get back a commit you “lost”.
Merging & history
Combine work, keep history useful, and find where it went wrong.
- Fast-forward vs merge commitsIf the target hasn’t moved, Git just slides the pointer; otherwise it records a commit with two parents.
- Three-way merges and conflictsGit compares both sides with their common ancestor; a conflict is a line both sides changed.
- Merge vs rebaseKeep the true shape of history, or replay your commits on top for a straight line — and never rebase shared commits.
- git bisectBinary-search history for the commit that broke something: 1,000 commits take about 10 tests.
- Trunk-based developmentEveryone merges small changes to main at least daily and hides unfinished work behind flags.
Engineering practice
Algorithms & complexity
How to reason about cost, and the techniques most problems reduce to.
- Big-O notationHow an algorithm’s time or memory grows with input size, ignoring constants — so you can rule out approaches before running them.
- Hash tablesHash a key to pick a bucket for average O(1) insert, lookup and delete — the structure behind maps, sets and caches.
- Graph traversal: BFS and DFSVisit reachable nodes level by level with a queue or path by path with a stack — shortest paths, components and topological order.
- Dynamic programmingSolve overlapping subproblems once and reuse the answers: define the state, the transition, the base cases and the order.
Code design
The rules of thumb that keep code readable, simple and cheap to change — and where each one stops helping.
- Coupling and cohesionKeep what changes together in one place and minimise what each part knows about the others.
- Guard clauses and the arrow anti-patternCheck failure conditions first and return early, so the main path stays unindented instead of vanishing into nested ifs.
- DRY: don’t repeat yourselfEvery piece of knowledge needs one authoritative home — duplicated rules, not code that merely looks alike.
- KISS: keep it simplePrefer the design with the fewest moving parts that meets the requirements; obvious code beats clever code.
- YAGNI: you aren’t gonna need itDon’t build features or generality before a real requirement needs them — but keep the tests and structure that make adding them cheap.
- Command–query separationA method either answers a question or changes state, not both — so asking never changes the answer.
- Law of DemeterTalk only to immediate collaborators: ask an object to do something instead of reaching through its internals.
- Composition over inheritanceBuild behaviour by combining small objects that delegate to each other, rather than extending class hierarchies.
- Code smellsSurface signs — long functions, data clumps, feature envy, shotgun surgery — that hint at a design problem worth refactoring.
SOLID principles
Five principles for modules that bend instead of break, in Go and TypeScript.
- Single responsibility principleA module should have one reason to change — it answers to one actor — so changes requested by different people don’t collide.
- Open–closed principleAdd new behaviour by adding code rather than editing tested code, at the points where variation is real.
- Liskov substitution principleEvery implementation must honour the interface’s full behavioural contract, so callers can use any of them safely.
- Interface segregation principleClients shouldn’t depend on methods they don’t use: prefer small, client-specific interfaces.
- Dependency inversion principleBusiness logic owns the interfaces it needs and infrastructure implements them, so dependencies point toward policy.
Testing practice
How test-driven work is paced, what mix of tests to write, and what to put in place of real dependencies.
- Red, green, refactorThe TDD loop: write a failing test, make it pass with the simplest code, then clean up with the tests green.
- The test pyramidMany fast unit tests, fewer integration tests and a handful of end-to-end tests — and why the opposite shape fails.
- Test doubles: stubs, spies, mocks and fakesThe five kinds of stand-in for a real dependency, and why outcome-based fakes usually beat interaction mocks.
Architecture & domain modeling
Structure a codebase around the business: modules, layers, contexts and the models inside them.
- Package by featureOrganise code by business capability instead of technical layer, so a change stays in one place and boundaries can be enforced.
- Ports and adaptersHexagonal architecture: the application defines ports and the outside world plugs in through adapters.
- Ubiquitous languageOne shared vocabulary for developers and domain experts, used in conversation, documents and the code.
- Bounded contextsA boundary inside which one model and its language are consistent — the main tool for splitting large systems.
- AggregatesA cluster of domain objects changed as one unit through a root that enforces its invariants — DDD’s consistency boundary.
- Value objectsSmall immutable types defined by their values — Money, Email, DateRange — that validate themselves and replace primitives.
Laws of software engineering
The named rules of thumb engineers quote — what each one claims, and where it holds.
- Conway's lawA system’s structure ends up mirroring the communication structure of the organization that builds it.
- Wirth's lawSoftware gets slower faster than hardware gets faster, so the speed users feel tends to stand still.
- Hyrum's lawWith enough users, every observable behavior of your API is depended on by someone, whatever the contract says.
- Goodhart's lawWhen a measure becomes a target, it stops being a good measure, because people optimize the number, not the goal.
- Cunningham's lawThe fastest way to get a right answer is often to post a wrong one: people correct more readily than they answer.
- Mooers's lawPeople won’t use an information system when having the information is more painful than not having it.
- Linus's law“Given enough eyeballs, all bugs are shallow” — the open-source claim about review, and why the evidence for it is thin.
- Brooks's lawAdding people to a late software project makes it later, because new people cost time before they save any.
- Hofstadter's lawIt always takes longer than you expect, even when you take Hofstadter’s law into account.
- Eagleson's lawCode you wrote six months ago might as well be someone else’s, so write it for a reader who has none of your context.