DEV Community

전규현 (Jeon gyuhyeon)
전규현 (Jeon gyuhyeon)

Posted on

The 90% Complete Trap: Why the Last 10% is Actually 90%

"How's the task progress?"

"90% complete!"

A week later...

"What's the percentage now?"

"Um... 95%."

Another week later...

"Still not done?"

"Almost there... maybe 98%?"

This is exactly the 90% syndrome.

There's an old joke in software development: "The first 90% takes 90% of the time, and the remaining 10% takes another 90%."

It's not a joke. It's reality.

Why 90% is Actually 50%

Developers make this mistake when judging progress:

# Developer's mind
visible_work = {
    "UI_implementation": 100,  # ✅ Done!
    "basic_functionality": 100,  # ✅ Done!
    "API_integration": 80     # Almost done
}
progress = sum(visible_work.values()) / 300
print(f"Progress: {progress:.0%}")  # 93%!

# Actual total work
all_work = {
    "UI_implementation": 100,
    "basic_functionality": 100,
    "API_integration": 80,
    "edge_cases": 0,     # Haven't even started
    "error_handling": 0,     # Haven't thought about it
    "performance_optimization": 0,     # Later...
    "security_verification": 0,       # Was that needed?
    "testing": 0,          # Of course later
    "bug_fixes": 0,       # Will there be bugs?
    "documentation": 0           # Really necessary...?
}
real_progress = 280 / 700
print(f"Actual progress: {real_progress:.0%}")  # 40%!
Enter fullscreen mode Exit fullscreen mode

Counting only visible: 90%, counting everything: 40%.

Psychology of Progress Illusion

1. Ambiguous Definition of "Done"

What is "done"?

  • Developer: "Code is all written" ✅
  • PM: "What about tests?"
  • Developer: "Oh... was that included?" 😅

2. The Trap of Pareto Principle

The Paradox of 80/20 Rule:

  • Implement 80% of features in 20% of time
  • Remaining 20% of features take 80% of time

The last 20% is the real deal.

3. Exponential Complexity Increase

// Initially: Simple
function addNumbers(a, b) {
  return a + b; // Done in 1 minute!
}

// Later: Reality
function addNumbers(a, b) {
  // Input validation
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new Error('Invalid input');
  }

  // Overflow check
  if (a > Number.MAX_SAFE_INTEGER - b) {
    throw new Error('Overflow');
  }

  // Precision handling
  const result = parseFloat((a + b).toPrecision(15));

  // Logging
  logger.info(`Addition performed: ${a} + ${b} = ${result}`);

  // Caching
  cache.set(`${a}+${b}`, result);

  return result; // Took 2 hours...
}
Enter fullscreen mode Exit fullscreen mode

Real Case: Login Feature

Actual progress report from a startup:

Day 1: "50% Complete!"

✅ Login Form UI
✅ Basic Validation
Enter fullscreen mode Exit fullscreen mode

Day 3: "80% Complete!"

✅ Backend API
✅ Session Management
Enter fullscreen mode Exit fullscreen mode

Day 5: "90% Complete!"

✅ Login Success Case
Enter fullscreen mode Exit fullscreen mode

Day 10: "95% Complete..."

⏳ Password Recovery
⏳ Social Login
⏳ 2FA
Enter fullscreen mode Exit fullscreen mode

Day 15: "98% Complete..."

⏳ Browser Compatibility
⏳ Mobile Optimization
⏳ Security Vulnerability Fixes
Enter fullscreen mode Exit fullscreen mode

Day 20: "99% Complete..."

⏳ Load Testing
⏳ Error Messages i18n
⏳ 5 Login Failure Limit
⏳ CAPTCHA
Enter fullscreen mode Exit fullscreen mode

Day 25: "Really Complete!"

Expected 5 days → Actual 25 days (5x)

Accurate Progress Measurement Methods

1. Binary Progress (0% or 100%)

The most honest method.

def binary_progress(task):
    """Done or not done"""
    if task.is_completely_done():
        return 100
    else:
        return 0
    # No middle ground!
Enter fullscreen mode Exit fullscreen mode

Advantage: Clear
Disadvantage: Hard to gauge progress

2. Definition of Done Checklist

Task: Login API
Progress Checklist:
  - [ ] Feature Implementation (20%)
  - [ ] Unit Tests (15%)
  - [ ] Integration Tests (15%)
  - [ ] Code Review (10%)
  - [ ] Documentation (10%)
  - [ ] Security Verification (10%)
  - [ ] Performance Testing (10%)
  - [ ] Deployment Prep (10%)

