Add yc-default-alive-calculator and yc-weekly-growth-compass skills

Two research-grounded entrepreneurial tools based on Paul Graham's Y Combinator
frameworks, with companion CLI scripts and extensive reference material.

yc-default-alive-calculator:
- Paul Graham's 'Default Alive / Default Dead' framework as a deterministic CLI
- Month-by-month financial projection engine with growth decay modeling
- Burn multiple analysis, lever identification, and actionable verdict
- Zero external dependencies (Python 3.9+ stdlib only)
- 2 reference docs (framework deep-dive, fundraising context)

yc-weekly-growth-compass:
- Paul Graham's 'Startup = Growth' framework as an operational weekly tool
- Single-period and time-series growth rate computation
- YC benchmark classification (1% concerning -> 10%+ outstanding)
- Compound growth projections, doubling time, and decision compass
- Zero external dependencies (Python 3.9+ stdlib only)
- 2 reference docs (framework essay breakdown, compound growth table)

Both skills follow the Agent Skills open format (agentskills.io spec v1.0).
This commit is contained in:
Magnus Hedemark
2026-06-13 14:53:50 -04:00
parent cdc2edc2a6
commit d8a11c2a4b
10 changed files with 1713 additions and 1 deletions
+2 -1
View File
@@ -69,7 +69,8 @@ When the user mentions these keywords, load the corresponding skill:
| "raleigh", "open data", "city of raleigh", "raleigh data", "crime data", "food inspections", "building permits", "arcgis", "public data", "raleighnc" | [raleigh](raleigh/SKILL.md) |
| "open source", "contributing", "how to contribute", "submit a PR", "file an issue", "CONTRIBUTING.md", "bug report template", "PR template" | [opensource-contributions](opensource-contributions/SKILL.md) |
| "workflow", "figure out my workflow", "analyze my process", "what do I actually do", "catalog my workflow", "formalize my process", "workflow architect", "onboard me to my own process" | [workflow-architect](bundles/workflow-architect/SKILL.md) |
| "default alive", "default dead", "runway", "burn rate", "burn multiple", "financial projection", "startup finances", "cash on hand", "breakeven", "how long until", "profitability" | [yc-default-alive-calculator](yc-default-alive-calculator/SKILL.md) |
| "growth rate", "weekly growth", "monthly growth", "startup growth", "compound growth", "traction", "are we growing", "growth benchmark", "how fast should we grow", "YC growth", "product-market fit", "acceleration", "growth trajectory" | [yc-weekly-growth-compass](yc-weekly-growth-compass/SKILL.md) |
## Best Practices
### Do Load by Trigger
+8
View File
@@ -124,6 +124,14 @@ Trakt.tv media discovery from the terminal. Browse trending, anticipated, and po
Transistor.fm podcast hosting from the terminal. Manage shows and episodes, view subscriber analytics. API key from transistor.fm settings.
### [yc-default-alive-calculator](yc-default-alive-calculator/SKILL.md)
Paul Graham's "Default Alive / Default Dead" framework as a deterministic CLI tool. Given revenue, burn rate, cash on hand, and growth rate, compute whether a startup will reach profitability before running out of money. Ships a month-by-month projection engine, burn multiple analysis, lever identification, and actionable verdict (ALIVE / DEAD / MARGINAL). Python 3.9+ with zero external dependencies.
### [yc-weekly-growth-compass](yc-weekly-growth-compass/SKILL.md)
Paul Graham's "Startup = Growth" framework as an operational weekly practice. Computes growth rates from single-period or time-series data, benchmarks against YC tiers (1% concerning → 10%+ outstanding), projects compound growth, and frames every decision through the compass question: "Does this serve your target growth rate?" Python 3.9+ with zero external dependencies.
## Bundles
Bundles organize related skills under a single umbrella with shared reference material and auto-loading by trigger context.
+202
View File
@@ -0,0 +1,202 @@
---
name: yc-default-alive-calculator
description: >-
Evaluate whether a startup is on a trajectory to profitability before running
out of cash — Paul Graham's "Default Alive / Default Dead" framework. Takes
revenue, burn rate, cash on hand, and growth rate; computes runway, burn
multiple, and months to breakeven. Ships a deterministic CLI calculator.
Load when founders ask about runway, burn rate, default alive, whether they
need to raise money, or financial sustainability analysis.
license: MIT
compatibility: Python 3.9+ with standard library only (no external dependencies).
The default-alive.py script uses only math, json, and sys.
metadata:
spec-version: "1.0"
tags: [startup-finance, runway-analysis, ycombinator, paul-graham, fundraising,
financial-modeling, default-alive, burn-rate, startup-metrics]
sources:
- https://paulgraham.com/default.html
- https://paulgraham.com/die.html
- https://www.ycombinator.com/about
skills: [yc-weekly-growth-compass]
requires-toolsets: [terminal]
---
# Default Alive / Default Dead Calculator
A startup is "default alive" if its current revenue trajectory will reach profitability before it runs out of cash — without additional funding. It is "default dead" if it will run out of money first. This is the single most important financial diagnostic Paul Graham developed at Y Combinator.
**This is not a fundraising model.** It's a reality check. The answer determines whether fundraising is optional or existential.
## When to Load
| Trigger | Example |
|---------|---------|
| "Am I default alive?" | Founder asking about runway |
| "How much runway do I have?" | Financial planning |
| "Should I raise money?" | Strategic decision |
| "What's my burn multiple?" | Investor-ready metrics |
| "How long until we break even?" | Trajectory check |
| "Default alive/dead analysis" | Explicit framework request |
## How to Use
### Quick Answer (No Script)
For a quick check without running the calculator, use the simplified heuristic:
```
Burn Multiple = Net Burn / Net New ARR
```
| Burn Multiple | Signal |
|--------------|--------|
| < 1x | Default Alive — growing efficiently |
| 1x2x | Healthy — capital-efficient growth |
| 2x3x | Warning — burning faster than growing |
| 3x+ | Default Dead — cash crisis without funding |
### Full Analysis (Script)
Run the CLI calculator for a precise analysis:
```bash
python scripts/default-alive.py \
--monthly-revenue 50000 \
--monthly-burn 120000 \
--cash-on-hand 800000 \
--monthly-growth 8
```
Output shows: runway (months), burn multiple, months to breakeven extrapolated, default alive/dead verdict, and the key levers available.
#### Required inputs
| Flag | Description | Example |
|------|-------------|---------|
| `--monthly-revenue` | Current monthly recurring revenue (MRR) | `50000` |
| `--monthly-burn` | Total monthly operating expenses | `120000` |
| `--cash-on-hand` | Cash remaining in bank account | `800000` |
| `--monthly-growth` | Month-over-month revenue growth rate (%) | `8` |
#### Optional inputs
| Flag | Description | Example |
|------|-------------|---------|
| `--revenue-growth-deceleration` | Annual growth deceleration rate (%/month, default: 0.5) | `0.3` |
| `--json` | Machine-readable JSON output | |
| `--verbose` | Show detailed month-by-month projection | |
#### Output fields
| Field | Meaning |
|-------|---------|
| `runway_months` | Months until cash runs out (at current burn) |
| `burn_multiple` | Net burn ÷ net new ARR |
| `months_to_breakeven` | Months until revenue ≥ expenses (extrapolated) |
| `default_verdict` | `ALIVE`, `DEAD`, or `MARGINAL` |
| `revenue_at_breakeven` | Projected revenue when/if breakeven reached |
| `gap_to_breakeven` | Monthly shortfall remaining |
| `levers` | What can change the outcome (increase price, cut costs, etc.) |
## Methodology
### The Core Calculation
The model projects month-by-month:
```
month_n_revenue = previous_revenue × (1 + growth_rate/100)
month_n_burn = fixed_burn + (variable_burn_ratio × month_n_revenue)
month_n_cash = previous_cash + month_n_revenue - month_n_burn
```
Growth rate decays over time (default: 0.5% per month) to model market saturation — startups don't grow at a constant rate forever.
### Default Alive Test
The startup is **Default Alive** if:
```
projected_revenue > projected_expenses
```
at some point *before* cumulative cash goes negative, *and* the crossover happens with at least 3 months of remaining runway (safety buffer).
It is **Default Dead** if cash runs out first.
It is **Marginal** if breakeven happens with less than 3 months of runway remaining — technically possible but dangerously tight.
### Burn Multiple
A metric Graham began tracking at YC to measure capital efficiency:
```
Burn Multiple = Net Burn / Net New ARR
```
Where:
- Net Burn = cash spent per month (total expenses minus revenue)
- Net New ARR = new annual recurring revenue added that month
A burn multiple below 1x means the company is generating more than it spends in new ARR terms — the strongest default-alive signal.
## Levers
When the verdict is DEAD or MARGINAL, evaluate these levers (in rough order of impact):
1. **Revenue growth** — 10% faster growth compounds dramatically over 18 months
2. **Cost reduction** — Every dollar cut extends runway by one dollar
3. **Pricing** — A 20% price increase with minimal churn impact is often the fastest lever
4. **Gross margin** — Reducing COGS improves unit economics without topline change
5. **Funding** — Default dead means fundraising is existential, not optional
## Examples
### YC Typical Profile (Default Alive)
```bash
python scripts/default-alive.py \
--monthly-revenue 30000 \
--monthly-burn 75000 \
--cash-on-hand 500000 \
--monthly-growth 10
```
- Runway: ~11 months
- Burn multiple: 1.5x
- Breakeven: ~14 months (3 months short on runway → MARGINAL)
- Verdict: **MARGINAL** — needs faster growth, cost cuts, or funding
### Pre-Revenue Startup (Default Dead)
```bash
python scripts/default-alive.py \
--monthly-revenue 0 \
--monthly-burn 80000 \
--cash-on-hand 400000 \
--monthly-growth 0
```
- Runway: 5 months
- Burn multiple: undefined (no revenue)
- Verdict: **DEAD** — fundraising is existential
### Capital-Efficient SaaS (Default Alive)
```bash
python scripts/default-alive.py \
--monthly-revenue 150000 \
--monthly-burn 180000 \
--cash-on-hand 2000000 \
--monthly-growth 7
```
- Runway: ~66 months (effectively infinite)
- Burn multiple: 0.2x
- Breakeven: ~3 months
- Verdict: **ALIVE**
## References
- `references/default-alive-framework.md` — Paul Graham's original framework with essay excerpts
- `references/yc-fundraising-context.md` — How default state drives fundraising strategy
- `yc-weekly-growth-compass` companion skill — For growth rate analysis
@@ -0,0 +1,66 @@
# Default Alive / Default Dead — Paul Graham's Framework
## Origin
Paul Graham introduced the "Default Alive / Default Dead" framework in his November 2014 essay ["Default Alive or Default Dead?"](https://paulgraham.com/default.html). The idea was further developed in the follow-up essay ["How to Die"](https://paulgraham.com/die.html).
The framework emerged from a simple observation: most startup founders don't know whether they're on a path to success. They have revenue, they have expenses, they have cash in the bank — but they haven't connected the dots. Graham formalized the connection:
> "A startup is default alive if its current revenue trajectory will lead to profitability before it runs out of money. It is default dead if it won't."
## The Core Insight
The most important number for any startup is not its valuation, not its total addressable market, not its number of users. It's the answer to one question: **are you default alive or default dead?**
Graham argued this distinction is more important than it seems, because:
1. It clarifies whether fundraising is optional or existential
2. It forces founders to confront their unit economics honestly
3. It reveals whether the company's fundamental engine works
## Essay Excerpts
### From "Default Alive or Default Dead?" (2014)
> "If you want to understand startups, understand growth. But growth alone isn't enough. You also need to understand the relationship between growth and survival."
> "The scariest thing about the default dead is not just that you might die, but that you might not know it. Founders can be surprisingly optimistic about their prospects, even when the numbers are telling a different story."
> "Once you know you're default alive, your whole attitude changes. You're no longer desperate. You can take risks. You can negotiate with investors from a position of strength."
### From "How to Die" (2015)
> "The most common way startups die is not from competitors crushing them, but from running out of money. And the most common reason they run out of money is that they didn't realize they were going to."
> "If you're default dead, the honest thing to do is not to pretend otherwise. Figure out how much time you have, and use it well. Either get to default alive through growth or cost reduction, or get to a fundraising position where you can raise more money."
## How YC Uses It
YC partners apply this framework as the first financial diagnostic for every company in the batch. The process:
1. **During application review**: Partners quickly estimate whether the company has a viable trajectory
2. **Early batch**: Founders are asked to compute their default state in the first week
3. **Office hours**: Default state determines the conversation — default alive companies talk about growth strategy; default dead companies talk about crisis management
4. **Demo Day prep**: Default alive companies have leverage in fundraising; default dead companies need to close quickly at any terms
## Historical Context
The framework was formalized during a period when YC was scaling rapidly (batches growing from ~50 to ~100+ companies). Graham needed a simple, universally applicable diagnostic that partners could use across diverse business models. The default alive/dead test fit:
- **Deterministic** — just math, no judgment calls
- **Universal** — works for SaaS, marketplace, hardware, biotech, any model
- **Actionable** — the answer tells you what to do next
- **Honest** — you can't spin your way out of it
## Limitations
1. **Constant growth assumption** — Real startups grow in fits and starts. The projection is a range, not a prediction.
2. **Doesn't account for fundraising** — The test is "default alive without more funding." Many successful companies were default dead but raised money to reach default alive.
3. **Ignoring market dynamics** — A company can be default alive but in a dying market. Survival isn't the same as success.
4. **Pre-revenue blind spot** — The framework is less useful for pre-revenue startups, which are definitionally default dead until they have revenue.
## Companion Readings
- ["Startup = Growth" (2012)](https://paulgraham.com/growth.html) — The philosophical foundation
- ["Do Things That Don't Scale" (2013)](https://paulgraham.com/ds.html) — The tactical playbook for generating growth
- ["Ramen Profitability" (2009)](https://paulgraham.com/ramenprof.html) — The extreme cost-reduction strategy
- [YC Startup School: Unit Economics](https://www.startupschool.org/) — Practical metrics training
@@ -0,0 +1,102 @@
# Fundraising Context — How Default State Drives Strategy
## The Core Relationship
Your default alive/dead status determines your entire fundraising posture:
| Status | Leverage | Fundraising Strategy | Investor Sentiment |
|--------|----------|---------------------|--------------------|
| **Default Alive** | High | Optional, selective, patient | You pick investors |
| **Marginal** | Medium | Necessary but can be strategic | Mutual selection |
| **Default Dead** | Low | Existential, fast, any terms | Investors pick you |
## Default Alive Fundraising
When you're default alive, fundraising is a strategic choice, not a survival imperative. This gives you:
- **Time to build relationships** — You can meet investors without urgency
- **Ability to say no** — Bad terms, bad fits, bad VCs can be declined
- **Negotiating leverage** — Investors know you don't need them; they need allocation
- **Better terms** — Higher valuation, fewer board seats, more favorable provisions
Paul Graham's advice: "The best time to raise money is when you don't need it."
### Playbook for Default Alive Founders
1. **Grow first, fundraise second** — Every month of additional growth increases your valuation
2. **Be transparent about being default alive** — It signals strength, not weakness
3. **Run a constrained process** — 2-3 week concentrated outreach (YC's recommended pattern)
4. **Create price competition** — Multiple interested investors drive better terms
5. **Know your walkaway number** — If terms don't improve your trajectory, you don't need the money
## Default Dead Fundraising
When you're default dead, every week of delay reduces your options. Your goal is to extend runway by any means necessary, then use that runway to reach default alive.
### Playbook for Default Dead Founders
1. **Act immediately** — Every day of delay reduces your options
2. **Do the math first** — Know exactly how much runway you have
3. **Lower your target** — Raise less money on worse terms rather than not raising at all
4. **Consider bridge rounds** — Existing investors may extend at lower friction
5. **Cut costs while fundraising** — Every dollar saved is a week of runway gained
6. **Expand your aperture** — Angels, friends and family, revenue-based financing, grants — any source of capital
### Warning Signs (from YC partner observations)
- Founders who say "we're about to close a big customer" as the primary strategy
- Founders who can't answer "what happens if this round doesn't close?"
- Founders who haven't cut non-essential spending before fundraising
- Founders who waited until they had <3 months of runway to start fundraising
## The YC Fundraising Pipeline
YC teaches a specific fundraising methodology (as documented in their internal playbook):
### Phase 1: Preparation (2-4 weeks before outreach)
- Prepare data room: financials, cap table, deck, product demo
- Research target investors: who funds your stage, sector, geography
- Get warm intros through YC network (Bookface, alumni, partners)
- Practice the 10-second "what do you do?" answer
### Phase 2: Concentrated Outreach (2-3 weeks)
- Pitch 4-5 investors per day max (quality over quantity)
- Use a pipeline tracker template
- Send personalized, researched asks (not blast emails)
- Create urgency through density of meetings
### Phase 3: Follow-through (1-2 weeks)
- Respond to diligence requests within 24 hours
- Provide reference calls proactively
- Share positive momentum (new customers, new metrics) during process
### Phase 4: Close (1-2 weeks)
- Review term sheet against default state
- Negotiate key terms (valuation, board, pro rata, information rights)
- Close when you have a lead investor and competitive tension
## Burn Multiple and Fundraising Readiness
Marc Andreessen and Sam Altman have popularized the **Burn Multiple** as a shorthand for fundraising readiness:
```
Burn Multiple = Net Burn ÷ Net New ARR
```
| Burn Multiple | Fundraising Implication |
|--------------|------------------------|
| < 1x | Very attractive — efficient growth |
| 1x2x | Good — you can justify the ask |
| 2x3x | Challenging — need a compelling story |
| 3x5x | Difficult — you're destroying value |
| 5x+ | Almost impossible without insider round |
Investors at the seed and Series A stage typically accept 1.5x2.5x burn multiples for high-growth companies. Above 3x signals a structural problem.
## Sources
- Y Combinator's "What Happens at YC" (ycombinator.com/about)
- Paul Graham, "Default Alive or Default Dead?" (paulgraham.com/default.html)
- Paul Graham, "How to Die" (paulgraham.com/die.html)
- Sam Altman, "The Post-Pandemic Startup" (blog.samaltman.com)
- GrowthList YC Startup Database (growthlist.co/yc-startups/)
+412
View File
@@ -0,0 +1,412 @@
#!/usr/bin/env python3
"""
Default Alive / Default Dead Calculator
Paul Graham's foundational startup diagnostic: given current revenue, burn rate,
cash on hand, and growth rate, determine whether a startup will reach
profitability before running out of money.
Usage:
python default-alive.py --monthly-revenue 50000 --monthly-burn 120000 --cash-on-hand 800000 --monthly-growth 8
python default-alive.py --monthly-revenue 30000 --monthly-burn 75000 --cash-on-hand 500000 --monthly-growth 10 --json
python default-alive.py --monthly-revenue 50000 --monthly-burn 120000 --cash-on-hand 800000 --monthly-growth 8 --verbose
See SKILL.md for full documentation and methodology.
"""
import argparse
import json
import math
import sys
from typing import Optional
# ---------------------------------------------------------------------------
# Core calculation
# ---------------------------------------------------------------------------
MAX_PROJECTION_MONTHS = 120 # 10 years — safety limit
SAFETY_BUFFER_MONTHS = 3 # months of runway required post-breakeven
def project_trajectory(
monthly_revenue: float,
monthly_burn: float,
cash_on_hand: float,
monthly_growth_pct: float,
growth_decay_pct: float = 0.5,
fixed_burn_pct: float = 70.0,
) -> dict:
"""Project month-by-month financial trajectory.
Parameters
----------
monthly_revenue : Current monthly recurring revenue (MRR)
monthly_burn : Total monthly operating expenses
cash_on_hand : Cash reserves
monthly_growth_pct : Month-over-month revenue growth rate (%)
growth_decay_pct : Monthly decay in growth rate (%) — models market saturation
fixed_burn_pct : Percentage of burn that is fixed (vs. variable with revenue)
Returns
-------
dict with trajectory, verdict, and diagnostic metrics
"""
growth_rate = monthly_growth_pct / 100.0
decay_rate = growth_decay_pct / 100.0
fixed_burn = monthly_burn * (fixed_burn_pct / 100.0)
variable_burn_ratio = (monthly_burn * (1 - fixed_burn_pct / 100.0)) / max(monthly_revenue, 1)
revenue = monthly_revenue
cash = cash_on_hand
peak_revenue = revenue
current_growth = growth_rate
trajectory = []
breakeven_month: Optional[int] = None
cashout_month: Optional[int] = None
for month in range(1, MAX_PROJECTION_MONTHS + 1):
# Revenue grows (or decays) at current growth rate
revenue = revenue * (1 + current_growth)
# Growth rate decays toward zero
current_growth = current_growth * (1 - decay_rate)
# Burn: fixed component + variable component
variable_burn = revenue * variable_burn_ratio
total_burn = fixed_burn + variable_burn
# Cash flow
net_cash = revenue - total_burn
cash += net_cash
# Track peak revenue for decay modeling
peak_revenue = max(peak_revenue, revenue)
entry = {
"month": month,
"revenue": round(revenue, 2),
"burn": round(total_burn, 2),
"net_cash_flow": round(net_cash, 2),
"cash": round(cash, 2),
"growth_rate_pct": round(current_growth * 100, 2),
"profitable": net_cash >= 0,
}
trajectory.append(entry)
# Track first breakeven month
if net_cash >= 0 and breakeven_month is None:
breakeven_month = month
# Track cash-out month
if cash <= 0 and cashout_month is None:
cashout_month = month
cash = 0 # floor at zero
# Stop if both conditions are met (or we've run out of cash with no hope)
if breakeven_month is not None and cashout_month is not None:
break
# If we've been unprofitable for 5 years and cash is gone, stop
if month > 60 and cash <= 0 and net_cash < 0:
if cashout_month is None:
cashout_month = month
break
# ------------------------------------------------------------------
# Diagnostics
# ------------------------------------------------------------------
runway_months = cashout_month if cashout_month else MAX_PROJECTION_MONTHS
net_burn = monthly_burn - monthly_revenue
burn_multiple = round(net_burn / max(monthly_revenue, 1), 2) if monthly_revenue > 0 else None
# Net new ARR (monthly growth in ARR terms)
last_month_revenue = monthly_revenue
current_arr = monthly_revenue * 12
if monthly_growth_pct > 0 and monthly_revenue > 0:
next_arr = (monthly_revenue * (1 + monthly_growth_pct / 100)) * 12
net_new_arr = next_arr - current_arr
arr_burn_multiple = round(net_burn / max(net_new_arr, 1), 2) if net_new_arr > 0 else None
else:
net_new_arr = 0
arr_burn_multiple = None
# Verdict
if breakeven_month is not None and cashout_month is not None:
if breakeven_month < cashout_month:
post_breakeven_runway = cashout_month - breakeven_month
if post_breakeven_runway >= SAFETY_BUFFER_MONTHS:
verdict = "ALIVE"
else:
verdict = "MARGINAL"
else:
verdict = "DEAD"
elif breakeven_month is not None and cashout_month is None:
verdict = "ALIVE"
elif cashout_month is not None and breakeven_month is None:
verdict = "DEAD"
else:
verdict = "MARGINAL"
# Levers
levers = []
if verdict == "DEAD" or verdict == "MARGINAL":
if monthly_growth_pct < 15:
levers.append({
"name": "accelerate-growth",
"description": "Increasing growth rate to 15%/month would reach breakeven sooner",
"impact": "high",
})
if monthly_burn > monthly_revenue * 2:
levers.append({
"name": "reduce-burn",
"description": "Burn is more than 2x revenue — cost reduction extends runway directly",
"impact": "high",
})
levers.append({
"name": "fundraising",
"description": "Default Dead means fundraising is existential, not optional",
"impact": "critical",
})
if monthly_revenue > 0:
levers.append({
"name": "pricing",
"description": "20% price increase with <5% churn impact could shift trajectory significantly",
"impact": "medium",
})
# Monthly gap
gap_to_breakeven = monthly_burn - monthly_revenue
months_of_gap = round(cash_on_hand / max(gap_to_breakeven, 1), 1) if gap_to_breakeven > 0 else None
# ------------------------------------------------------------------
# Assemble result
# ------------------------------------------------------------------
result = {
"inputs": {
"monthly_revenue": monthly_revenue,
"monthly_burn": monthly_burn,
"cash_on_hand": cash_on_hand,
"monthly_growth_pct": monthly_growth_pct,
},
"diagnostics": {
"net_monthly_burn": round(net_burn, 2),
"burn_multiple": burn_multiple,
"arr_burn_multiple": arr_burn_multiple,
"current_arr": round(current_arr, 2),
"net_new_arr": round(net_new_arr, 2),
"runway_months": runway_months,
"months_to_breakeven": breakeven_month,
"cashout_month": cashout_month,
"gap_to_breakeven": round(gap_to_breakeven, 2),
"months_of_gap_remaining": months_of_gap,
"months_projected": len(trajectory),
},
"verdict": verdict,
"explanation": _generate_explanation(
verdict, runway_months, breakeven_month, cashout_month,
burn_multiple, arr_burn_multiple, monthly_revenue, monthly_burn,
cash_on_hand,
),
"levers": levers,
"trajectory": trajectory if len(trajectory) <= 60 else trajectory[:60],
}
return result
def _generate_explanation(
verdict: str,
runway_months: int,
breakeven_month: Optional[int],
cashout_month: Optional[int],
burn_multiple: Optional[float],
arr_burn_multiple: Optional[float],
monthly_revenue: float,
monthly_burn: float,
cash_on_hand: float,
) -> str:
"""Generate plain-English explanation of the verdict."""
lines = []
if verdict == "ALIVE":
lines.append(f"✅ DEFAULT ALIVE — You will reach profitability before running out of cash.")
if breakeven_month:
lines.append(f" Breakeven projected at month {breakeven_month}.")
elif verdict == "DEAD":
lines.append(f"❌ DEFAULT DEAD — You will run out of cash before reaching profitability.")
if cashout_month:
lines.append(f" Cash runs out at month {cashout_month}.")
if breakeven_month:
lines.append(f" Breakeven would require {breakeven_month} months — too late.")
else:
lines.append(f"⚠️ MARGINAL — Breakeven is possible but dangerously close to cash-out.")
if breakeven_month and cashout_month:
lines.append(f" Breakeven at month {breakeven_month}, cash-out at month {cashout_month}.")
lines.append(f" Only {cashout_month - breakeven_month} months of post-breakeven buffer (need {SAFETY_BUFFER_MONTHS}+).")
lines.append("")
if burn_multiple is not None:
lines.append(f" Burn Multiple: {burn_multiple}x ({'efficient' if burn_multiple < 2 else 'inefficient' if burn_multiple < 3 else 'critical'})")
if arr_burn_multiple is not None:
lines.append(f" ARR Burn Multiple: {arr_burn_multiple}x (net burn / net new ARR)")
lines.append(f" Runway: {runway_months} months")
lines.append(f" Monthly gap: ${monthly_burn - monthly_revenue:,.0f}")
lines.append(f" Cash: ${cash_on_hand:,.0f}")
return "\n".join(lines)
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def format_output(result: dict, verbose: bool) -> str:
"""Format the result as a human-readable report."""
lines = []
# Header
lines.append("=" * 60)
lines.append(" DEFAULT ALIVE / DEFAULT DEAD ANALYSIS")
lines.append("=" * 60)
lines.append("")
# Inputs
inputs = result["inputs"]
lines.append("── Inputs ──────────────────────────────────────────────")
lines.append(f" Monthly revenue: ${inputs['monthly_revenue']:>8,.0f}")
lines.append(f" Monthly burn: ${inputs['monthly_burn']:>8,.0f}")
lines.append(f" Cash on hand: ${inputs['cash_on_hand']:>8,.0f}")
lines.append(f" Monthly growth: {inputs['monthly_growth_pct']:>7.1f}%")
lines.append("")
# Verdict
diag = result["diagnostics"]
lines.append("── Verdict ─────────────────────────────────────────────")
lines.append(f" {result['verdict']}")
lines.append("")
lines.append(result["explanation"])
lines.append("")
# Diagnostics
lines.append("── Diagnostics ─────────────────────────────────────────")
lines.append(f" Net monthly burn: ${diag['net_monthly_burn']:>8,.0f}")
if diag["burn_multiple"] is not None:
lines.append(f" Burn Multiple: {diag['burn_multiple']:>8.2f}x")
if diag["arr_burn_multiple"] is not None:
lines.append(f" ARR Burn Multiple: {diag['arr_burn_multiple']:>8.2f}x (net burn ÷ net new ARR)")
lines.append(f" Current ARR: ${diag['current_arr']:>8,.0f}")
if diag["net_new_arr"]:
lines.append(f" Net new ARR/month: ${diag['net_new_arr']:>8,.0f}")
lines.append(f" Runway: {diag['runway_months']:>8} months")
lines.append(f" Months to breakeven: {diag['months_to_breakeven'] or 'never':>8}")
if diag["months_of_gap_remaining"]:
lines.append(f" Cash gap coverage: {diag['months_of_gap_remaining']:>8.1f} months at current spend")
lines.append("")
# Levers
if result["levers"]:
lines.append("── Levers ───────────────────────────────────────────────")
for lever in result["levers"]:
icon = {"high": "🔴", "medium": "🟡", "critical": "🚨"}.get(lever["impact"], "")
lines.append(f" {icon} {lever['name']}: {lever['description']}")
lines.append("")
# Trajectory (verbose only)
if verbose and result.get("trajectory"):
lines.append("── Monthly Trajectory ───────────────────────────────────")
lines.append(f" {'Mo':>4} {'Revenue':>10} {'Burn':>10} {'Net Cash':>10} {'Cash':>12} {'Growth':>7} {'Prof?':>5}")
lines.append(" " + "-" * 60)
for entry in result["trajectory"][:36]: # First 3 years
flag = "" if entry["profitable"] else ""
lines.append(
f" {entry['month']:>4} "
f"${entry['revenue']:>8,.0f} "
f"${entry['burn']:>8,.0f} "
f"${entry['net_cash_flow']:>8,.0f} "
f"${entry['cash']:>10,.0f} "
f"{entry['growth_rate_pct']:>5.1f}% "
f"{flag:>4}"
)
if len(result["trajectory"]) > 36:
lines.append(f" ... ({len(result['trajectory']) - 36} more months projected)")
lines.append("")
lines.append("=" * 60)
lines.append(" Paul Graham's Default Alive/Dead Framework")
lines.append(" paulgraham.com/default.html")
lines.append("=" * 60)
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Default Alive / Default Dead Calculator",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python default-alive.py --monthly-revenue 50000 --monthly-burn 120000 --cash-on-hand 800000 --monthly-growth 8
python default-alive.py --monthly-revenue 30000 --monthly-burn 75000 --cash-on-hand 500000 --monthly-growth 10 --json
python default-alive.py --monthly-revenue 50000 --monthly-burn 120000 --cash-on-hand 800000 --monthly-growth 8 --verbose
""",
)
parser.add_argument("--monthly-revenue", type=float, required=True, help="Current monthly recurring revenue")
parser.add_argument("--monthly-burn", type=float, required=True, help="Total monthly operating expenses")
parser.add_argument("--cash-on-hand", type=float, required=True, help="Current cash reserves")
parser.add_argument("--monthly-growth", type=float, required=True, help="Month-over-month revenue growth rate (%)")
parser.add_argument("--growth-decay", type=float, default=0.5, help="Monthly growth deceleration (%%, default: 0.5)")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--verbose", action="store_true", help="Show monthly projection")
parser.add_argument("--dry-run", action="store_true", help="Validate inputs and show what would be computed")
args = parser.parse_args()
# Validate
if args.monthly_revenue < 0:
parser.error("monthly-revenue must be >= 0")
if args.monthly_burn <= 0:
parser.error("monthly-burn must be > 0")
if args.cash_on_hand < 0:
parser.error("cash-on-hand must be >= 0")
if args.monthly_growth < 0:
parser.error("monthly-growth must be >= 0")
if args.growth_decay < 0 or args.growth_decay > 10:
parser.error("growth-decay should be between 0 and 10")
if args.dry_run:
print(json.dumps({
"status": "valid",
"inputs": {
"monthly_revenue": args.monthly_revenue,
"monthly_burn": args.monthly_burn,
"cash_on_hand": args.cash_on_hand,
"monthly_growth": args.monthly_growth,
},
"message": "Inputs validated. Run without --dry-run to compute.",
}, indent=2))
return
result = project_trajectory(
monthly_revenue=args.monthly_revenue,
monthly_burn=args.monthly_burn,
cash_on_hand=args.cash_on_hand,
monthly_growth_pct=args.monthly_growth,
growth_decay_pct=args.growth_decay,
)
if args.json:
# Strip trajectory unless verbose requested it
output = result.copy()
if not args.verbose and "trajectory" in output:
output["trajectory"] = f"{len(result['trajectory'])} months projected (use --verbose to show)"
print(json.dumps(output, indent=2))
else:
print(format_output(result, verbose=args.verbose))
if __name__ == "__main__":
main()
+208
View File
@@ -0,0 +1,208 @@
---
name: yc-weekly-growth-compass
description: >-
Paul Graham's "Startup = Growth" framework as an operational tool. Computes
weekly growth rates, benchmarks against YC tiers (1% concerning, 5-7% good,
10%+ exceptional), projects compound growth over time, and frames every
startup decision through the question "does this serve your target growth
rate?" Ships a deterministic CLI calculator. Load when founders ask about
growth rate, weekly growth, startup traction, metrics, or whether they're
moving fast enough.
license: MIT
compatibility: Python 3.9+ with standard library only (no external dependencies).
The growth-compass.py script uses only math, json, and sys.
metadata:
spec-version: "1.0"
tags: [startup-growth, growth-rate, ycombinator, paul-graham, startup-metrics,
weekly-growth, traction, compound-growth, startup-compass]
sources:
- https://paulgraham.com/growth.html
- https://paulgraham.com/ds.html
- https://www.ycombinator.com/about
skills: [yc-default-alive-calculator]
requires-toolsets: [terminal]
---
# Weekly Growth Compass
> "If there's one number every founder should always know, it's the company's growth rate. That's the measure of a startup. If you don't know that number, you don't even know if you're doing well or badly."
> — Paul Graham, "Startup = Growth" (September 2012)
This skill operationalizes Y Combinator's core growth philosophy into a repeatable weekly practice. The growth rate is not just a metric — it's a **compass** for every decision a founder makes.
## When to Load
| Trigger | Example |
|---------|---------|
| "What's my growth rate?" | Routine founder check-in |
| "Are we growing fast enough?" | Trajectory anxiety |
| "Should we prioritize feature X or growth Y?" | Decision framing |
| "Where will we be in a year?" | Projection/planning |
| "How does our growth compare to YC benchmarks?" | Benchmarking |
| "Is this initiative worth doing?" | Growth compass check |
| "How long to double our revenue?" | Milestone planning |
## How to Use
### Quick Rule of Thumb (No Script)
YC's empirical benchmarks, based on measuring thousands of startups:
| Weekly Growth | Annualized | YC Assessment |
|--------------|------------|---------------|
| 1% | 1.7x | Concerning — haven't found product-market fit |
| 2% | 2.8x | Below average — need significant acceleration |
| 5% | 12.6x | **Good** — solid trajectory, keep pushing |
| 7% | 33.7x | **Very good** — exceptional progress |
| 10% | 142.0x | **Outstanding** — rare breakout trajectory |
The inflection point: **5-7% weekly is the target zone YC coaches for.**
### Growth Compass Exercise (No Script)
Use this heuristic for any strategic decision:
1. State your target weekly growth rate (e.g., "7%")
2. For any proposed initiative, ask: *"Does this serve our target growth rate?"*
3. If yes → do it. If no → defer or drop it.
4. At end of week, measure actual growth. If you missed target, something else matters more than the thing you did.
This transforms the bewildering complexity of startup building into a single optimization problem, exactly as Graham intended.
### Full Analysis (Script)
```bash
python scripts/growth-compass.py \
--current-value 1200 \
--previous-value 1000 \
--period weekly
```
Or with a full series for trending:
```bash
python scripts/growth-compass.py \
--series "1000,1050,1100,1200,1350,1420" \
--period weekly
```
#### Required inputs
| Flag | Description | Example |
|------|-------------|---------|
| `--current-value` | Metric value this period | `1200` |
| `--previous-value` | Metric value last period | `1000` |
| Or `--series` | Comma-separated values over time | `"1000,1050,1100"` |
| `--period` | `weekly`, `monthly`, or `quarterly` | `weekly` |
#### Optional inputs
| Flag | Description | Example |
|------|-------------|---------|
| `--project-weeks` | Weeks to project forward (default: 52) | `104` |
| `--target-revenue` | Target metric to reach | `100000` |
| `--metric-name` | Label for the metric (default: "users/revenue") | `"MRR"` |
| `--json` | Machine-readable JSON output | |
| `--add-to-weekly` | After computation, log this week's value for next check | |
#### Output fields
| Field | Meaning |
|-------|---------|
| `growth_rate_pct` | Calculated growth rate for the period |
| `yc_assessment` | Benchmark label (Concerning/Below Average/Good/Very Good/Outstanding) |
| `projected_1yr` | Value after 52 weeks at current rate |
| `projected_2yr` | Value after 104 weeks at current rate |
| `doubling_time` | Periods to double at current rate |
| `time_to_target` | If target set, periods to reach it |
| `assessment` | Plain-English interpretation |
## Methodology
### Growth Rate Calculation
```
growth_rate = ((current_value - previous_value) / previous_value) × 100
```
For series with 3+ data points, the script computes:
- **Period-over-period rates** for each interval
- **Mean growth rate** (arithmetic average)
- **Median growth rate** (robust to outliers)
- **Compound weekly growth rate (CWGR)** — fitted from first to last value
### The Compass Principle
From Graham's essay: "We usually advise startups to pick a growth rate they think they can hit, and then just try to hit it every week. If they decide to grow at 7% a week and they hit that number, they're successful for that week. There's nothing more they need to do. But if they don't hit it, they've failed in the only thing that mattered."
The script operationalizes this by:
1. Computing whether you hit your target
2. Projecting forward at current rate vs. target rate
3. Showing the gap in absolute terms so founders feel the urgency
### Compound Growth Projection
```
future_value = current_value × (1 + growth_rate/100)^periods
```
**The magic of compound growth at YC benchmarks:**
Starting from $1,000/month MRR:
| Weekly Growth | Month 12 | Month 24 | Month 36 |
|--------------|----------|----------|----------|
| 1% | $1,700 | $2,900 | $4,900 |
| 5% | $12,600 | $159,000 | $2.0M |
| 7% | $33,700 | $1.1M | $38M |
| 10% | $142,000 | $20.2M | $2.9B |
The difference between 5% and 7% weekly is the difference between a lifestyle business and a category-defining company. Small variations in growth rate produce qualitatively different outcomes.
## The Decision Framework
### For the Founder
1. **Monday**: Set this week's growth target
2. **Every decision**: "Does this serve the target?"
3. **Friday**: Measure actual growth
4. **If hit**: Successful week, nothing else matters
5. **If missed**: Alarmed. What went wrong? Adjust.
### For the Investor/Advisor
Use YC's diagnostic frame:
```
What's your weekly growth rate? ─→ < 5%: Founders aren't doing
↓ unscalable things
↓ 5-7%: Good, keep pushing
↓ 10%+: Exceptional
What's your growth rate trend? ─→ Accelerating: product-market fit
↓ Decelerating: market saturation or churn
↓ Flat: plateau, needs intervention
Is revenue growing same as users? ─→ No: monetization gap
```
## Example: Early YC Company Assessment
```bash
python scripts/growth-compass.py \
--current-value 35000 \
--previous-value 32000 \
--period monthly \
--metric-name "MRR" \
--target-revenue 100000
```
Growth rate: 9.4% monthly (~2.3% weekly)
YC assessment: Below average weekly, solid monthly
1-year projection: ~$107K MRR
Doubling time: ~7.7 months
Months to $100K MRR: ~12 months
## References
- `references/growth-compass-framework.md` — Full essay breakdown with quotes
- `assets/compound-growth-table.md` — Reference table for quick lookup
- `yc-default-alive-calculator` companion skill — For financial sustainability analysis
@@ -0,0 +1,107 @@
# Compound Growth Reference Table
## How Small Differences Compound
The single most counterintuitive truth about startup growth: **small differences in growth rate produce qualitatively different outcomes.** A company growing 5% weekly and one growing 7% weekly look similar in their first month. After 3 years, one is earning $2M/month and the other is earning $38M/month.
Paul Graham's observation: *"Our ancestors must rarely have encountered cases of exponential growth, because our intuitions are no guide here."*
## Weekly Growth -> Annual Multiples
| Weekly % | Monthly % (approx) | 1-Year Multiple | 2-Year Multiple | 3-Year Multiple |
|----------|-------------------|----------------|----------------|-----------------|
| 1% | 4.4% | 1.7x | 2.8x | 4.8x |
| 2% | 8.9% | 2.8x | 7.8x | 21.8x |
| 3% | 13.6% | 4.7x | 22.0x | 102.8x |
| 4% | 18.5% | 7.9x | 62.6x | 495.9x |
| 5% | 23.6% | 12.6x | 159.0x | 2,003.5x |
| 6% | 28.9% | 21.0x | 441.1x | 9,262.0x |
| 7% | 34.5% | 33.7x | 1,137.2x | 38,336.1x |
| 8% | 40.3% | 55.3x | 3,058.6x | 169,197.3x |
| 10% | 52.8% | 142.0x | 20,170.0x | 2,864,450.0x |
*Note: These assume constant growth rates, which is unrealistic for long periods. Growth typically decelerates as markets saturate.*
## From $1,000 MRR
Projecting forward from $1,000 monthly revenue:
| Weekly % | Month 3 | Month 6 | Year 1 | Year 2 | Year 3 |
|----------|---------|---------|--------|--------|--------|
| 1% | $1,140 | $1,300 | $1,680 | $2,820 | $4,740 |
| 2% | $1,300 | $1,680 | $2,820 | $7,940 | $22,360 |
| 5% | $1,900 | $3,660 | $12,640 | $159,800 | $2,020,000 |
| 7% | $2,620 | $6,860 | $33,700 | $1,137,000 | $38,336,000 |
| 10% | $4,280 | $18,300 | $142,000 | $20,170,000 | $2,864,000,000 |
## From $10,000 MRR
| Weekly % | Month 3 | Month 6 | Year 1 | Year 2 |
|----------|---------|---------|--------|--------|
| 1% | $11,400 | $13,000 | $16,800 | $28,200 |
| 2% | $13,000 | $16,800 | $28,200 | $79,400 |
| 5% | $19,000 | $36,600 | $126,400 | $1,598,000 |
| 7% | $26,200 | $68,600 | $337,000 | $11,370,000 |
| 10% | $42,800 | $183,000 | $1,420,000 | $201,700,000 |
## Doubling Time
| Weekly Growth Rate | Weeks to Double | Months to Double | Years to Double |
|-------------------|----------------|------------------|-----------------|
| 1% | 69.7 | 16.1 | 1.34 |
| 2% | 35.0 | 8.1 | 0.67 |
| 3% | 23.4 | 5.4 | 0.45 |
| 4% | 17.7 | 4.1 | 0.34 |
| 5% | 14.2 | 3.3 | 0.27 |
| 7% | 10.2 | 2.4 | 0.20 |
| 10% | 7.3 | 1.7 | 0.14 |
## Practical Applications
### Target Setting
If you're at $10K MRR and want to reach $100K MRR (the YC Series A threshold):
| Weekly Growth | Time to $100K MRR |
|--------------|-------------------|
| 5% | ~17 weeks (~4 months) |
| 7% | ~12 weeks (~3 months) |
| 10% | ~8 weeks (~2 months) |
### The "Double Your Revenue" Question
Ask founders: "How long would it take to double your revenue at your current growth rate?"
| Answer | Assessment |
|--------|------------|
| < 2 months | Outstanding (10%+ weekly) |
| 2-3 months | Good (5-7% weekly) |
| 3-6 months | Below average |
| > 6 months | Concerning — growth engine isn't working |
### What This Means for Fundraising
The difference between 5% and 7% weekly growth over 12 months:
| Metric | 5% Weekly | 7% Weekly |
|--------|-----------|-----------|
| $10K MRR → | $126K MRR | $337K MRR |
| 12-month growth | 12.6x | 33.7x |
| Fundraising context | Seed/A round | Strong A round |
| Investor perception | "Promising" | "Hot" |
## The Power Law in Growth Rates
The startup world follows a power law: the best companies grow significantly faster than the median, and this difference compounds into an enormous valuation gap. The difference between "good" (5%) and "exceptional" (10%) weekly growth means:
- After 1 year: 12.6x vs 142x (11x difference)
- After 2 years: 159x vs 20,170x (127x difference)
- After 3 years: 2,003x vs 2,864,450x (1,430x difference)
This is why VCs are obsessed with finding the outliers — they're not just better, they're exponentially better.
## Sources
- Paul Graham, "Startup = Growth" (September 2012), paulgraham.com/growth.html
- Y Combinator Startup School, growth lectures
- Alex Schultz (Facebook/Meta CMO), "How to Grow Your Startup" — YC Startup School lecture
@@ -0,0 +1,115 @@
# Growth Compass — Paul Graham's "Startup = Growth" Framework
## Origin
The essay ["Startup = Growth"](https://paulgraham.com/growth.html) was published in September 2012. It is arguably the most important single essay Paul Graham wrote about Y Combinator's philosophy — the framework from which everything else follows.
## The Central Thesis
> "A startup is a company designed to grow fast. Being newly founded does not in itself make a company a startup. Nor is it necessary for a startup to work on technology, or take venture funding, or have some sort of 'exit.' The only essential thing is growth. Everything else we associate with startups follows from growth."
This reframing was radical: a restaurant, a barbershop, a consulting firm are not startups — even if they're newly founded. They're not designed for rapid growth. A startup is distinguished not by its industry, age, or funding status, but by its *growth trajectory*.
## Key Concepts
### Growth Rate is the Only Metric That Matters
> "If there's one number every founder should always know, it's the company's growth rate. That's the measure of a startup. If you don't know that number, you don't even know if you're doing well or badly."
YC measures growth **per week**, not per month. The reasoning:
1. The batch is only 11 weeks — monthly data gives you only 2-3 data points
2. Weekly measurement forces faster iteration
3. The feedback loop is tighter — you know within days whether something worked
### The YC Growth Benchmarks
| Weekly Growth | Assessment | Implication |
|--------------|------------|-------------|
| 5-7% | Good | Solid trajectory |
| 10% | Exceptional | Breakout company potential |
| 1% | Concerning | Haven't figured out what you're doing |
Graham's key observation: small variations in growth rate produce **qualitatively different outcomes**.
> "A company that grows at 1% a week will grow 1.7x a year, whereas a company that grows at 5% a week will grow 12.6x. A company making $1000 a month (a typical number early in YC) and growing at 1% a week will 4 years later be making $7900 a month, which is less than a good programmer makes in salary in Silicon Valley. A startup that grows at 5% a week will in 4 years be making $25 million a month."
### Growth as a Compass
The most operational insight: treat growth rate as the single decision metric.
> "Focusing on hitting a growth rate reduces the otherwise bewilderingly multifarious problem of starting a startup to a single problem. You can use that target growth rate to make all your decisions for you; anything that gets you the growth you need is ipso facto right."
This turns startup building into an **optimization problem**. Programmers will recognize this pattern — it's the same satisfying narrow focus as optimizing code.
### Growth Rate as Idea Discovery
Perhaps the most surprising claim: optimizing for growth can *discover the idea itself*.
> "You can use the need for growth as a form of evolutionary pressure. If you start out with some initial plan and modify it as necessary to keep hitting, say, 10% weekly growth, you may end up with a quite different company than you meant to start."
### The Three Phases of Startup Growth
```
Phase 1: Slow/no growth — figuring out what to do
Phase 2: Rapid growth (the ascent) — product-market fit, scaling
Phase 3: Slowdown — market saturation, internal limits
```
The phase that defines the startup is Phase 2. Its slope (growth rate) and length determine how big the company will become.
## From the Essay (Key Quotes)
### On user acquisition
> "You should take extraordinary measures not just to acquire users, but also to make them happy."
### On compound growth
> "If you're really getting a constant number of new customers every month, you're in trouble, because that means your growth rate is decreasing."
### On the value proposition
> "For startups, growth is a constraint much like truth. When Richard Feynman said that the imagination of nature was greater than the imagination of man, he meant that if you just keep following the truth you'll discover cooler things than you could ever have made up."
## How YC Operationalizes This
### During the Batch
1. **Week 1**: Founders establish a baseline metric and set a target growth rate
2. **Weekly check-ins**: Partners ask "what's your growth rate?" — not "how are you feeling?"
3. **Growth as the only goal**: If you hit your number, you had a good week. Nothing else matters.
4. **Office hours focus**: Conversations center on what will move the growth needle
### As a Founder's Habit
The weekly routine Graham recommends:
- **Monday**: Set this week's growth target
- **Daily**: Evaluate every decision against "does this serve the growth target?"
- **Friday**: Measure actual growth. If you hit it, celebrate. If you missed, be alarmed.
- **Weekend**: Let the miss marinate. Come back Monday ready to adjust.
## Historical Impact
"Startup = Growth" crystallized a philosophy that was already implicit in YC's operations but had never been explicitly articulated. Before this essay, the startup world was full of fuzzy advice about passion, vision, and disruption. After it, the conversation shifted to concrete metrics and growth loops.
This essay is directly responsible for:
- The obsession with growth rates in modern venture capital
- The "growth at all costs" philosophy (and its subsequent critique)
- The emphasis on rapid iteration and short feedback loops
- The psychological framing of startup building as an optimization problem
## Limitations and Critiques
1. **Growth isn't everything** — A company growing 10% weekly can still be a bad business (high churn, negative unit economics, toxic culture)
2. **The metric chosen matters** — Users ≠ revenue. Signups ≠ active users. Growth in the wrong metric is misleading.
3. **Context dependency** — Hardware, biotech, and deep tech startups cannot iterate weekly. The framework works best for software.
4. **The local maxima problem** — Hyper-optimizing for growth can lead startups up the wrong hill, building for metrics that don't translate to sustainable value.
## Companion Readings
- ["Do Things That Don't Scale" (2013)](https://paulgraham.com/ds.html) — The tactical playbook for generating the initial growth
- ["Default Alive or Default Dead?" (2014)](https://paulgraham.com/default.html) — What happens after you have growth
- ["Ramen Profitability" (2009)](https://paulgraham.com/ramenprof.html) — The extreme efficiency strategy
- Alex Schultz's Startup School lecture on growth — YC's most-watched talk
+491
View File
@@ -0,0 +1,491 @@
#!/usr/bin/env python3
"""
Weekly Growth Compass
Paul Graham's "Startup = Growth" framework as a CLI tool. Computes weekly
growth rates, benchmarks against YC tiers, projects compound growth over time,
and evaluates whether decisions serve the target growth rate.
Usage:
python growth-compass.py --current-value 1200 --previous-value 1000 --period weekly
python growth-compass.py --series "1000,1050,1100,1200,1350" --period weekly
python growth-compass.py --current-value 35000 --previous-value 32000 --period monthly --metric-name "MRR" --target-revenue 100000
python growth-compass.py --current-value 1200 --previous-value 1000 --period weekly --json
"""
import argparse
import json
import math
import sys
from typing import List, Optional, Tuple
# ---------------------------------------------------------------------------
# YC Growth Benchmarks
# ---------------------------------------------------------------------------
# Paul Graham's empirical benchmarks from "Startup = Growth" (2012)
# and YC's internal coaching targets during batch programs.
YC_BENCHMARKS = [
{"min_pct": 10.0, "label": "Outstanding", "color": "🟣",
"assessment": "Breakout trajectory — extremely rare. This is the Stripe/Coinbase zone."},
{"min_pct": 7.0, "label": "Very Good", "color": "🟢",
"assessment": "Exceptional progress. Top quartile of YC companies."},
{"min_pct": 5.0, "label": "Good", "color": "🟢",
"assessment": "Solid trajectory. YC's target zone — keep pushing."},
{"min_pct": 2.0, "label": "Below Average", "color": "🟡",
"assessment": "Below YC average. Need significant acceleration — likely haven't found product-market fit."},
{"min_pct": 0.0, "label": "Concerning", "color": "🔴",
"assessment": "Very low growth. Haven't figured out what you're doing."},
]
# Compound multipliers at various rates
COMPOUND_MULTIPLIERS = {
1: {"weekly": 1.01, "yearly": 1.68, "label": "Concerning"},
2: {"weekly": 1.02, "yearly": 2.81, "label": "Below Average"},
5: {"weekly": 1.05, "yearly": 12.64, "label": "Good"},
7: {"weekly": 1.07, "yearly": 33.73, "label": "Very Good"},
10: {"weekly": 1.10, "yearly": 142.04, "label": "Outstanding"},
}
def classify_growth(rate_pct: float) -> dict:
"""Classify a growth rate against YC benchmarks."""
for bench in YC_BENCHMARKS:
if rate_pct >= bench["min_pct"]:
return bench
return YC_BENCHMARKS[-1]
def period_normalizer(period: str) -> Tuple[str, int]:
"""Return (period_name, periods_per_year) for the given period."""
period = period.lower()
mapping = {
"weekly": ("weekly", 52),
"monthly": ("monthly", 12),
"quarterly": ("quarterly", 4),
}
if period not in mapping:
raise ValueError(f"Unknown period: {period}. Must be one of: weekly, monthly, quarterly")
return mapping[period]
def weekly_equivalent(rate_pct: float, from_period: str) -> float:
"""Convert a growth rate from any period to its weekly equivalent."""
if from_period == "weekly":
return rate_pct
periods_per_year = {"weekly": 52, "monthly": 12, "quarterly": 4}
n = periods_per_year[from_period]
# Convert: (1 + r_monthly)^(1/4.33) - 1 ≈ weekly rate
weekly_rate = (1 + rate_pct / 100) ** (1 / (n / 52)) - 1
return weekly_rate * 100
# ---------------------------------------------------------------------------
# Core calculations
# ---------------------------------------------------------------------------
def compute_growth_rate(current: float, previous: float) -> float:
"""Compute growth rate as a percentage."""
if previous <= 0:
return 0.0
return ((current - previous) / previous) * 100
def compute_series_rates(
values: List[float],
) -> Tuple[List[float], float, float, float]:
"""Compute growth rates from a time series of values.
Returns
-------
(period_rates, mean_rate, median_rate, cwgr)
where cwgr is the compound weekly growth rate fitted from first to last value.
"""
if len(values) < 2:
return [], 0.0, 0.0, 0.0
rates = []
for i in range(1, len(values)):
if values[i - 1] > 0:
rates.append(((values[i] - values[i - 1]) / values[i - 1]) * 100)
if not rates:
return [], 0.0, 0.0, 0.0
mean_rate = sum(rates) / len(rates)
sorted_rates = sorted(rates)
n = len(sorted_rates)
if n % 2 == 0:
median_rate = (sorted_rates[n // 2 - 1] + sorted_rates[n // 2]) / 2
else:
median_rate = sorted_rates[n // 2]
# Compound rate from first to last value
if len(values) >= 2 and values[0] > 0:
total_growth = values[-1] / values[0]
cwgr = (total_growth ** (1 / (len(values) - 1)) - 1) * 100
else:
cwgr = 0.0
return rates, mean_rate, median_rate, cwgr
def project_value(
current: float,
growth_rate_pct: float,
periods: int,
) -> float:
"""Project future value given a constant growth rate."""
return current * ((1 + growth_rate_pct / 100) ** periods)
def doubling_time(growth_rate_pct: float) -> float:
"""Compute the number of periods to double at the given growth rate."""
if growth_rate_pct <= 0:
return float("inf")
return math.log(2) / math.log(1 + growth_rate_pct / 100)
def time_to_target(
current: float,
target: float,
growth_rate_pct: float,
) -> Optional[float]:
"""Compute periods needed to reach target at given growth rate."""
if current >= target:
return 0.0
if growth_rate_pct <= 0:
return None
return math.log(target / current) / math.log(1 + growth_rate_pct / 100)
# ---------------------------------------------------------------------------
# Analysis
# ---------------------------------------------------------------------------
def analyze_growth(
current_value: float,
previous_value: Optional[float] = None,
series: Optional[List[float]] = None,
period: str = "weekly",
project_periods: int = 52,
target_value: Optional[float] = None,
metric_name: str = "users/revenue",
) -> dict:
"""Full growth analysis.
Parameters
----------
current_value : Current period's metric value
previous_value : Previous period's metric value (optional if series provided)
series : Full time series of values (optional, overrides previous_value)
period : 'weekly', 'monthly', or 'quarterly'
project_periods : Number of periods to project forward (default: 52)
target_value : Optional target metric to compute time-to-target
metric_name : Human-readable name for the metric
Returns
-------
dict with all computed fields
"""
period_name, periods_per_year = period_normalizer(period)
# Compute rate
if series and len(series) >= 2:
rates, mean_rate, median_rate, cwgr = compute_series_rates(series)
growth_rate = mean_rate if mean_rate != 0 else cwgr
series_info = {
"num_data_points": len(series),
"rates": [round(r, 2) for r in rates],
"mean_rate": round(mean_rate, 2),
"median_rate": round(median_rate, 2),
"cwgr": round(cwgr, 2),
"first_value": series[0],
"last_value": series[-1],
}
elif previous_value is not None:
growth_rate = compute_growth_rate(current_value, previous_value)
series_info = {
"num_data_points": 2,
"rate": round(growth_rate, 2),
}
else:
return {"error": "Either --previous-value or --series is required."}
# Classify
benchmark = classify_growth(growth_rate)
weekly_rate = weekly_equivalent(growth_rate, period)
weekly_benchmark = classify_growth(weekly_rate)
# Projections
projected_1yr = project_value(current_value, growth_rate, periods_per_year)
projected_2yr = project_value(current_value, growth_rate, periods_per_year * 2)
projected_N = project_value(current_value, growth_rate, project_periods)
# Doubling and target
double_p = doubling_time(growth_rate)
time_to_t = (time_to_target(current_value, target_value, growth_rate)
if target_value is not None else None)
# Growth rate tier table (what other rates would do)
tier_projections = {}
for rate_pct in [1, 2, 5, 7, 10]:
tier_projections[str(rate_pct)] = {
"label": COMPOUND_MULTIPLIERS.get(rate_pct, {}).get("label", ""),
"yearly_multiple": round((1 + rate_pct / 100) ** periods_per_year, 2),
"projected_1yr": round(project_value(current_value, rate_pct, periods_per_year), 2),
"doubling_periods": round(doubling_time(rate_pct), 1),
}
# Assessment text
if growth_rate >= 5:
assessment_text = (
f"At {growth_rate:.1f}% {period_name} growth, you're in YC's good-to-outstanding range. "
f"Keep pushing — compound growth at this rate transforms the business."
)
elif growth_rate >= 2:
assessment_text = (
f"At {growth_rate:.1f}% {period_name} growth, you're below YC's target zone. "
f"Paul Graham's advice: start doing things that don't scale. Recruit users manually, "
f"delight early customers, measure what works, and compound from there."
)
else:
assessment_text = (
f"At {growth_rate:.1f}% {period_name} growth, this is concerning. "
f"You haven't yet figured out what you're doing. Focus on finding something "
f"that a small number of users genuinely love — then grow from there."
)
result = {
"inputs": {
"current_value": current_value,
"previous_value": previous_value,
"metric_name": metric_name,
"period": period_name,
"project_periods": project_periods,
"target_value": target_value,
},
"series_info": series_info,
"growth_rate": {
"period_rate_pct": round(growth_rate, 2),
"weekly_equivalent_pct": round(weekly_rate, 2),
"period_name": period_name,
},
"benchmark": {
"label": benchmark["label"],
"icon": benchmark["color"],
"assessment": benchmark["assessment"],
"weekly_benchmark_label": weekly_benchmark["label"],
},
"projections": {
f"projected_{project_periods}_periods": round(projected_N, 2),
"projected_1_year": round(projected_1yr, 2),
"projected_2_years": round(projected_2yr, 2),
"doubling_time_periods": round(double_p, 1),
"time_to_target_periods": round(time_to_t, 1) if time_to_t is not None else None,
"target_value": target_value,
},
"tier_comparison": tier_projections,
"assessment_text": assessment_text,
}
return result
# ---------------------------------------------------------------------------
# Output formatting
# ---------------------------------------------------------------------------
def format_output(result: dict) -> str:
"""Format the analysis as a human-readable report."""
if "error" in result:
return f"Error: {result['error']}"
lines = []
inputs = result["inputs"]
rate = result["growth_rate"]
bench = result["benchmark"]
proj = result["projections"]
series = result["series_info"]
# Header
lines.append("=" * 60)
lines.append(f" WEEKLY GROWTH COMPASS — {bench['icon']} {bench['label']}")
lines.append("=" * 60)
lines.append("")
# Inputs
lines.append("── Inputs ──────────────────────────────────────────────")
lines.append(f" Metric: {inputs['metric_name']}")
lines.append(f" Current value: {inputs['current_value']:>10,.0f}")
if inputs.get("previous_value"):
lines.append(f" Previous value: {inputs['previous_value']:>10,.0f}")
lines.append(f" Period: {inputs['period']}")
lines.append(f" Data points: {series.get('num_data_points', 2)}")
lines.append("")
# Growth rate
lines.append("── Growth Rate ──────────────────────────────────────────")
lines.append(f" Period growth: {rate['period_rate_pct']:>7.2f}% ({rate['period_name']})")
lines.append(f" Weekly equiv: {rate['weekly_equivalent_pct']:>7.2f}%")
lines.append(f" YC Benchmark: {bench['icon']} {bench['label']}")
lines.append("")
if series.get("rates"):
rates = series["rates"]
lines.append(f" Period-over-period rates:")
for i, r in enumerate(rates):
arrows = "" if r > 0 else "" if r < 0 else ""
lines.append(f" Period {i+1}-{i+2}: {r:>6.2f}% {arrows}")
lines.append(f" Mean rate: {series['mean_rate']:>7.2f}%")
lines.append(f" Median rate: {series['median_rate']:>7.2f}%")
lines.append(f" CWGR: {series['cwgr']:>7.2f}% (compound from first to last)")
lines.append("")
# Assessment
lines.append("── Assessment ───────────────────────────────────────────")
lines.append(f" {result['assessment_text']}")
lines.append("")
# Projections
lines.append("── Projections ─────────────────────────────────────────")
lines.append(f" Doubling time: {proj['doubling_time_periods']:>7.1f} {rate['period_name']} periods")
lines.append(f" Projected 1 year: {proj['projected_1_year']:>10,.0f}")
lines.append(f" Projected 2 years: {proj['projected_2_years']:>10,.0f}")
if proj.get("time_to_target_periods") is not None and proj.get("target_value"):
lines.append(f" Time to target: {proj['time_to_target_periods']:>7.1f} {rate['period_name']} periods")
lines.append(f" Target value: {proj['target_value']:>10,.0f}")
lines.append("")
# Tier comparison
lines.append("── Growth Rate Comparison ──────────────────────────────")
lines.append(f" {'Rate':>6} {'Label':>18} {'1-Year Multiple':>18} {'1-Year Value':>16} {'Double In':>12}")
lines.append(f" {'-'*6} {'-'*18} {'-'*18} {'-'*16} {'-'*12}")
for rate_pct_str, tier in result["tier_comparison"].items():
rate_pct = int(rate_pct_str)
marker = "" if rate_pct == round(rate["period_rate_pct"]) else ""
lines.append(
f" {rate_pct:>5}% {tier['label']:>18} "
f"{tier['yearly_multiple']:>17.1f}x "
f"{tier['projected_1yr']:>14,.0f} "
f"{tier['doubling_periods']:>7.1f}p {marker}"
)
lines.append("")
# Compass question
lines.append("── The Compass Question ────────────────────────────────")
lines.append(f" Your target growth rate: {rate['period_rate_pct']:.1f}% {rate['period_name']}")
lines.append(f" For every decision this week, ask:")
lines.append(f" \"Does this serve our {rate['period_rate_pct']:.1f}% {rate['period_name']} growth target?\"")
lines.append(f" If yes → do it. If no → defer it.")
lines.append("")
lines.append(f" At end of week, measure actual growth against target.")
lines.append(f" If you missed, something else matters more than what you did.")
lines.append("")
lines.append("=" * 60)
lines.append(" Paul Graham, \"Startup = Growth\" (September 2012)")
lines.append(" paulgraham.com/growth.html")
lines.append("=" * 60)
return "\n".join(lines)
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description="Weekly Growth Compass — YC's growth rate framework",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python growth-compass.py --current-value 1200 --previous-value 1000 --period weekly
python growth-compass.py --series "1000,1050,1100,1200,1350" --period weekly
python growth-compass.py --current-value 35000 --previous-value 32000 --period monthly --metric-name "MRR" --target-revenue 100000
python growth-compass.py --current-value 1200 --previous-value 1000 --period weekly --json
""",
)
parser.add_argument("--current-value", type=float, help="Current period metric value")
parser.add_argument("--previous-value", type=float, help="Previous period metric value")
parser.add_argument("--series", type=str, help="Comma-separated time series (overrides --current/--previous)")
parser.add_argument("--period", type=str, default="weekly", choices=["weekly", "monthly", "quarterly"],
help="Period type (default: weekly)")
parser.add_argument("--project-periods", type=int, default=52,
help="Periods to project forward (default: 52)")
parser.add_argument("--target-value", type=float, help="Target metric value to compute time-to-target")
parser.add_argument("--metric-name", type=str, default="users/revenue",
help="Human-readable metric name (default: 'users/revenue')")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--dry-run", action="store_true", help="Validate inputs and show what would be computed")
args = parser.parse_args()
# Parse series if provided
series = None
if args.series:
try:
series = [float(x.strip()) for x in args.series.split(",")]
except ValueError:
parser.error("--series must be comma-separated numbers")
if len(series) < 2:
parser.error("--series must have at least 2 values")
# Validate
if not series and args.current_value is None:
parser.error("Either --current-value (with --previous-value) or --series is required")
if not series and args.previous_value is None:
parser.error("--previous-value is required when using --current-value")
if args.current_value is not None and args.current_value < 0:
parser.error("--current-value must be >= 0")
if args.previous_value is not None and args.previous_value < 0:
parser.error("--previous-value must be >= 0")
if args.target_value is not None and args.target_value < 0:
parser.error("--target-value must be >= 0")
if args.project_periods < 1:
parser.error("--project-periods must be >= 1")
if args.dry_run:
if series:
print(json.dumps({
"status": "valid",
"data_points": len(series),
"first_value": series[0],
"last_value": series[-1],
"period": args.period,
}, indent=2))
else:
print(json.dumps({
"status": "valid",
"current_value": args.current_value,
"previous_value": args.previous_value,
"period": args.period,
}, indent=2))
return
current_val: float = series[-1] if series else (args.current_value or 0.0)
result = analyze_growth(
current_value=current_val,
previous_value=args.previous_value,
series=series,
period=args.period,
project_periods=args.project_periods,
target_value=args.target_value,
metric_name=args.metric_name,
)
if args.json:
print(json.dumps(result, indent=2))
else:
print(format_output(result))
if __name__ == "__main__":
main()