《独立开发者的商业指南·合同、应收与风险》12.3 保险、备份与应急预案的联动

保险、备份和预案不是三选一,而是三层覆盖:保险赔钱,备份减损,预案缩短停机。本节用 LedgerLite 的 100 万美元网络安全险对照 280,947 美元的单次最大损失,算出 3.56 倍覆盖与 10,000 美元缺口;再把 Tidecast 的 IP 与 Steam 缺口、Nord 的关键人与应收缺口放进同一张表,用缺口占 SDE 的比例判断「已知」是否等于「可承受」。

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

买保险的人常犯一个错:把保单当成「出事就赔」的开关。真出事时才发现,保单有免赔额、有除外责任、有单次限额和年度总限额;备份也一样,能恢复数据,但不能恢复客户;应急预案能缩短停机,但不能把损失降到零。三者不是三选一,而是三层覆盖,各管一段。

这一节把三层放在同一张表上,用「覆盖倍数 = 保障额度 ÷ 单次最大损失」和「缺口 = 最大损失 − 实际覆盖」两个数,判断哪些风险已经转移、哪些只是减损、哪些还完全敞着。最后用一个可承受阈值,把「已知」和「可承受」分开。

三层覆盖:各管一段

层作用覆盖什么不覆盖什么现金口径
保险转移财务损失责任赔偿、响应费用、部分营业中断免赔额、除外责任、未投保风险保障额度 ÷ 单次最大损失
备份降低损失数据、配置、构建产物收入中断、客户流失、法律责任恢复比例 × 最大损失
预案缩短停机与降低概率替代流程、责任人、触发条件无法消除的损失最大损失 − 恢复 − 保险
无自留无全部缺口 = 最大损失

三层的关系是「先减损,再转移,最后自留」。备份和预案先把损失压小,保险再对剩余损失赔付,剩下的免赔额和除外责任由自己承担。顺序不能反:如果先让保险赔,再让备份恢复,保险人会要求扣除已经恢复的部分;如果先有预案缩短停机,营业中断的赔付也会相应减少。

保险的核心数是覆盖倍数。它不是「保单金额越大越好」,而是「保障额度能不能覆盖一次事件的合理上限」。覆盖倍数小于 1,说明保额不够;大于 1,说明保额够,但不等于没有缺口——免赔额、除外责任和年度总限额仍然会留下自留部分。

备份的核心数是恢复比例。它不是「有没有备份」,而是「备份能恢复多少、要多久」。每天备份一次,RPO 是 24 小时;恢复要 8 小时,RTO 是 8 小时。备份能降低数据损失,但不能降低收入中断,也不能降低法律责任。

预案的核心数是剩余缺口。它不是「有没有写」,而是「写了之后,最坏情况还剩多少」。预案可以缩短停机、降低概率、提供替代路径,但它不能把损失变成零;它的价值是把「不可承受」压到「可承受」。

LedgerLite:$1M 网络安全险够不够

LedgerLite 的网络安全险保额是 $1,000,000。要判断够不够,先算单次最大损失。这里取三个部分:

  • 责任赔偿上限:公司层面 12 个月费用 $240,240;
  • 泄露响应成本:年收入的 15%,即 $36,036(教学假设);
  • 7 天营业中断(教学假设):MRR $20,020 × 7 ÷ 30 = $4,671。

三项相加,单次最大损失是 $240,240 + $36,036 + $4,671 = $280,947。

保单有 $10,000 的免赔额(教学假设)。覆盖倍数是:

覆盖倍数 = 1,000,000 ÷ 280,947 = 3.56
实际赔付 = 280,947 − 10,000 = 270,947
缺口 = 280,947 − 270,947 = 10,000

3.56 倍看起来绰绰有余,但缺口不是零:$10,000 的免赔额要自己承担。更重要的是,保单不覆盖所有事。LedgerLite 的云中断不在网络安全险的责任范围内;一周停机(教学假设)的损失约 $6,674,备份和预案能恢复 50%,剩余缺口 $3,337。这里的 $6,674 是现金影响,包含 SLA 抵扣;12.1 的登记册只算收入损失 $4,671,所以两个数字不同,口径也不同。把两块缺口加起来,LedgerLite 的总缺口是 $10,000 + $3,337 = $13,337,占 SDE $130,240 的 10.2%。

