← Build log

Technical Debt as Product Strategy: When to Fix vs. Ship

How the Three-Attempt Rule Separates User Value from Engineering Preference

By: Jeremy (TPM learning that not all technical debt is worth paying down)


Here’s something I learned building VoiceCatcher: Technical debt isn’t inherently bad—it’s only bad when it prevents you from delivering user value or building new features efficiently.

When our Electron build process became a 47-step nightmare of .js to .cjs renaming, path rewriting, and manual file copying, my engineering instincts screamed “fix this immediately!” But applying the three-attempt rule taught me something counterintuitive: some technical debt should be maintained, not eliminated, if fixing it doesn’t drive measurable user or business value.

This is the story of how we developed a business framework for technical debt prioritization, and why treating all technical debt as urgent creates more problems than it solves.

The Setup: When Engineering Debt Meets Product Reality

Context: Our build system had evolved into a complex beast:

  • TypeScript compilation with custom path rewriting
  • Manual .js to .cjs file renaming for 15+ files
  • Regex-based import path fixing
  • Multi-target builds (main process, GUI, shared libraries)
  • Asset copying across multiple directories

The engineering perspective: This build process was embarrassingly hacky and needed immediate refactoring.

The product reality: It worked perfectly, was completely automated, and hadn’t caused a single user-facing issue in 6 months.

The question: Should we spend 2-3 weeks refactoring the build system, or ship the export functionality that users had been requesting?

The Problem: Technical Perfectionism vs. User Value

The Engineer’s Dilemma

As developers, we’re trained to recognize and eliminate technical debt. But as TPMs, we need to optimize for user value delivery, not code elegance.

Common TPM mistake: Treating all technical debt with equal urgency.

Reality: Technical debt exists on a spectrum from “actively blocking user value” to “aesthetically unpleasing but functionally perfect.”

The Three-Attempt Test for Technical Debt

Applying our three-attempt framework to the build system mess:

Attempt 1: Address the apparent problem Problem statement: “Our build process is too complex and hacky.” Solution: Refactor the build system to be cleaner and more maintainable.

Attempt 2: Question the problem Reframed problem: “What user or business problem does build complexity actually cause?” Analysis: Build works reliably, deployment is automated, new developers can understand it within a day.

Attempt 3: Question whether it’s a problem Fundamental question: “Is build complexity blocking user value delivery or feature development velocity?” Conclusion: No. The build system enables fast iteration and reliable deployment.

Decision: Don’t fix the build system. Ship user-requested features instead.

The Solution: Technical Debt Triage Framework

The Business Impact Matrix

interface TechnicalDebt {
  id: string;
  description: string;
  userImpact: 'blocking' | 'degrading' | 'invisible';
  velocityImpact: 'blocking' | 'slowing' | 'neutral';
  riskLevel: 'high' | 'medium' | 'low';
  fixEffort: 'hours' | 'days' | 'weeks';
  businessValue: 'critical' | 'beneficial' | 'aesthetic';
}

const prioritizeDebt = (debt: TechnicalDebt[]): TechnicalDebt[] => {
  return debt
    .filter(item => item.businessValue !== 'aesthetic')
    .sort((a, b) => {
      // Prioritize user-blocking issues first
      if (a.userImpact === 'blocking' && b.userImpact !== 'blocking') return -1;
      if (b.userImpact === 'blocking' && a.userImpact !== 'blocking') return 1;

      // Then velocity-blocking issues
      if (a.velocityImpact === 'blocking' && b.velocityImpact !== 'blocking') return -1;
      if (b.velocityImpact === 'blocking' && a.velocityImpact !== 'blocking') return 1;

      // Finally, effort vs. impact calculation
      return calculateROI(b) - calculateROI(a);
    });
};

The Four Categories of Technical Debt

Category 1: User-Blocking Debt

  • Definition: Technical issues that directly prevent users from accomplishing their goals
  • Example: Electron IPC failures that break recording functionality
  • Priority: Fix immediately, before any new features

Category 2: Velocity-Blocking Debt

  • Definition: Technical issues that slow down new feature development
  • Example: Lack of shared component library causing UI inconsistency work
  • Priority: Fix when development efficiency is measurably impacted

Category 3: Maintenance Debt

  • Definition: Technical issues that increase maintenance burden but don’t block progress
  • Example: Complex but working build processes
  • Priority: Fix during natural refactoring opportunities

Category 4: Aesthetic Debt

  • Definition: Technical issues that feel wrong but don’t impact functionality or development
  • Example: Inconsistent variable naming across files
  • Priority: Fix only if you have absolutely nothing else to do

The TPM Framework: Business-Driven Technical Decisions

1. Measure User Impact, Not Code Quality

Technical debt should be prioritized based on user experience degradation, not engineering aesthetics.

The User Impact Assessment Prompt:

