skip to content

Computer science fundamentals

18 roadmaps951 questionsupdated

Logic and proofs, automata and compilation, information and coding, complexity classes, concurrency, memory and serialization. The theory interviewers use to see how you reason without a framework.

on this pageshow

guide

overview

~2 min

Computer science fundamentals is the theory beneath the tools: the part of a round where no framework, library or search result answers for you. Interviewers use it to see whether you reason from first principles. Can you say why a validation pattern cannot check nested brackets, why no compressor shrinks every file, why a shared counter loses updates, why a collected heap still grows, or why adding a field broke a reader running the previous release? They rarely want a formal proof. They want you to recognise which limit or guarantee is in play and to state it exactly, neither weaker nor stronger than it is. The hub has seven sections in two groups. Four are theory that shapes how you argue: [discrete mathematics](/topics/found-discrete-math) supplies logic, proof, counting, relations and graphs; [automata and languages](/topics/found-automata-languages) classifies what patterns, grammars and parsers can recognise and follows source text through translation; [information theory](/topics/found-information-theory) sets the floor on size and the price of reliability; [complexity classes](/topics/found-complexity-classes) separates feasible problems from hard and impossible ones. Three are theory you meet in running systems: [concurrency](/topics/found-concurrency), the largest section and the one backend rounds reach most often, covers threads, locks, races, memory models and async designs; [memory management](/topics/found-memory-management) covers where values live and who reclaims them; [serialization](/topics/found-serialization) covers bytes on the wire, how they change without breaking readers, and what decoding them costs. Start with logic, because every other section states its claims as implications, bounds and invariants. Then take memory management and concurrency together, since threads, stacks and a shared heap are one picture, and serialization after them. The remaining theory sections can be taken in any order once you can argue precisely; complexity classes is the one to reach first if you design or estimate algorithms for a living. Questions run from junior definitions to principal-level judgement about which guarantee a design actually relies on, so expect to return to a section as your level rises rather than treating it as done.

primer

A few ideas recur across all seven sections. Hold these and most questions below read as applications rather than facts to memorise. ### Say exactly what is promised Much of this subject is the gap between a guarantee and the stronger claim people round it up to. An implication is not its converse. A lock promises exclusion, not fairness. Membership in NP describes checking, not solving. An approximation ratio bounds every run, not the average one. A compatibility rule names which reader can decode which writer. Interviewers listen for whether you state a promise at its real strength, and they follow up on the word you inflated. ### Many limits are counting arguments Several of the hub's impossibility results come from comparing the sizes of two sets. There are more long inputs than short outputs, so a lossless scheme cannot shrink them all; there are more keys than fingerprints, so collisions are forced; a machine with finitely many states cannot tell apart infinitely many nesting depths. Recognising a limit early saves you from designing something that cannot exist, and the argument is usually short enough to give out loud. ### Power comes from memory What a model can remember decides what it can recognise or compute. Finite state handles flat patterns; adding a stack handles nesting; unbounded read-write storage reaches general computation and, with it, questions no program can settle. Complexity theory asks the same question in quantities instead of kinds: how much time or space a problem needs as the input grows, and whether a proposed answer is cheaper to check than to find. ### Hardness moves along reductions To show a problem is hard, you translate a problem already known to be hard into it; to show it is easy, you translate it into something already solved. The direction is the whole argument. It is also a practical skill: strip a requirement down to its structure and see whether it is a known hard problem wearing domain vocabulary. ### Shared state needs an agreed order Concurrency bugs come from steps that interleave in ways the author did not picture, and from writes one thread makes that another does not yet see. The primitives in the hub, from locks and atomics to channels and structured scopes, are ways of imposing order: making a compound action indivisible, establishing which write comes before which read, or removing sharing altogether. ### Lifetime follows placement and reachability A value's lifetime depends on where it is placed and who can still refer to it. Stack frames end with their call; heap objects live until something decides they are dead, by tracing from roots, by counting owners, or by compile-time rules about who owns what. Each scheme has a characteristic failure: cycles, pauses, fragmentation, or memory that stays reachable long after it stops being useful. ### Bytes outlive the code that wrote them Once a value is serialized, it may be read by a different process, machine, language or release than the one that wrote it. That makes the wire form a contract: it must stand on its own, it must evolve without stranding old readers or old data, and its decoder must treat every input as potentially hostile.

