king.
2. Separation of Market vs. Personal Metrics: Market data is isolated from individual experience factors. This allows engineers to swap benchmark sources without recalibrating personal valuation logic.
3. Explicit Liquidity Modeling: Equity is not treated as cash. The framework applies vesting schedules, strike prices, and liquidity probability weights to annualize long-term incentives accurately.
4. Configurable Thresholds: Walk-away points, target ranges, and fallback levers are externalized into configuration objects. This enables rapid scenario testing without code changes.
Implementation
// compensation-engine.ts
export interface MarketBenchmark {
location: string;
seniority: 'mid' | 'senior' | 'staff' | 'principal';
baseRange: [number, number];
equityPercentile: number;
bonusMultiplier: number;
source: string;
}
export interface OfferPackage {
baseSalary: number;
annualBonusTarget: number;
equityGrant: number; // shares or units
vestingSchedule: { cliffMonths: number; totalMonths: number };
strikePrice: number;
signOnBonus: number;
benefitsValue: number; // annualized monetary equivalent
}
export interface NegotiationConfig {
targetTotalComp: number;
walkAwayThreshold: number;
fallbackLever: 'equity' | 'signOn' | 'bonus' | 'benefits';
liquidityProbability: number; // 0.0 to 1.0
}
export class CompensationEngine {
private readonly config: NegotiationConfig;
constructor(config: NegotiationConfig) {
this.config = config;
}
/**
* Annualizes equity by applying vesting acceleration and liquidity probability.
* Prevents overvaluation of illiquid or heavily cliff-vested grants.
*/
private annualizeEquity(pkg: OfferPackage): number {
const monthlyVest = pkg.equityGrant / (pkg.vestingSchedule.totalMonths / 12);
const vestedInYear1 = pkg.vestingSchedule.cliffMonths <= 12
? monthlyVest
: 0;
return vestedInYear1 * pkg.strikePrice * this.config.liquidityProbability;
}
/**
* Calculates true annualized total compensation.
*/
public calculateTotalComp(pkg: OfferPackage): number {
const annualBonus = pkg.baseSalary * pkg.annualBonusTarget;
const annualEquity = this.annualizeEquity(pkg);
return pkg.baseSalary + annualBonus + annualEquity + pkg.signOnBonus + pkg.benefitsValue;
}
/**
* Generates a structured counter-offer based on target gaps.
* Returns actionable parameters instead of vague ranges.
*/
public generateCounterOffer(current: OfferPackage, benchmark: MarketBenchmark): {
recommendedBase: number;
equityAdjustment: number;
fallbackLever: string;
justification: string;
} {
const currentTotal = this.calculateTotalComp(current);
const benchmarkMid = (benchmark.baseRange[0] + benchmark.baseRange[1]) / 2;
const gap = this.config.targetTotalComp - currentTotal;
const recommendedBase = Math.max(current.baseSalary, benchmarkMid);
const equityAdjustment = gap > 0 ? Math.ceil(gap / (current.strikePrice * this.config.liquidityProbability)) : 0;
const lever = gap > 0 ? this.config.fallbackLever : 'none';
const justification = gap > 0
? `Current package falls ${Math.abs(gap).toLocaleString()} below target. Recommend base alignment to ${recommendedBase.toLocaleString()} or ${equityAdjustment} additional equity units. Fallback: ${lever} adjustment.`
: 'Package meets or exceeds target. Proceed to documentation phase.';
return { recommendedBase, equityAdjustment, fallbackLever: lever, justification };
}
/**
* Evaluates whether an offer meets minimum viability thresholds.
*/
public isViable(pkg: OfferPackage): boolean {
return this.calculateTotalComp(pkg) >= this.config.walkAwayThreshold;
}
}
Execution Workflow
- Ingest Benchmarks: Pull data from multiple sources, filter by location, seniority, and company size. Normalize to a single
MarketBenchmark object.
- Model the Offer: Map the received package to
OfferPackage. Annualize benefits and sign-on bonuses to ensure apples-to-apples comparison.
- Calculate & Compare: Run
calculateTotalComp() against your targetTotalComp. Identify the exact gap.
- Generate Parameters: Use
generateCounterOffer() to produce precise numbers, fallback levers, and justification statements.
- Execute Communication: Structure responses around total value, not base salary. Reference market data, quantify impact, and request written confirmation.
Pitfall Guide
1. Anchoring to Base Salary Only
Explanation: Treating base salary as the sole negotiation variable ignores equity, bonuses, and benefits that often constitute the majority of total compensation. This limits leverage to a single, frequently capped budget line.
Fix: Model total compensation annually. Use base salary as an anchor only when equity liquidity is uncertain or vesting schedules are heavily back-loaded.
2. Ignoring Equity Liquidity & Vesting Cliffs
Explanation: A large equity grant with a 12-month cliff and no secondary market liquidity is functionally illiquid for the first year. Engineers frequently overvalue these grants by assuming immediate cash equivalence.
Fix: Apply a liquidity probability weight (0.3β0.7 for pre-IPO, 0.9β1.0 for public companies). Annualize only the vested portion and discount by liquidity risk.
3. Failing to Document Verbal Agreements
Explanation: Recruiters may verbally commit to accelerated vesting, performance bonuses, or remote-work flexibility without updating the contract. Verbal terms are unenforceable and frequently disappear during onboarding.
Fix: Require a written offer letter or contract addendum that explicitly lists base, equity, bonus structure, vesting schedule, and non-monetary terms before accepting.
4. Overleveraging Competing Offers
Explanation: Presenting multiple offers aggressively can trigger defensive budgeting or rescinded offers. Recruiters may perceive the candidate as transactional rather than role-aligned.
Fix: Frame competing offers as market validation, not ultimatums. Use phrasing like "This aligns with market data for this level; I'd prefer to structure our package to match that range." Maintain relationship capital.
5. Neglecting Fiscal Budget Cycles
Explanation: Many organizations operate on quarterly or annual budget approvals. Negotiating outside approval windows forces recruiters to defer decisions or offer suboptimal terms.
Fix: Identify the company's fiscal year end and budget planning cycle. Time negotiations to coincide with headcount approvals or post-review periods when flexibility is highest.
6. Skipping BATNA Quantification
Explanation: Without a clearly defined Best Alternative to a Negotiated Agreement, engineers accept subpar offers out of urgency or uncertainty. This erodes leverage and increases regret risk.
Fix: Quantify your walk-away threshold in total compensation terms. Document your current role's trajectory, market demand, and alternative opportunities. Treat BATNA as a hard constraint, not a suggestion.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Pre-IPO Startup | Prioritize equity + sign-on bonus | High upside potential, cash constraints common | Low immediate cash, high long-term variance |
| Public Tech Company | Focus on base + RSU refreshers | Predictable liquidity, structured bands | Stable cash, moderate equity growth |
| Remote-First Global | Negotiate location-adjusted base + benefits | Geographic arbitrage creates flexibility | Medium cash, high benefit valuation |
| Contract-to-Perm | Secure higher hourly rate + conversion bonus | Risk premium for non-perm status | High short-term cash, delayed equity |
Configuration Template
// negotiation-config.ts
import { NegotiationConfig } from './compensation-engine';
export const defaultNegotiationConfig: NegotiationConfig = {
targetTotalComp: 145000, // Adjust based on market benchmark
walkAwayThreshold: 125000, // Hard minimum after annualization
fallbackLever: 'equity', // Primary adjustment lever if base is capped
liquidityProbability: 0.65, // Conservative estimate for late-stage private
};
export const aggressiveConfig: NegotiationConfig = {
targetTotalComp: 160000,
walkAwayThreshold: 135000,
fallbackLever: 'signOn',
liquidityProbability: 0.85,
};
export const conservativeConfig: NegotiationConfig = {
targetTotalComp: 130000,
walkAwayThreshold: 115000,
fallbackLever: 'benefits',
liquidityProbability: 0.45,
};
Quick Start Guide
- Initialize the Engine: Import
CompensationEngine and select a configuration profile matching your risk tolerance and market conditions.
- Input Market Data: Populate a
MarketBenchmark object using aggregated survey data. Ensure location, seniority, and company size filters align with your target role.
- Model the Offer: Map the received package to
OfferPackage. Verify vesting schedules, strike prices, and benefit valuations are accurately annualized.
- Run Analysis: Execute
calculateTotalComp() and generateCounterOffer(). Review the output for precise numbers, fallback levers, and justification statements.
- Execute & Document: Communicate the counter-offer using structured phrasing. Request a written contract addendum reflecting all agreed terms before proceeding.