以下情境为教学假设,人物与数字不代表经核实的真实案例。
融资到账之后,投资人关系才真正开始。签约前,材料是一次性的项目;签约后,报表是一个每月重复的产品。产品有固定字段、固定节奏和固定口径,任何一项变动都会被对方看见。很多创始人把报表当义务,想起来才写,结果每个月都要重新解释「为什么这个数和上个月不一样」;更贵的是,解释一次不够,对方会从头再问一遍。
这一节只讨论报表本身:放哪五个字段、月度与季度各更新什么、口径为什么必须和卷一一致、每月要花多少小时。它不讨论董事会怎么开、信息权怎么谈,那些是下一节。
报表是产品,不是义务
投资人报表的读者不是税务局,也不是会计师,而是已经把钱交给你、需要判断「这笔钱是否按计划使用」的人。这个读者有三个特点:第一,他不会每天看,但会在关键节点翻旧账;第二,他不掌握你的内部上下文,只能靠连续几期报表建立趋势;第三,他最怕的不是坏消息,而是「上一次说的和这一次不一样」。
因此报表的第一目标不是「好看」,而是「可连续比较」。可连续比较要求三件事:字段固定、顺序固定、口径固定。字段固定让读者知道去哪里找;顺序固定让读者能一眼看出哪一格变了;口径固定让上一期和这一期能直接相减。三件事里,口径最容易被忽视,也最贵。
LedgerLite 在融资后进入月度报表节奏:每月 10 个工作日内发出上月月报,每季度末发一份季报,董事会前一并更新。教学假设下,月报由创始人自己写,不请财务;这也是本节计算维护成本的前提。
五个固定字段与两个节奏
月报不需要覆盖所有经营细节。五个字段足够回答「钱还在不在、收入有没有长、客户有没有漏、下一批收入在哪里、有什么坏事要提前说」。季度报表在五个字段上加预算与实际、 cohort 和 runway 情景,但不改变五个字段的定义。
| 字段 | 月度内容 | 季度补充 | 口径 | 常见漂移 |
|---|---|---|---|---|
| 现金 | 期末现金、月净烧钱、零收入 runway | 预算与实际、最低水位 | 银行账户可用余额,不含授信 | 把应收算进现金 |
| 收入 | MRR/ARR、净增桥、NRR/GRR | cohort、按套餐/渠道拆分 | 订阅收入、按月折算、折扣已扣 | 把一次性收入算进 ARR |
| 流失 | 收入流失、客户流失、原因分类 | 流失 cohort、挽留结果 | 收入口径为主,客户口径并列 | 这个月用收入、下个月用客户 |
| Pipeline | 阶段、加权金额、预计成交月 | win/loss 复盘、转化率 | 加权金额 = 金额 × 阶段概率 | 阶段定义每季换一次 |
| 风险 | 前三大风险、owner、下一步、时限 | 风险矩阵与缓解进度 | 只写会影响现金或交付的事 | 风险栏留空或只写「无」 |
节奏比字段更容易被低估。教学假设:月报在次月第 10 个工作日前发出;季报在季度结束后 30 天内发出;董事会材料在会议前 5 个工作日发出。三个时间点一旦定下,就不要因为「这个月没什么好说」而跳过。跳过一期,下一期就要补两期,补的那期通常口径已经变了。
月报和季报的分工也要固定。月报回答「这个月发生了什么」,季报回答「这个季度和预算差多少、下个季度怎么调」。月报里不写长战略,季报里不重复月报的每个数字。把两者混在一起,结果是月报太长没人看,季报太短说不清。
口径必须和卷一一致
卷一把收入、毛利、SDE、现金流和 runway 的口径已经定完;投资人报表不重新发明一套,只沿用同一套。原因很实际:投资人在签约前看过数据室里的历史数据,签约后看月报;如果月报换了口径,他会以为业务变了,而不是口径变了。
以 LedgerLite 为例,月报里的 ARR 必须还是 MRR × 12:20,020 × 12 = 240,240;毛利率必须还是收入减变动成本:202,240 ÷ 240,240 = 84.2%;月流失必须还是收入口径 1.5%,旁边附 GRR 83%、NRR 102%。三个数在卷一和卷三必须是同一个算法,连四舍五入的位置都一样。
| 指标 | 卷一已定口径 | 报表里不能改成 | 改了会发生什么 |
|---|---|---|---|
| ARR | MRR × 12,订阅、按月、折扣已扣 | 把年付合同全额一次性计入 | ARR 突然跳高,下一期又掉回,趋势断裂 |
| 毛利率 | (收入 − 变动成本) ÷ 收入 | 把支持成本挪到运营费用 | 毛利率变高,SDE 变低,两期不可比 |
| 流失 | 当月流失 MRR ÷ 期初 MRR | 改成客户数流失 | 1.5% 可能变成另一个数,历史序列作废 |
| CAC 回收 | CAC ÷ 月毛利/客户 | 用 LTV/CAC 代替 | 依赖流失假设,无法和上月直接比 |
| Runway | 现金 ÷ 月固定成本,零收入 | 加入增长假设 | 8.6 个月被说成更长,风险被隐藏 |
口径漂移的代价可以算。教学假设:每改一次定义,投资人会要求一次解释电话(1 小时)和一次历史重述(2 小时),合计 3 小时;如果一年改三个定义,就是 9 小时,按 $100/小时折 $900。这还没算对方重新建模型的信任成本。更常见的情况是:创始人不是故意改,而是每个月从不同报表里取数,取着取着就变了。解决办法不是「下次注意」,而是把口径写进模板,取数只从固定的一张表来。
卷一已经把五个核心指标和阈值讲完,这里只强调一句:报表不是重新定义指标的地方,而是证明同一套指标在连续期间没有变形。需要回看指标定义时,回到卷一的五个核心指标一节 ;需要回看关账流程时,回到月度关账一节 。
维护成本:每月 4 小时 × 12 个月
按教学假设,LedgerLite 的月报每月 4 小时:取数 1.5 小时、对账 1 小时、写 commentary 1 小时、发送与答疑 0.5 小时。一年 12 期,48 小时,按 $100/小时折 $4,800。
| 工作 | 每月小时 | 年度小时 | 年度成本($100/小时) |
|---|---|---|---|
| 取数(MRR、现金、Pipeline) | 1.5 | 18 | $1,800 |
| 对账(银行、账单、CRM) | 1.0 | 12 | $1,200 |
| 写 commentary 与风险栏 | 1.0 | 12 | $1,200 |
| 发送与答疑 | 0.5 | 6 | $600 |
| 月报合计 | 4.0 | 48 | $4,800 |
季度报表在月报之上再加 6 小时:预算与实际 2 小时、cohort 1 小时、runway 情景 1 小时、董事会材料整理 2 小时。一年 4 期,24 小时,折 $2,400。月报加季报合计 72 小时,$7,200。
| 报表 | 每期小时 | 每年期数 | 年度小时 | 年度成本 |
|---|---|---|---|---|
| 月报 | 4 | 12 | 48 | $4,800 |
| 季报增量 | 6 | 4 | 24 | $2,400 |
| 合计 | 72 | $7,200 |
这个数要和「不写报表」的成本比。教学假设:没有固定报表时,投资人每月问 2–3 个问题,每个问题连同找数、解释、来回确认约 1 小时,一年 24–36 小时,折 $2,400–$3,600;这还没算被打断的上下文切换。固定报表的 48 小时看起来更多,但它把「随时被问」变成「每月一次集中回答」,并且留下了可追溯的记录。真正省下的不是小时数,而是不可预测性。
如果取数能自动化,4 小时可以降到 2 小时,一年省 24 小时,折 $2,400。但自动化要在口径稳定之后做:先手动跑三期,确认五个字段的定义不再变,再把取数脚本接上。反过来,先写脚本再改口径,脚本会跟着改三次,省下的时间又还回去。
月报模板与季度补充
月报不需要长,但要有固定骨架。教学假设的六段式如下:
- 一句话总结:这个月最重要的一个变化,以及它对现金或增长的影响。
- 五个数:现金、MRR/ARR、流失、Pipeline、风险,各一行,附上期对比。
- MRR 桥:新签、扩张、收缩、流失四段,期末减期初必须对得上。
- 三个动作:这个月做了什么,结果是什么,下个月继续还是停。
- 三个风险:每个风险写 owner、下一步、时限;没有风险也要写「本月无新增」,不能留空。
- 需要什么帮助:具体到人、事、时间,不写「希望多支持」。
季度报表在月报骨架上加三块:预算与实际(收入、成本、现金各一行,差异超过 10%(教学假设)要解释)、cohort(按签约月份看留存和扩张)、runway 情景(零收入、当前计划、延迟收款三种)。三块都不改变五个字段的定义,只是把同一套数拉长时间看。
commentary 是最容易写成空话的部分。对比两种写法:一种写「本月收入增长良好,团队继续努力」;另一种写「MRR 从 19,280 走到 20,020,净增 740;新签 +780、扩张 +310、收缩 −60、流失 −290;净增主要来自新签,NRR 102%,存量没有明显恶化」。第二种只多了三行数字,但投资人能判断增长质量,也能在三个月后回看趋势。月报的价值不在于形容词,而在于把「发生了什么」拆到可复算的粒度。
发送前的五项检查
月报写完不等于可以发。教学假设的发送前检查有五项,每项不超过 10 分钟:
| 检查 | 问题 | 不过怎么办 |
|---|---|---|
| 数字对账 | 五个数能否和银行、账单、CRM 对上 | 先对账,不发送 |
| 上期对比 | 每个数是否有上期值 | 补上期,或注明「首期」 |
| 风险 owner | 每个风险是否有负责人和时限 | 补 owner,不留空 |
| 口径未变 | 五个字段定义是否和上期一致 | 改回口径,变更单独说明 |
| 附件可打开 | 链接和附件是否有权限 | 换链接或附 PDF |
五项检查的意义不是形式,而是把「发出去之后才发现的错」提前到「发出去之前」。教学假设每项 10 分钟,五项 50 分钟,已经包含在每月 4 小时的维护成本里;如果不做检查,省下的 50 分钟通常会在解释电话里还回去。
下面的 reporting.ts 把五个固定字段、月度/季度期数和口径基线固化成一次检查。它不生成报表内容,只回答三个问题:字段齐不齐、口径有没有漂移、这一年要写多少期。金额和比率不在这个模块里计算,因为报表的核心风险不是算错,而是字段缺失和定义悄悄改变。
// file: src/capital/reporting.ts
// tier: L1
// verify: node --test test/capital/reporting.test.ts
import { type Result, fail, ok } from "../core/result.ts";
export type Cadence = "monthly" | "quarterly";
export type ReportField =
| "cash"
| "revenue"
| "churn"
| "pipeline"
| "risks";
export const REPORT_FIELDS: ReportField[] = [
"cash",
"revenue",
"churn",
"pipeline",
"risks",
];
export type ReportDraft = {
period: string;
cadence: Cadence;
values: Partial<Record<ReportField, string>>;
definitions: Record<string, string>;
};
export type ReportCheck = {
period: string;
cadence: Cadence;
missing: ReportField[];
drift: string[];
complete: boolean;
};
const PERIOD_PATTERN = /^\d{4}-(0[1-9]|1[0-2])$/;
export function reportsPerYear(cadence: Cadence): number {
return cadence === "monthly" ? 12 : 4;
}
/** Teaching cost model: reports per year times hours per report. */
export function annualReportingHours(
cadence: Cadence,
hoursPerReport: number,
): number {
if (!Number.isFinite(hoursPerReport) || hoursPerReport < 0) {
return 0;
}
return reportsPerYear(cadence) * hoursPerReport;
}
function isCadence(value: string): value is Cadence {
return value === "monthly" || value === "quarterly";
}
/**
* A report is complete only when every fixed field has a value and every
* definition matches the baseline used in earlier periods. A changed
* definition is reported as drift even when the new value is correct.
*/
export function checkReport(
draft: ReportDraft,
baseline: Record<string, string>,
): Result<ReportCheck> {
if (!PERIOD_PATTERN.test(draft.period)) {
return fail("period must be YYYY-MM");
}
if (!isCadence(draft.cadence)) {
return fail("cadence must be monthly or quarterly");
}
const missing = REPORT_FIELDS.filter((field) => {
const value = draft.values[field];
return value === undefined || value.trim() === "";
});
const drift = Object.keys(baseline).filter(
(key) => draft.definitions[key] !== baseline[key],
);
return ok({
period: draft.period,
cadence: draft.cadence,
missing,
drift,
complete: missing.length === 0 && drift.length === 0,
});
}
代码有五个细节。第一,REPORT_FIELDS 的顺序就是月报的顺序,五个字段不能因为某个月「没有 Pipeline」而删掉一格。第二,missing 只检查空值,不把「0」当缺失;0 是一个有效读数,空才是缺口。第三,drift 拿本期定义和基线逐键比较,定义变了就报告,即使新值本身正确。第四,reportsPerYear 把月度 12 期、季度 4 期写死,避免「这个季度忙,少发一期」变成习惯。第五,annualReportingHours 对负数返回 0,而不是抛错;它只是成本估算,不应该让一个坏参数把整张报表卡住。
不融资的对照
Nord Studio 不融资,没有投资人报表,只有内部月度关账和两位合伙人的利润分配安排。它的报表读者是合伙人自己,字段可以随业务调整,但一旦要卖公司,买方仍会按投资人的标准重问一遍。
Tidecast 不融资,也没有董事会,只保留平台销售报表和年度税务记录。它的现金流是买断脉冲,月报的「MRR」不适用,改用滚动三个月净得;这不是报表做得好,而是业务形态不同。
两个对照说明一件事:报表的复杂度应该跟资金来源匹配。没有外部投资人时,内部报表可以简;一旦拿了钱,五个字段和固定节奏就是最低成本的选择,不是额外负担。
小结
- 融资后,报表从一次性材料变成每月产品;第一目标不是好看,而是可连续比较。
- 五个固定字段:现金、收入、流失、Pipeline、风险;顺序和口径都不能随月份变。
- 月报次月 10 个工作日内发,季报季度结束后 30 天内发,董事会材料会前 5 个工作日发(教学假设)。
- 口径必须和卷一一致:ARR = MRR × 12、毛利率 = (收入 − 变动成本) ÷ 收入、流失用收入口径、runway 用零收入口径。
- 月报 4 小时 × 12 期 = 48 小时,$4,800;季报增量 6 小时 × 4 期 = 24 小时,$2,400;合计 72 小时,$7,200($100/小时教学假设)。
- 口径每改一次约 3 小时返工;一年改三个定义就是 9 小时、$900,另加信任成本。
- Nord 与 Tidecast 不融资,报表可以简;拿了外部钱,固定字段与固定节奏是最低成本的信任维护。
阅读导航:上一节:3.1 融资材料:一页纸与数据室 · 下一节:3.3 董事会、观察员与信息权 。
常见错误与边界
- 把月报写成小作文。 投资人要的是五个数和四段桥,不是感想;长文会让关键数字更难找。
- 字段随月份增减。 这个月没有 Pipeline 就删掉一格,下个月再加回来,读者无法横向比较;缺数据就写「本月无新增」,不要删字段。
- 口径悄悄改变。 从收入流失改成客户流失、从 ARR 改成合同额,都会让历史序列断裂;改口径要单独说明并重算历史。
- 把一次性收入算进 ARR。 一次性实施费、培训费不是订阅收入;混进去会让 ARR 虚高,下一期掉回时更难解释。
- 只在有好消息时发。 坏消息晚一期,信任成本会翻倍;月报的价值在于「没有惊喜」,不是「只报喜」。
- 风险栏留空。 留空等于告诉对方「你没在想风险」;没有新增风险也要写「本月无新增」并附上现有风险的状态。
- 把月报和季报混在一起。 月报回答这个月发生了什么,季报回答和预算差多少;混在一起会两边都说不清。
- 跳过一期再补。 补的那期通常口径已经变了,读者会以为业务变化;宁可写短,不要跳期。
- 先自动化再定口径。 脚本跟着口径改三次,省下的时间又还回去;先手动跑三期,再考虑取数自动化。
- 把报表当法务文件。 报表是经营沟通,不是审计报告;它不需要每一行都盖章,但需要每一行都能在账上找到出处。
本节边界是「已融资后的常规报表」:它不讨论融资前的数据室(上一节),也不讨论董事会席位、观察员和信息权的治理成本(下一节)。报表不能替代关账;如果三张表本身没结平,月报只是把错误更快地发给更多人。先把卷一的月度关账跑顺,再谈报表节奏,顺序反了,五个字段会变成五处返工。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。