Contrapositive
The restatement of 'if P then Q' as 'if not Q then not P'. It is always equivalent to the original; the converse and the inverse are separate claims.
Pigeonhole principle
Placing more items than there are boxes forces two items into one box; the standard argument behind forced collisions and many impossibility results.
Equivalence relation
A relation that is reflexive, symmetric and transitive, splitting a set into disjoint classes; the property any equality used for grouping or hashing must have.
Regular language
A set of strings some finite automaton recognises. Classical regular expressions describe exactly these, and none can require arbitrarily deep nesting to balance.
Context-free grammar
Rules that rewrite one symbol into a sequence of symbols; recognisable by a finite control plus a stack, and the usual way to describe nested syntax to a parser.
Undecidable problem
A yes-or-no question that no algorithm answers correctly on every input while always halting; whether an arbitrary program terminates is the standard example.
Certificate
A short piece of evidence that a yes-answer is correct, checkable in polynomial time. Having one for every yes-instance is what places a problem in NP.
Polynomial-time reduction
An efficient translation of one problem's instances into another's that preserves the answer, used to carry hardness or tractability from one problem to the other.
NP-complete
In NP and at least as hard as every problem in NP under polynomial-time reductions, so a polynomial algorithm for any one would give one for all.
Entropy
The average information per symbol of a source, in bits; the lower bound on the average code length of any lossless encoding of that source.
Error-correcting code
Redundancy structured so a receiver can locate and repair a bounded number of corrupted bits, instead of only detecting that something changed.
Data race
Two threads accessing the same memory with no ordering between them, at least one of them writing; the result depends on timing and in many memory models is undefined.
Happens-before
The ordering a memory model guarantees between actions. When a write happens before a read of the same location, the read cannot observe an older value than that write.
Deadlock
A set of threads each blocked on a resource another member holds, so none proceeds; it needs mutual exclusion, hold-and-wait, no preemption and a circular wait together.
Root set
The references a tracing collector starts from, such as thread stacks, registers and globals; anything not reachable from them can be reclaimed.
Live set
The memory still reachable after a collection finishes. Its trend over time, not peak usage, shows whether a program is retaining more than it should.
Backward compatibility
For a wire schema, readers on the new version can still decode data written under an older one. Forward compatibility is the mirror case: old readers, new data.
Self-describing encoding
A wire format that carries field identities and types alongside the values, so any reader can parse it without first obtaining a separate schema.

