flowchart LR
A[Entry point] --> B[Load config]
B --> C[Load input data]
C --> D[Call model library]
D --> E[Write outputs]
src/project/model.py is library code: reusable logicscripts/run_model.py is a script: often one runnable taskmodel-run ... is the application interface (in this case a CLI)python -m package.modulemain() functionmodel-run --config configs/base.yamlflowchart LR
A[Entry point] --> B[Load config]
B --> C[Load input data]
C --> D[Call model library]
D --> E[Write outputs]
flowchart TB
code[Code<br/>Reusable logic]
config[Config<br/>Run instructions]
data[Input data<br/>Domain facts]
output[Outputs<br/>Results of the run]
code --> run((Run))
config --> run
data --> run
run --> output
| If you see this… | A good next fix is… |
|---|---|
sys.path hacks |
make imports real and package the code cleanly |
| hard-coded local paths | move the path into config |
script_2024.py, script_2025.py |
drive behavior from inputs instead of duplicated files |
| notebooks as source of truth | try to move reusable logic into src/ |
main.py just because uv added it |
document and support one real entry point |
src/ for reusable codescripts/ for task-focused orchestrationnotebooks/ for exploration, not the source of truthconfigs/ for declared runsdocs/ for the humans (and some robots)The live demo for this talk lives here:
live_demos/2026-03-16_libraries_dont_run_themselves
src/Libraries can’t just run themselves. Good repos make that obvious.