这个比例低于本节设定的 50% 可承受阈值(教学假设),所以 LedgerLite 的风险是「已知且可承受」的。注意,这不是说 LedgerLite 不会出事,而是说出事之后,最坏情况的自留部分在一年 SDE 的 10.2% 以内,不需要动用权益或借债。

还要检查保单的年度总限额。覆盖倍数是按单次事件算的;如果保单的年度总限额也是 $1M,一年内两次 $280,947 的事件就会把限额用掉 56.2%,第三次就没有保障了。案例只给了一个保额数字,没有给年度总限额;实际签约时要问清楚「单次限额」和「年度总限额」是不是同一个数。

Tidecast:没有保险的两种缺口

Tidecast 没有保险。它的两个主要风险都只能靠预案,缺口是全额减去预案能恢复的部分。

第一个是 IP 侵权。Tidecast 的音乐是买断授权,不得转授权;IP 授权报价是保底 $60,000/年 + 8% 净收入,3 年最低保证 $180,000。如果侵权导致授权终止,最坏情况是三年的最低保证全部落空,单次最大损失取 $180,000。12.1 的登记册按一年保底 $60,000 计期望损失;这里做覆盖测试,取三年最低保证 $180,000,因为一次侵权可能让整份授权终止。预案能做什么?授权链条审计、清权记录、替换曲库。教学假设预案能恢复 20%,剩余缺口是 $180,000 × 80% = $144,000。

第二个是 Steam 下架。Steam 占净收入的 80%,全年 $110,537。如果 Steam 下架,官网直售只能接住一部分;教学假设预案能恢复 20%,剩余缺口是 $110,537 × 80% = $88,430。

两块缺口合计 $144,000 + $88,430 = $232,430,占 SDE $121,171 的 191.8%。这个数字远超 50% 的可承受阈值。Tidecast 的风险是「已知」的,但不是「可承受」的;它需要转移或回避,而不是继续写预案。

量化的路径有三条:

  1. 买 IP 保险。假设 $180,000 保额、年保费 $4,500(教学假设),IP 缺口从 $144,000 降到免赔额(假设 $5,000),总缺口变成 $5,000 + $88,430 = $93,430,占 SDE 77.1%。
  2. 降低 Steam 占比。如果 Steam 从 80% 降到 60%,Steam 最大损失从 $110,537 降到 $82,903;按 20% 恢复,缺口 $66,322;加上 IP 保险后的 $5,000,总缺口 $71,322,占 SDE 58.9%。
  3. 继续降到 50%。Steam 最大损失 $69,086,缺口 $55,269;加上 $5,000,总缺口 $60,269,占 SDE 49.7%,刚好低于 50%。

所以 Tidecast 的「可承受」不是一句态度,而是一组动作:IP 保险 + Steam 占比降到 50%。这两个动作都可以量化,也都可以写进下一年的预算。

Nord:E&O 覆盖专业责任,但不覆盖集中度

Nord 有 $500,000 的 E&O 保险。E&O 覆盖专业责任:咨询交付出错、建议失误、交付物缺陷导致的客户索赔。它不覆盖关键人依赖,也不覆盖客户集中。

先算专业责任的单次最大损失。Nord 的咨询年收入是 $112,200;如果一个大客户因为交付问题要求退还全年咨询费,再加上 $24,000 的重做成本(教学假设取直接成本),单次最大损失是 $136,200。E&O 保额 $500,000,免赔额 $10,000(教学假设,与第 6.2 节一致):

覆盖倍数 = 500,000 ÷ 136,200 = 3.67
实际赔付 = 136,200 − 10,000 = 126,200
缺口 = 10,000

3.67 倍,缺口只有 $10,000。专业责任这一块,Nord 的覆盖是够的。

但 Nord 的两个更大缺口不在 E&O 里。

关键人依赖:一位合伙人缺席 6 个月,咨询收入减半,损失 $28,050。备份(文档化、客户交接清单)能恢复 40%,剩余缺口 $16,830。E&O 不赔「合伙人不能工作」,因为这不是专业责任,而是经营中断。

单一大额应收:年末应收中的单笔大额 $11,264。预案(信用额度、催收流程)能恢复 50%,剩余缺口 $5,632。E&O 不赔客户不付钱,因为这不是专业责任,而是信用风险。