The seven sections are separate topics, but they share more machinery than their names suggest. **Discrete mathematics is the notation.** Logic gives the form of every guard, precondition and specification. Relations and orders underlie equality, hashing and sorting contracts. Graph theory returns as dependency graphs, state machines and the wait-for graph behind a deadlock. Counting and modular arithmetic supply collision bounds, hashing and the arithmetic of checksums. **Automata and complexity are two views of computational power.** [Automata and languages](/topics/found-automata-languages) sorts problems by the kind of memory a recogniser needs; [complexity classes](/topics/found-complexity-classes) sorts them by how much time or space they take. Both end at the same wall: [reductions and decidability](/topics/found-complexity-classes-reductions) show that some questions have no algorithm at all, which is why a checker of a nontrivial behavioural property has to accept some mix of false alarms, missed cases and non-answers. [Syntax analysis](/topics/found-automata-languages-parsing) ties the theory back to practice, because every decoder, configuration loader and compiler front end is a recogniser for some grammar. **Information theory meets the wire.** Serialization decides how many bytes a value costs, entropy says how few it could cost, and compression and integrity checks are the layer between the two. The line between detecting accidental corruption and resisting deliberate tampering is where these sections meet security. **Concurrency and memory management share one runtime.** Threads share a heap but each owns a stack, so what lives where decides what can race. A tracing collector has to find roots in every running thread, reference counts shared across threads need atomic updates, and the memory model decides when one thread's write to the heap becomes visible to another. Many concurrency questions are memory questions asked with two threads. **Serialization is where memory meets the network.** An in-memory value holds addresses and layout that mean nothing outside its process, so it has to be re-expressed before it leaves. The decoder that reverses this is a parser, with a parser's costs, and a decoder facing bytes from an unknown sender is an [attack surface](/topics/found-serialization-security), which ties it back to bounds on time and space.

  1. Symbolic Logic →

    Implication, negation and equivalence are the form every guarantee in this hub takes; misreading them is the usual way a guarantee gets overstated.

  2. Stack vs Heap →

    Where a value lives decides its lifetime and who can reach it, which the concurrency, collection and serialization sections all assume.

  3. Threads and Processes →

    The execution units and what they share; without this picture, races, locks and memory models have nothing to attach to.

  4. Race Conditions and Deadlocks →

    The failures every synchronization primitive exists to prevent. Learn to name the bad interleaving before the tools that forbid it.

  5. Wire Form Model →

    Why an in-memory value cannot simply be copied out, and the text-versus-binary and schema choices later serialization questions build on.

  6. Nondeterminism and Certificates →

    What NP does and does not claim: the most often misstated idea in the theory sections, and the entry point to hardness and reductions.

  • Reading 'if P then Q' as also promising its converse; only the contrapositive is guaranteed, and guard conditions and alert rules are where the slip shows.

  • Saying NP means exponential time, or calling a problem NP-hard after translating it into SAT; that direction only bounds it from above.

  • Proposing one regular expression to validate arbitrarily nested input; unbounded depth needs a stack, which means a parser rather than a pattern.

  • Treating a matching CRC or checksum as proof a message was not altered; it catches noise, and anyone who edits the payload can recompute it.

  • Making each shared variable atomic when the invariant spans several of them; a compound check-then-act still needs one critical section around all of it.

  • Checking a condition-variable predicate once after waking instead of re-testing it in a loop; a wakeup only means the state may have changed.

  • Acquiring the same locks in different orders on different code paths; one consistent global order removes the circular wait a deadlock needs.

  • Assuming a garbage collector rules out leaks; anything still reachable is kept, so unbounded caches and registries leak in collected runtimes too.

  • Waiting in asynchronous tests with fixed sleeps instead of a completion signal, which makes the suite slow and flaky at the same time.

  • Renaming, renumbering or retyping a wire field without checking how readers identify fields; the change can look harmless while old readers silently lose the value.

The same handful of choices appears under different names across the sections; saying which one you are making is often the substance of a senior answer. - **Expressive power versus analysability.** A more powerful formalism describes more and lets you prove less. Regular languages admit linear-time matching by an automaton, grammars need a parser, and general computation makes most interesting questions about behaviour undecidable. Formats and rule languages kept deliberately weak are easier to validate and to bound. - **Exact versus approximate.** Lossy encoders, approximation algorithms and heuristics for intractable problems give up exactness for size or speed. What decides is who consumes the result: a person viewing media tolerates error, while a signature check or a ledger does not. - **Detection versus correction.** Redundancy can tell you a message changed or let you repair it. Correction spends more bits on every message; detection spends a retransmission when it fires. The error rate and the cost of asking again decide which is cheaper. - **Locks versus lock-free versus no sharing.** Locks are the easiest to reason about and serialize the work they guard; atomics and lock-free structures avoid blocking but are hard to get right; message passing and isolation remove shared mutable state at the price of copying and coordination. - **Reclamation cost now versus later.** Tracing collection defers the work and pays in pauses or background overhead; reference counting pays on every handle change and needs help with cycles; compile-time ownership pays mainly in design effort and in limits on how data may be shared. - **Self-describing versus schema-driven.** Carrying field identity with each value makes bytes readable by anyone and larger; relying on a shared schema makes them compact and cheap to decode, and turns the schema into something you must distribute and version.

