Resource Planning Fundamentals
Resource planning ensures the right people with the right skills are available at the right time. In IT, people are the primary resource. Unlike physical resources, people cannot be stockpiled, easily substituted, or run at 100% utilization without burnout.
Key Concepts
- Allocation: What percentage of a person's time is dedicated to this project (e.g., 80% allocation)
- Utilization: What percentage of available time is spent on productive work (target: 70-80%)
- Capacity: The total available hours for productive work after accounting for meetings, PTO, and overhead
- Velocity: The team's proven rate of delivering work (story points per sprint)
- Bench Time: Time when a resource is available but not assigned to billable/project work
Skill Matrix
| Team Member | React | Node.js | AWS | PostgreSQL | K8s | Security |
|---|---|---|---|---|---|---|
| Alice | Expert | Intermediate | Beginner | Intermediate | None | Beginner |
| Bob | Beginner | Expert | Expert | Expert | Intermediate | Intermediate |
| Carol | Intermediate | Intermediate | Intermediate | Beginner | Expert | Intermediate |
| Dave | Expert | Expert | Intermediate | Intermediate | Beginner | Expert |
// Capacity Planning Model
interface TeamMember {
name: string;
role: string;
skills: { skill: string; level: 'none' | 'beginner' | 'intermediate' | 'expert' }[];
hoursPerWeek: number;
allocation: Record; // project -> percentage
pto: { startDate: Date; endDate: Date }[];
}
interface CapacityPlan {
period: { start: Date; end: Date };
team: TeamMember[];
totalCapacityHours: number;
allocatedHours: number;
availableHours: number;
utilizationRate: number;
overallocated: string[]; // members over 100%
underutilized: string[]; // members under 50%
skillGaps: string[];
}
function calculateCapacity(
members: TeamMember[],
periodWeeks: number,
overheadFactor: number = 0.2 // 20% for meetings, admin, etc.
): CapacityPlan {
let totalCapacity = 0;
let totalAllocated = 0;
const overallocated: string[] = [];
const underutilized: string[] = [];
for (const member of members) {
const effectiveHours = member.hoursPerWeek * periodWeeks * (1 - overheadFactor);
totalCapacity += effectiveHours;
const totalAllocation = Object.values(member.allocation)
.reduce((sum, pct) => sum + pct, 0);
const allocatedHours = effectiveHours * (totalAllocation / 100);
totalAllocated += allocatedHours;
if (totalAllocation > 100) overallocated.push(
`${member.name}: ${totalAllocation}% allocated`
);
if (totalAllocation < 50) underutilized.push(
`${member.name}: ${totalAllocation}% allocated`
);
}
return {
period: { start: new Date(), end: new Date() },
team: members,
totalCapacityHours: Math.round(totalCapacity),
allocatedHours: Math.round(totalAllocated),
availableHours: Math.round(totalCapacity - totalAllocated),
utilizationRate: Math.round((totalAllocated / totalCapacity) * 100),
overallocated,
underutilized,
skillGaps: []
};
}
Resource Leveling vs. Resource Smoothing
| Aspect | Resource Leveling | Resource Smoothing |
|---|---|---|
| Priority | Resource constraints take priority | Schedule constraints take priority |
| End Date | May change (delay) the end date | End date is fixed |
| Technique | Delay tasks to avoid overallocation | Shift tasks within their float |
| Use When | Resources are the hard constraint | The deadline is the hard constraint |
Capacity Planning Anti-Patterns
- 100% Utilization Target: Aiming for 100% utilization leaves no room for learning, innovation, or unexpected work. Target 70-80%.
- Fractional Allocation: Assigning someone 20% to 5 projects is worse than 100% to 1 project. Context switching destroys productivity.
- Ignoring Ramp-Up: New team members are not productive on day one. Budget 2-4 weeks for onboarding.
- Single Points of Failure: If only one person can do critical work, you have a bus factor of 1. Cross-train.