Nord 的总缺口是 $5,000 + $16,830 + $5,632 = $27,462,占 SDE $34,320 的 80.0%,超过 50% 阈值。Nord 的风险也是「已知」但「不可承受」的。

要把 Nord 压到 50% 以下,缺口必须降到 $17,160 以内。可选的工具不是 E&O,而是另外两类:关键人保险(覆盖合伙人缺席)和信用保险(覆盖应收坏账)。如果关键人保险覆盖 $28,050、信用保险覆盖 $5,632,总缺口只剩 E&O 的 $5,000,占 SDE 14.6%。这就是「保险要按风险类型买」的意思:E&O 再好,也赔不了关键人和应收。

覆盖倍数与缺口:一张表

把三家的七条风险放进同一张表,按「最大损失 − 恢复 − 保险赔付」算缺口:

案例风险层单次最大损失保障额度覆盖倍数缺口
LedgerLite数据泄露保险$280,947$1,000,0003.56×$10,000
LedgerLite云中断备份 + 预案$6,674$00$3,337
TidecastIP 侵权预案$180,000$00$144,000
TidecastSteam 下架预案$110,537$00$88,430
Nord关键人依赖备份 + 预案$28,050$00$16,830
Nord专业责任保险$136,200$500,0003.67×$5,000
Nord单一大额应收预案$11,264$00$5,632
合计$753,672$273,229

这张表最重要的不是合计,而是结构:七条风险里只有两条有保险,两条的覆盖倍数都大于 3;另外五条只有备份或预案,缺口合计 $258,229,占全部缺口的 94.5%。保险覆盖的是「低频高损、有明确责任方」的风险;备份和预案覆盖的是「高频、责任分散、保险不赔」的风险。两者不能互相替代。

按案例汇总缺口占 SDE 的比例:

案例缺口合计SDE占 SDE判断
LedgerLite$13,337$130,24010.2%已知且可承受
Tidecast$232,430$121,171191.8%已知但不可承受
Nord$27,462$34,32080.0%已知但不可承受

50% 这个阈值是教学假设,不是行业标准。它的作用是给「可承受」一个可比较的定义:缺口不超过半年 SDE,说明一次最坏事件不会把当年的 owner earnings 全部吃掉;超过 50%,就需要在保险、备份、预案或业务结构上做调整。

这就是「风险敞口不是零,而是已知且可承受」的完整含义。敞口不是零:LedgerLite 还有 $13,337 的自留,Tidecast 还有 $232,430,Nord 还有 $27,462。已知:每一笔都能从案例数字复算,都有责任人和缓解措施。可承受:只有 LedgerLite 当前达标;Tidecast 和 Nord 的缺口还是「已知但不可承受」,它们的动作清单就是下一年的预算项。把「已知」变成「可承受」,才是这一节和下一章的分界。

写成代码:continuity-link.ts

下面的 continuity-link.ts 先算恢复,再算保险,最后算缺口。coverRisk 的顺序是:recoveryBps 先把最大损失压成净损失,保险再对净损失赔付,赔付上限是保额,扣除免赔额;gapCents 是净损失减去实际赔付。limitMultipleBps 用毛损失做分母,因为覆盖倍数回答的是「保额够不够覆盖事件本身」,不是「赔付后还剩多少」。

// file: src/contract/continuity-link.ts
// tier: L1
// verify: node --test test/contract/continuity-link.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";

export type ContinuityLayer = "insurance" | "backup" | "plan" | "none";

export type RiskCover = {
  name: string;
  layer: ContinuityLayer;
  maxLossCents: Cents;
  limitCents: Cents;
  deductibleCents: Cents;
  recoveryBps: Bps;
};

export type CoverRow = {
  name: string;
  layer: ContinuityLayer;
  maxLossCents: Cents;
  limitCents: Cents;
  deductibleCents: Cents;
  recoveryCents: Cents;
  netLossCents: Cents;
  coveredCents: Cents;
  gapCents: Cents;
  limitMultipleBps: Bps;
};

export type CoverSummary = {
  rows: CoverRow[];
  totalMaxLossCents: Cents;
  totalGapCents: Cents;
  worstGapName: string;
  worstGapCents: Cents;
};

function validAmount(value: Cents): boolean {
  return Number.isInteger(value) && value >= 0;
}

