The Analyst's Path

Phase 12 · Finance Plus, AI and the quant-code track · free

Software Engineering: Testing, Clean Code & AI-Assisted Dev

CS2.06 · 16,872 words

Every module before this one asked can you make the computer produce the right answer? This one asks a harder question, and it is the question that separates someone who can code from someone who can be trusted with production: how do you know it is right…

Learning objectives

By the end you can:

  1. State and defend the module's spine (code is a claim, a test is the evidence) and explain why "I ran it and it looked right" is not a verification procedure.
  2. Write pytest tests fluently: assert-based test functions, pytest.approx for floats, pytest.raises for error paths, @pytest.mark.parametrize for tables of cases, fixtures for shared setup, and conftest.py for shared fixtures, and run them with -q, -x, -k, and coverage.
  3. Execute the TDD loop (red, green, refactor) and explain, in mechanism, why a test that has never been seen to fail is not known to test anything.
  4. Choose what to test using a systematic tree (the contract, equivalence classes, boundary values, error paths, and invariants) and explain why the happy path is the least informative test you can write.
  5. Name the oracle problem and solve it four ways: hand computation, an independent second implementation, a closed-form or published reference, and a property/invariant, and recognise a change-detector test (one whose expected value came from running the code under test) as evidence of nothing.
  6. Diagnose and prevent silent numeric defects: float equality, ddof (population vs sample), integer division, annualisation factors, percent vs basis points, arithmetic vs geometric returns, in-place mutation of a caller's data, and off-by-one windowing, and set numeric tolerances that are tight enough to catch a real error and loose enough to survive IEEE-754.
  7. Apply clean-code discipline where it pays: intention-revealing names, one job per function, pure functions and explicit dependencies, guard clauses over nesting, no magic numbers, no mutable default arguments, and comments that explain why rather than restate what.
  8. Add type hints and read a mypy error, knowing exactly what static typing does and does not guarantee at runtime; run a linter/formatter (ruff, black) and articulate what a linter catches that a test cannot, and vice versa.
  9. Replace print with structured logging: a module-level logger, correct levels, logger.exception inside except, the run's seed and config logged for reproducibility, and never leak a secret or personal datum into a log line.
  10. Package a project properly: src/ layout, pyproject.toml, editable install, pinned dependencies, semantic versioning, and a one-command test entry point, so "it works on my machine" becomes a claim anyone can falsify on theirs.
  11. (The crown unit.) Review AI-generated code to a professional standard: name the six defect classes (hallucinated API · happy-path-only logic · silent numeric defect · insecure pattern · over-confident comment · untestable structure), run the REVIEW protocol (Read · Execute · Verify APIs · Interrogate numbers · Examine security · Write the test) end to end, and state the two laws that govern the whole practice, understand it yourself first, and never let the assistant write the test for the code it just wrote.
  12. (Productivity objective: R10 duality.) Use a coding copilot the way a senior engineer does, for scaffolding, boilerplate, test-case enumeration, error-message archaeology, and refactoring drudgery, while keeping the specification, the oracle, and the final judgement entirely in your own hands.

The duality, stated once (R10). This module carries two objectives at once. The understanding objective (items 1–11) is what the crown gate rewards: you earn the belt by writing the failing test by hand, naming the defect class, and fixing the number. The productivity objective (item 12) is the payoff you keep. The order is not negotiable and it is enforced by the exercises: the by-hand version is gated before the AI shortcut in every callout below. A copilot makes a good engineer fast; it makes a bad engineer dangerous at speed, because it removes the one thing that used to slow a wrong idea down, the time it took to type it.


Prerequisites & connections

Builds on. CS1.01 and CS1.02 (functions, list/dict/set choice, comprehensions, mutability and aliasing, simple classes, this module tests code, it does not teach you to write it). CS1.04 (algorithmic thinking and Big-O: the brute-force O(n²) oracle in CS2.06-e06 is obviously correct only because you can see it is exhaustive). CS2.01 (command line and virtual environments, pytest, ruff and mypy are all invoked from a shell inside an environment you control). CS2.02 (Git: a suite's real power arrives when it runs on every commit, and a bisect is only useful if there is a test to bisect against). CS2.03 (SQL: the injection material in CS2.06-e05 assumes you can read a SELECT). Nothing here requires statistics beyond a mean and a standard deviation; where a financial quantity appears (Sharpe, drawdown, annualised volatility) the formula is given to you, because this module grades whether you can verify an implementation, not whether you can derive the theory.

This page is an excerpt

The full module runs to 16,872 words and carries the worked examples, the tables, the quiz that gates the next module and the spaced-repetition deck built from it. All of it is free and none of it needs an account.