以下情境为教学假设,人物与数字不代表经核实的真实案例。
合伙人退出不是「一个人走、另一个人留下」这么简单。它要同时回答四个问题:股权按什么价格买回、钱什么时候付、公司被整体出售时少数方能不能跟、走的人能不能立刻做同类生意。四个问题各自对应一种机制,也各自对应一笔可以算的现金。
Nord Studio 是这一节的主案例:两位合伙人各 50%,没有期权池,没有书面退出条款。年收入 154,320 美元,SDE 34,320 美元;4.5 倍 SDE 的估值锚是 154,440 美元,3.0 倍 SDE 的买方报价是 102,960 美元。50% 股权按 4.5 倍算是 77,220 美元,按 3.0 倍算是 51,480 美元。
四种机制:一张表看清
| 机制 | 触发 | 价格基础 | 现金节奏 | 主要风险 |
|---|---|---|---|---|
| 回购 | 合伙人主动退出或约定事件 | 4.5× SDE × 50% = 77,220 | 一次性或短期 | 公司现金不够,回购拖垮经营 |
| 分期买断 | 同上,但现金分年支付 | 同上,可加利息 | 24 个月,每月 3,217.50 | 违约、公司后续经营恶化 |
| 拖售/随售 | 整体出售公司 | 3.0× SDE = 102,960,50% 为 51,480 | 交割时 | 少数方被低价拖走,或无法跟售 |
| 竞业与保密 | 退出后 24 个月 | 从买断价中留置 20% | 12 个月后释放 | 范围过宽、执行成本、补偿不足 |
Nord 没有书面退出条款,所以四种机制都不是「默认生效」,而是要临时谈。50/50 让谈判更慢:任何一方都可以拒绝,退出价格和付款方式都要求双方同时点头。书面条款的价值不是「限制谁」,而是「把谈判从一次性博弈变成可执行的公式」。
回购:77,220 美元有多重
77,220 美元是 50% 股权按 4.5 倍 SDE 算出来的价格。把它放回 Nord 的现金流里,有三种等价的看法:
| 口径 | 金额 | 覆盖 77,220 需要 |
|---|---|---|
| 年 SDE | 34,320 | 2.25 年 |
| 年税后可支配现金 | 25,740 | 3.0 年 |
| 月均 SDE | 2,860 | 27 个月 |
三种口径说的是同一件事:一次性回购要拿走公司两年多的 SDE,或者三年的税后现金。Nord 没有披露现金余额,但仅从流量看,一次性付 77,220 美元就意味着把未来两年的经营缓冲全部押上。回购条款如果写「退出时一次性支付」,等于假设公司随时有两年的 SDE 趴在账上;大多数两人服务型公司没有这个余量。
回购价格也不是只有 4.5 倍一个锚。双方可以约定 3.0 倍到 4.5 倍之间的公式,或者用「过去 12 个月 SDE × 倍数 − 未偿债务」的净值法。价格每降 0.5 倍,50% 股权就少 8,580 美元:4.5 倍是 77,220,4.0 倍是 68,640,3.5 倍是 60,060,3.0 倍是 51,480。四个价格之间的差距,比很多条款的谈判时间更值钱。
| SDE 倍数 | 企业价值 | 50% 股权价值 | 与 4.5 倍的差额 |
|---|---|---|---|
| 4.5× | 154,440 | 77,220 | 0 |
| 4.0× | 137,280 | 68,640 | −8,580 |
| 3.5× | 120,120 | 60,060 | −17,160 |
| 3.0× | 102,960 | 51,480 | −25,740 |
分期买断:24 个月,每月 3,217.50
把 77,220 美元分成 24 个月,每月是 3,217.50 美元。这个数字放在 Nord 的月度现金流里,压力立刻显形:
| 对照 | 金额 | 月供占比 |
|---|---|---|
| 月供 | 3,217.50 | 100% |
| 产品订阅 MRR | 3,510 | 91.7% |
| 月总收入 | 12,860 | 25.0% |
| 月均 SDE | 2,860 | 112.5% |
| 月税后现金 | 2,145 | 150.0% |
月供是月均 SDE 的 112.5%,是月税后现金的 150%。也就是说,光靠经营现金流,公司每个月都差 1,072.50 美元才能付完这笔买断;差额要靠期初现金、增长或者另一条收入线补。把月供和产品订阅对比更直观:3,217.50 美元相当于产品 MRR 的 91.7%,几乎等于把整条产品线的月收入拿去买断。
| 累计月份 | 累计支付 | 占年 SDE |
|---|---|---|
| 6 | 19,305 | 56.25% |
| 12 | 38,610 | 112.5% |
| 18 | 57,915 | 168.75% |
| 24 | 77,220 | 225.0% |
如果 SDE 下滑,覆盖倍数会更难看。教学敏感度如下:
| SDE 情景 | 年 SDE | 年供 38,610 占比 |
|---|---|---|
| 下滑 20% | 27,456 | 140.6% |
| 下滑 10% | 30,888 | 125.0% |
| 基准 | 34,320 | 112.5% |
| 增长 10% | 37,752 | 102.3% |
分期买断的结构要点不是「分几期」,而是「公司现金流断了怎么办」。常见保护包括:以退出方剩余股权作质押、约定公司出售时加速清偿、设置宽限期与违约金、把部分对价与未来收入挂钩。这些条款不改变 77,220 美元的总价,但改变「谁承担经营下行的风险」。
拖售、随售与竞业保密
整体出售走的是另一条价格锚。买方按 3.0 倍 SDE 报价 102,960 美元,50% 股权分到 51,480 美元;和 4.5 倍回购的 77,220 美元相比,少 25,740 美元,折价 33.3%。
| 路径 | 倍数 | 企业价值 | 50% 所得 | 与回购的差 |
|---|---|---|---|---|
| 合伙人回购 | 4.5× | 154,440 | 77,220 | 0 |
| 整体出售 | 3.0× | 102,960 | 51,480 | −25,740(−33.3%) |
拖售条款允许达到约定比例的股东强制其他股东一起出售;随售条款允许少数股东按同等条件跟着卖。两者保护的方向相反:拖售保护买方「买得到 100%」,随售保护少数方「不被落下」。
在 50/50 的结构里,拖售条款的作用有限,因为任何一方都不到 50% 以上,强制出售仍然要两人同意。但引入第三位股东后就不同了:45/45/10 的结构里,45% + 10% = 55%,已经超过半数;如果拖售门槛写成「超过 50%」,第三人那 10% 就成了关键票。第三人按 3.0 倍卖是 10,296 美元,按 4.5 倍卖是 15,444 美元,差 5,148 美元。随售条款让他至少能按同一每股价格跟卖,而不是被留在没有流动性的 minority 位置上。
竞业与保密:24 个月与 20% 留置
竞业与保密条款把「走的人」和「留下的公司」之间的边界画清楚。Nord 的两位合伙人各拿 48,000 美元年薪(两人工资合计 96,000 美元);买断价 77,220 美元分 24 个月,年均 38,610 美元,相当于原年薪的 80.4%。两年合计 77,220 美元,比原年薪两年 96,000 美元少 18,780 美元。
| 项 | 金额/期限 | 说明 |
|---|---|---|
| 退出方原年薪 | 48,000 | 96,000 ÷ 2 |
| 买断年均 | 38,610 | 77,220 ÷ 2 |
| 买断/原年薪 | 80.4% | 每年少 9,390 |
| 24 个月合计缺口 | 18,780 | 96,000 − 77,220 |
| 竞业期限 | 24 个月 | 教学假设 |
| 留置比例 | 20% | 15,444 美元 |
| 留置期限 | 12 个月 | 覆盖竞业期的一半 |
20% 的留置是 15,444 美元,只覆盖 24 个月竞业期的前 12 个月。如果违约发生在第 13 到 24 个月,留置已经释放,公司只能靠违约金或诉讼追偿。教学假设违约金为买断价的 25%,即 19,305 美元;它比留置高,但执行成本也更高。
竞业条款的边界要用「范围、期限、地域、补偿」四个维度写清楚。范围太宽,退出方无法谋生,条款容易被挑战;范围太窄,起不到保护作用。对 Nord 这种服务型公司,真正的风险不是「合伙人再开一家公司」,而是「合伙人带走前 3 家客户」,这 3 家占收入 48%。竞业条款只能限制行为,不能替代客户关系和合同承继的安排。
把退出写成代码
下面的 partner-exit.ts 把四种机制里的可计算部分固化成函数:buyoutValue 按 SDE × 倍数 × 股权比例算价格,installmentPlan 算等额分期与最后一期尾差,cashPressure 把月供对照月 SDE 与月税后现金,dragDiscount 算拖售折价,holdback 算留置与释放。
// file: src/capital/partner-exit.ts
// tier: L1
// verify: node --test test/capital/partner-exit.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 BuyoutTerms = {
sdeCents: Cents;
multipleBps: Bps;
equityBps: Bps;
};
export type InstallmentPlan = {
monthlyCents: Cents;
lastCents: Cents;
months: number;
totalCents: Cents;
};
export type CashPressure = {
monthlySdeCents: Cents;
monthlyAfterTaxCents: Cents;
paymentToSdeBps: Bps;
paymentToCashBps: Bps;
shortfallCents: Cents;
};
export type DragComparison = {
differenceCents: Cents;
discountBps: Bps;
};
export type Holdback = {
heldCents: Cents;
releasedCents: Cents;
};
/** Equity value = SDE x multiple x equity share. */
export function buyoutValue(terms: BuyoutTerms): Result<Cents> {
if (!Number.isInteger(terms.sdeCents) || terms.sdeCents < 0) {
return fail("SDE must be a non-negative integer amount");
}
if (!Number.isInteger(terms.multipleBps) || terms.multipleBps < 0) {
return fail("multiple must be a non-negative integer in basis points");
}
if (
!Number.isInteger(terms.equityBps) ||
terms.equityBps < 0 ||
terms.equityBps > 10000
) {
return fail("equity must be between zero and 10000 basis points");
}
const enterpriseCents = applyRate(terms.sdeCents, terms.multipleBps);
return ok(applyRate(enterpriseCents, terms.equityBps));
}
/** Equal installments; the final payment absorbs any rounding remainder. */
export function installmentPlan(
principalCents: Cents,
months: number,
): Result<InstallmentPlan> {
if (!Number.isInteger(principalCents) || principalCents < 0) {
return fail("principal must be a non-negative integer amount");
}
if (!Number.isInteger(months) || months <= 0) {
return fail("months must be a positive integer");
}
const monthlyCents = Math.floor(principalCents / months);
const lastCents = principalCents - monthlyCents * (months - 1);
return ok({
monthlyCents,
lastCents,
months,
totalCents: monthlyCents * (months - 1) + lastCents,
});
}
/** Monthly payment measured against monthly SDE and monthly after-tax cash. */
export function cashPressure(
monthlyPaymentCents: Cents,
annualSdeCents: Cents,
annualAfterTaxCents: Cents,
): Result<CashPressure> {
if (!Number.isInteger(monthlyPaymentCents) || monthlyPaymentCents < 0) {
return fail("payment must be a non-negative integer amount");
}
if (!Number.isInteger(annualSdeCents) || annualSdeCents <= 0) {
return fail("annual SDE must be a positive integer amount");
}
if (!Number.isInteger(annualAfterTaxCents) || annualAfterTaxCents < 0) {
return fail("annual after-tax cash must be a non-negative integer amount");
}
const monthlySdeCents = Math.round(annualSdeCents / 12);
const monthlyAfterTaxCents = Math.round(annualAfterTaxCents / 12);
if (monthlySdeCents <= 0) {
return fail("monthly SDE must be positive");
}
return ok({
monthlySdeCents,
monthlyAfterTaxCents,
paymentToSdeBps: divide(monthlyPaymentCents, monthlySdeCents),
paymentToCashBps:
monthlyAfterTaxCents === 0
? 0
: divide(monthlyPaymentCents, monthlyAfterTaxCents),
shortfallCents: Math.max(0, monthlyPaymentCents - monthlyAfterTaxCents),
});
}
/** Gap between a full-price buyout and a lower-multiple drag sale. */
export function dragDiscount(
buyoutCents: Cents,
dragCents: Cents,
): Result<DragComparison> {
if (!Number.isInteger(buyoutCents) || buyoutCents <= 0) {
return fail("buyout value must be a positive integer amount");
}
if (!Number.isInteger(dragCents) || dragCents < 0) {
return fail("drag value must be a non-negative integer amount");
}
return ok({
differenceCents: buyoutCents - dragCents,
discountBps: divide(buyoutCents - dragCents, buyoutCents),
});
}
/** Amount held back from closing and the amount released later. */
export function holdback(
principalCents: Cents,
holdbackBps: Bps,
): Result<Holdback> {
if (!Number.isInteger(principalCents) || principalCents < 0) {
return fail("principal must be a non-negative integer amount");
}
if (
!Number.isInteger(holdbackBps) ||
holdbackBps < 0 ||
holdbackBps > 10000
) {
return fail("holdback must be between zero and 10000 basis points");
}
const heldCents = applyRate(principalCents, holdbackBps);
return ok({
heldCents,
releasedCents: principalCents - heldCents,
});
}
/** Buyout price expressed as years of SDE. */
export function sdeYears(
principalCents: Cents,
annualSdeCents: Cents,
): Result<Bps> {
if (!Number.isInteger(principalCents) || principalCents < 0) {
return fail("principal must be a non-negative integer amount");
}
if (!Number.isInteger(annualSdeCents) || annualSdeCents <= 0) {
return fail("annual SDE must be a positive integer amount");
}
return ok(divide(principalCents, annualSdeCents));
}
代码有四个边界。第一,buyoutValue 的乘法顺序是「SDE × 倍数 × 股权」,每一步都取整到美分;先算企业价值再乘股权,避免两次四舍五入叠加。第二,installmentPlan 用 Math.floor 算月供,最后一期吸收余数;本金不能被月数整除时,最后一期会比前面略大。第三,cashPressure 同时给出「月供 / 月 SDE」和「月供 / 月税后现金」两个比率,因为前者看经营,后者看真实可支配现金。第四,holdback 只算留置金额,不算释放条件;释放条件要写在协议里,不能写在代码里。
小结
- 退出要同时定价、定时、定边界:价格、付款节奏、拖售/随售、竞业与保密,四种机制各自对应一笔现金。
- Nord 的 50% 股权按 4.5× SDE 是 77,220 美元,等于 2.25 年 SDE、3 年税后现金、27 个月月均 SDE。
- 24 个月分期每月 3,217.50 美元,是月均 SDE 的 112.5%、月税后现金的 150%;年供 38,610 美元是年 SDE 的 112.5%。
- 整体出售按 3.0× SDE 是 102,960 美元,50% 分到 51,480 美元,比 4.5× 回购少 25,740 美元,折价 33.3%。
- 45/45/10 的结构里,55% 的联盟可以拖售 45%;第三人的 10% 在 3.0× 和 4.5× 之间差 5,148 美元,随售条款决定他能不能跟。
- 买断年均 38,610 美元是原年薪 48,000 美元的 80.4%;24 个月少 18,780 美元,竞业补偿要单独定价。
- 20% 留置是 15,444 美元,只覆盖 24 个月竞业期的前 12 个月;后 12 个月要靠违约金或其他担保。
阅读导航:上一节:2.2 期权池与归属 · 下一节:3.1 融资材料:一页纸与数据室 。
常见错误与边界
- 没有书面退出条款。 Nord 的 50/50 无退出条款,价格、付款、竞业都要临时谈;谈不成就是僵局,3 个月按 SDE 算是 8,580 美元。
- 只谈价格,不谈付款。 77,220 美元一次性付是 2.25 年 SDE;分 24 个月每月 3,217.50 美元,是月税后现金的 150%,现金流压力完全不同。
- 用 4.5× 回购价去对比 3.0× 出售价。 两个锚差 25,740 美元,折价 33.3%;谈判时要先确定用哪个锚,再谈倍数。
- 把拖售条款当成多数决。 50/50 下拖售仍要两人同意;只有引入第三人、出现 55% 联盟后,拖售才真正能强制少数方。
- 忽略随售条款。 没有随售,10% 的第三人可能被留在一个没有流动性的 minority 位置;有随售,他至少能按同一每股价格跟卖。
- 竞业期限写 24 个月却不给补偿。 买断年均 38,610 美元只有原年薪的 80.4%;竞业限制越久,补偿缺口越大。
- 留置比例覆盖不了竞业期。 20% 留置只覆盖 12 个月,竞业期是 24 个月;后 12 个月没有担保,违约只能追偿。
- 把买断价当成竞业补偿。 买断价买的是股权,竞业限制限制的是未来收入;两者要分开定价,否则退出方会觉得「拿钱还被限制」。
- 忽略客户集中。 Nord 前 3 家客户占 48%;竞业条款限制不了客户跟着人走,客户合同和关系承继要单独安排。
- 把结构算术当成法律意见。 本节只算价格和现金流;合伙协议、竞业条款、拖售随售要由当地执业律师起草。
合伙人退出解决「已经进来的人怎么出去」,下一章解决「新钱进来时要看什么材料」。一个是收尾,一个是开场,但用的是同一张 cap table 和同一套估值锚。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。