mutable state patterns to ensure auditability and mathematical precision.
Step 1: Cap Table & Valuation Modeling Architecture
The cap table engine must handle pre-money/post-money calculations, option pool shuffles, SAFE conversion, and liquidation preferences. Immutability prevents state corruption during term sheet iterations.
import { z } from 'zod';
export const TermSheetSchema = z.object({
preMoneyValuation: z.number().positive(),
investmentAmount: z.number().positive(),
optionPoolPercent: z.number().min(0).max(100),
liquidationPreferenceMultiple: z.number().min(1),
participating: z.boolean(),
safeInstruments: z.array(z.object({
principal: z.number().positive(),
discountRate: z.number().min(0).max(1),
valuationCap: z.number().positive().optional(),
})),
});
export type TermSheet = z.infer<typeof TermSheetSchema>;
export class CapTableEngine {
private readonly termSheet: TermSheet;
constructor(termSheet: TermSheet) {
this.termSheet = TermSheetSchema.parse(termSheet);
}
calculatePostMoney(): number {
const { preMoneyValuation, investmentAmount } = this.termSheet;
return preMoneyValuation + investmentAmount;
}
calculateInvestorOwnership(): number {
const postMoney = this.calculatePostMoney();
return this.termSheet.investmentAmount / postMoney;
}
calculateOptionPoolDilution(): number {
const { preMoneyValuation, optionPoolPercent } = this.termSheet;
// Option pool is carved out of pre-money
const adjustedPreMoney = preMoneyValuation * (1 - optionPoolPercent / 100);
return optionPoolPercent / 100;
}
simulateSafeConversion(safe: NonNullable<TermSheet['safeInstruments']>[0]): number {
const postMoney = this.calculatePostMoney();
const effectivePrice = safe.valuationCap
? Math.min(safe.valuationCap, postMoney) / postMoney
: postMoney / postMoney;
const discountPrice = postMoney * (1 - safe.discountRate);
const conversionValue = safe.principal / (discountPrice / postMoney);
return conversionValue / postMoney;
}
getFullDilutionOwnership(): Record<string, number> {
const investorOwnership = this.calculateInvestorOwnership();
const optionDilution = this.calculateOptionPoolDilution();
const safeDilution = this.termSheet.safeInstruments.reduce(
(acc, safe) => acc + this.simulateSafeConversion(safe), 0
);
return {
investors: investorOwnership,
optionPool: optionDilution,
safeHolders: safeDilution,
founders: 1 - investorOwnership - optionDilution - safeDilution,
};
}
}
Step 2: Technical Due Diligence Validator
Investors evaluate architecture maturity, security posture, and deployment reproducibility. The validator enforces structural requirements before data room exposure.
export const DDRequirementsSchema = z.object({
repositoryStructure: z.enum(['monorepo', 'polyrepo']),
ciCdPipeline: z.boolean(),
infrastructureAsCode: z.boolean(),
secretManagement: z.enum(['env', 'vault', 'aws-secrets']),
observability: z.object({
logging: z.boolean(),
metrics: z.boolean(),
tracing: z.boolean(),
}),
dataRetentionPolicy: z.number().min(30),
securityScanFrequency: z.enum(['on-merge', 'daily', 'weekly']),
});
export type DDRequirements = z.infer<typeof DDRequirementsSchema>;
export class TechnicalDueDiligenceValidator {
private readonly requirements: DDRequirements;
constructor(reqs: DDRequirements) {
this.requirements = DDRequirementsSchema.parse(reqs);
}
evaluate(): { score: number; blockers: string[]; recommendations: string[] } {
const blockers: string[] = [];
const recommendations: string[] = [];
let score = 0;
if (!this.requirements.ciCdPipeline) blockers.push('CI/CD pipeline missing');
else score += 20;
if (!this.requirements.infrastructureAsCode) blockers.push('No IaC detected');
else score += 15;
if (this.requirements.secretManagement === 'env') {
blockers.push('Environment variables used for secrets');
} else {
score += 15;
}
const { logging, metrics, tracing } = this.requirements.observability;
if (logging && metrics && tracing) score += 20;
else if (logging && metrics) { recommendations.push('Add distributed tracing'); score += 10; }
else { blockers.push('Insufficient observability stack'); score += 5; }
if (this.requirements.dataRetentionPolicy < 90) {
recommendations.push('Extend data retention to 90+ days for audit compliance');
} else {
score += 10;
}
if (this.requirements.securityScanFrequency === 'weekly') {
recommendations.push('Increase scan frequency to daily or on-merge');
score += 10;
} else {
score += 10;
}
return { score, blockers, recommendations };
}
}
Architecture Decisions & Rationale
- Strict TypeScript + Zod: Prevents runtime math errors during term sheet iterations. Financial modeling requires deterministic types; loose typing causes dilution miscalculations.
- Immutable State: Cap table engines must not mutate historical term sheets. Each negotiation round generates a new instance, preserving audit trails for investor Q&A.
- Modular DD Validation: Separates infrastructure scoring from financial modeling. Investors evaluate technical and financial readiness independently; coupling them creates false confidence.
- Functional Core, Imperative Shell: Calculation logic remains pure. I/O (file parsing, investor communication) sits at the boundary, ensuring testability and reproducibility.
Pitfall Guide
-
Valuation Obsession Over Term Structure
Founders chase higher pre-money valuations while ignoring liquidation preferences, participation rights, and anti-dilution provisions. A $12M pre-money with 2x participating preferred stock dilutes founders more than a $9M pre-money with 1x non-participating. Always model full liquidation scenarios before accepting valuation.
-
Option Pool Math Misalignment
Investors typically require the option pool to be carved out of pre-money. Founders who calculate pool size post-money absorb unexpected dilution. The correct formula: Adjusted Pre-Money = Stated Pre-Money × (1 - Pool %). Validate this before signing.
-
Data Room Fragmentation
Scattered repositories, inconsistent documentation, and missing runbooks trigger DD delays. Structure the data room hierarchically: Architecture, Security, Compliance, Financials, Cap Table. Automate access logging to track investor engagement.
-
Misaligned Unit Economics
Investors model LTV/CAC, gross margin, burn multiple, and cohort retention. Presenting vanity metrics (MRR growth without churn adjustment) destroys credibility. Align product telemetry with financial reporting. Use standardized cohorts and net revenue retention (NRR) > 110% for SaaS.
-
Technical Debt as a DD Dealbreaker
Investors view unmanaged technical debt as operational risk. Missing error boundaries, untested migration scripts, or undocumented API contracts signal scaling risk. Freeze feature development during DD windows. Ship infrastructure hardening instead.
-
Cap Table Math Errors
Manual spreadsheets fail under complex scenarios: SAFE conversions, vesting cliffs, acceleration clauses, and pro-rata rights. Programmatic modeling eliminates human error. Always simulate down-round anti-dilution (full ratchet vs weighted average).
-
Ignoring Post-Close Governance
Board composition, information rights, and drag-along provisions dictate post-investment control. Founders who neglect these clauses lose strategic autonomy. Model governance impact alongside dilution.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Pre-seed validation (<$500K) | SAFE/Convertible Note | Speed > precision; defers valuation complexity | Low legal cost, high dilution uncertainty |
| Seed growth ($1M–$3M) | Priced Round | Institutional DD readiness; cleaner cap table | Moderate legal cost, predictable dilution |
| Series A scaling ($5M+) | Priced Round + Board Seat | Governance alignment; follow-on readiness | High legal cost, structured control |
| Non-dilutive funding | Grants/Revenue-Based Financing | Preserves equity; aligns with product-led growth | Zero dilution, repayment obligation |
Configuration Template
// fundraising.config.ts
export const fundraisingConfig = {
capTable: {
strictMode: true,
autoCalculateDilution: true,
safeConversionStrategy: 'weighted_average', // 'full_ratchet' | 'weighted_average'
optionPoolCalculation: 'pre_money', // 'pre_money' | 'post_money'
},
dueDiligence: {
requiredScore: 75,
autoBlockOnCritical: true,
scanFrequency: 'on-merge',
secretManager: 'aws-secrets',
observability: {
logging: true,
metrics: true,
tracing: true,
},
},
investorReporting: {
cadence: 'monthly',
metrics: ['nrr', 'gross_margin', 'burn_multiple', 'cohort_retention'],
dataRoomAccessLog: true,
},
};
Quick Start Guide
- Initialize the cap table engine with your term sheet parameters using
CapTableEngine and Zod validation.
- Run the DD validator against your repository structure, CI/CD pipeline, and security posture. Address blockers before data room exposure.
- Simulate dilution scenarios across base, upside, and down-round cases using
getFullDilutionOwnership().
- Export results to a structured data room with access logging and immutable term sheet versioning.
- Iterate negotiation rounds by creating new engine instances; never mutate historical calculations.