| name | capacity-planning |
| description | Analyze team capacity, plan resource allocation, and balance workload across projects. Forecast staffing needs and optimize team utilization while maintaining sustainable pace. |
Capacity Planning
Overview
Capacity planning ensures teams have sufficient resources to deliver work at sustainable pace, prevents burnout, and enables accurate commitment to stakeholders.
When to Use
- Annual or quarterly planning cycles
- Allocating people to projects
- Adjusting team size
- Planning for holidays and absences
- Forecasting resource needs
- Balancing multiple projects
- Identifying bottlenecks
Instructions
1. Capacity Assessment
# Team capacity calculation and planning
class CapacityPlanner:
# Standard work hours per week
STANDARD_WEEK_HOURS = 40
# Activities that reduce available capacity
OVERHEAD_HOURS = {
'meetings': 5, # standups, 1-on-1s, planning
'training': 2, # learning new tech
'administrative': 2, # emails, approvals
'support': 2, # helping teammates
'contingency': 2 # interruptions, emergencies
}
def __init__(self, team_size, sprint_duration_weeks=2):
self.team_size = team_size
self.sprint_duration_weeks = sprint_duration_weeks
self.members = []
def calculate_team_capacity(self):
"""Calculate available capacity hours"""
# Base capacity
base_hours = self.team_size * self.STANDARD_WEEK_HOURS * self.sprint_duration_weeks
# Subtract overhead
overhead = sum(self.OVERHEAD_HOURS.values()) * self.team_size * self.sprint_duration_weeks
# Subtract absences
absence_hours = self.calculate_absences()
# Available capacity
available_capacity = base_hours - overhead - absence_hours
return {
'base_hours': base_hours,
'overhead_hours': overhead,
'absence_hours': absence_hours,
'available_capacity': available_capacity,
'utilization_target': '85%', # Leave 15% buffer
'target_commitment': available_capacity * 0.85
}
def calculate_absences(self):
"""Account for vacation, sick, etc."""
absence_days = 0
# Standard absences
vacation_days = 15 # annual
sick_days = 5 # annual
holidays = 10 # annual
# Convert to per-sprint
absence_days = (vacation_days + sick_days + holidays) / 52 * self.sprint_duration_weeks
absence_hours = absence_days * 8 * self.team_size
return absence_hours
def allocate_to_projects(self, projects, team):
"""Allocate capacity across multiple projects"""
allocation = {}
total_allocation = 0
# Allocate by priority
for project in sorted(projects, key=lambda p: p.priority):
required_hours = project.effort_hours
available = self.calculate_team_capacity()['available_capacity'] - total_allocation
if available >= required_hours:
allocation[project.id] = {
'project': project.name,
'allocated': required_hours,
'team_members': int(required_hours / (self.STANDARD_WEEK_HOURS * self.sprint_duration_weeks)),
'allocation_percent': (required_hours / available * 100)
}
total_allocation += required_hours
else:
allocation[project.id] = {
'project': project.name,
'allocated': available,
'status': 'Insufficient capacity',
'shortfall': required_hours - available,
'recommendation': 'Add resources or defer scope'
}
total_allocation = available
return allocation
def identify_bottlenecks(self, skills, projects):
"""Find skill constraints"""
bottlenecks = []
for skill in skills:
people_with_skill = sum(1 for p in self.members if skill in p.skills)
projects_needing_skill = sum(1 for p in projects if skill in p.required_skills)
utilization = (projects_needing_skill / people_with_skill * 100) if people_with_skill > 0 else 0
if utilization > 100:
bottlenecks.append({
'skill': skill,
'people_available': people_with_skill,
'projects_needing': projects_needing_skill,
'utilization': utilization,
'severity': 'Critical',
'actions': ['Cross-train team', 'Hire specialist', 'Adjust scope']
})
return bottlenecks
2. Capacity Planning Template
Capacity Plan for Q1 2025:
Team: Platform Engineering (12 people)
Period: January 1 - March 31, 2025
Planned Duration: 13 weeks
---
## Team Composition
Engineers:
- Senior Engineers: 3 (1.2 FTE each)
- Mid-Level Engineers: 6 (0.95 FTE each)
- Junior Engineers: 2 (0.8 FTE each)
- DevOps: 1 (1.0 FTE)
Total Available FTE: 11.1 (accounting for overhead, absences)
Total Available Hours: 11.1 * 40 * 13 = 5,772 hours
---
## Planned Absences
Vacation: 8 weeks across team (estimated)
Sick/Personal: 2 weeks across team
Holiday: 1 week (MLK, Presidents Day)
Total: ~480 hours
---
## Capacity Allocation
Project A: Critical Infrastructure
Allocation: 60% (6,600 hours needed)
Team: 3 senior, 3 mid-level engineers
FTE: 6.6
Status: Committed
Project B: Feature Development
Allocation: 30% (3,300 hours needed)
Team: 2 mid-level, 2 junior engineers
FTE: 3.3
Status: Committed
Infrastructure & Maintenance:
Allocation: 10% (1,100 hours)
Team: DevOps, 1 senior engineer
FTE: 1.1
Status: Operational capacity
Total: 100% allocation, 0% buffer
---
## Risk Assessment
Risks:
1. Zero buffer capacity (100% allocation)
Impact: Any absence/issue creates crisis
Mitigation: Cross-training, automation
2. Junior engineer ramp-up time
Impact: Mid-level engineers pulled for mentoring
Mitigation: Assign 1 mentoring hour/week
3. Infrastructure bottleneck (1 DevOps)
Impact: Scaling limitations
Mitigation: Hire additional DevOps by Feb 1
---
## Recommendations
1. Reduce capacity planning from 100% to 85%
2. Hire 1 additional DevOps engineer
3. Cross-train 2 engineers on critical systems
4. Schedule vacations strategically (not during Phase 2)
5. Build 15% buffer for emergencies
3. Resource Leveling
// Balance workload across team members
class ResourceLeveling {
levelWorkload(team, tasks) {
const workloadByPerson = {};
// Initialize team member workload
team.forEach(person => {
workloadByPerson[person.id] = {
name: person.name,
skills: person.skills,
capacity: person.capacity_hours,
assigned: [],
utilization: 0
};
});
// Assign tasks to balance workload
const sortedTasks = tasks.sort((a, b) => b.effort - a.effort); // Largest first
sortedTasks.forEach(task => {
const suitable = team.filter(p =>
this.hasSufficientSkills(p.skills, task.required_skills) &&
this.hasCapacity(workloadByPerson[p.id].utilization, p.capacity_hours)
);
if (suitable.length > 0) {
const leastUtilized = suitable.reduce((a, b) =>
workloadByPerson[a.id].utilization < workloadByPerson[b.id].utilization ? a : b
);
workloadByPerson[leastUtilized.id].assigned.push(task);
workloadByPerson[leastUtilized.id].utilization += task.effort;
}
});
return {
assignments: workloadByPerson,
balanceMetrics: this.calculateBalance(workloadByPerson),
unassignedTasks: tasks.filter(t => !Object.values(workloadByPerson).some(p => p.assigned.includes(t)))
};
}
calculateBalance(workloadByPerson) {
const utilizations = Object.values(workloadByPerson).map(p => p.utilization);
const average = utilizations.reduce((a, b) => a + b) / utilizations.length;
const variance = Math.sqrt(
utilizations.reduce((sum, u) => sum + Math.pow(u - average, 2)) / utilizations.length
);
return {
average_utilization: average.toFixed(1),
std_deviation: variance.toFixed(1),
balance_score: this.calculateBalanceScore(variance),
recommendations: this.getBalancingRecommendations(variance)
};
}
calculateBalanceScore(variance) {
if (variance < 5) return 'Excellent';
if (variance < 10) return 'Good';
if (variance < 15) return 'Fair';
return 'Poor - needs rebalancing';
}
}
4. Capacity Forecasting
12-Month Capacity Forecast:
Team Growth Plan:
Q1 2025: 12 people (current)
Q2 2025: 13 people (hire 1 DevOps)
Q3 2025: 15 people (hire 2 engineers)
Q4 2025: 15 people (stable)
Monthly Capacity (FTE):
January 2025: 10.8 FTE (below normal - ramp-up)
February 2025: 11.1 FTE (normal)
March 2025: 11.0 FTE (1 person on leave)
Q2 Average: 12.5 FTE (new hire contributing)
Q3 Average: 14.2 FTE (2 new hires)
Q4 Average: 15.0 FTE (all at full capacity)
---
Project Commitments vs. Available Capacity:
Q1: Committed 11.0 FTE, Available 11.1 FTE (safe)
Q2: Committed 12.0 FTE, Available 12.5 FTE (buffer 4%)
Q3: Committed 13.0 FTE, Available 14.2 FTE (buffer 9%)
Q4: Committed 14.0 FTE, Available 15.0 FTE (buffer 7%)
---
Risk Alerts:
- Q1 is tight (98% utilized)
- Skill gap: Backend expertise in Q2
- Attrition risk: Plan for 1 departure in Q3
Best Practices
✅ DO
- Plan capacity at 85% utilization (15% buffer)
- Account for meetings, training, and overhead
- Include known absences (vacation, holidays)
- Identify skill bottlenecks early
- Balance workload fairly across team
- Review capacity monthly
- Adjust plans based on actual velocity
- Cross-train on critical skills
- Communicate realistic commitments to stakeholders
- Build contingency for emergencies
❌ DON'T
- Plan at 100% utilization
- Ignore meetings and overhead
- Assign work without checking skills
- Create overload with continuous surprises
- Forget about learning/training time
- Leave capacity planning to last minute
- Overcommit team consistently
- Burn out key people
- Ignore team feedback on workload
- Plan without considering absences
Capacity Planning Tips
- Use velocity data from past sprints
- Track actual vs. planned utilization
- Review capacity weekly in standups
- Maintain 15% buffer for emergencies
- Cross-train on critical functions