STUD.com
← Objectives

Compute · Assess & Decide · Analysis & Finance

Compute NRR, GRR, net churn, and the Rule of 40

You get Gross Dollar Renewal Rate, Net Dollar Retention Rate, net churn rate, and the Rule-of-40 sum, each computed from your supplied revenue figures with the standard SaaS definitions.

You receive: A pure function that, given starting recurring revenue, expansion, downsell, churn, dollars eligible for renewal, dollars renewed, growth percent, and margin percent, returns GRR, NRR, net churn, the Rule-of-40 sum, and whether it clears 40.

Part of Get First Users

What's verified: STUD verifies the ratio ARITHMETIC: that GRR, NRR, net churn, and the Rule-of-40 sum are computed correctly from the inputs using the standard SaaS definitions (gross excludes expansion and caps at 100%; net includes expansion and can exceed 100%; net churn is the complement of NRR; Rule-of-40 sum is growth plus margin against 40). STUD does NOT verify that the supplied revenue figures are real or correctly extracted from the books, does NOT verify which retention definition is right for a given business, and does NOT assert that clearing the Rule of 40 is the correct goal. The Rule of 40 threshold itself is the book's stated rule of thumb (a SaaS convention, not a law); STUD only checks the math against it, not that 40 is the right bar. Definitional notes: GRR here is the gross RENEWAL rate (dollars renewed / dollars eligible for renewal), one legitimate convention, computed on a DIFFERENT base than the starting-revenue NRR returned alongside it, so the two are not a directly comparable pair and this is not the starting-ARR GRR some frameworks use. The Rule-of-40 margin is caller-chosen (free cash flow, EBITDA, or operating); STUD adds growth plus the margin as supplied and does not opine on which margin is correct, and the sum only compares across companies on the same margin basis. starting_revenue and dollars_eligible_for_renewal must be greater than 0.

Opens soon

Cost20 credits
AcceptanceAutomated check against your inputs
ProtectionHeld until verified delivery

This objective is verified and ready. It opens soon, once sign-in and payments are live.

Deliverable interface

The exact vocabulary the automated check enforces: keys, tokens, entry points, and worked examples. Generated from the verification source.

{
 "constants": {},
 "entryName": "compute_nrr_grr_rule_of_40",
 "expectNote": "expect is the comparison-space value (your return value goes through normalize first when one is published)",
 "hiddenCaseCount": 12,
 "hiddenCaseNames": [
  "leaky_bucket_fails",
  "perfect_retention_breakeven",
  "expansion_cancels_losses",
  "high_nrr_saas",
  "rule40_just_below",
  "rule40_negative_growth",
  "full_renewal_with_expansion",
  "severe_churn",
  "small_cohort_high_margin",
  "rule40_exactly_40_split",
  "large_enterprise",
  "downsell_heavy_positive_nrr"
 ],
 "inputKeys": [
  "churn",
  "dollars_eligible_for_renewal",
  "dollars_renewed",
  "downsell",
  "expansion",
  "growth_pct",
  "margin_pct",
  "starting_revenue"
 ],
 "normalizeSource": "def _normalize_compute_nrr_grr_rule_of_40(r):\n    if not isinstance(r, dict):\n        return repr(r)\n    try:\n        return {\n            \"grr\": round(float(r[\"grr\"]), 6),\n            \"nrr\": round(float(r[\"nrr\"]), 6),\n            \"net_churn\": round(float(r[\"net_churn\"]), 6),\n            \"rule_of_40_sum\": round(float(r[\"rule_of_40_sum\"]), 6),\n            \"clears_rule_of_40\": bool(r[\"clears_rule_of_40\"]),\n        }\n    except Exception:\n        return repr(r)\n",
 "returnShapes": [
  [
   "clears_rule_of_40",
   "grr",
   "net_churn",
   "nrr",
   "rule_of_40_sum"
  ]
 ],
 "signature": "def compute_nrr_grr_rule_of_40(inp):",
 "submission": "python exposing the entry function; inp is one input object; graded on held-out cases",
 "tier": "calculator",
 "visibleCases": [
  {
   "expect": {
    "clears_rule_of_40": true,
    "grr": 0.85,
    "net_churn": -0.05,
    "nrr": 1.05,
    "rule_of_40_sum": 60
   },
   "input": {
    "churn": 100000,
    "dollars_eligible_for_renewal": 1000000,
    "dollars_renewed": 850000,
    "downsell": 50000,
    "expansion": 200000,
    "growth_pct": 50,
    "margin_pct": 10,
    "starting_revenue": 1000000
   },
   "name": "healthy_expansion_clears"
  },
  {
   "expect": {
    "clears_rule_of_40": true,
    "grr": 1,
    "net_churn": 0,
    "nrr": 1,
    "rule_of_40_sum": 40
   },
   "input": {
    "churn": 0,
    "dollars_eligible_for_renewal": 1000000,
    "dollars_renewed": 1000000,
    "downsell": 0,
    "expansion": 0,
    "growth_pct": 100,
    "margin_pct": -60,
    "starting_revenue": 1000000
   },
   "name": "rule40_boundary_100_minus_60"
  }
 ],
 "vocabulary": [
  "churn",
  "clears_rule_of_40",
  "dollars_eligible_for_renewal",
  "dollars_renewed",
  "downsell",
  "expansion",
  "growth_pct",
  "grr",
  "margin_pct",
  "net_churn",
  "nrr",
  "rule_of_40_sum",
  "starting_revenue"
 ]
}

Get early access to STUD the day it goes live.