← Back to Blog
Building in Public

Prevention Over Cure: The Infinite ROI of Process Investment

christian crumlish•
A robot installing guards before failure can occur, process investment creating cheaper future operations.
“An ounce of prevention”

November 4, 2025

Let’s talk about the False Economy Principle: Cheap fixes now create expensive problems later. Expensive prevention now creates cheap operations forever. It was Tuesday. My regular workday was done, and I had time to turn to Piper Morgan before dinner.

The symptom appears

Foundation branch merge complete. 186 files changed. +47,677 insertions, -1,334 deletions. Major work landing.

Time to push to origin. Share the work. Deploy the foundation.

git push origin main

Pre-push hook fails: ModuleNotFoundError: No module named 'services.integrations.mcp.notion_adapter'

Wait. The module exists. Tests pass locally. Why can’t pre-push hook import it?

Traditional approach:

  • Fix the import path
  • Or disable the failing test
  • Or use --no-verify to bypass hook
  • Push the code, move on
  • Time cost: 5–10 minutes

What we actually did:

  • Investigate root cause thoroughly
  • Understand why it fails
  • Fix underlying problem
  • Prevent future occurrences
  • Document requirements
  • Time cost: 3.5 hours

10 minutes vs 3.5 hours. Why invest 21x more time?

Because 10-minute fix solves today’s symptom. 3.5-hour investigation solves entire problem class permanently.

The investigation begins

Programmer agent starts root cause investigation. Not “how to bypass this error” but “why does this error exist?”

First discovery: 19 directories under services/ missing __init__.py files.

Not 1 or 2. 19 directories. Systematically missing across the codebase.

How long have they been missing? Git archaeology begins.

Timeline reconstruction:

  • services/api/__init__.py missing since June 20, 2025
  • 137 days without this file
  • services/integrations/mcp/ missing since August
  • ~90 days without these files

Wait. How did this work for 137 days without __init__.py?

Root cause identified: Python 3.3+ PEP 420 namespace package trap.

The trap:

  • Python 3.3+ allows imports without __init__.py (PEP 420 namespace packages)
  • Works in dev environment (most contexts forgive missing files)
  • Fails in strict validation (pytest collection, type checkers, some hooks)
  • Masks the problem until strict tooling runs

The insidiousness: It works until it doesn’t. Developers assume it’s fine (imports work!). But tests fail mysteriously. Type checkers get confused. Strict tooling breaks.

You don’t know there’s a problem for 137 days. Then suddenly: ImportError.

Analysis complete: 20,000+ word root cause document. Timeline reconstructed. Problem understood. Fixes identified.

Not just “fix the imports.” But “understand why this happened and prevent it systematically.”

The cure: Quick fixes (what we didn’t do)

Let’s talk about what the quick-fix approach might look like:

Quick Fix Option 1: Manual file creation (10 minutes)

# Create the missing __init__.py files
touch services/api/__init__.py
touch services/integrations/mcp/__init__.py
# ... (17 more)
git add .
git commit -m "Add missing __init__.py files"
git push

Pros: Fast, simple, problem solved today

Cons:

  • Nothing prevents future developers from forgetting __init__.py
  • Will happen again in 3–6 months
  • Each occurrence costs 10+ minutes
  • Accumulates to hours over project lifetime

Quick Fix Option 2: Disable failing test (5 minutes)

# Just skip the problematic test
@pytest.mark.skip("Import issues, will fix later")
def test_service_container():
...

Pros: Even faster, unblocks push immediately

Cons:

  • Test stays disabled (technical debt)
  • Problem still exists, just hidden
  • Will cause issues later when someone needs that test
  • “Fix later” rarely happens

Quick Fix Option 3: Bypass hook (2 minutes)

git push --no-verify

Pros: Fastest, pushes immediately

Cons:

  • Hook exists for reason, bypassing defeats purpose
  • Problem completely unaddressed
  • Will break things downstream
  • Creates uncertainty about code quality

Any quick fix: 2–10 minutes now, recurring cost forever.

The prevention: Root cause solution (what we actually did)

Instead of quick fix, we invested 3.5 hours (mostly asynchronous for me, with occasional check-ins) in comprehensive prevention:

Prevention 1: Automated fix script

Created create_missing_init_files.sh:

