Preparing your learning space...
50% through FDE Project Management tutorials
A project rarely fails during planning — it fails while running. This tutorial covers the three controls that keep a live project healthy: managing priorities, handling scope changes, and managing technical debt.
Prioritization is deciding — explicitly and repeatedly — what work gets done first and what waits. It isn't a one-time meeting; it's a weekly habit.
Why it is useful: There is always more to do than time allows. A clear priority system means you cut the right things when the schedule tightens, instead of letting everything slide together.
def rice(reach, impact, confidence, effort):
return (reach * impact * confidence) / effort
items = {
"Export CSV": rice(200, 3, 0.8, 8),
"Dark mode": rice(400, 2, 0.6, 12),
"Audit log": rice(50, 4, 0.9, 6),
}
for name, score in sorted(items.items(), key=lambda x: x[1], reverse=True):
print(f"{name}: {score:.1f}")
# Export CSV: 60.0
# Dark mode: 40.0
# Audit log: 30.0
Simple explanation: RICE turns gut feel into a comparable number. In this example "Export CSV" wins even though "Dark mode" reaches more users, because export is more impactful and cheaper to build. The point is not the formula — it's that every feature is scored the same way.
A scope change is any addition, removal, or rewrite of agreed work. Managing it means having a process to evaluate, approve, and absorb changes — instead of silently letting them in.
Why it is useful: Unmanaged change is the number one cause of late software. A simple change-request loop lets you say yes or no deliberately, with the cost visible before anyone commits.
changes = []
def submit_change(desc, impact_days, requested_by, status="open"):
changes.append({"desc": desc, "impact_days": impact_days,
"requested_by": requested_by, "status": status})
submit_change("Add dark mode", 3, "Marketing")
submit_change("Switch export to XLSX", 1.5, "Ops")
submit_change("Remove audit log page", -1, "Customer")
open_days = sum(c["impact_days"] for c in changes if c["status"] == "open")
print(f"Open change backlog: {open_days} extra days") # 3.5 extra days
Simple explanation: Every change, even a rejection, goes through the same small loop. Allowing negative impacts matters too — removing scope is also a change and should be counted when it goes through the process.
Technical debt is the future cost of today's shortcuts — code written fast that will need rework, missing tests, hardcoded config, unpatched dependencies.
Why it is useful: A little debt is often a smart trade. The skill is tracking it, limiting it, and paying some every iteration so it never compounds into "we can't ship anything anymore."
debt = [
{"area": "export module", "type": "no tests", "est_h": 8, "paid": False},
{"area": "auth config", "type": "hardcoded secrets", "est_h": 4, "paid": False},
{"area": "legacy API", "type": "slow endpoint", "est_h": 12, "paid": True},
]
unpaid = sum(d["est_h"] for d in debt if not d["paid"])
print(f"{unpaid} hours of unpaid debt") # 12 hours of unpaid debt
Simple explanation: Writing debt down with a rough cost makes it visible and concrete. "We have 12 hours of known debt" beats "we should refactor someday." The register turns vague guilt into an actual number you can manage.
Save your progress and earn XP for completing tutorials.
4 questions · Pass with 70%+
1In the RICE formula (Reach × Impact × Confidence) / Effort, why might "Export CSV" outrank "Dark mode" even though Dark mode reaches more users?
2Which statement about the change-request loop is false?
3What is the difference between "sloppy code" and technical debt?
4Which practice best prevents technical debt from compounding into "we can't ship anything anymore"?
Technology
Forward Deployed Engineer
Lesson group
FDE Project Management
Progress
50% complete