interviewpath
  • 0day streak
  • 10total XP
  • Level 1
0/6

Unit 9 · Research methods · Lesson 11 of 12

Research coding: prototypes, reference checks and instrumentation

Research code must be fast to change and trustworthy in its numbers. Readable prototypes, numerical care, reference implementations with differential tests and logging of every configuration are what research-engineering interviews and real projects check.
About 5 hFocus mode

Step 1: Learn the ideas

  • Readable prototypes and configuration management
  • Reference implementations and differential testing
  • Property-based and metamorphic tests
  • Numerical correctness and tolerances
  • Gradient checking and sanity checks
  • Instrumentation and structured logs
  • Measurement code: timers, warm-up, percentiles, units
  • Iteration speed: proxies, caching, parallel sweeps
  • Notebooks versus scripts

Why it matters

A bug in research code becomes a false claim. Unlike product code, there is often no user to notice that an output is wrong; the only defence is checks built into the code. Research-engineering interviews therefore probe testing strategy, numerical care and how you would trust your implementation of a paper.

Core ideas

Prototype for change. Research code changes daily, so optimize for reading and modifying: small pure functions for the core computation, configuration in files rather than constants scattered through the code, one entry point per experiment, notebooks for exploration but scripts for anything that produces a reported number. Skip production concerns such as polish and scalability unless the research needs them, but never skip correctness checks.

Reference implementations and differential testing. For every optimized or intricate component, keep a slow version you can verify by reading it: naive attention beside a fused kernel, a sequential MapReduce beside the distributed one (MIT 6.5840 Lab 1 supplies such a sequential version), brute-force search beside an index, an exact percentile beside a streaming sketch. Generate many random and edge-case inputs and compare outputs. Csmith applied this idea at scale, finding compiler bugs by compiling generated programs with several compilers and comparing results. Property-based testing tools such as Hypothesis generate inputs and shrink failures to minimal examples.

Tolerances are part of the specification. Floating-point results change with summation order, fusion and precision. Compare with explicit absolute and relative tolerances derived from the precision in use, and investigate failures instead of loosening tolerances until tests pass.

Numerical traps. Compute softmax and log-likelihoods with max subtraction or log-sum-exp. Watch for overflow and underflow in half precision, cancellation when subtracting nearly equal numbers, counter overflow and silent dtype promotion. For ML code, check gradients against finite differences on tiny inputs, as the CS231n notes describe, and confirm that a model can overfit a tiny batch before scaling up.

Measurement code is research code. Use monotonic clocks with adequate resolution, exclude warm-up explicitly, remember that percentiles depend on the interpolation method and sample count, and keep units straight (milliseconds versus seconds, bits versus bytes, MB versus MiB). Test the measurement path on synthetic inputs with known answers.

Instrument everything. Each run writes its resolved configuration, commit and diff, environment, seeds and raw measurements as structured records (for example JSON lines) into its own directory, starting at launch so crashed runs remain traceable. This makes later analysis and reproduction possible.

Iteration speed. Fast feedback beats raw speed: tiny configurations for debugging, cached preprocessing, parallel parameter sweeps and early validation of configurations. Check periodically that small proxies rank variants the same way full runs do; a misleading proxy is worse than a slow experiment.

Tests at the right level. Unit tests on core math; property and metamorphic tests (permuting independent inputs should not change an aggregate, doubling every capacity should not reduce throughput); regression tests against stored small outputs; and an end-to-end smoke run that exercises the whole pipeline in under a minute.

Common misconceptions

  • "Research code does not need tests." It needs different tests: fewer on interfaces, more on numerical and measurement correctness.
  • "The loss goes down, so the implementation is right." Many bugs still let training improve; compare against references and sanity checks.
  • "Bit-exact equality is the right comparison." For floating point it usually is not; choose tolerances deliberately.
  • "I will add logging once results look interesting." By then the configurations of earlier runs are gone.
  • "Notebooks are fine for final results." Hidden state and out-of-order execution make reported numbers hard to trust.

How to approach it

Take one optimized function from your own work or a course lab, write a reference and a differential test with generated inputs, then add a run harness that logs configurations and raw data. In interviews, be ready to explain how you would verify an implementation of a paper's algorithm before trusting its numbers; graduates should propose reference checks and tests, senior candidates should also discuss harness design and the cost of each check.

Suggested order

  1. 1Read Wilson et al., "Good enough practices in scientific computing", focusing on the software and project-organization sections.
  2. 2Read the "Gradient Checks" and sanity-check sections of the CS231n notes (Neural Networks Part 3).
  3. 3Read the introduction and the differential-testing section of the Csmith paper (Yang et al., PLDI 2011).
  4. 4Work through the Hypothesis quickstart and do the "Differential-test an optimized function" exercise with a naive reference.
  5. 5Do MIT 6.5840 Lab 1 (MapReduce), treating the supplied sequential implementation as your reference and comparing outputs on extra inputs you generate.
  6. 6Add a run harness to one of your projects that logs resolved configuration, commit, environment, seeds and raw measurements for every run.

Step 2: Check off what you can do

Tap each one you can genuinely do or explain. Each earns 10 XP.

Step 3: Go deeper

  • Good enough practices in scientific computingStart here

    PLOS Computational Biology (open access)

    Minimum practical habits for research software, data and project organization.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • Finding and Understanding Bugs in C Compilers

    University of Utah (author preprint)

    Large-scale example of differential testing against independent implementations.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • Hypothesis quickstart (property-based testing for Python)

    Hypothesis documentation

    Property-based testing for comparing optimized code with a naive reference on generated inputs.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • Stanford CS231n Deep Learning for Computer Vision

    Stanford University

    Section: Neural Networks Part 3: Gradient checks

    Numerical gradient checking and sanity checks before training at scale.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • MIT 6.5840 Distributed SystemsStart here

    MIT PDOS

    Section: Lab 1: MapReduce

    A lab that ships a sequential reference implementation and tests for correctness and crash recovery.

    Open(opens in a new tab)Link responded, 5 Oct 2026

Step 4: Practice

Keep each reference implementation in the repository permanently and run the differential test before every batch of experiments. In the MapReduce lab, compare against the sequential version on inputs you generate, not only the provided ones. In the CS231n or CS336 assignments, verify gradients or kernels against naive versions before measuring speed.

Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).

Step 5: Test yourself

Answer each one out loud or on paper before you finish the lesson.

  1. Question 1What is the reference implementation for your most complex component?
  2. Question 2How did you choose the tolerance in your numerical comparisons?
  3. Question 3Can you reconstruct the configuration of a run from two weeks ago?
  4. Question 4Does your small-scale proxy rank variants the same way full runs do?
  5. Question 5Which metamorphic property should your system satisfy?
  6. Question 6How is warm-up excluded in your measurement code?

You’re done when you can

  • Write a differential test with generated inputs and explicit tolerances comparing an optimized function with a naive reference.
  • Show a run directory with resolved configuration, commit, environment, seeds and raw measurements for every run.
  • Explain and fix a numerically unstable softmax or log-likelihood computation.
  • Describe in under three minutes how you would test an implementation of a published algorithm before trusting its numbers.

Finish the lesson

How confident do you feel?

Lesson complete!

+50 XP

Reviews will come up on the Practice tab so it sticks.

Your notes and study time

Log study time