Clean, Maintainable Analysis Code
Code that survives its author
Analyses that can't be re-run, debugged or handed off are debt. Naming, typing, function boundaries, docstrings and reproducibility turn throwaway scripts into assets.
What you will learn
- Name functions, variables and files to read as prose
- Use type hints and docstrings as executable documentation
- Structure analyses into importable, re-runnable modules
What is this?
Clean analysis code is reproducibility plus legibility: functions named as verbs, variables named as nouns, types declared, steps isolated, and every run leaving the inputs untouched. It means someone else — or you, in six weeks — can pick up the script, understand intent, and re-run it to identical results.
Why do you need it?
- Analyses are re-run constantly; a script that needs rewiring each time is a tax on every rerun.
- Clear names reduce the 'what was this column again?' tax tenfold.
- Type hints catch whole bug classes before the program runs.
- Trust: stakeholders rely on figures that can be re-derived from source by anyone.
Mental model
A well-labeled, documented kitchen vs a chef's hidden notebooks
Clean analysis code
The covered recipe kitchen lets any cook reproduce the dish exactly. A chaotic pantry with unnamed sauces might produce tonight's plate but nothing anyone can repeat — same is true of analysis scripts.
How it works
- Refactor repeated SQL/Pandas blocks into one named function with a signature that states intent.
- Name variables as data (raw_revenue, active_users) not mechanics (x1, tmp2).
- Add type hints (prices: pd.Series -> float) that double as documentation.
- Write 1-3 line docstrings stating the what and the why, not the how.
- Isolate config (paths, thresholds) at the top so runs are parameterized, not edited.
- Keep raw inputs untouched, writes to a distinct output dir, and pin versions.
From throwaway to asset.
Examples
Dirty vs clean shape
# tens of lines of df manipulation... then:def compute_mrr(df: pd.DataFrame, price_col: str = "price") -> float: """Total monthly-recurring revenue for active subscriptions.""" active = df[df["status"] == "active"] return (active[price_col] * active["quantity"]).sum() # config isolated:INPUT = Path("exports/mrr_2026-06.csv")OUTPUT = Path("reports/mrr_summary.csv")THRESHOLDS = {"active_min_days": 30}One named, typed, documented function plus top-of-file config makes the whole analysis inspectable and re-runnable.
In production
- A weekly pipeline lives as importable modules (ingest.py, clean.py, report.py) with a thin run.py entry.
- A hand-off analysis uses type-annotated functions so the downstream team reuses logic instead of re-deriving it.
- An audit trail: config files and pinned versions let compliance re-run any quarter's numbers.
Best practices
- Functions return values instead of printing — output is decided by the caller.
- Split scripts into steps visible on one screen; a wall of 200 lines hides the story.
- Add an if __name__ == '__main__': guard so modules import without side effects.
- Commit the code and the data contract file, never the secrets.
Common mistakes
- Magic numbers buried mid-file ('if x > 30') with no explanation of the 30.
- Functions that mutate their inputs, making idempotence impossible.
- Docstrings that restate the code instead of the 'why'.
- Paths, thresholds and seeds duplicated across files so reruns drift.
Debugging tips
- If the 'same script' gives different results across runs, hunt for unseeded sampling or nondeterministic ordering.
- When a coworker misreads a name, rename it — names are documentation with a beating heart.
- Divide a failing step into a tiny reproduce.py that isolates one function on five rows.
- Run with -W error to surface warnings you were ignoring.
Interview checkpoint
Quick quiz
The best reason to prefer functions over one long script:
Type hints only help editors, never catch real bugs.