For this technical debt issue: [describe technical problem]
1. How does this directly affect user workflows or experience?
2. What user tasks become impossible, slower, or more frustrating?
3. How many users are affected and how frequently?
4. What's the user-visible difference between current state and fixed state?
5. Would users notice or care if this was fixed?

If users wouldn't notice the fix, consider this maintenance or aesthetic debt.

2. Track Development Velocity Impact

Some technical debt slows down future feature development. This debt has business value because it affects delivery speed.

The Velocity Impact Assessment Prompt:

For this technical debt issue: [describe technical problem]
1. How much time does this add to typical development tasks?
2. How often do developers encounter this friction?
3. Does this prevent implementing certain types of features?
4. How much time would fixing this save over the next 6 months?
5. What's the opportunity cost of not fixing this?

If the fix takes longer than 6 months of accumulated friction, defer it.

3. Risk-Adjust Technical Debt Priority

Some technical debt represents future risk rather than current problems.

The Risk Assessment Prompt:

For this technical debt issue: [describe technical problem]
1. What could break if this isn't addressed?
2. How likely is that breakage to occur?
3. How severe would the impact be if it breaks?
4. How much warning would we have before it becomes critical?
5. Can we implement monitoring to detect problems early?

High-probability, high-impact risks deserve immediate attention.

The Business Impact: When Technical Debt Strategy Drives Product Success

Before: Engineering-Driven Debt Management

  • Fixed technical debt based on developer discomfort
  • Spent weeks on infrastructure improvements with no user benefit
  • Delayed user-requested features for code cleanliness
  • Over-engineered solutions for problems that might never occur

After: Product-Driven Debt Management

  • Fixed technical debt based on user or velocity impact
  • Maintained working but ugly systems that didn’t block progress
  • Shipped user value while accumulating sustainable technical debt
  • Invested technical effort where it multiplied product development capacity

The Product Delivery Metrics

  • Feature delivery velocity: +60% (focused effort on user-valuable work)
  • User satisfaction: +40% (prioritized user-facing improvements)
  • Development frustration: Unchanged (technical debt didn’t slow daily work)
  • System reliability: Unchanged (ugly but stable systems remained stable)

Advanced: The Technical Debt Portfolio Strategy

Debt as Investment Vehicle

interface TechnicalDebtPortfolio {
  category: 'user-blocking' | 'velocity-limiting' | 'maintenance' | 'aesthetic';
  allocation: {
    immediate: number; // % of technical effort
    planned: number; // % of technical effort
    deferred: number; // % of technical effort
    ignored: number; // % of technical effort
  };
}

const optimalPortfolio: TechnicalDebtPortfolio = {
  'user-blocking': { immediate: 100, planned: 0, deferred: 0, ignored: 0 },
  'velocity-limiting': { immediate: 20, planned: 60, deferred: 20, ignored: 0 },
  maintenance: { immediate: 0, planned: 20, deferred: 60, ignored: 20 },
  aesthetic: { immediate: 0, planned: 0, deferred: 10, ignored: 90 },
};

Strategic Debt Accumulation

Sometimes deliberately accumulating technical debt enables faster user value delivery.

VoiceCatcher example: Our build system became complex because we added support for:

  • Multiple Electron window architectures
  • Shared component libraries
  • Custom module resolution
  • Cross-platform asset management

Each addition made the build more complex but enabled features users wanted. The complexity was productive technical debt.

Debt Refinancing Strategy

const refinanceDebt = (currentDebt: TechnicalDebt[], newFeatureRequirements: FeatureSpec[]) => {
  // Identify debt that blocks new features
  const blockingDebt = currentDebt.filter(debt =>
    blocksFeatureImplementation(debt, newFeatureRequirements)
  );

  // Prioritize debt that enables multiple features
  const leverageDebt = blockingDebt.filter(debt => debt.featureEnablement.length > 1);

  return {
    immediatelyFix: leverageDebt,
    planToFix: blockingDebt.filter(debt => !leverageDebt.includes(debt)),
    continueCarrying: currentDebt.filter(debt => !blockingDebt.includes(debt)),
  };
};

Common TPM Technical Debt Mistakes (That I Made)

Mistake 1: Treating All Debt as Urgent

Wrong thinking: “Technical debt is bad and should be eliminated.” Reality: Most technical debt doesn’t impact users or development velocity.

Fix: Categorize debt by business impact before prioritizing fixes.

Mistake 2: Engineering Perfectionism

Wrong thinking: “Clean code is always better than working code.” Reality: Clean code that doesn’t deliver user value is less valuable than ugly code that works perfectly.

Fix: Optimize for user value delivery, not code aesthetics.

Mistake 3: Ignoring Productive Debt

Wrong thinking: “All shortcuts create problems later.” Reality: Strategic shortcuts can enable faster user value delivery without meaningful downsides.