function validRate(value: Bps): boolean {
  return Number.isInteger(value) && value >= 0 && value <= 10000;
}

function isLayer(value: string): value is ContinuityLayer {
  return (
    value === "insurance" ||
    value === "backup" ||
    value === "plan" ||
    value === "none"
  );
}

/**
 * A loss is first reduced by backup or plan recovery, then paid by insurance
 * up to the limit, minus the deductible. The gap is what remains after both
 * layers. The limit multiple uses the gross loss, not the net loss, because
 * the question is whether the policy is large enough for the event itself.
 */
export function coverRisk(input: RiskCover): Result<CoverRow> {
  if (input.name.trim() === "") {
    return fail("risk name cannot be empty");
  }
  if (!isLayer(input.layer)) {
    return fail("layer must be insurance, backup, plan or none");
  }
  if (
    !validAmount(input.maxLossCents) ||
    !validAmount(input.limitCents) ||
    !validAmount(input.deductibleCents)
  ) {
    return fail("amounts must be non-negative integers in cents");
  }
  if (!validRate(input.recoveryBps)) {
    return fail("recovery must be between 0 and 10000 bps");
  }
  if (input.maxLossCents === 0) {
    return fail("max loss must be greater than zero");
  }

  const recoveryCents = applyRate(input.maxLossCents, input.recoveryBps);
  const netLossCents = input.maxLossCents - recoveryCents;
  const coveredCents = Math.max(
    0,
    Math.min(netLossCents, input.limitCents) - input.deductibleCents,
  );
  const gapCents = netLossCents - coveredCents;

  return ok({
    name: input.name,
    layer: input.layer,
    maxLossCents: input.maxLossCents,
    limitCents: input.limitCents,
    deductibleCents: input.deductibleCents,
    recoveryCents,
    netLossCents,
    coveredCents,
    gapCents,
    limitMultipleBps: divide(input.limitCents, input.maxLossCents),
  });
}

/**
 * The summary sums the gaps and names the largest one. A gap is not a failure
 * by itself; it is only a failure when it cannot be absorbed by SDE. The
 * caller supplies that test in the chapter, not in this function.
 */
export function summarizeCover(risks: RiskCover[]): Result<CoverSummary> {
  const rows: CoverRow[] = [];
  for (const risk of risks) {
    const row = coverRisk(risk);
    if (!row.ok) {
      return row;
    }
    rows.push(row.value);
  }
  const totalMaxLossCents = rows.reduce(
    (acc, row) => acc + row.maxLossCents,
    0,
  );
  const totalGapCents = rows.reduce((acc, row) => acc + row.gapCents, 0);
  let worstGapName = "";
  let worstGapCents = -1;
  for (const row of rows) {
    if (row.gapCents > worstGapCents) {
      worstGapName = row.name;
      worstGapCents = row.gapCents;
    }
  }
  return ok({
    rows,
    totalMaxLossCents,
    totalGapCents,
    worstGapName,
    worstGapCents: worstGapCents < 0 ? 0 : worstGapCents,
  });
}

把七条风险换成美分代进去:

  • LedgerLite 数据泄露:最大损失 28,094,700 美分,保额 100,000,000 美分,免赔 1,000,000 美分,恢复 0。覆盖倍数 35,594 bps(3.56×),实际赔付 27,094,700 美分($270,947),缺口 1,000,000 美分($10,000)。
  • LedgerLite 云中断:最大损失 667,400 美分,无保险,恢复 5,000 bps(50%)。净损失 333,700 美分($3,337),缺口同额。
  • Tidecast IP 侵权:最大损失 18,000,000 美分,无保险,恢复 2,000 bps(20%)。净损失 14,400,000 美分($144,000),缺口同额。
  • Tidecast Steam 下架:最大损失 11,053,700 美分,无保险,恢复 2,000 bps。净损失 8,842,960 美分($88,429.60),缺口同额。
  • Nord 关键人依赖:最大损失 2,805,000 美分,无保险,恢复 4,000 bps(40%)。净损失 1,683,000 美分($16,830),缺口同额。
  • Nord 专业责任:最大损失 13,620,000 美分,保额 50,000,000 美分,免赔 500,000 美分,恢复 0。覆盖倍数 36,711 bps(3.67×),赔付 13,120,000 美分($131,200),缺口 500,000 美分($5,000)。
  • Nord 单一大额应收:最大损失 1,126,400 美分,无保险,恢复 5,000 bps(50%)。净损失 563,200 美分($5,632),缺口同额。