#!/bin/bash
# Automatically creates missing __init__.py files in services/
find services/ -type d ! -path "*/\.*" -exec sh -c \
'test -f "$1/__init__.py" || touch "$1/__init__.py"' _ {} \;

Benefit: One command fixes all missing files. Reusable for future discoveries.

Time to create: 15 minutes

Time saved: Every future occurrence takes 1 minute instead of 10+ minutes

Prevention 2: Pre-commit hook enforcement

Created check-init-py.sh hook:

#!/bin/bash
# Enforces __init__.py in all services/ directories
for dir in services/*/; do
if [ ! -f "${dir}__init__.py" ]; then
echo "ERROR: Missing __init__.py in ${dir}"
exit 1
fi
done

Integrated with .pre-commit-config.yaml:

- repo: local
hooks:
- id: check-init-py
name: Check for __init__.py files
entry: ./scripts/check-init-py.sh
language: system
pass_filenames: false

Benefit: Impossible to commit code without __init__.py. Catches at commit time. Zero human attention needed.

Time to create: 30 minutes

Time saved: Infinite. Problem can never recur.

Prevention 3: Comprehensive documentation

Added 60+ lines to CLAUDE.md:

## Python Package Structure Requirements

### __init__.py Files
ALL directories under services/ MUST contain __init__.py files.

Why: Python 3.3+ allows namespace packages (PEP 420) which work
without __init__.py in most contexts but fail in strict validation.

Enforcement: Pre-commit hook checks automatically.

Verification:
python -c "import services.integrations.mcp.notion_adapter"

If import fails, missing __init__.py somewhere in path.

Benefit: All future agents know the requirement. No repeated questions. No rediscovery.

Time to create: 45 minutes

Time saved: Every agent that would have asked takes 0 minutes instead of 15 minutes (explanation + back-and-forth)

Prevention 4: Environment standardization

Implemented editable install:

pip install -e .

Added to pyproject.toml:

[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"

Benefit: Standardizes environment. Hooks work same as dev. No sys.path mysteries.

Time to create: 25 minutes

Time saved: All future environment-related import issues prevented

Prevention 5: Additional warning hook

Created check-manual-tests.sh:

#!/bin/bash
# Warns about misnamed manual tests
grep -r "load_dotenv()" tests/ | grep "test_.*\.py" | \
while read -r match; do
echo "WARNING: Manual test named as automated test: ${match}"
done

Benefit: Catches tests that should be manual but are named for automation. Informational, not blocking.

Time to create: 15 minutes

Time saved: Prevents test suite confusion, false failures

Total prevention time: 2 hours 10 minutes (out of 3.5 hours total)

Total future time saved: ∞ (problems literally cannot occur anymore)

The mathematics of prevention

Let’s try to calculate the actual ROI:

Without prevention (quick fix approach):

Initial fix: 10 minutes
Problem recurrence rate: Once every 6 months (conservative estimate)
Project lifespan: 3 years (36 months)
Total occurrences: 6 times
Time per occurrence: 10 minutes investigation + 10 minutes fix = 20 minutes
Total time cost: 6 × 20 minutes = 2 hours over 3 years

But this assumes:

  • Problem only affects same directories (unrealistic)
  • Instant recognition and fix (unrealistic)
  • No accumulated technical debt (unrealistic)
  • No debugging time when tests mysteriously fail (unrealistic)

Realistic estimate: 5–10 hours over 3 years (including debugging, confusion, lost productivity)

With prevention (root cause approach):

Initial investment: 3.5 hours
Future occurrences: 0 (prevented by pre-commit hooks)
Total time cost: 3.5 hours one-time

Break-even: Immediate (if problem would have occurred 2+ more times)

Actual ROI: If problem would have cost 5–10 hours over project lifetime, prevention saves 1.5–6.5 hours net.

But that’s not the real ROI. The real value is:

The invisible savings:

Productivity preserved:

  • No mysterious test failures disrupting flow
  • No debugging sessions hunting import issues
  • No developer confusion about why things don’t work
  • No lost context switching from “feature work” to “why won’t this import?”

Knowledge captured:

  • Documentation explains requirement clearly
  • All agents understand Python package structure
  • No repeated discovery of same problem
  • Institutional knowledge preserved

Confidence maintained:

  • Pre-commit hooks prevent bad commits
  • Code quality gates automated
  • No surprises in CI/CD
  • Production deployments safer

The real ROI: Impossible to quantify precisely. But conservatively 10:1. Optimistically 100:1. Realistically somewhere between.

Why prevention feels expensive

Here’s why teams don’t invest in prevention:

Problem 1: Immediate pain vs delayed benefit

Quick fix: Pain stops NOW (dopamine hit)
Prevention: Pain stops LATER (no immediate reward)

Human brains prefer immediate reward (LLM “brains” too… they got it from us!). Prevention requires delayed gratification discipline.

Problem 2: Visible cost vs invisible savings

Quick fix: 10 minutes spent (visible cost)
Prevention: 3.5 hours spent (very visible cost)

Future problems prevented: Invisible (didn’t happen)

You see the 3.5 hours. You don’t see the 10+ hours saved because those problems never occurred.

Problem 3: “Ship now” pressure

“We need to push this code today! Can’t spend 3.5 hours on infrastructure!”

But mathematics: 3.5 hours invested = 10+ hours saved over project lifetime = net +6.5 hours.

False economy: Saving time now costs more time later.

Fortunately, this is a side project without deadlines but for most efforts this is a real issue.

Problem 4: Unclear when “enough” prevention

“Should we build 1 hook or 5? Where do we stop?”

Without framework, prevention feels arbitrary. Easy to under-invest or over-invest.

The framework: Prevent entire problem class, not just immediate symptom.

Missing __init__.py is symptom. Problem class is "Python package structure not enforced." Solution: Enforce Python package structure systematically.

The “False Economy” Principle

Tuesday’s test infrastructure work demonstrates False Economy Principle perfectly:

False economy thinking:

  • “3.5 hours on infrastructure is expensive”
  • “10 minutes fixing import is cheap”
  • “We should take the cheap option”
  • Result: Waste 10+ hours over project lifetime

True economy thinking:

  • “3.5 hours now prevents recurring 20-minute problems”
  • “If problem occurs 10+ times over 3 years, that’s 3.5+ hours”
  • “Prevention breaks even after 10 occurrences”
  • Result: Save net 7–15 hours over project lifetime

The principle: Cheap now, expensive later vs expensive now, cheap forever.

Quick fixes are false economy. They feel cheap (10 minutes!) but cost more over time (recurring every 6 months).

Prevention is true economy. Feels expensive (3.5 hours!) but costs less over time (one-time investment).

The discipline: Choose true economy over false economy. Even when it feels expensive now.

Examples of prevention ROI

Tuesday’s test infrastructure isn’t unique. Pattern appears everywhere:

Example 1: Pre-commit hooks

Investment: 2 hours (creating 2 hooks + documentation)
Prevents: Missing __init__.py, misnamed manual tests
Frequency prevented: Every 6 months (conservatively)
Time per occurrence: 20-30 minutes
ROI: Breaks even after 4 occurrences (~2 years), infinite savings beyond

Example 2: Completion Matrix

Investment: 90 minutes per feature (Levels 2–3 verification)
Prevents: Undocumented code, missing context, debugging mysteries
Frequency prevented: Every time someone touches that code (2–3 times/year per feature)
Time per occurrence: 2–4 hours debugging/understanding
ROI: 4:1 to 8:1 per year per feature

Example 3: Phase -1 Investigation

Investment: 15–30 minutes per feature
Prevents: Rebuilding existing infrastructure
Frequency saved: Once per feature
Time saved: 6–10 hours per feature
ROI: 12:1 to 40:1 (immediate, not delayed)

Example 4: Comprehensive Documentation

Investment: 30–60 minutes per significant change
Prevents: “How does this work?” questions, rediscovery, confusion
Frequency prevented: Every time someone reads the docs instead of asking (10+ times over feature lifetime)
Time per prevention: 15–30 minutes
ROI: 5:1 to 20:1 over feature lifetime

Pattern: Prevention consistently provides 5–20x ROI over time

What makes for effective prevention?

Not all prevention is equal. Effective prevention has characteristics:

Automated enforcement

Good: Pre-commit hook prevents bad commits (zero human attention)
Bad: Documentation says “remember to do X” (requires human remembering)

Humans forget. Automation doesn’t. Best prevention requires no human intervention.

Comprehensive scope

Good: Prevents entire problem class (“enforce Python package structure”)
Bad: Prevents specific symptom (“add this one missing file”)

Narrow fixes prevent one symptom. Broad fixes prevent all symptoms in problem class.

Clear documentation

Good: 60+ lines explaining requirement, rationale, verification
Bad: Code comment saying “needed for tests”

Future developers need to understand WHY prevention exists. Otherwise they bypass or remove it.

Easy verification

Good: python -c "import services.api" tests if structure is correct
Bad: "Run full test suite and see if it works"

Quick verification enables fast feedback. Slow verification gets skipped.

Appropriate severity

Good: Missing __init__.py blocks commit (critical requirement)
Good: Misnamed manual test warns (useful but not critical)
Bad: Everything blocks (too strict, gets bypassed)
Bad: Everything warns (too lenient, gets ignored)

Match enforcement to severity. Critical requirements block. Useful guidance warns.

The investment decision framework

How do you decide when to invest in prevention?

Question 1: How often will this recur?

Once ever: Quick fix appropriate
Occasionally (1–2 times/year): Prevention probably worth it
 Frequently (monthly): Prevention definitely worth it
Every commit: Prevention mandatory

Question 2: How expensive is each occurrence?

5 minutes: Prevention needs to be very cheap to justify
30 minutes: Prevention justified if problem recurs 3+ times
2+ hours: Prevention justified if problem recurs even once
Unknown/variable: Prevention provides insurance value

Question 3: Can it be automated?

Yes: Strong case for prevention (minimal ongoing cost)
Partially: Moderate case (reduced future cost)
No: Weaker case (still requires manual work)

Question 4: How certain is the solution?

Very certain: Invest in comprehensive prevention
Somewhat certain: Invest in lightweight prevention, iterate
Uncertain: Quick fix, observe, invest in prevention after second occurrence

Question 5: What’s the blast radius?

Affects one developer: Lower priority for prevention
Affects team: Medium priority
Affects production: High priority
Affects users: Mandatory prevention

Tuesday’s score:

  • Recurrence: Frequent (every 6 months without prevention)
  • Cost per occurrence: 30 minutes minimum
  • Automatable: Yes (pre-commit hooks)
  • Certainty: Very certain (Python package structure is well-understood)
  • Blast radius: Affects team (everyone hits import issues)

Result: 5/5 factors favor prevention. Clear decision.

What this means for other teams

The prevention pattern applies beyond Piper Morgan:

Any team should invest in prevention when:

  1. Problem will recur (not one-time)
  2. Prevention can be automated (minimal ongoing cost)
  3. Problem affects multiple people (shared pain)
  4. Solution is well-understood (not experimental)
  5. ROI is positive (time saved > time invested)

Common prevention opportunities:

  • Pre-commit hooks (catch issues before commit)
  • CI/CD checks (catch issues before deployment)
  • Documentation (prevent “how does this work” questions)
  • Automated tests (prevent regressions)
  • Configuration validation (prevent deployment errors)
  • Style guides (prevent inconsistency)
  • Code review checklists (prevent common mistakes)

The discipline: When problem occurs, ask “How do we prevent this entire problem class?” not just “How do we fix this instance?”

The evening outcome

9:30 PM: Prevention work complete.

Immediate result:

  • Foundation branch pushed successfully
  • Tests import correctly
  • Pre-commit hooks enforcing requirements
  • Documentation comprehensive
  • Automated scripts ready

Long-term result:

  • Missing __init__.py literally cannot happen (blocked by pre-commit hook)
  • All agents understand Python package requirements (documented in CLAUDE.md)
  • Future import issues take 1 minute to fix (automated script exists)
  • Environment standardized (editable install prevents sys.path issues)
  • Test suite reliable (manual tests properly identified)

Time invested: 3.5 hours one-time

Time saved: 10–20 hours over project lifetime (conservative)

ROI: 3:1 to 6:1 (quantifiable) + ∞ (productivity preserved, confidence maintained)

But more importantly: Problem class prevented permanently. Not just symptoms fixed. Root causes addressed. Entire category of future pain eliminated.

That’s prevention over cure. That’s true economy. That’s mature engineering.

← Back to BlogAlso available on Medium