Definition
Technical debt is the implied cost of future rework that arises when development teams choose expedient solutions over better approaches. Coined by Ward Cunningham in 1992 as a financial metaphor, it describes the phenomenon where shortcuts taken today create obligations that must be repaid later -- with interest. Like financial debt, technical debt compounds: each new feature built atop a compromised foundation incurs additional integration cost, each deferred cleanup makes the eventual cleanup harder, and each undocumented workaround increases the probability that future developers will introduce bugs.
Technical debt manifests in many forms: duplicated code, missing tests, obsolete documentation, tightly coupled modules, hardcoded configuration, suppressed warnings, and architectural compromises. Its most insidious characteristic is invisibility -- unlike feature work, technical debt produces no visible output, making it easy to deprioritize until it becomes a crisis.
In the Prismatic Platform, technical debt is treated as a first-class engineering concern through the zero-compromise quality doctrine. The platform maintains a zero technical debt policy enforced through Quality Debt Points (QDP) tracking and elimination, continuous quality gate enforcement, and the no-mercy-no-doubts doctrine that rejects incomplete implementations. As of the current platform state, all 905 identified QDP have been eliminated, achieving and maintaining zero quality debt.
Overview
The Financial Metaphor
Ward Cunningham's original metaphor drew a precise parallel between financial and technical obligations:
| Financial Debt | Technical Debt |
|---|---|
| Principal | Original shortcut or compromise |
| Interest | Ongoing maintenance burden, slower development |
| Compounding | Each new feature built on debt increases future cost |
| Default | System becomes unmaintainable, requires rewrite |
| Repayment | Refactoring, cleanup, test addition |
| Credit rating | Code quality metrics, coverage percentage |
Martin Fowler extended the metaphor with the Technical Debt Quadrant, classifying debt along two axes -- deliberate vs. inadvertent and reckless vs. prudent:
| Reckless | Prudent | |
|---|---|---|
| Deliberate | "We don't have time for design" | "We must ship now and will deal with consequences" |
| Inadvertent | "What is layering?" | "Now we know how we should have done it" |
The Prismatic Platform's no-mercy doctrine eliminates the reckless quadrant entirely and constrains the prudent-deliberate quadrant to documented, time-bounded exceptions with mandatory remediation plans.
The Compounding Problem
Technical debt compounds at a rate that consistently surprises organizations. Research by Stripe (2018) estimated that developers spend approximately 33% of their time dealing with technical debt. Studies by McKinsey found that organizations typically carry technical debt amounting to 20-40% of their entire technology estate's value.
The compounding mechanism works through multiple channels:
- Cognitive load -- Developers spend more time understanding and navigating around debt than writing new code
- Integration friction -- New features require increasingly elaborate workarounds to function with compromised components
- Testing overhead -- Untested or poorly tested code requires manual verification, slowing the entire team
- Onboarding cost -- New team members need more time to become productive in a debt-laden codebase
- Incident frequency -- Debt-ridden systems fail more often, consuming engineering time in firefighting
Technical Details
Measuring Technical Debt
Effective debt management requires measurement. The Prismatic Platform implements multi-dimensional debt measurement through the Quality Debt Points system:
defmodule PrismaticQuality.TechnicalDebtMeasurement do
@moduledoc """
Measures and tracks technical debt across the platform.
Quantifies debt through Quality Debt Points (QDP), mapping
specific code quality violations to point values that represent
estimated remediation effort. Enables trend analysis, debt
budgeting, and elimination velocity tracking.
"""
alias PrismaticQuality.{QDPRegistry, QualityDNA, DebtClassifier}
@type debt_category :: :compilation_warnings | :missing_typespecs | :credo_violations |
:dialyzer_warnings | :missing_tests | :forbidden_patterns |
:missing_docs | :unsafe_map_access | :deprecated_api_usage
@type debt_measurement :: %{
total_qdp: non_neg_integer(),
by_category: %{debt_category() => non_neg_integer()},
by_app: %{atom() => non_neg_integer()},
trend: :improving | :stable | :degrading,
velocity: float(),
estimated_hours: float()
}
@qdp_weights %{
compilation_warnings: 3,
missing_typespecs: 1,
credo_violations: 2,
dialyzer_warnings: 5,
missing_tests: 4,
forbidden_patterns: 10,
missing_docs: 1,
unsafe_map_access: 3,
deprecated_api_usage: 2
}
@spec measure() :: {:ok, debt_measurement()} | {:error, term()}
def measure do
with {:ok, violations} <- collect_all_violations(),
{:ok, history} <- QDPRegistry.get_history() do
by_category = categorize_violations(violations)
by_app = group_by_app(violations)
total = calculate_total_qdp(by_category)
{:ok, %{
total_qdp: total,
by_category: by_category,
by_app: by_app,
trend: calculate_trend(total, history),
velocity: calculate_velocity(history),
estimated_hours: estimate_remediation_hours(by_category)
}}
end
end
@spec measure_app(atom()) :: {:ok, non_neg_integer()} | {:error, term()}
def measure_app(app_name) do
with {:ok, violations} <- collect_app_violations(app_name) do
qdp = violations
|> Enum.map(fn v -> Map.get(@qdp_weights, v.category, 1) end)
|> Enum.sum()
{:ok, qdp}
end
end
defp collect_all_violations do
tasks = [
Task.async(fn -> collect_compilation_warnings() end),
Task.async(fn -> collect_credo_violations() end),
Task.async(fn -> collect_typespec_gaps() end),
Task.async(fn -> collect_forbidden_patterns() end),
Task.async(fn -> collect_test_gaps() end)
]
results = Task.await_many(tasks, 120_000)
{:ok, List.flatten(results)}
end
defp categorize_violations(violations) do
violations
|> Enum.group_by(& &1.category)
|> Map.new(fn {cat, items} ->
weight = Map.get(@qdp_weights, cat, 1)
{cat, length(items) * weight}
end)
end
defp calculate_total_qdp(by_category) do
by_category |> Map.values() |> Enum.sum()
end
defp calculate_trend(current, history) do
case history do
[] -> :stable
[prev | _] when current < prev.total -> :improving
[prev | _] when current > prev.total -> :degrading
_ -> :stable
end
end
defp calculate_velocity(history) do
case history do
[h1, h2 | _] ->
days = Date.diff(h1.date, h2.date)
if days > 0, do: (h2.total - h1.total) / days, else: 0.0
_ -> 0.0
end
end
defp estimate_remediation_hours(by_category) do
# Rough estimate: 1 QDP ≈ 15 minutes of remediation
total_qdp = calculate_total_qdp(by_category)
total_qdp * 0.25
end
defp group_by_app(violations) do
violations
|> Enum.group_by(& &1.app)
|> Map.new(fn {app, items} -> {app, length(items)} end)
end
defp collect_app_violations(_app), do: {:ok, []}
defp collect_compilation_warnings, do: []
defp collect_credo_violations, do: []
defp collect_typespec_gaps, do: []
defp collect_forbidden_patterns, do: []
defp collect_test_gaps, do: []
endDebt Prevention Through Quality Gates
The most effective debt management strategy is prevention. The Prismatic Platform prevents debt introduction through blocking quality gates that reject any change introducing new violations:
defmodule PrismaticQuality.DebtPrevention do
@moduledoc """
Prevents introduction of new technical debt through blocking
quality gate checks integrated into the commit pipeline.
Implements the platform's zero-debt policy by comparing the
current violation count against the established baseline and
rejecting any change that increases the count.
"""
alias PrismaticQuality.{TechnicalDebtMeasurement, QualityFloorGuardian}
@type gate_result :: :pass | {:fail, String.t()}
@spec check_no_new_debt() :: gate_result()
def check_no_new_debt do
with {:ok, current} <- TechnicalDebtMeasurement.measure(),
{:ok, baseline} <- QualityFloorGuardian.get_baseline() do
cond do
current.total_qdp > baseline.total_qdp ->
delta = current.total_qdp - baseline.total_qdp
{:fail, "New technical debt detected: +#{} QDP (#{.} > #{.})"}
current.total_qdp > 0 and current.trend == :degrading ->
{:fail, "Quality trend degrading despite QDP count within budget"}
true ->
:pass
end
end
end
@spec check_debt_budget(non_neg_integer()) :: gate_result()
def check_debt_budget(max_qdp) do
case TechnicalDebtMeasurement.measure() do
{:ok, %{total_qdp: total}} when total <= max_qdp -> :pass
{:ok, %{total_qdp: total}} -> {:fail, "QDP budget exceeded: #{}/#{}"}
{:error, reason} -> {:fail, "Measurement failed: #{inspect()}"}
end
end
@spec enforce_zero_debt() :: gate_result()
def enforce_zero_debt do
check_debt_budget(0)
end
endDebt Elimination Strategies
When debt exists, systematic elimination is required. The platform used the following strategies to eliminate 905 QDP:
| Strategy | QDP Eliminated | Example |
|---|---|---|
| Batch compilation warning fixes | 70 | Adding warnings_as_errors: true to all mix.exs |
| Typespec addition | 150+ | Systematic @spec annotation on public functions |
| Credo compliance | 200+ | Addressing all --strict violations |
| Dialyzer resolution | 89 | Fixing type mismatches, underspecs |
| Test coverage | 120+ | Adding tests for uncovered functions |
| Forbidden pattern removal | 100+ | Replacing stubs, mocks, placeholders |
| Documentation | 99 | Adding CLAUDE.md to all umbrella apps |
| Quality DNA initialization | 99 | Creating quality-dna/current-state.json |
Quality DNA: Cross-Session Debt Memory
The Quality DNA system maintains a persistent record of quality state across sessions, preventing debt from being reintroduced:
defmodule PrismaticQuality.QualityDNA do
@moduledoc """
Persistent quality state that survives across development sessions.
Stores the baseline quality measurements for each umbrella app,
enabling detection of quality regression even when different
developers or CI runs operate on the codebase.
"""
@type dna_state :: %{
app: atom(),
qdp: non_neg_integer(),
compilation_warnings: non_neg_integer(),
credo_violations: non_neg_integer(),
dialyzer_warnings: non_neg_integer(),
test_coverage: float(),
typespec_coverage: float(),
last_measured: DateTime.t(),
quality_score: non_neg_integer()
}
@dna_path ".claude/quality-dna/current-state.json"
@spec load() :: {:ok, [dna_state()]} | {:error, term()}
def load do
case File.read(@dna_path) do
{:ok, content} -> {:ok, Jason.decode!(content, keys: :atoms)}
{:error, reason} -> {:error, {:file_read, reason}}
end
end
@spec save([dna_state()]) :: :ok | {:error, term()}
def save(states) do
content = Jason.encode!(states, pretty: true)
File.write(@dna_path, content)
end
@spec check_regression(atom(), dna_state()) :: :ok | {:regression, map()}
def check_regression(app, current_state) do
case load() do
{:ok, stored} ->
case Enum.find(stored, &(&1.app == app)) do
nil -> :ok
baseline -> compare_states(baseline, current_state)
end
{:error, _} -> :ok
end
end
defp compare_states(baseline, current) do
regressions =
[:qdp, :compilation_warnings, :credo_violations, :dialyzer_warnings]
|> Enum.filter(fn key ->
Map.get(current, key, 0) > Map.get(baseline, key, 0)
end)
if Enum.empty?(regressions) do
:ok
else
{:regression, %{
regressed_metrics: regressions,
baseline: Map.take(baseline, regressions),
current: Map.take(current, regressions)
}}
end
end
endImplementation in Prismatic Platform
The Prismatic Platform's zero-debt achievement required systematic, sustained effort across the entire 115-application umbrella. Key implementation milestones:
- QDP System Creation -- Defined quantifiable debt metrics with weighted scoring
- Baseline Measurement -- Measured initial debt: 905 QDP across all apps
- Quality Floor Guardian -- Automated monitoring preventing new debt introduction
- Batch Elimination -- Systematic campaigns targeting specific debt categories
- Quality DNA -- Persistent state ensuring debt cannot silently return
- Zero Achievement -- All 905 QDP eliminated, zero-debt baseline established
- Continuous Enforcement -- Pre-commit hooks and CI gates maintain zero state
Current platform quality state: 100/100 score, 0 QDP, 13/13 domains at zero violations.
Comparison with Alternatives
| Approach | Measurement | Prevention | Elimination | Sustainability |
|---|---|---|---|---|
| Prismatic Zero-Debt | QDP system, Quality DNA | Blocking quality gates | Systematic campaigns | Continuous enforcement |
| SonarQube | SQALE method, time-based | Quality profiles | Manual remediation | Dashboard monitoring |
| CodeClimate | Maintainability rating | PR checks | Suggested changes | Grade tracking |
| Manual Tracking | Spreadsheets, tickets | Code review | Sprint allocation | Discipline-dependent |
| Ignore It | None | None | Crisis-driven | Unsustainable |
| Periodic Cleanup | Ad-hoc assessment | None | Sprint/hackathon | Oscillating quality |
The Prismatic approach is uniquely aggressive in its zero-tolerance stance. Most organizations accept some level of technical debt as inevitable; the platform rejects this premise and demonstrates that zero debt is achievable and maintainable through sufficiently rigorous automation.
Best Practices
-
Measure before managing -- You cannot manage what you cannot measure. Establish quantifiable metrics (like QDP) before attempting systematic debt reduction. The platform's multi-dimensional measurement system provides the foundation for all debt management decisions.
-
Prevent before eliminating -- Preventing new debt introduction is more cost-effective than eliminating existing debt. Blocking quality gates at the pre-commit and CI stages ensure that debt never enters the codebase.
-
Eliminate systematically -- Target specific debt categories in focused campaigns rather than addressing debt opportunistically. The platform's category-based elimination (compilation warnings first, then Credo, then Dialyzer) enabled measurable progress tracking.
-
Make debt visible -- Track debt in the same systems used for feature work. The QDP system and Quality DNA make debt a first-class metric visible alongside feature development progress.
-
Automate enforcement -- Human discipline is insufficient for zero-debt maintenance. The platform's 11-phase pre-commit hook, CI gates, and Quality Floor Guardian automate enforcement so that maintaining zero debt requires no active decision-making.
-
Budget for paydown -- If you inherit debt, allocate explicit capacity for elimination. The platform's initial 905-QDP elimination required dedicated effort alongside feature work.
-
Track velocity -- Measure both debt introduction rate and elimination rate. The velocity metric in the debt measurement system reveals whether the team is winning or losing the debt war.
-
Connect debt to business impact -- Express debt in terms stakeholders understand: slower feature delivery, higher incident rate, longer onboarding time. The remediation hours estimate translates QDP into engineering time.
Common Pitfalls
-
The "we'll fix it later" trap -- Deferred cleanup is the primary mechanism of debt accumulation. The no-mercy doctrine forbids this pattern: fix immediately or do not deliver.
-
Measuring the wrong things -- Lines of code, cyclomatic complexity, and code coverage are proxies for quality, not direct measures of debt. The QDP system measures actual violations against platform standards, not abstract metrics.
-
Heroic elimination -- Attempting to eliminate all debt in a single sprint leads to burnout and unstable changes. The platform eliminated 905 QDP through systematic, incremental campaigns across multiple sessions.
-
Invisible compounding -- Debt that compounds silently (e.g., missing tests that cause other tests to become unreliable) is the most dangerous. The Quality DNA system detects subtle regression patterns that individual metrics might miss.
-
Debt normalization -- Teams that live with debt long enough stop noticing it. The platform's zero tolerance policy and automated enforcement prevent normalization by making every violation immediately visible and blocking.
-
False economy of speed -- Skipping quality to ship faster creates an illusion of velocity that reverses within weeks as the team spends increasing time on debt-related rework. The platform's quality-first approach may slow individual commits but accelerates sustained delivery velocity.
-
Incomplete elimination -- Reducing debt from 905 to 50 QDP and declaring victory leaves compounding seeds in the codebase. The platform pursued complete elimination to zero, recognizing that any remaining debt provides a nucleus for future accumulation.
Use Cases
Zero-Debt Platform Achievement
The Prismatic Platform tracked and eliminated 905 Quality Debt Points across 115 umbrella applications through systematic campaigns: 321 mix.exs transformations (70 Elixir version standardizations, 99 warnings_as_errors additions, 89 Dialyzer configurations, 63 coverage settings), followed by category-specific violation elimination. The result: 100/100 quality score maintained through continuous enforcement.
Quality Floor Guardian Monitoring
The Quality Floor Guardian continuously monitors the platform's quality metrics and responds to changes based on configured thresholds. At the 100% quality level (current state), it operates in OPTIMAL mode. Any regression triggers escalating responses: WARNING at 98-99%, CRITICAL at 95-98% with auto-evolution trigger, and EMERGENCY below 95% with commit blocking.
New Application Onboarding
When a new umbrella application is added to the platform, the Universal Quality Standard enforces zero-debt configuration from creation: warnings_as_errors: true, Dialyzer configuration, coverage thresholds, quality aliases, CLAUDE.md documentation, and Quality DNA initialization. New applications start at zero debt and remain there.
Continuous Integration Debt Gate
Every CI/CD pipeline run executes mix quality.gates which verifies zero compilation warnings, zero Credo violations (strict mode), zero Dialyzer warnings, zero forbidden patterns, and adequate test coverage. Any violation blocks the pipeline, preventing debt from reaching the main branch.
Cross-Session Quality Continuity
The Quality DNA system persists quality state in .claude/quality-dna/current-state.json, ensuring that quality baselines survive across development sessions, CI runs, and developer machines. When a new session begins, the first check compares current state against the stored DNA baseline, detecting any regression that may have occurred between sessions.
Related Concepts
- Quality Debt -- Prismatic-specific formalization of technical debt as Quality Debt Points (QDP)
- QDP -- Quality Debt Points, the quantifiable measurement unit for technical debt in the platform
- Refactoring -- Systematic code transformation to improve structure without changing behavior, the primary debt repayment mechanism
- Code Quality -- The measurable attributes of code that technical debt degrades
- Quality Gate -- Enforcement checkpoints that prevent new technical debt introduction
- Zero Compromise Quality -- Platform doctrine rejecting any quality compromise that would create debt
- Zero Tolerance -- Principle of accepting no quality violations, the philosophical foundation of zero-debt policy
- No Mercy No Doubts -- Platform doctrine mandating complete execution without shortcuts
- Clean Run -- Zero-warning, zero-error execution requirement that prevents warning-based debt
- Quality DNA -- Persistent quality state system that prevents debt regression across sessions
- Static Analysis -- Code examination that identifies and quantifies specific debt instances
- Quality Floor Guardian -- Autonomous monitoring system that detects and escalates debt introduction
See Also
- Martin Fowler: Technical Debt -- Original articulation of the debt quadrant
- Ward Cunningham: The WyCash Portfolio Management System -- Where the metaphor was first introduced
- Quality Gate Documentation -- Platform enforcement implementation
- Quality DNA System -- Cross-session quality persistence
Connect & Contribute
Created by Tomas Korcak (korczis) | Open Source under GHL