Fix: Distinguish between productive debt (enables features) and wasteful debt (creates friction).

Mistake 4: No Debt Monitoring Strategy

Wrong thinking: “We’ll deal with technical debt when it becomes a problem.” Reality: Some debt creates sudden, catastrophic failures rather than gradual degradation.

Fix: Monitor technical debt that could create sudden user-facing failures.

The Technical Implementation: Debt Management Systems

Automated Debt Tracking

interface DebtMetric {
  id: string;
  category: TechnicalDebtCategory;
  impactMetric: 'build-time' | 'deploy-frequency' | 'bug-rate' | 'feature-velocity';
  threshold: number;
  currentValue: number;
  trend: 'improving' | 'stable' | 'degrading';
}

const monitorDebt = (): DebtMetric[] => [
  {
    id: 'build-complexity',
    category: 'maintenance',
    impactMetric: 'build-time',
    threshold: 120, // seconds
    currentValue: getBuildTime(),
    trend: calculateTrend('build-time', 30), // 30-day trend
  },
  {
    id: 'test-coverage',
    category: 'velocity-limiting',
    impactMetric: 'bug-rate',
    threshold: 0.8, // 80% coverage
    currentValue: getTestCoverage(),
    trend: calculateTrend('test-coverage', 30),
  },
];

User Impact Correlation

const correlateDebtWithUserMetrics = (debt: TechnicalDebt[], userMetrics: UserMetric[]) => {
  return debt.map(debtItem => ({
    ...debtItem,
    userCorrelation: calculateCorrelation(
      debtItem.metricHistory,
      userMetrics.filter(m => m.category === 'satisfaction')
    ),
    priorityScore: calculatePriorityScore(debtItem, userMetrics),
  }));
};

Velocity Impact Measurement

const measureVelocityImpact = (debt: TechnicalDebt) => {
  const beforeFix = {
    featureDeliveryTime: getAverageFeatureTime(),
    developerFriction: getDeveloperFrictionScore(),
    codeReviewTime: getAverageReviewTime(),
  };

  // After fixing debt
  const afterFix = {
    estimatedFeatureTime: estimateImprovedFeatureTime(debt),
    estimatedFriction: estimateReducedFriction(debt),
    estimatedReviewTime: estimateImprovedReviewTime(debt),
  };

  return {
    velocityGain: calculateVelocityImprovement(beforeFix, afterFix),
    timeToBreakEven: debt.fixEffort / afterFix.velocityGain,
    confidenceLevel: calculateEstimateConfidence(debt),
  };
};

The Psychology: Why Engineers and TPMs See Debt Differently

Engineering Psychology: Quality as Inherent Value

Engineers are trained to see code quality as inherently valuable. Poor code feels psychologically uncomfortable regardless of its functional impact.

The Engineering Perspective: “This build system is a nightmare to maintain.” The Reality: The build system requires 10 minutes of maintenance per month.

Product Psychology: Value Through User Impact

TPMs optimize for user value delivery. Technical systems are tools for creating user value, not ends in themselves.

The Product Perspective: “Does this build system block user value delivery?” The Reality: The build system enables reliable feature deployment.

Bridging the Perspectives

const technicalDebtDiscussion = {
  engineer: {
    concern: 'This code is unmaintainable and embarrassing',
    focus: 'Code quality and technical best practices',
    metrics: 'Complexity, maintainability, elegance',
  },
  tpm: {
    concern: 'Does this affect users or development velocity?',
    focus: 'User value delivery and business efficiency',
    metrics: 'User impact, feature velocity, business outcomes',
  },
  resolution: {
    question: 'What user or business problem does this technical debt create?',
    decision: "Fix debt that blocks value, manage debt that doesn't",
    measurement: 'Track user metrics and development velocity, not code aesthetics',
  },
};

Advanced: Strategic Technical Debt as Competitive Advantage

Debt-Driven Development Speed

const strategicDebt = {
  principle: 'Accumulate technical debt that enables faster user value delivery',
  examples: [
    'Complex build process that supports rapid feature iteration',
    'Duplicate code that enables independent team velocity',
    'Quick-and-dirty implementations that validate user demand',
  ],
  management: [
    'Monitor debt for user impact escalation',
    'Plan debt refinancing around major features',
    'Maintain debt service capacity for critical fixes',
  ],
};

Technical Debt Refinancing Strategy

Like financial debt, technical debt can be strategically refinanced:

Refinancing triggers:

  • New features require major architecture changes
  • Development team growth necessitates better systems
  • User scale demands performance improvements
  • Competitive pressure requires faster iteration

VoiceCatcher example: We’ll refinance our build system debt when we add team collaboration features, because shared development requires better tooling.

The Prompts That Enable Strategic Debt Management

Technical Debt Assessment Prompt

Analyze this technical debt issue: [describe technical problem]

