Building a Career Ladder That Actually Works for Engineers
Design engineering career ladders with clear expectations, measurable competencies and dual-track progression that keeps ICs and managers alike.

Most engineering career ladders fail because they describe aspirational qualities rather than observable behaviors. "Demonstrates technical excellence" tells an engineer nothing about what they need to do differently tomorrow. Effective ladders define concrete expectations that engineers can self-assess against, managers can evaluate consistently, and organizations can use to make fair compensation decisions.
The goal isn't a perfect rubric—it's a shared language that makes growth conversations productive rather than political.
Defining Observable Competency Dimensions
Each level needs competencies described in terms of observable behaviors, not personality traits. "Takes ownership" is vague. "Identifies risks in their project and escalates them with proposed mitigations before they become blockers" is actionable.
// ❌ Vague competency definitions
const vagueLevel = {
title: "Senior Engineer",
expectations: [
"Demonstrates technical excellence",
"Shows leadership",
"Mentors others",
"Has strong communication skills",
],
};
// What does "demonstrates" mean? How do you measure it?// ✅ Observable behavior-based competency framework
interface CompetencyDimension {
name: string;
description: string;
levels: Map<string, BehaviorExpectation[]>;
}
interface BehaviorExpectation {
behavior: string;
examples: string[];
antiPatterns: string[];
}
const technicalCompetency: CompetencyDimension = {
name: "Technical Execution",
description:
"Ability to design, implement, and ship reliable software",
levels: new Map([
[
"mid",
[
{
behavior:
"Completes well-scoped features independently with" +
" guidance on approach",
examples: [
"Implements API endpoint from design doc",
"Adds test coverage for existing module",
"Debugs production issues with support from seniors",
],
antiPatterns: [
"Needs step-by-step instructions for every task",
"Frequently blocked without asking for help",
],
},
],
],
[
"senior",
[
{
behavior:
"Designs and delivers complex features across" +
" multiple components with minimal guidance",
examples: [
"Designs schema migration strategy for live system",
"Leads technical implementation of cross-team feature",
"Identifies and resolves systemic performance issues",
],
antiPatterns: [
"Only works on isolated tasks within one service",
"Avoids ambiguous problems",
],
},
],
],
[
"staff",
[
{
behavior:
"Sets technical direction for large initiatives" +
" and influences architecture decisions across teams",
examples: [
"Writes RFC adopted across multiple teams",
"Leads migration of core system with clear rollback plan",
"Defines technical standards others follow",
],
antiPatterns: [
"Technical decisions don't extend beyond own team",
"Architecture docs gather dust without adoption",
],
},
],
],
]),
};Anti-patterns are as important as positive examples. They make it explicit what doesn't count toward the next level, preventing misaligned self-assessments.
The Dual-Track Problem
Forcing engineers into management to advance their career loses your best technical talent. A dual track with genuine parity—same compensation bands, same organizational influence—retains both builders and leaders.
interface CareerTrack {
name: "individual-contributor" | "management";
levels: LevelDefinition[];
}
interface LevelDefinition {
title: string;
level: number;
compensationBand: string;
scopeDescription: string;
keyDifferentiator: string;
}
const icTrack: CareerTrack = {
name: "individual-contributor",
levels: [
{
title: "Software Engineer",
level: 1,
compensationBand: "L1",
scopeDescription: "Individual tasks within a team",
keyDifferentiator:
"Delivers assigned work with guidance",
},
{
title: "Software Engineer II",
level: 2,
compensationBand: "L2",
scopeDescription: "Features within a team",
keyDifferentiator:
"Independently delivers multi-week features",
},
{
title: "Senior Software Engineer",
level: 3,
compensationBand: "L3",
scopeDescription: "Team-level technical decisions",
keyDifferentiator:
"Owns team technical direction, mentors juniors",
},
{
title: "Staff Software Engineer",
level: 4,
compensationBand: "L4",
scopeDescription: "Cross-team technical influence",
keyDifferentiator:
"Sets direction across multiple teams",
},
{
title: "Principal Software Engineer",
level: 5,
compensationBand: "L5",
scopeDescription: "Organization-wide technical strategy",
keyDifferentiator:
"Shapes company technical strategy and culture",
},
],
};
const managementTrack: CareerTrack = {
name: "management",
levels: [
{
title: "Engineering Manager",
level: 3, // Parallel to Senior IC
compensationBand: "L3",
scopeDescription: "Single team delivery and growth",
keyDifferentiator:
"Manages team of 5-8, owns delivery and people growth",
},
{
title: "Senior Engineering Manager",
level: 4,
compensationBand: "L4",
scopeDescription: "Multiple teams or complex domain",
keyDifferentiator:
"Manages managers or large team, owns domain strategy",
},
{
title: "Director of Engineering",
level: 5,
compensationBand: "L5",
scopeDescription: "Department-level strategy",
keyDifferentiator:
"Sets department direction, manages senior managers",
},
],
};Notice the compensation bands align between tracks. A Staff Engineer and Senior Engineering Manager occupy the same band. This prevents the management track from becoming the only path to higher pay.
Promotion Criteria and Evidence Collection
Promotions should be evidence-based decisions, not popularity contests or recency-bias exercises. Structure the evidence collection process throughout the review period.
interface PromotionPacket {
candidate: string;
currentLevel: string;
targetLevel: string;
period: { start: Date; end: Date };
evidence: CompetencyEvidence[];
sponsorStatement: string;
peerFeedback: PeerFeedback[];
recommendation: "promote" | "not-yet" | "needs-discussion";
}
interface CompetencyEvidence {
dimension: string;
targetBehavior: string;
examples: WorkExample[];
}
interface WorkExample {
description: string;
impact: string;
date: Date;
scope: "individual" | "team" | "cross-team" | "org-wide";
verifiedBy: string;
}
interface PeerFeedback {
reviewer: string;
relationship: "peer" | "report" | "cross-team" | "stakeholder";
strengths: string[];
growthAreas: string[];
}
function assessReadiness(
packet: PromotionPacket,
targetLevel: LevelDefinition
): {
ready: boolean;
gaps: string[];
strengths: string[];
} {
const gaps: string[] = [];
const strengths: string[] = [];
for (const evidence of packet.evidence) {
const hasSubstantialExamples =
evidence.examples.length >= 2 &&
evidence.examples.some(
(e) => e.scope !== "individual"
);
if (hasSubstantialExamples) {
strengths.push(evidence.dimension);
} else {
gaps.push(
`${evidence.dimension}: needs more examples at` +
` ${targetLevel.scopeDescription} scope`
);
}
}
return {
ready: gaps.length === 0,
gaps,
strengths,
};
}Calibration Across Teams
Different teams and managers apply standards differently. Calibration sessions ensure fairness across the organization.
interface CalibrationSession {
participants: string[];
candidates: PromotionPacket[];
levelBeing Calibrated: string;
}
function prepareCalibration(
packets: PromotionPacket[]
): {
groupedByLevel: Map<string, PromotionPacket[]>;
discussionOrder: PromotionPacket[];
} {
const grouped = new Map<string, PromotionPacket[]>();
for (const packet of packets) {
const level = packet.targetLevel;
const existing = grouped.get(level) ?? [];
existing.push(packet);
grouped.set(level, existing);
}
// Discuss borderline cases first — they benefit most
// from cross-team perspective
const ordered = packets.sort((a, b) => {
const aGaps = assessReadiness(a, {} as LevelDefinition).gaps.length;
const bGaps = assessReadiness(b, {} as LevelDefinition).gaps.length;
// Candidates with some gaps but also strengths are borderline
const aBorderline = aGaps > 0 && aGaps < 3 ? 0 : 1;
const bBorderline = bGaps > 0 && bGaps < 3 ? 0 : 1;
return aBorderline - bBorderline;
});
return {
groupedByLevel: grouped,
discussionOrder: ordered,
};
}Key Takeaways
Effective career ladders describe observable behaviors rather than aspirational traits—"identifies risks and escalates with proposed mitigations" beats "demonstrates leadership" every time. Build dual tracks with genuine parity: same compensation bands for IC and management at equivalent levels, so engineers don't have to manage people just to earn more. Include anti-patterns alongside positive examples at each level to prevent misaligned self-assessments and make expectations unambiguous. Structure promotion decisions around evidence packets collected throughout the review period, not recency-biased narratives assembled the week before reviews. Run calibration sessions across teams to normalize standards and prevent promotion inflation in teams with lenient managers. The career ladder isn't a bureaucratic exercise—it's the framework that determines whether your best engineers stay and grow or leave for organizations that value their contributions more clearly.


