《独立开发者的商业指南·收入与财务工程》13.2 单位经济与估值区间

单位经济回答一个客户赚不赚钱,估值区间回答整门生意值多少。本节把两者接起来:先用 LedgerLite 与 Nord Studio 的单位指标说明哪些数能进估值模型,再把三案例的基准倍数展开成低、基准、高三档区间,最后用敏感性表说明改善月流失与 CAC 如何移动谈判位置。

以下情境为教学假设,人物与数字不代表经核实的真实案例。

第 2 章算过单位经济,第 11 章算过估值区间。两章之间隔了十节,读者容易把它们当成两件事:单位经济是运营指标,估值是交易指标。实际上它们是一条链上的两段。买方为什么给 3.2 倍 ARR 而不是 2.0 倍?因为单位经济证明每个客户在两三个月内回本、生命周期毛利是获客成本的三十倍。单位经济不直接乘出估值,但它决定了倍数能不能站住。

这一节把链子接起来。第一步,把三案例的单位指标放到同一张表;第二步,把基准倍数展开成区间;第三步,用敏感性表说明改善哪几个指标能移动谈判位置。

三案例的单位指标

单位经济的标准算法只适用于订阅。买断制与服务型生意要换单位,否则会算出没有意义的 LTV。

指标LedgerLiteTidecastNord Studio(产品线)
单位客户·月份客户·月
单位收入$77.00$14.39(净得)$39.00
单位毛利$64.80$12.62(SDE/份)$32.92
月流失1.5%不适用3.0%
CAC$140不适用不适用
回本周期2.2 个月不适用不适用
LTV$4,320单份净贡献$1,097
LTV/CAC30.9不适用不适用

LedgerLite 的三个数互相印证:月毛利 64.80 美元、CAC 140 美元、回本 2.2 个月;按 1.5% 月流失,LTV 4,320 美元,LTV/CAC 30.9。这三个数一起出现时,单位经济是健康的。

Tidecast 的单位是「份」。9,600 份卖完,每份净得 14.39 美元,扣除营销、服务器、工具后每份贡献 SDE 12.62 美元。它没有「生命周期」,所以没有 LTV,也没有回本周期。硬套订阅公式会得到「LTV = 12.62 ÷ 0 = 无穷大」这种荒谬结果。

Nord Studio 的产品线可以算单位经济:39 美元 × 84.4% = 32.92 美元月毛利,月流失 3.0%,LTV 1,097 美元。但 CAC 没有单独给出——它的客户主要来自转介绍,获客成本混在合伙人的时间里。没有 CAC,就没有 LTV/CAC,也就无法判断产品线的单位经济是否健康。这是服务型生意的常见问题:成本算得清,获客算不清。

估值基数与区间

第 11 章讲过,倍数的分母必须与生意形态匹配。这一节把三案例的基准倍数展开成三档,得到区间。

案例基数低基准高区间宽度
LedgerLiteARR $240,2402.8× = $672,6723.2× = $768,7683.6× = $864,86425.0%
Tidecast净得 $138,1711.5× = $207,2571.8× = $248,7082.1× = $290,15933.3%
Nord StudioSDE $34,3203.0× = $102,9604.5× = $154,4405.0× = $171,60044.4%

三档的 spread 不是行业统计,而是教学设定:订阅读数的确定性最高,区间最窄;买断制依赖续作与季节,区间居中;服务型生意依赖人,区间最宽,且悲观档直接落到买方折价后的 3.0 倍。

区间宽度本身是信息。LedgerLite 的 25.0% 说明买卖双方对未来十二个月的 ARR 分歧不大;Nord 的 44.4% 说明分歧很大,而分歧的来源不是数字算错,而是「两位合伙人走了之后收入还剩多少」这个问题没有答案。

区间不是「取中间值成交」。真实交易里,基准档是卖方报价,悲观档是买方还价,最终价格由谈判与结构条款决定。区间的用途是让双方知道自己在争什么:争的是倍数,还是争的是把哪一档当成基准。

单位经济如何移动区间

单位经济不直接乘出估值,但它改变基准倍数的谈判位置。用 LedgerLite 做一次敏感性:固定 ARR 与区间宽度,只改月流失率。

月流失LTVLTV/CAC回本对基准倍数的含义
1.5%$4,32030.92.2 个月基准 3.2× 可站住
1.2%$5,40038.62.2 个月客户留存更强,卖方有理由要求高于 3.2×
1.0%$6,48046.32.2 个月单位经济显著优于基准,买方更难压价
2.0%$3,24023.12.2 个月LTV/CAC 仍健康,但留存恶化会被追问
3.0%$2,16015.42.2 个月接近警戒线,买方会把基准倍数往下压