汇总结果是总最大损失 75,367,200 美分($753,672),总缺口 27,322,860 美分($273,228.60),最大缺口是 Tidecast 的 IP 侵权,14,400,000 美分($144,000)。

代码只算「一次事件」的覆盖与缺口,不处理年度总限额、多次事件叠加和保险拒赔。它也不判断 50% 阈值是否适合你的生意;阈值是管理判断,不是代码逻辑。代码的用处是让每一笔缺口都能复算,避免「有保险就放心」和「没保险就绝望」这两种极端。

小结

  • 保险、备份、预案是三层覆盖:保险赔钱,备份减损,预案缩短停机;三者不能互相替代。
  • 覆盖倍数 = 保障额度 ÷ 单次最大损失;LedgerLite 的数据泄露是 3.56×,Nord 的专业责任是 3.67×。
  • 覆盖倍数大于 1 不等于没有缺口;免赔额、除外责任和年度总限额仍然留下自留部分。
  • LedgerLite 的单次最大损失 $280,947,保额 $1M,缺口 $10,000;加上云中断缺口 $3,337,总缺口 $13,337,占 SDE 10.2%。
  • Tidecast 没有保险,IP 缺口 $144,000、Steam 缺口 $88,430,合计 $232,430,占 SDE 191.8%;需要 IP 保险 + Steam 占比降到 50% 才能低于阈值。
  • Nord 的 E&O 覆盖专业责任(缺口 $5,000),但不覆盖关键人($16,830)和应收($5,632);总缺口 $27,462,占 SDE 80.0%。
  • 七条风险里只有两条有保险,保险覆盖的是低频高损、责任明确的风险;其余五条靠备份和预案。
  • 50% 阈值是教学假设;按这个口径,只有 LedgerLite 当前「已知且可承受」,Tidecast 和 Nord 还需要补缺口。
  • 风险敞口不是零,而是已知且可承受;「已知」靠这张表,「可承受」靠缺口占 SDE 的比例。

阅读导航:上一节:12.2 业务连续性计划 · 下一节:13.1 三案例合同矩阵 。

常见错误与边界

  1. 把保险当成「出事就赔」。 免赔额、除外责任、单次限额和年度总限额都会留下缺口;保单不是开关。
  2. 只看保额不看覆盖倍数。 $1M 的保额对 $280,947 的损失是 3.56×,对 $2M 的损失只有 0.5×;保额要和单次最大损失比。
  3. 覆盖倍数大于 1 就认为没有缺口。 LedgerLite 的 3.56× 仍然留下 $10,000 免赔额;倍数和缺口要一起看。
  4. 用备份替代保险。 备份能恢复数据,不能赔客户索赔;两者管的是不同的损失。
  5. 用保险替代备份。 保险赔钱,但不能恢复服务;停机期间客户流失,保险不赔。
  6. 用预案替代保险。 预案能缩短停机,但不能把 IP 侵权或平台下架的损失变成零;Tidecast 的缺口就是例子。
  7. 把 E&O 当成万能险。 E&O 只覆盖专业责任,不覆盖关键人、应收坏账和客户集中;Nord 的两个大缺口都不在 E&O 里。
  8. 忽略年度总限额。 覆盖倍数是按单次事件算的;一年多次事件会共享年度限额,第三次可能没有保障。
  9. 用毛损失算缺口。 缺口要减掉恢复和实际赔付;用毛损失会高估缺口,用保额会低估缺口。
  10. 把 50% 阈值当成行业标准。 它是教学假设;你的阈值取决于现金储备、融资能力和风险偏好。

本节的边界是「一次事件的保险、备份、预案覆盖与缺口」:只覆盖三层覆盖、覆盖倍数、缺口和占 SDE 的比例,不展开保单条款解释、理赔流程和保险定价。所有保额、免赔额、恢复比例和 50% 阈值都是教学假设;具体保单以合同为准,本节不构成保险或法律意见。风险的期望损失与排序见 12.1 风险登记册:概率、影响、责任人 ;停机损失与冗余回本点见 12.2 业务连续性计划 。

继续阅读

探索更多技术文章

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

全部文章 返回首页

「books」更多文章

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