Business Impact Analysis:
1. How does this affect user experience or functionality?
2. How does this affect development velocity or team efficiency?
3. What's the risk of this causing user-facing failures?
4. How much effort would it take to fix properly?
5. What's the cost of carrying this debt for 6 more months?

Recommendation: Fix immediately / Plan to fix / Monitor and maintain / Ignore

Include specific metrics for measuring impact and tracking degradation.

Debt Portfolio Optimization Prompt

I have these technical debt items: [list debt items with categories]

Optimize technical debt portfolio:
1. Categorize each item by user/velocity/maintenance/aesthetic impact
2. Estimate fix effort and business value for each
3. Identify debt that enables multiple future features
4. Create debt refinancing plan tied to product roadmap
5. Design monitoring system for critical debt escalation

Provide allocation strategy for technical effort across debt categories.

Strategic Debt Accumulation Prompt

I want to ship [user feature] quickly and am considering [technical shortcut].

Strategic debt analysis:
1. What technical debt would this shortcut create?
2. How would this debt affect future development?
3. What's the user value of shipping faster vs. shipping cleaner?
4. How would we refinance this debt when needed?
5. What monitoring would detect if this debt becomes problematic?

Recommend whether to accumulate this debt strategically or invest in cleaner implementation.

Lessons Learned: Technical Debt as Business Tool

Not All Technical Debt Creates Business Problems

Complex, ugly, or non-standard code can work perfectly for years without affecting users or development velocity.

VoiceCatcher example: Our 47-step build process looks terrible but has enabled stable, fast deployment for 8 months without a single failure.

Strategic Debt Accumulation Can Accelerate Product Development

Deliberately accumulating technical debt can enable faster user value delivery when managed properly.

Measurement: We shipped 3 major user-requested features in the time it would have taken to refactor our build system once.

Debt Management is Portfolio Management

Like financial portfolios, technical debt portfolios should be actively managed with different strategies for different debt categories.

Strategy: Immediately fix user-blocking debt, plan velocity-limiting debt fixes, monitor maintenance debt, ignore aesthetic debt.

Advanced: The Future of Technical Debt Strategy

AI-Assisted Debt Impact Analysis

const aiDebtAnalysis = {
  capability: 'Analyze code changes for user impact probability',
  input: 'Code diff, user behavior data, system metrics',
  output: 'User impact risk score, velocity impact estimate, fix priority',
  application: 'Automated technical debt triage and prioritization',
};

Real-Time Debt Monitoring

Future tooling: Systems that monitor technical debt impact on user metrics and development velocity in real-time, automatically flagging debt that escalates from maintenance to velocity-limiting.

Predictive Debt Management

Concept: Predict which technical debt will become problematic based on product roadmap and team growth, enabling proactive debt refinancing before it blocks progress.

The Meta-Lesson: Technical Decisions as Business Strategy

The biggest insight from managing VoiceCatcher’s technical debt: Technical perfection and business success are not the same thing.

Technical debt management isn’t about eliminating all imperfection—it’s about:

  • Optimizing for user value delivery over code aesthetics
  • Investing technical effort where it multiplies business capability
  • Strategic debt accumulation to enable faster product iteration
  • Risk management for debt that could create sudden user impact

The framework: Treat technical debt as a business tool, not a business problem.

Practical Implementation for TPMs

Start with User Impact Assessment

Before addressing any technical debt, understand how it affects users and whether they would notice improvements.

Track Development Velocity Correlation

Measure how technical debt actually affects feature delivery speed, not how it feels to developers.

Build Debt Monitoring Systems

Monitor technical debt that could escalate into user-facing problems, but don’t monitor debt that’s stable and contained.

Embrace Strategic Debt Accumulation

Sometimes accumulating technical debt enables faster user value delivery and should be done deliberately.

The Bottom Line

Technical debt isn’t inherently good or bad—it’s a tool that can either accelerate or decelerate business value delivery.

VoiceCatcher’s strategic technical debt management enabled us to ship user-requested features instead of spending weeks on infrastructure that users would never notice. More importantly, it taught us to optimize for business outcomes rather than engineering aesthetics.

As TPMs, we need to recognize that technical decisions are business decisions. The goal isn’t perfect code—it’s perfect user value delivery through whatever technical means are most effective.

Our ugly, complex, hacky build system became one of our most valuable business assets because it enabled reliable, fast product iteration while staying completely invisible to users.

That’s the power of treating technical debt as product strategy rather than engineering embarrassment.


Jeremy is a technical product manager turned solo developer, building VoiceCatcher as a local-first voice transcription tool. Follow the journey at [project repo] or connect on [social links].

Next in this series: “Performance Constraints Drive UX Decisions: The Whisper.cpp Story” - How the technical limitations of local speech recognition shaped our entire product architecture, and why constraints often enable better user experiences than unlimited resources.