回本周期不变,因为月毛利与 CAC 没变;变的是 LTV,因为 LTV 由月流失决定。这张表说明一件事:改善留存比降低 CAC 更能移动估值,因为留存同时影响 LTV 与「收入的可持续性」,而 CAC 只影响回本速度。

同样的逻辑适用于 Nord:产品线的月流失从 3.0% 降到 2.0%,LTV 会从 1,097 升到 1,646,但服务收入占比 72.7% 的结构没变,估值折价的核心原因仍在。单位经济改善能修好「产品线」这一小块,修不好「依赖两位合伙人」这个结构问题。

把它接成一条链

把三节的内容连起来,得到一条完整的链:

  1. 单位经济:每个客户或每份产品贡献多少毛利,多久回本。
  2. 结构指标:经常性收入占比、毛利率、集中度、创始人依赖。
  3. 估值基数:ARR、净得收入或 SDE。
  4. 基准倍数:由结构指标与单位经济共同支撑。
  5. 区间与结构:低、基准、高三档,再叠加 earn-out、托管与营运资金调整。

链上任何一环缺失,估值就会变成拍脑袋。缺单位经济,倍数没有支撑;缺结构指标,倍数无法解释折价;缺区间,价格变成一个没有误差范围的点;缺结构条款,headline 价格不等于净得。

unit-val.ts 把前四环写成了函数:unitMetrics 算回本、LTV、LTV/CAC 与健康度,unitValuation 拒绝在没有经常性收入时套用 ARR 倍数,并把基准倍数展开成区间。它的一个明确边界是:当基数为零却使用 arr 口径时直接报错,而不是返回一个看起来正常的零。

结构条款改写净得

区间给出的是 headline 价格,不是卖方最终拿到的钱。同样的 768,768 美元,结构不同,净得可以差出十几万。

案例Headline结构净得区间(教学假设)
LedgerLite$768,768现金 70%、earn-out 30%、托管 10%、营运资金下调 $3,000$650,453 – $765,768
Tidecast$248,708 整体出售;或 3 年授权 $180,000 保底 + 8% 分成二选一授权约 $213,161
Nord Studio$154,440 标准锚;买方 $102,960;合伙人退出 $77,220折价或分期$77,220 – $102,960

LedgerLite 的下限算法是:现金部分先扣托管 76,877 美元,earn-out 只兑现一半(115,315 美元),再减营运资金调整 3,000 美元,得到 650,453 美元。上限是 earn-out 与托管都全额兑现,769,768 减 3,000 得 765,768 美元。两个数之间的 115,315 美元,就是 earn-out 条款的价格。

Tidecast 的授权路径要单独算:3 年保底 180,000 美元,加上按年净得 138,171 美元的 8% 计算、三年合计 33,161 美元,得到 213,161 美元,比整体出售的 248,708 美元少 35,547 美元。授权不是「更稳所以更贵」,在本案例里它比出售便宜,换的是保留 IP 与续作空间。

Nord 的合伙人退出报价 77,220 美元,是 50% × 4.5 × SDE,用的是标准锚;买方整体收购 102,960 美元,用的是折价锚。同一年、同一门生意,两个价格用了两套倍数。这不是矛盾,而是交易对象不同:合伙人买的是内部权益,买方买的是可转移的生意。

把结构条款放回区间,才能回答「这笔交易值不值」。只看 headline,LedgerLite 比 Tidecast 贵三倍;看净得下限,差距会缩小,因为 earn-out 与托管都在打折。

这也是卷三要把交易拆成估值、尽调、结构、交割、过渡五段的原因:任何一段省略,净得都会算错。

小结

单位经济与估值区间的关系不是乘法,而是支撑关系。LedgerLite 的 3.2 倍 ARR 站得住,因为每个客户 2.2 个月回本、LTV/CAC 30.9;Nord 的 4.5 倍 SDE 被压到 3.0 倍,因为服务收入占 72.7%、交付依赖两位合伙人。改善留存能移动基准倍数,但修不好结构性问题。

区间宽度是风险信号:25.0%、33.3%、44.4% 的差异,对应三种不同的收入确定性。谈判时先确认双方争的是哪一档,再谈倍数与结构条款。

阅读导航:上一节:13.1 三张表的横向对照 · 下一节:13.3 卷一评审与进入卷二 。