A few argument shapes recur across the sections under different names; recognising one tells you how an unfamiliar question is likely to be answered. - **Count and compare.** Pigeonhole and cardinality arguments explain forced hash collisions, the impossibility of universal compression, and why finite memory cannot track unbounded nesting. - **Assume a perfect decider, derive a contradiction.** Self-reference turns a hypothetical flawless analyser against itself; the same skeleton shows that many properties of programs cannot be decided in general. - **Translate to something known.** Reductions carry hardness between problems, grammars are rewritten into forms a parsing technique accepts, and a business requirement becomes tractable once restated as a familiar problem. - **Keep a stack for nesting.** Pushdown recognisers, recursive-descent parsers, call stacks and bracket matching share one idea: memory that grows and shrinks with depth. - **Ask what is reachable.** Tracing collection, leak diagnosis and deadlock detection in a wait-for graph all come down to a graph traversal and a question about what is reachable or cyclic. - **Impose one order.** Lock ordering, happens-before edges, schema versions and message sequence numbers all prevent trouble by making one ordering the agreed one. - **Spend redundancy for robustness.** Parity bits, checksums, error-correcting codes and optional fields with defaults each add a little to every message so that damage or change can be tolerated.

explore

→ has its own guide

report an issue with this guide →

questions

951 · 7 sections

In a replication mesh where each link joins two machines, why does the sum of machines' link counts equal twice the link count?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Every link has two ends, and each end raises exactly one machine's link count by one. Totalling link counts therefore counts every link twice. This is the handshake lemma: the degree sum equals 2E, so it is always even.

open as a page

A filter allows a request when authenticated AND (hasRole OR isServiceCall); what is the equivalent reject condition with the negation pushed inward?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Reject when NOT authenticated OR (NOT hasRole AND NOT isServiceCall). De Morgan's laws flip each connective as the negation moves inward: the outer AND becomes OR, the inner OR becomes AND, and every atom is negated.

open as a page

A spec says every queued job is eventually acknowledged: what refutes that claim, and what do passing runs establish?

level: juniorimportance: must knowfreq 62%
basics
~20 s

One queued job that is never acknowledged refutes the claim completely; a single counterexample is a full disproof. Passing runs only fail to find one. They establish the claim only if they exhaust a finite domain.

open as a page

An alert rule says 'if the retry budget is exhausted, the request fails' - which restatement of it is guaranteed to hold?

level: juniorimportance: must knowfreq 72%
basics
~10 s

Only the contrapositive: 'if the request succeeded, the retry budget was not exhausted'. The converse and the inverse are different claims that can be false while the original rule still holds.

open as a page

A service truncates each key's digest to 32 bits as a fingerprint; why are collisions eventually unavoidable?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A 32-bit fingerprint has only 2^32 possible values, so any 2^32 + 1 distinct keys must contain two that share one. That is the pigeonhole principle; a better function cannot avoid it, only a wider field delays it.

open as a page

A deterministic finite acceptor reads a digit string one symbol at a time — what decides that it accepts?

level: juniorimportance: must knowfreq 66%
basics
~20 s

One fact decides it: the state the machine is left in after the final symbol. If that state is in the accepting set, the string is accepted. Passing through an accepting state earlier in the run means nothing.

open as a page

In a grammar written down as production rules, what distinguishes a terminal from a nonterminal?

level: juniorimportance: must knowfreq 60%
basics
~10 s

Terminals are the literal symbols that appear in the generated text; nonterminals are named placeholders that some rule rewrites. A derivation starts at one designated start symbol and rewrites nonterminals until only terminals remain.

open as a page

In a compiler front end, what is a token, and what does the scanner throw away to produce one?

level: juniorimportance: must knowfreq 65%
basics
~20 s

A token is one lexical unit: a class such as identifier, number, keyword, operator or punctuator, together with its text and the source span it came from. The scanner throws away whitespace and comments but keeps the spans.

open as a page

In a regular expression, what is the difference between a greedy quantifier and a lazy one?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A greedy quantifier takes as many repetitions as it can and gives characters back only when the rest of the pattern fails; a lazy one takes as few as possible and grows one at a time.

open as a page

In a route-matching pattern, what does the Kleene star applied to a subexpression mean?

level: juniorimportance: must knowfreq 72%
basics
~20 s

The Kleene star means zero or more repetitions of the subexpression it follows, joined end to end. Zero is the trap: a starred part can match the empty string, so on its own it never forces that text to be present.

open as a page

