Portfolio & Documentation
Make the work legible to the people hiring you.
Mastery · Markets, Strategies & Getting Hired
Why this week
A hiring manager will give your repository five to ten minutes. In that time they need to answer three questions: what is this, is it real, and does this person think clearly? A year of excellent work behind a vague README and a wall of crates fails all three. The same work with a one-paragraph summary, a screenshot of the live terminal, a one-page architecture overview and a benchmark report that states its machine and method passes all three before they've opened a source file.
This week is about legibility. You'll write the README and the architecture overview, generate the benchmark report from data instead of typing numbers, make the code document itself where it matters, and shape the ten-minute story you'll tell in interviews. Two drills turn documentation habits into checks: lint an architecture document for the mistakes reviewers spot first, and render benchmark comparisons that refuse to compare what can't be compared.
Write for a skeptical expert in a hurry. Every claim should be one click from its evidence: a number links to the report, the report names its machine and commit, and the architecture overview links to the crate that implements each box.
Day 1 — The README
The first screen decides
What it is, that it's real, how to run it
The first screen of the README, before any scrolling, should contain:
- One paragraph: what the system is, at what scale and speed, and why you built it. "A price-time-priority exchange in Rust: gateway, pre-trade risk, a single-threaded matching engine with a replicated journal, and A/B market data. ~X M orders/s and Y µs p99 order-to-ack on a tuned host" (with the numbers linked to the report).
- Proof it runs: a screenshot or short recording of the GPUI terminal running your engine, or of the deployed demo's page, plus the demo link.
- Quick start in three commands or fewer, and you've tested them on a clean machine.
Below the fold: the headline performance table (generated, see Day 3), the architecture overview link, a crate map (one line per crate, in data-flow order), how to run the tests and benchmarks, what's deliberately out of scope (no clearing, one asset class, simulated members), and the licence.
The Diátaxis framework is a useful check on everything else you write. A tutorial, a how-to guide, a reference and an explanation are different documents. Mixing them is why most READMEs are hard to use. The README is mostly explanation plus one how-to (quick start). Everything else gets linked.
The four kinds of documentation and why mixing them hurts. Sort every document you plan to write this week into one of the four, then cut whatever belongs elsewhere.
A short argument for one architecture file that gives a newcomer the map: the coarse-grained modules, where things live, and the invariants, while avoiding details that rot. That's the shape of Day 2's document.
What must a hiring reader be able to learn from your README's first screen, without scrolling, and why those things?
Draft the first screen of your README: the one paragraph, the image and the quick start. Then cut the paragraph to 60 words without losing a number.
Day 2 — The one-page architecture overview
A map, not a tour
Components, threads, flows and invariants on one page
The overview answers where does an order go, who owns what, and what must always be true? Use the top two levels of the C4 model: context (the exchange, its members, market-data consumers) and containers/components (gateway, risk, engine, journal, market data, the demo and the terminal). One diagram, one paragraph per component, and an explicit invariants section:
- Every component has an owner thread. State is mutated by exactly one thread. Name it.
- Every arrow that crosses a thread boundary is a queue, and you name its type (the week 5 ring, the week 16 broadcast ring). A direct call across threads in the diagram is either a bug in the diagram or a lock in the code. Conversely, a queue between two components on the same thread is a hop you're paying for nothing.
- The order path is one line: gateway → risk → engine → market data. Readers anchor everything else to it. The repo's README diagram draws gateway → risk → journal → engine → marketdata, because the journal sequences and appends each command before the engine applies it, and a week 51 test checks that the diagram draws exactly that path.
- Decisions have records. For the three biggest choices (single-threaded matching, ack-after-replication, the wire format) write a short architecture decision record: context, decision, consequences.
Keep the diagram as code (Mermaid renders directly in GitHub markdown) so it's diffable and reviewable like everything else, and generate the component table from the same data if you can. Then the drill's checks can run in CI.
Use levels 1 and 2 only. Note the advice to label every arrow with what flows along it and how. For you that's 'NewOrder via SPSC ring', not just a line.
The original ADR post: title, context, decision, status, consequences. Write three for your exchange. An interviewer who asks 'why single-threaded?' gets the ADR's answer, alternatives included.
The drill encodes the overview as data and lints it the way a careful reviewer would. Then you derive the order path from the flows instead of typing it.
Write the one-page architecture overview
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Transport {
Call, // a direct function call: only valid within one thread
Queue, // an in-process queue (SPSC ring, broadcast ring, ...)
Tcp,
Multicast,
}
#[derive(Debug, Clone)]
pub struct Component {
pub name: &'static str,
pub crate_name: &'static str,
pub thread: &'static str, // owning thread; "" = not stated
}
#[derive(Debug, Clone)]
pub struct Flow {
pub from: &'static str,
pub to: &'static str,
pub via: Transport,
}
#[derive(Debug, Clone)]
pub struct ArchDoc {
pub entry: &'static str, // where orders enter
pub components: Vec<Component>,
pub flows: Vec<Flow>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Issue {
/// The entry or a flow endpoint isn't a listed component (each name once).
UnknownComponent(&'static str),
/// A component with thread == "".
NoOwnerThread(&'static str),
/// A Call between two components on different (stated) threads.
CallAcrossThreads(&'static str, &'static str),
/// A Queue between two components on the same (stated) thread.
QueueWithinThread(&'static str, &'static str),
/// A listed component not reachable from `entry` by following flows
/// between listed components (all of them, if the entry is unknown).
Unreachable(&'static str),
}
/// Every issue, each once, in any order.
pub fn lint(doc: &ArchDoc) -> Vec<Issue> {
let _ = doc;
todo!()
}
/// The order path: start at `entry`; repeatedly follow the FIRST flow (in
/// list order) from the current component to a listed component not yet on
/// the path; stop when there is none. Empty if the entry isn't listed.
pub fn order_path(doc: &ArchDoc) -> Vec<&'static str> {
let _ = doc;
todo!()
}
Day 3 — The benchmark report
Numbers a stranger can trust
Generated, contextualised, compared like for like
Your performance numbers are the most scrutinised part of the repo, and the easiest to get wrong. Rules for the report:
- Generate it.
cargo xtask report --markdown PATHrenders the report from the benchmark JSON, andcargo xtask report --from hft-report.json --markdown -re-renders a saved report without running anything. No number is ever typed by hand, so the README can't drift from the data. - State the context on every report: CPU model, core count, kernel, governor and isolation settings, rustc version, build profile, commit hash, date, and whether the run was on the tuned reference machine or a laptop. The generated report records the commit, CPU, core count, OS, rustc and date; state the rest next to it.
- Report distributions, not a number: p50, p99, p99.9 and max with sample counts, using the coordinated-omission-corrected open-loop numbers from week 25. Link the full HDR histogram plots.
- Compare only like with like. Same machine, same profile, enough repetitions. Call a change a change only when it's outside run-to-run noise, and say so when it isn't.
- Say what's missing. Kernel networking rather than bypass, simulated members, one host. Limitations stated up front read as competence; limitations discovered by the reader read as overclaiming.
Analyse the system while the benchmark runs, so you can explain what limits the number. Add one 'what limits this' sentence per headline result to your report.
Rewatch with your report in front of you. Does any headline number come from a closed-loop run, an average, or a percentile without a sample count? Fix it before someone else finds it.
Last month's report says p99 order-to-ack was 41 µs on the reference box; today's laptop run says 30 µs. Can you write '27% faster' in the changelog?
The drill builds the comparison logic a report needs when it sets two runs side by side: medians over repeated runs, a conservative "ranges must not overlap" rule for calling a change, three-significant-figure formatting, and an explicit refusal to compare across machines or build profiles. cargo xtask report --markdown renders one report rather than a comparison, and formats its numbers the drill's way: three significant figures, truncated, with the unit switched to keep them.
Present results with honest comparisons
#[derive(Debug, Clone)]
pub struct Run {
pub machine: &'static str,
pub profile: &'static str, // build profile, e.g. "release+lto"
pub p99_ns: Vec<u64>, // one p99 per repeated benchmark run
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Verdict {
Incomparable(&'static str), // "machine" | "profile" | "samples"
NoChange,
Faster(u64), // percent
Slower(u64), // percent
}
/// Median; for an even count, the mean of the two middle values rounded
/// down. 0 for an empty slice.
pub fn median(xs: &[u64]) -> u64 {
let _ = xs;
todo!()
}
/// Rules, in order:
/// machine differs → Incomparable("machine"); profile differs →
/// Incomparable("profile"); either run has < 5 samples → Incomparable("samples").
/// Every `after` sample below every `before` sample → Faster(p);
/// every `after` sample above every `before` sample → Slower(p);
/// otherwise → NoChange.
/// p = |median(before) − median(after)| × 100 / median(before),
/// rounded to the nearest integer (halves up).
pub fn compare(before: &Run, after: &Run) -> Verdict {
let _ = (before, after);
todo!()
}
/// Three significant figures, truncated (not rounded), switching units:
/// < 1_000 ns → "850 ns"; < 1_000_000 → "1.23 µs" / "12.3 µs" / "123 µs";
/// < 1_000_000_000 → "1.23 ms" ...; otherwise seconds, e.g. "1.50 s".
pub fn fmt_ns(ns: u64) -> String {
let _ = ns;
todo!()
}
/// "| {name} | {fmt_ns(median before)} | {fmt_ns(median after)} | {verdict} |"
/// verdict: "faster by 20%" | "slower by 12%" | "no change (within noise)"
/// | "not comparable (machine)" (or profile / samples)
pub fn row(name: &str, before: &Run, after: &Run) -> String {
let _ = (name, before, after);
todo!()
}
Day 4 — Code that documents itself
The reader who opens a file
Module docs, SAFETY comments and examples that compile
Some readers will open crates/sync/src/spsc.rs. Make that a good experience:
- Module-level docs that state the invariants before any code: "single producer, single consumer;
headis written only by the producer…". - A
// SAFETY:comment on everyunsafeblock, arguing why it's sound. Clippy'sundocumented_unsafe_blockslint enforces it, so turn it on for the crates that useunsafe. - Doc examples are tests.
cargo testcompiles and runs them, so the example in the README of each crate can't rot. - Public items documented in library crates (
#![warn(missing_docs)]), each with a sentence on cost where it matters: "O(1), no allocation", "takes a lock". - Tests named for the behaviour they protect.
w07_sweep_follows_price_then_time_priorityis documentation.
Documentation tests, intra-doc links and crate-level docs. Make every crate's top-level example a compiling doc test.
Pick your three most important crates. For each, list the invariants a newcomer must know before changing code, and check that each one is written down in the module docs, enforced by a test, or both. Fix the gaps.
Day 5 — The ten-minute story
Your year, told well
Problem, architecture, one deep decision, one bug, numbers, next
Interviewers will ask "tell me about this project". Have a ten-minute version and a two-minute version, both with the same spine:
- The problem in one sentence, and why it's hard (determinism plus microsecond latency plus recovery).
- The architecture along the order path, in one breath, with the thread model.
- One deep decision you can defend against pushback, such as single-threaded matching, ack-after-replication or the wire format, with the alternative you rejected.
- One bug story with a measurement: the false-sharing hunt, the coordinated-omission correction that changed your p99 by 10×, the replay that disagreed with production.
- The numbers, with their machine and method, and what limits them.
- What's next and what you would do differently.
Tailor the emphasis to the role. Trading-infrastructure teams want steps 2–5. Exchange operators want determinism, recovery and failover. Quant developer roles want the backtester and the strategy work from weeks 41–45.
Write the two-minute version of your project story as you'd say it, then record yourself telling the ten-minute version. Mark every sentence that names a technology without a mechanism or a number, and rewrite it.
Why should the README's performance table be generated by `cargo xtask report` rather than written by hand?
Day 6 — The stranger test
Ten minutes, clean machine, no help
The only review that counts
Ask someone who hasn't seen the project (or yourself, on a fresh VM) to go from the repo URL to running the demo and the test suite in ten minutes, without asking you anything. Write down every place they hesitate. Each hesitation is a documentation bug: a missing prerequisite, an outdated command, an unexplained term, a broken link. Fix them all, then run the test again with someone else.
Record the stranger test: every hesitation, its cause, and the fix you made. Which fixes were to the docs and which to the code or tooling?
Capstone
Finish crates/demo as the front door of the repository: the final README (first screen as on Day 1, crate map, the Mermaid architecture diagram, limitations), the one-page architecture overview with three ADRs, the benchmark report generated by cargo xtask report --markdown, and the live demo link from week 47, with a screenshot of the GPUI terminal running your engine.
The documents already exist in the repo, written for the reference exchange. The README has the crate table, an Architecture section whose Mermaid diagram shows every crate, and sections on running the tests, the demo and the benchmarks. docs/ARCHITECTURE.md has the order path, a threads-and-queues table, the invariants, three decisions written as short ADRs, and a section per crate naming the weeks that build it. Make them yours: your numbers, your demo link, your limitations on the first page. Then write the code that holds them to the workspace, demo::docs, which reads Markdown as data:
sectionssplits a document at its headings (a#inside a fenced code block doesn't count);mermaid_blocksreturns the Mermaid diagrams;parse_flowchartturns a Mermaid flowchart into nodes and labelled edges, queried withnode,has_edgeandpath;relative_linkslists the links that point into the repo;week_listreads "Weeks 7–8 and 12" as[7, 8, 12].
The report renderer behind cargo xtask report --markdown is provided, with a golden-file test that holds its output to the byte.
Make the documentation testable. The week's tests hold the docs to the same standard as code. Every workspace crate has a section in docs/ARCHITECTURE.md, titled with its name in backticks, whose week list matches the Curriculum: line of the crate's own docs. The README's Architecture section holds one diagram, which names every crate and the terminal and draws the order path as built: gateway → risk → journal → engine → marketdata, with the gateway → risk hop labelled as a ring, reports going back to the gateway and market data flowing out to clients. Every relative link in the README, CONVENTIONS.md, docs/ARCHITECTURE.md and the terminal's README resolves. Every flag and HFT_DEMO_* variable that the README's demo section mentions exists in demo --help, and the section points at Dockerfile.demo and fly.toml. If you encoded the architecture as data for the drill, lint it in CI too.
Decisions. Generate, don't type. Keep diagrams as code. Put limitations on the first page, not the last. Keep the README short and link outward. Every page should survive being read by someone smarter than you who's in a hurry.
Pitfalls. Numbers without a machine. Screenshots of a demo that's no longer deployed. A quick start that only works with your shell's environment. Architecture diagrams that show the system you planned in week 12 rather than the one you built. And superlatives: "blazingly fast" costs credibility that "2.1 M orders/s, p99 1.8 µs on a 2-socket Xeon, method linked" earns.
crates/demoThe repo tells the story
A README, architecture overview and generated benchmark report that a stranger can verify in ten minutes, linked to the live demo.
cargo xtask test --week 51 # tests named w51_*
cargo xtask bench --week 51 # p50 / p99 / p99.9 on your machine
cargo xtask report --upload # update this page and your lab- All week 51 tests pass w51_*
Feeds into
- Week 52 launches what you documented this week: the README is the launch page, the report is the evidence in your announcement, and the stranger test is the dress rehearsal.