常见错误与边界

  1. 给买断制算 LTV。 没有生命周期就没有 LTV,硬套公式会得到无穷大或零。
  2. 拿 LTV/CAC 直接乘出估值。 LTV/CAC 是健康度指标,不是倍数。它影响倍数能否站住,不决定倍数是多少。
  3. 忽略 CAC 缺失。 服务型生意的获客成本常混在人力里,缺失时不能默认它为零,应标注「不可计算」。
  4. 把区间中点当成交价。 区间是谈判范围,成交价由基准档、结构与双方议价决定。
  5. 只改善一个指标就要求提价。 留存改善能移动倍数,但集中度与创始人依赖不变时,折价仍然存在。
  6. 用同一 spread 套所有案例。 收入确定性不同,区间宽度就不同。统一 spread 会掩盖风险差异。

边界:本节的三档 spread 是教学设定,不是市场统计。真实交易中,倍数区间应由可比交易、买方战略与尽调结果共同决定。

// file: src/value/unit-val.ts
// tier: L1
// verify: node --test test/value/unit-val.test.ts

import { type Cents } from "../core/money.ts";
import { type Bps, applyRate, divide } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
import { type ScenarioRange, scenarioRange } from "./range.ts";

export type UnitEconomics = {
  monthlyGrossProfitPerCustomerCents: Cents;
  cacCents: Cents;
  monthlyChurnBps: Bps;
};

export type UnitMetrics = {
  paybackMonthsBps: Bps;
  ltvCents: Cents;
  ltvToCacBps: Bps;
  verdict: "healthy" | "watch" | "fragile";
};

export type ValuationBasis = "arr" | "sde" | "revenue";

export type UnitValuation = {
  basis: ValuationBasis;
  baseCents: Cents;
  range: ScenarioRange;
  impliedLtvMultipleBps: Bps;
};

function checkPositive(value: Cents, label: string): string | null {
  if (!Number.isInteger(value) || value <= 0) {
    return `${label} must be a positive integer amount in cents`;
  }
  return null;
}

/**
 * Payback is expressed in months, multiplied by 10000 so 2.2 months reads as
 * 22000 bps. LTV uses gross profit and monthly churn, the only combination
 * that stays comparable across the subscription cases in this book.
 */
export function unitMetrics(input: UnitEconomics): Result<UnitMetrics> {
  const grossError = checkPositive(
    input.monthlyGrossProfitPerCustomerCents,
    "monthly gross profit per customer",
  );
  if (grossError) {
    return fail(grossError);
  }
  const cacError = checkPositive(input.cacCents, "CAC");
  if (cacError) {
    return fail(cacError);
  }
  if (
    !Number.isInteger(input.monthlyChurnBps) ||
    input.monthlyChurnBps <= 0 ||
    input.monthlyChurnBps > 10000
  ) {
    return fail("monthly churn must be between 1 and 10000 bps");
  }

  const paybackMonthsBps = divide(input.cacCents, input.monthlyGrossProfitPerCustomerCents);
  // divide(a, b) already returns a / b * 10000, so dividing cents by churn in
  // bps yields cents: gross * 10000 / churnBps.
  const ltvCents = divide(
    input.monthlyGrossProfitPerCustomerCents,
    input.monthlyChurnBps,
  );
  const ltvToCacBps = divide(ltvCents, input.cacCents);

  let verdict: UnitMetrics["verdict"] = "healthy";
  if (ltvToCacBps < 30000 || paybackMonthsBps > 120000) {
    verdict = "fragile";
  } else if (ltvToCacBps < 90000 || paybackMonthsBps > 60000) {
    verdict = "watch";
  }

  return ok({ paybackMonthsBps, ltvCents, ltvToCacBps, verdict });
}

/**
 * A valuation multiple only means something next to its base. This function
 * refuses to build a range when the basis is `arr` but the base value is zero,
 * which is exactly the mistake made when a one-off product is labelled SaaS.
 */
export function unitValuation(
  basis: ValuationBasis,
  baseCents: Cents,
  baseMultipleBps: Bps,
  spreadBps: Bps,
  ltvCents: Cents,
): Result<UnitValuation> {
  const baseError = checkPositive(baseCents, "valuation base");
  if (baseError) {
    return fail(baseError);
  }
  if (!Number.isInteger(ltvCents) || ltvCents <= 0) {
    return fail("LTV must be a positive integer amount in cents");
  }
  if (basis === "arr" && baseCents === 0) {
    return fail("ARR basis requires a recurring revenue base");
  }
  const range = scenarioRange(baseCents, baseMultipleBps, spreadBps);
  if (!range.ok) {
    return range;
  }
  return ok({
    basis,
    baseCents,
    range: range.value,
    impliedLtvMultipleBps: divide(range.value.baseCents, ltvCents),
  });
}

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《移动游戏产品的架构·商业化与发行工程》附录 C 商业化与发行评审清单
  2. 《移动游戏产品的架构·商业化与发行工程》附录 B 双案例商业化事实表
  3. 《移动游戏产品的架构·商业化与发行工程》附录 A 商业化与发行术语速查