Completed: 3 of 8 ✓
Progress: 37.5% (Accurate!)
Enter fullscreen mode Exit fullscreen mode

3. Story Point Based

const storyPoints = {
  'Login Form': 3, // ✅ Done
  'Validation Logic': 5, // ✅ Done
  'Session Management': 8, // ✅ Done
  'Error Handling': 5, // ⏳ In Progress
  'Security Hardening': 8, // ❌ Not Started
  'Testing': 5, // ❌ Not Started
};

const completed = 3 + 5 + 8; // 16
const total = 34;
const progress = (completed / total) * 100; // 47%
Enter fullscreen mode Exit fullscreen mode

4. Using Burndown Chart

Remaining Work (hours)
100 |*
 80 |  *
 60 |    *  ← Expected
 40 |      * * * * *  ← Actual (flattened)
 20 |              * * * *
  0 |____________________
    1  3  5  7  9  11 13 (days)
Enter fullscreen mode Exit fullscreen mode

The flattened section is the 90% syndrome.

How to Prevent 90% Syndrome

1. Break Tasks Small

❌ "Login Feature" (40 hours)

✅ In small units:
- Login Form UI (2 hours)
- Email Validation (1 hour)
- Password Validation (1 hour)
- API Endpoint (2 hours)
- JWT Token Generation (2 hours)
- Session Storage (1 hour)
...Each at a completable size
Enter fullscreen mode Exit fullscreen mode

2. Include Hidden Tasks in Advance

def realistic_estimate(visible_work):
    hidden_multiplier = {
        "testing": 0.3,
        "debugging": 0.2,
        "refactoring": 0.1,
        "documentation": 0.1,
        "code_review": 0.1,
        "integration": 0.2
    }

    total = visible_work * (1 + sum(hidden_multiplier.values()))
    return total  # 2x
Enter fullscreen mode Exit fullscreen mode

3. Use Three-Point Estimation

def three_point_estimate(optimistic, realistic, pessimistic):
    """PERT estimation"""
    estimate = (optimistic + 4 * realistic + pessimistic) / 6

    # Example: Login feature
    # Optimistic: 3 days, Realistic: 5 days, Pessimistic: 10 days
    # Estimate: (3 + 4*5 + 10) / 6 = 5.5 days

    return estimate
Enter fullscreen mode Exit fullscreen mode

4. Daily Check-in

## Daily Progress Check

### What I Completed Yesterday (Specifically)

- ✅ Login Form HTML/CSS
- ✅ Client Validation Logic

### What I'll Do Today (Measurably)

- [ ] POST /login API
- [ ] JWT Token Generation

### Blockers

- 🚫 Need to choose JWT library

### Actual Remaining Work (Honestly)

- About 15 hours expected
Enter fullscreen mode Exit fullscreen mode

Response Strategies by Role

Tips for PMs

When you hear 90% report:

  1. "Show me the Definition of Done checklist"
  2. "Have you written tests?"
  3. "Have you handled edge cases?"
  4. "Have you updated documentation?"

Most will answer "Oh..."

Tips for Developers

When reporting progress:

  • "Feature is 90%, overall is 60%"
  • "Happy Path complete, exception handling in progress"
  • "Need 3 more days" (specific time)

Tips for Management

Understanding project status:

  • Look at remaining task count rather than progress
  • Look at burndown chart slope
  • Check demo-able features

90% Syndrome Checklist

Check if project has fallen into 90% syndrome:

  • [ ] Progress stays in 90% range for 2+ weeks
  • [ ] Hear "almost done" 3+ times
  • [ ] Remaining task list keeps growing
  • [ ] Expected completion date keeps getting pushed
  • [ ] Team members look exhausted

3 or more? 90% syndrome.

Conclusion: Honest Progress is Best

"90% complete" isn't a lie. It's just a misconception.

Solutions:

  1. Break tasks small
  2. Calculate hidden tasks in advance
  3. Use Binary (0/100) or checklist
  4. Daily check-in
  5. Honest communication

Accepting that the last 10% is 50% of the total enables more accurate planning.

Don't say "90% complete".
Say "9 of 10 tasks complete".

Numbers don't lie.


Need accurate progress management and transparent project tracking? Check out Plexo.

Top comments (0)