Run-length encoding replaces repeats with count-symbol pairs, so on what input does it make the output larger?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Run-length encoding expands any input whose symbols rarely repeat: a single symbol still costs a count plus the symbol, so alternating bytes double in size. It pays only when the average run is longer than one pair.

open as a page

What does the Shannon entropy of an event log's status field, measured in bits, actually tell you?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Shannon entropy is the average surprisal of a field's values in bits: the sum of p times log2(1/p) over every value. It measures how uncertain the next value is, not how many distinct values exist.

open as a page

In information terms, an alert that fires in one minute out of 1024 carries ten bits of surprisal - what does that number mean?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Ten bits is the surprisal of an outcome with probability 1/1024, since -log2(1/1024) = 10. One bit is the uncertainty resolved by one fair yes/no answer, so rarer outcomes carry more bits and a certain one carries zero.

open as a page

A compression tool claims it makes every possible input file smaller — why is that claim impossible?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Lossless compression must be reversible, so distinct inputs need distinct outputs. There are more n-bit inputs than shorter strings, so no map can shrink them all; a scheme that shrinks one input must expand another.

open as a page

A preview pipeline offers a lossy encoder for every stored artifact; which payload classes must refuse it, and why?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Anything whose value is its exact bits must stay lossless: executables, archives, source, ledgers, and anything later checked by a digest or signature. Lossy fits terminal media a human tolerates approximately, never the master copy everything else is derived from.

open as a page

An unfamiliar requirement is written in business language; how do you check whether it is a known hard problem in disguise?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Strip the domain nouns and restate the requirement as objects, a relation between them, and an objective. That skeleton — cover everything, pick a subset under a budget, order with no repeats — is what you match against the known catalogue.

open as a page

Why can a conflict graph be checked for two-colourability in linear time when three-colourability is NP-complete?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Two colours leave no freedom: fix one vertex and propagation forces every other, so one traversal either succeeds or exposes an odd cycle. A third colour restores a choice at each vertex, and those choices interact globally.

open as a page

A teammate says a task is in NP and therefore needs exponential time. What does NP membership actually claim?

level: juniorimportance: must knowfreq 74%
basics
~20 s

NP membership claims only that a yes-answer has a short certificate a checker can validate in polynomial time. It says nothing about how long finding that answer takes, and it does not exclude an easy problem.

open as a page

Why can no tool flag exactly the programs that loop forever, however sophisticated its analysis becomes?

level: juniorimportance: must knowfreq 66%
basics
~10 s

Deciding whether an arbitrary program stops is undecidable, so no checker can be always-terminating, never-wrong and complete at once. Every real loop detector therefore misses cases, raises false alarms, or sometimes fails to answer.

open as a page

Why does complexity theory draw the line for 'efficient' at polynomial running time rather than at a fixed step budget?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Polynomial time is a claim about scaling, not about one machine. A polynomial bound survives a change of machine model and stays polynomial when routines call each other; a fixed step budget expires with the next hardware or the next larger input.

open as a page

What is a coroutine, and what actually happens when one suspends, compared with an operating-system thread that blocks waiting for I/O?

level: juniorimportance: must knowfreq 58%
basics
~20 s

A coroutine is a function that can pause partway through and resume later. Suspending saves its state, hands control back to a scheduler, and frees the underlying thread for other work. A blocked thread keeps its whole stack and OS slot while doing nothing.

open as a page

What is an event loop, and what does run-to-completion mean for the callbacks it dispatches?

level: juniorimportance: must knowfreq 60%
basics
~20 s

An event loop is a single thread cycling forever: wait for ready events, take the next task from a queue, run its handler to completion, repeat. No handler is ever interrupted mid-way by another handler, so loop-owned state needs no locks — but a slow handler delays everything behind it.

open as a page

In asynchronous programming, what is the distinction between a future and a promise, and why do many libraries hand out two separate objects for a single asynchronous result?

level: juniorimportance: must knowfreq 62%
basics
~20 s

A future is the read side of a result that is not ready yet: you await it or attach a continuation. A promise is the write side: the producer completes it once, with a value or an error. Splitting them stops consumers from completing results.

open as a page

