Defensive coding for research software
Research code's worst bug is the one that runs to completion: the
unit mismatch, the silently dropped rows, the column shifted by
one, the reused random stream - producing plausible, wrong numbers
that reach a paper. Defensive coding is the discipline of making
wrongness LOUD: validate at boundaries, make implicit physics and
randomness explicit, and prefer a crash today to a correction
notice next year. It complements rseng-numerical-accuracy (float
behavior) and feeds rseng-testing (the checks become tests).
Validate at the boundaries
Data enters code at boundaries - files, instruments, APIs, user
parameters - and every boundary is a corruption opportunity:
- Schema-check tabular data on ingest: expected columns, dtypes,
units, ranges, allowed categories, uniqueness of keys
(pandera-style declarative schemas for DataFrames; JSON Schema
for configs and records). The schema is executable documentation
of the data contract (rseng-data-management's data dictionary,
enforced).
- Sanity-check the science, not just the types: physical ranges
(no negative concentrations, latitudes within +/-90), plausible
magnitudes, conservation totals, expected row counts within
tolerance of yesterday's. Domain assertions catch what dtype
checks cannot.
- Missing data is a decision, not a default: silent NaN
propagation and silent row-dropping (the pandas default in many
operations) are the classic silent killers - count and REPORT
what was excluded and why, and make exclusion rules explicit
code (rseng-research-integrity checks this at the manuscript end;
this skill prevents it at the source).
- Fail loud, fail early: raise on contract violations at ingest
rather than letting bad data flow downstream; in batch settings
quarantine-and-log per item (rseng-big-data-processing) - but
never silently skip.
Units and quantities
Unit errors are the canonical silent research bug - flagship
missions have died of them:
- Attach units in code, not in comments: a quantity library
(pint; astropy.units in astronomy) makes units part of the
value, checked at every operation - adding meters to seconds
raises instead of publishing.
- Enforce at boundaries even when the core stays plain-numeric
for performance: convert-and-strip on ingest (asserting the
expected unit), reattach on output, and document the internal
convention in ONE place (rseng-scientific-file-formats' metadata
discipline carries units in the files themselves).
- Degrees vs radians, per-second vs per-minute, and log-vs-linear
are unit bugs in spirit: name them in variable names or types
when a full quantity system is overkill (angle_rad,
rate_per_s).
Disciplined randomness
- Explicit generators, never global state: create a seeded RNG
object (numpy's Generator API) and pass it - seeding the global
makes order-dependent, library-colliding randomness
(the Scientific Python RNG guidance is the reference).
- Parallel work gets derived streams: spawn per-worker
generators from a master seed (seed sequences), never the same
seed in every worker - identical streams across "independent"
replicates is a silent statistics-destroyer
(rseng-hpc-computing job arrays included).
- Seeds are provenance: record them with outputs and in configs
(rseng-reproducibility owns the bookkeeping); stochastic tests
assert distributions or use fixed seeds knowingly
(rseng-testing).
The habit, proportioned
Not every script needs schemas: exploration code needs only the
cheap habits (explicit NaN policy, named units, seeded RNGs);
shared pipelines add ingest validation; published analyses add the
full contract checks in CI (rseng-ci-cd) so drift in upstream data
sources is caught at the pull request, not in the plot. When a
defensive check fires in production, keep it as a regression test -
each catch is a documented failure mode (rseng-trainer's
errors-are-curriculum applies: explain what the check just
prevented).
Working with this skill
This skill is source-independent: its authority is the tool
documentation and Scientific Python guidance linked below. It is
the prevention layer for the failure modes rseng-research-integrity
hunts post-hoc.
Learn more (verified):
Related skills
Check whether any of these applies before moving on:
- rseng-big-data-processing - quarantine-and-log at batch scale
- rseng-data-management - data dictionaries the schemas enforce
- rseng-hpc-computing - per-worker RNG streams in parallel jobs
- rseng-numerical-accuracy - float behavior behind silent errors
- rseng-research-integrity - post-hoc hunt for same failures
- rseng-testing - fired checks become regression tests
1---2name: rseng-defensive-coding3description: Covers defenses against silently wrong research results: validating data at boundaries (schemas, assertions, sanity checks), explicit physical units and quantities in code (pint/astropy-style), disciplined randomness (explicit seeded generators, parallel streams), and fail-loud handling of NaN and missing data. Use PROACTIVELY when code ingests external or instrument data, when values carry physical units, when randomness enters simulations or sampling, or when NaN or missing-data handling is implicit; also when the user mentions data validation, unit errors, seeds or silent bugs, or reviews analysis code whose failure would be invisible. For floating-point behavior and tolerances see rseng-numerical-accuracy; for diagnosing an existing bug see rseng-debugging.4license: CC-BY-4.05---67# Defensive coding for research software89Research code's worst bug is the one that runs to completion: the10unit mismatch, the silently dropped rows, the column shifted by11one, the reused random stream - producing plausible, wrong numbers12that reach a paper. Defensive coding is the discipline of making13wrongness LOUD: validate at boundaries, make implicit physics and14randomness explicit, and prefer a crash today to a correction15notice next year. It complements rseng-numerical-accuracy (float16behavior) and feeds rseng-testing (the checks become tests).1718## Validate at the boundaries1920Data enters code at boundaries - files, instruments, APIs, user21parameters - and every boundary is a corruption opportunity:2223- Schema-check tabular data on ingest: expected columns, dtypes,24 units, ranges, allowed categories, uniqueness of keys25 (pandera-style declarative schemas for DataFrames; JSON Schema26 for configs and records). The schema is executable documentation27 of the data contract (rseng-data-management's data dictionary,28 enforced).29- Sanity-check the science, not just the types: physical ranges30 (no negative concentrations, latitudes within +/-90), plausible31 magnitudes, conservation totals, expected row counts within32 tolerance of yesterday's. Domain assertions catch what dtype33 checks cannot.34- Missing data is a decision, not a default: silent NaN35 propagation and silent row-dropping (the pandas default in many36 operations) are the classic silent killers - count and REPORT37 what was excluded and why, and make exclusion rules explicit38 code (rseng-research-integrity checks this at the manuscript end;39 this skill prevents it at the source).40- Fail loud, fail early: raise on contract violations at ingest41 rather than letting bad data flow downstream; in batch settings42 quarantine-and-log per item (rseng-big-data-processing) - but43 never silently skip.4445## Units and quantities4647Unit errors are the canonical silent research bug - flagship48missions have died of them:4950- Attach units in code, not in comments: a quantity library51 (pint; astropy.units in astronomy) makes units part of the52 value, checked at every operation - adding meters to seconds53 raises instead of publishing.54- Enforce at boundaries even when the core stays plain-numeric55 for performance: convert-and-strip on ingest (asserting the56 expected unit), reattach on output, and document the internal57 convention in ONE place (rseng-scientific-file-formats' metadata58 discipline carries units in the files themselves).59- Degrees vs radians, per-second vs per-minute, and log-vs-linear60 are unit bugs in spirit: name them in variable names or types61 when a full quantity system is overkill (angle_rad,62 rate_per_s).6364## Disciplined randomness6566- Explicit generators, never global state: create a seeded RNG67 object (numpy's Generator API) and pass it - seeding the global68 makes order-dependent, library-colliding randomness69 (the Scientific Python RNG guidance is the reference).70- Parallel work gets derived streams: spawn per-worker71 generators from a master seed (seed sequences), never the same72 seed in every worker - identical streams across "independent"73 replicates is a silent statistics-destroyer74 (rseng-hpc-computing job arrays included).75- Seeds are provenance: record them with outputs and in configs76 (rseng-reproducibility owns the bookkeeping); stochastic tests77 assert distributions or use fixed seeds knowingly78 (rseng-testing).7980## The habit, proportioned8182Not every script needs schemas: exploration code needs only the83cheap habits (explicit NaN policy, named units, seeded RNGs);84shared pipelines add ingest validation; published analyses add the85full contract checks in CI (rseng-ci-cd) so drift in upstream data86sources is caught at the pull request, not in the plot. When a87defensive check fires in production, keep it as a regression test -88each catch is a documented failure mode (rseng-trainer's89errors-are-curriculum applies: explain what the check just90prevented).9192## Working with this skill9394This skill is source-independent: its authority is the tool95documentation and Scientific Python guidance linked below. It is96the prevention layer for the failure modes rseng-research-integrity97hunts post-hoc.9899Learn more (verified):100 - https://pandera.readthedocs.io - pandera DataFrame validation101 - https://pint.readthedocs.io - pint physical quantities102 - https://json-schema.org - JSON Schema for configs and records103 - https://docs.astropy.org/en/stable/ - astropy (units module)104 - https://blog.scientific-python.org/numpy/numpy-rng/ -105 Scientific Python guidance on NumPy random number generators106107<!-- related-skills:begin -->108109## Related skills110111Check whether any of these applies before moving on:112113- rseng-big-data-processing - quarantine-and-log at batch scale114- rseng-data-management - data dictionaries the schemas enforce115- rseng-hpc-computing - per-worker RNG streams in parallel jobs116- rseng-numerical-accuracy - float behavior behind silent errors117- rseng-research-integrity - post-hoc hunt for same failures118- rseng-testing - fired checks become regression tests119120<!-- related-skills:end -->