以下情境为教学假设,人物与数字不代表经核实的真实案例。
利润表按科目分类:收入、变动成本、运营费用、薪酬、税。成本结构按行为分类:这笔钱会不会随着收入一起下降。两套分类可以同时成立,但回答的问题不同。前者用于对外报表,后者用于回答「收入少 10%,利润会少多少」。本节只做后者,不重定义 3.1 利润表 的科目。
固定与变动的划分不是会计事实,而是管理判断。同一笔云账单,按用量计费是变动成本,签了年度保底就是固定成本;同一笔营销,按获客付费是变动成本,签了年度排期就是固定成本。分类之前先写清用途,否则「毛利率」会变成一个无法比较的数。
固定、变动与阶梯:三种行为
| 行为 | 定义 | 本案例 | 预算含义 |
|---|---|---|---|
| 变动 | 随收入或数量成比例变化 | 云用量、支付费、平台分成、退款 | 收入下降时自动下降,利润有缓冲 |
| 固定 | 在一定业务量区间内不随收入变化 | 工资、工具订阅、法务保险 | 收入下降时不变,利润被放大 |
| 阶梯 | 到某个阈值跳一档,之后再次固定 | 招聘第三人、签年度保底 | 跨越阈值前要单独预留,不能按月摊 |
混合成本要先拆再归类。例如支持费用可能一半是固定值班、一半随客户数增长;如果无法拆分,就按「不随收入下降」归入固定,宁可保守。分类还要固定观察窗口:年度预算里营销是固定承诺,按月看又可能随投放暂停而下降;同一笔钱在不同窗口下可以有不同答案,但同一张表里必须一致。
先问用途,再问分类
同一笔成本在三种问题下可能得到三种答案:
| 问题 | 关注点 | 分类倾向 |
|---|---|---|
| 明年预算够不够 | 承诺与合同 | 年度承诺算固定 |
| 这个月少卖要不要削成本 | 可暂停性 | 可暂停算变动 |
| 招第三个人值不值 | 阶梯阈值 | 按档位单独算 |
所以成本结构表要带一行「分类口径」:窗口是月还是年、营销是排期还是按获客付费、工资是否含创始人。没有这一行,两个人都能算出「正确」但不同的贡献毛益,然后在一张会上争论两个不能比较的数。
本节的分类口径固定为年度窗口:营销按年度排期算固定,创始人薪酬算固定,外包按合同算固定。换成月度窗口,营销可能随投放暂停而下降;口径变了,结论要重新算,不能把两张表混着用。
LedgerLite:84.2% 毛利率背后的固定成本
LedgerLite 的变动成本是事实表明确给出的 $38,000:云 $18,000、支付 $12,000、支持 $8,000。运营费用 $72,000 和创始人薪酬 $60,000 在本节按固定成本处理,因为它们在年度内不随收入同比例下降。
| 项 | 金额 | 行为 |
|---|---|---|
| 云 | $18,000 | 变动 |
| 支付 | $12,000 | 变动 |
| 支持 | $8,000 | 变动 |
| 营销 | $16,800 | 固定,年度排期 |
| 工具 | $9,000 | 固定,订阅 |
| 外包 | $20,000 | 固定,按合同 |
| 法务保险 | $6,000 | 固定 |
| 其他 | $20,200 | 固定 |
| 创始人薪酬 | $60,000 | 固定 |
| 变动合计 | $38,000 | |
| 固定合计 | $132,000 |
贡献毛益是收入减变动成本:$240,240 − $38,000 = $202,240,贡献率 84.18%。固定成本 $132,000 除以贡献率,保本收入约 $156,802;当前收入 $240,240 比保本高 34.73%,这就是安全边际。经营杠杆等于贡献毛益除以经营利润:$202,240 ÷ $70,240 = 2.8793,也就是收入每变动 1%,经营利润同方向变动约 2.88%。
把收入下调 10% 做一次压力测试:
| 项 | 预算 | 收入 −10% | 变化 |
|---|---|---|---|
| 收入 | $240,240 | $216,216 | −$24,024 |
| 变动成本 | $38,000 | $34,200 | −$3,800 |
| 贡献毛益 | $202,240 | $182,016 | −$20,224 |
| 固定成本 | $132,000 | $132,000 | $0 |
| 经营利润 | $70,240 | $50,016 | −$20,224 |
| 利润变化率 | −28.8% |
收入只少 10%,利润少 28.8%,差额被固定成本放大。这就是为什么「毛利率 84.2%」本身不代表安全:毛利率高只说明变动成本低,固定成本仍然要在收入下降时全额支付。
创始人薪酬是否算固定成本,会改变保本点。把它当利润分配而不是成本,固定成本降到 $72,000,保本收入约 $85,528;但预算里薪酬是必须支付的现金,所以本节把它计入固定成本。两种口径都合理,前提是写清楚,不能一边把它算进成本、一边又用 SDE 当利润。
Tidecast:平台费是变动成本,营销是固定成本
Tidecast 的报表给的是净得收入 $138,171,那是扣掉退款、平台分成和支付费之后的数。做成本结构时不能拿净得收入当基数、再把平台费当成本,否则同一笔钱被扣两次。正确做法是用标价收入 $191,904 作基数,把退款、平台分成、支付费和服务器都列为变动成本。
| 项 | 金额 | 行为 |
|---|---|---|
| 退款(4%) | $7,676 | 变动 |
| Steam 分成(30%) | $44,215 | 变动 |
| 官网支付费(5%) | $1,842 | 变动 |
| 服务器 | $3,000 | 变动 |
| 营销 | $12,000 | 固定 |
| 工具 | $2,000 | 固定 |
| 变动合计 | $56,733 | |
| 固定合计 | $14,000 |
贡献毛益 = $191,904 − $56,733 = $135,171,贡献率 70.44%。固定成本 $14,000 除以贡献率,保本标价收入约 $19,876;按 $19.99 的标价,约 994 份。这个保本点只覆盖营销、工具和平台变动成本,不覆盖 14 个月、$84,000 的开发机会成本;把开发算进去的项目保本约 7,018 份,见 5.1 自上而下与自下而上的预算 。
Tidecast 的经营杠杆只有 1.1155,收入少 10%,利润大约少 11.2%。这不是因为它更安全,而是因为固定成本只有 $14,000;买断制游戏的固定成本主要发生在开发期,上线后反而很轻。低杠杆的另一面是收入没有续费底座:今年卖 9,600 份,明年要从零开始。安全边际 89.64% 只说明「上线后经营成本很容易覆盖」,不说明「这部作品能回本」。
季节性还会让季度固定成本覆盖不均。Q4 占全年销量 38%,Q1 只有月均的一部分;用全年保本点去判断 Q1 会误判,季度预算要按销量权重分摊固定成本,年度再看总账。
Nord Studio:工资是固定成本,经营杠杆最高
Nord Studio 的咨询和产品两条线共享两位合伙人的工资。按成本行为归类,外包 $18,000 随咨询工时变动;工具 $6,000 和两人工资 $96,000 在年度内固定。
| 项 | 金额 | 行为 |
|---|---|---|
| 外包 | $18,000 | 变动 |
| 工具 | $6,000 | 固定 |
| 两人工资 | $96,000 | 固定 |
| 变动合计 | $18,000 | |
| 固定合计 | $102,000 |
收入 $154,320,贡献毛益 $136,320,贡献率 88.34%;保本收入约 $115,468,安全边际 25.18%;经营杠杆 $136,320 ÷ $34,320 = 3.9720。三家里 Nord 的杠杆最高:收入少 10%,贡献毛益降到 $122,688,经营利润从 $34,320 降到 $20,688,降幅 39.7%。
| 项 | 预算 | 收入 −10% | 变化 |
|---|---|---|---|
| 收入 | $154,320 | $138,888 | −$15,432 |
| 变动成本 | $18,000 | $16,200 | −$1,800 |
| 贡献毛益 | $136,320 | $122,688 | −$13,632 |
| 固定成本 | $102,000 | $102,000 | $0 |
| 经营利润 | $34,320 | $20,688 | −$13,632 |
| 利润变化率 | −39.7% |
25.18% 的安全边际意味着收入再低约四分之一,经营利润就归零。对两人服务加产品的结构,这不是抽象风险:咨询工时少 10%,收入就少 $11,220,已经超过 SDE 的 32%。固定工资不会跟着工时下降,杠杆就这样放大。
招聘第三人是典型的阶梯成本。工资 $55,000/年,固定成本从 $102,000 跳到 $157,000,保本收入升到约 $177,731。事实表给出的「需新增收入 ≥$75,000」比单纯用贡献率算出的 $62,260 更高,差额是税和爬坡期的缓冲;这两个数不矛盾,前者是经营门槛,后者是覆盖工资的数学下限。现金水位能否承受爬坡期,见 8.3 runway 与最低现金水位 。
把成本结构写成代码
下面的 cost-structure.ts 把成本行为、贡献毛益、保本收入、经营杠杆和安全边际固化成 analyzeCostStructure。输入是收入与带 behavior 的成本条目,输出全部用整数美分和基点;贡献毛益不为正、收入为零等非法输入返回 Result 错误。
// file: src/fin/cost-structure.ts
// tier: L1
// verify: node --test test/fin/cost-structure.test.ts
import { type Cents, add } from "../core/money.ts";
import { type Bps, divide } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type CostBehavior = "fixed" | "variable";
export type CostItem = {
label: string;
amount: Cents;
behavior: CostBehavior;
};
export type CostStructure = {
revenue: Cents;
fixed: Cents;
variable: Cents;
contribution: Cents;
contributionMarginBps: Bps;
operatingIncome: Cents;
breakEvenRevenue: Cents;
/** Contribution divided by operating income; null at or below break-even. */
operatingLeverageBps: Bps | null;
marginOfSafetyBps: Bps;
};
/**
* Management view of cost behaviour, not a replacement for the income
* statement categories. Revenue minus variable cost is contribution;
* contribution minus fixed cost is operating income. Break-even revenue is
* the fixed base divided by the contribution margin ratio.
*/
export function analyzeCostStructure(
revenue: Cents,
items: CostItem[],
): Result<CostStructure> {
if (!Number.isInteger(revenue) || revenue <= 0) {
return fail("revenue must be a positive integer number of cents");
}
if (items.length === 0) {
return fail("at least one cost item is required");
}
let fixed = 0;
let variable = 0;
for (const item of items) {
if (item.label.trim() === "") {
return fail("cost item label must not be empty");
}
if (!Number.isInteger(item.amount) || item.amount < 0) {
return fail(`cost item ${item.label} must be a non-negative integer`);
}
if (item.behavior === "fixed") {
fixed = add(fixed, item.amount);
} else if (item.behavior === "variable") {
variable = add(variable, item.amount);
} else {
return fail(`unknown cost behaviour: ${item.behavior}`);
}
}
const contribution = revenue - variable;
if (contribution <= 0) {
return fail("contribution margin must be positive");
}
const operatingIncome = contribution - fixed;
const breakEvenRevenue = Math.round((fixed * revenue) / contribution);
return ok({
revenue,
fixed,
variable,
contribution,
contributionMarginBps: divide(contribution, revenue),
operatingIncome,
breakEvenRevenue,
operatingLeverageBps: operatingIncome > 0
? divide(contribution, operatingIncome)
: null,
marginOfSafetyBps: divide(revenue - breakEvenRevenue, revenue),
});
}
代码有四个边界。第一,收入基数必须和变动成本范围一致:Tidecast 用标价收入 $191,904 配平台费,不能用净得收入 $138,171 再扣一次平台费。第二,固定与变动是管理分类,不是会计科目;创始人薪酬在本节算固定成本,换一张表也可以算利润分配,但同一张表不能两种算法混用。第三,经营利润小于等于零时,经营杠杆返回 null 而不是一个负数或无穷大;保本点之下谈「杠杆倍数」没有意义。第四,安全边际可以为负,负值表示当前收入已经在保本点之下,这不是错误,而是需要正视的亏损信号。
把三个案例代入,贡献率分别是 84.18%、70.44%、88.34%,保本收入分别是 $156,802、$19,876、$115,468,经营杠杆分别是 2.8793、1.1155、3.9720。成本结构表不能替代利润表,它只是把「收入变化如何传到利润」这条链路显性化。更细的成本归集与分摊见 成本清单 。
小结
- 固定与变动是管理分类,不是会计科目;分类前先写清用途和观察窗口,同一张表里必须一致。
- LedgerLite 的变动成本 $38,000、固定成本 $132,000,贡献率 84.18%,保本收入约 $156,802,经营杠杆 2.8793。
- 收入少 10%,LedgerLite 的经营利润少 28.8%;毛利率高不等于安全,固定成本会在收入下降时放大打击。
- Tidecast 必须用标价收入 $191,904 配平台变动成本 $56,733;经营保本约 994 份,项目保本约 7,018 份。
- Nord Studio 的固定成本 $102,000,贡献率 88.34%,保本收入约 $115,468,经营杠杆 3.9720,安全边际只有 25.18%。
- 阶梯成本要单独预留:Nord 招聘第三人使固定成本增加 $55,000,保本收入升到约 $177,731。
阅读导航:上一节:5.1 自上而下与自下而上的预算 · 下一节:5.3 预算偏差与季度校准 。
常见错误与边界
- 把固定与变动当会计事实。 同一笔云账单可以按用量算变动、按年度保底算固定;分类服务于决策,不服务于报表。
- 收入基数和成本范围不一致。 Tidecast 用净得收入再扣平台费会重复计算;标价收入配平台费,净得收入只能配净得成本。
- 把毛利率当安全边际。 毛利率只反映变动成本,LedgerLite 的 84.2% 背后是 $132,000 固定成本,收入少 10% 利润少 28.8%。
- 忽略创始人薪酬。 把 $60,000 薪酬从固定成本里拿掉,保本收入从 $156,802 降到 $85,528,会高估安全垫。
- 用年度保本点判断单季。 Tidecast 的 Q4 占销量 38%,Q1 用全年保本点会误判;季度要按权重分摊固定成本。
- 把低杠杆当安全。 Tidecast 的杠杆 1.1155,是因为上线后固定成本低,不是因为收入可持续;一次性收入没有续费底座。
- 在保本点以下算杠杆。 经营利润小于等于零时杠杆没有意义;代码返回
null,不要用一个负数继续乘。 - 忽略阶梯成本。 Nord 招聘第三人不是线性增加成本,而是固定成本跳档;$55,000 工资要把保本收入推到约 $177,731。
- 把安全边际当现金流。 安全边际是收入到保本点的距离,不是账上现金;应收、递延和税都会让现金更紧。
- 只算一年不算窗口。 买断制的固定成本集中在开发期,订阅制的固定成本摊在全年;用同一个年度窗口比较会失真。
成本结构回答「收入变化如何传到利润」,下一节回答「实际和预算差了多少、哪些差额值得行动」。前者是静态结构,后者是动态校准;把两者接起来,预算才不是年初的一张纸。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。