Two threads each run the statement `count = count + 1` a thousand times on the same shared variable, and the final total is less than two thousand. Explain why, and what it means for an operation to be an atomic read-modify-write.

level: juniorimportance: must knowfreq 78%
basics
~20 s

The statement is three steps: read, add one, write back. Two threads can read the same old value and both write the same new value, so one increment is lost. An atomic read-modify-write performs all three steps as one indivisible hardware operation that no other thread can interleave with.

open as a page

A worker thread spins in a loop reading an ordinary boolean 'stop' variable while another thread sets it to true and then exits. Sometimes the worker never leaves the loop. Explain how that is possible even though the write definitely executed.

level: juniorimportance: must knowfreq 58%
basics
~20 s

Nothing orders the write against the read, so the reader has no obligation to observe it. The compiler may load the variable once into a register and loop on that copy, and the write may sit in the writer's store buffer. Publish the flag through a synchronization edge.

open as a page

A managed heap split into a young area and an older area rests on what observation about object lifetimes?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Most objects die very young — the weak generational hypothesis. Splitting the heap by age lets a collector sweep the young area often and cheaply, where almost everything is already garbage, and touch the older area rarely.

open as a page

In a tracing garbage collector, what is a root, and why must a collection start from the root set?

level: juniorimportance: must knowfreq 68%
basics
~20 s

A root is a reference the collector can find without tracing anything first — a running thread's stack slot or register, a global table entry, a handle registered by code outside the collected heap. Everything the program can still touch starts at one of them.

open as a page

In a runtime that reclaims unreachable memory automatically, how can a program still leak memory?

level: juniorimportance: must knowfreq 74%
basics
~20 s

Automatic reclamation frees only what nothing can reach. A leak there is memory that stays reachable from something live - an unbounded cache, a growing registry, a collection nobody trims - but will never be used again.

open as a page

When you print the memory map of a running process, which regions does it contain and what does each hold?

level: juniorimportance: must knowfreq 76%
basics
~20 s

A process maps several regions with different rules: read-only code and constants, initialized static data, zero-filled static data, one growable heap, one stack per thread, and per-thread thread-local storage. Placement decides lifetime and who may write.

open as a page

What does one frame on a thread's call stack hold, and why does returning from the call release it for free?

level: juniorimportance: must knowfreq 72%
basics
~20 s

A call frame holds that call's return address, saved registers, local variables and spilled temporaries, plus space for outgoing arguments. Returning moves the stack pointer back past the whole frame, so release is one instruction with no bookkeeping.

open as a page

Why does the JSON/XML/YAML family dominate partner-facing API interchange despite producing large, verbose documents?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Ubiquity and human readability win. A parser is already on every platform, so a partner integrates with no shared artifact; a person can read, paste and hand-edit a document; and the text diffs in review. Verbosity is the accepted price.

open as a page

What distinguishes a text encoding from a binary encoding on the wire, and what does each choice cost?

level: juniorimportance: must knowfreq 72%
basics
~20 s

A text encoding writes values as readable characters you can grep, diff and hand-edit; a binary encoding packs them as raw sized bytes that are typically smaller and cheaper to parse, but unreadable without a decoder.

open as a page

Why can a program not write a value's in-memory bytes straight to a file for another process to read later?

level: juniorimportance: must knowfreq 72%
basics
~20 s

An in-memory value is laid out for one running process: it holds machine addresses valid only there, plus alignment holes whose contents are undefined. Another process shares none of that context, so the value must be re-expressed in a flat, self-standing form.

open as a page

In a JSON request body, what is the difference between an absent field, a null field, and an empty-string field?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Absent means the sender said nothing about the field. Null means the sender said it has no value. Empty means it has a value that happens to be zero-length. Three different statements, and many encodings collapse them.

open as a page

A sensor's JSON telemetry record is re-encoded in MessagePack or CBOR: what changes, and what stays the same?

level: middleimportance: must knowfreq 62%
basics
~20 s

The data model survives; only the syntax is replaced. Maps, arrays, strings, numbers, booleans and null become tagged bytes instead of punctuation and decimal text, so records shrink and decode cheaper — but every key name still travels on every record.

open as a page
Computer science fundamentals interview questions & primer · KataJob