1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! # Technical Debt Measurement
//!
//! **Technical debt**, a metaphor coined by Ward Cunningham, describes the
//! accumulated cost of past shortcuts — expedient decisions that shipped
//! something sooner but left the codebase harder to change afterward, in
//! the same way financial debt lets you spend now at the cost of interest
//! later. Unmeasured debt loses the prioritization competition against
//! feature work by default, not because it matters less, but because it
//! has no visible advocate; quantifying it in terms decision-makers can
//! weigh — cost to fix versus cost of carrying it — is what lets it compete
//! fairly.
//!
//! ## Formula
//!
//! ```text
//! Debt carrying cost = (velocity tax + elevated defect cost) × periods
//!
//! velocity tax = extra cost per period from related work going slower
//! elevated defect cost = extra expected defect cost per period from carrying the item
//! periods = number of periods the item is left unfixed
//! ```
//!
//! ## Why it matters
//!
//! For each debt item, the chapter recommends estimating two figures: the
//! cost to fix it, and the cost of carrying it unfixed — how much slower
//! related work goes, how much additional defect risk it carries, how much
//! it blocks other work. This carrying cost, summed across however many
//! periods the item is left unaddressed, gives decision-makers a real basis
//! for comparison against feature work's cost and expected value, rather
//! than an abstract, unquantified complaint. Debt also compounds: each new
//! shortcut makes the next change slightly harder.
//!
//! ## Example
//!
//! The topic doc's enterprise example: a telecommunications company
//! allocated a fixed 15% of engineering capacity to debt remediation and
//! resolved its top five highest-carrying-cost items within a year,
//! measurably improving change failure rate for billing-related deploys —
//! the return on quantifying and targeting the highest-carrying-cost items
//! first.
//!
//! ```rust
//! use software_engineering::technical_debt::debt_carrying_cost;
//!
//! // A billing-engine shortcut: slower related work (velocity tax) plus
//! // elevated defect risk, both recurring per period, carried for a year
//! // (12 monthly periods) before remediation.
//! let cost = debt_carrying_cost(2_000.0, 500.0, 12.0);
//! assert_eq!(cost, 30_000.0);
//!
//! // Carrying the same item twice as long doubles its carrying cost.
//! let longer = debt_carrying_cost(2_000.0, 500.0, 24.0);
//! assert_eq!(longer, cost * 2.0);
//! ```
//!
//! ## Money
//!
//! [`debt_carrying_cost`] takes plain `f64` amounts. For currency-checked
//! accounting, use [`rusty_money::Money`] directly rather than through a
//! wrapper this crate provides — its own `add`/`mul` already return
//! `Result`, rejecting a currency mismatch (a USD velocity tax against a
//! EUR defect cost, say) instead of silently summing incompatible amounts,
//! so this formula needs no adapter to use it that way:
//!
//! ```rust
//! use rusty_money::{Money, iso};
//!
//! // $2,000/month velocity tax + $500/month elevated defect cost,
//! // carried for 12 months = $30,000.
//! let velocity_tax = Money::from_major(2_000, iso::USD);
//! let defect_cost = Money::from_major(500, iso::USD);
//! let cost = velocity_tax.add(defect_cost).unwrap().mul(12).unwrap();
//! assert_eq!(cost, Money::from_major(30_000, iso::USD));
//!
//! // Mismatched currencies are rejected rather than silently summed.
//! let eur_defect_cost = Money::from_major(500, iso::EUR);
//! assert!(velocity_tax.add(eur_defect_cost).is_err());
//! ```
//!
//! ## Pitfalls
//!
//! - **No visible, tracked debt backlog** — debt loses the prioritization
//! competition by default and compounds invisibly.
//! - **Vague, unquantified debt claims** — rarely compete well against
//! concrete, quantified feature requests in planning.
//! - **Prioritizing debt by age or advocacy volume rather than impact** —
//! misdirects limited remediation capacity away from the highest-carrying
//! -cost items.
//! - **No protected capacity for remediation** — debt paydown only happens
//! reactively, after a crisis, rather than as routine, deliberate practice.
//! - **Treating all debt as equally worth fixing**, instead of accepting
//! some debt as permanent when its cost to fix exceeds its cost to carry.
//!
//! ## Sources
//!
//! - Chapter 4.5, Technical debt measurement.
//! - Cunningham, Ward, "The `WyCash` Portfolio Management System," *OOPSLA*
//! (1992).
//! - Kruchten, Philippe, Robert Nord, and Ipek Ozkaya, *Managing Technical
//! Debt: Reducing Friction in Software Development*.
//!
//! Topic doc: software-engineering-metrics/locales/en-001/chapters/04-05-technical-debt-measurement.md
/// Debt carrying cost: the ongoing cost of leaving a debt item unfixed.
///
/// `(velocity_tax_per_period + elevated_defect_cost_per_period) × periods`.
/// The velocity tax captures how much slower related work goes while the
/// item is carried; the elevated defect cost captures the additional
/// expected defect risk it carries. Summing both per period gives a figure
/// decision-makers can weigh directly against the item's one-time cost to
/// fix, and against competing feature work.
///
/// # Arguments
///
/// * `velocity_tax_per_period` — extra cost per period from related work
/// going slower while the item is unfixed (any currency unit).
/// * `elevated_defect_cost_per_period` — extra expected defect cost per
/// period attributable to carrying the item, in the same unit.
/// * `periods` — number of periods (e.g. months) the item is carried
/// unfixed.
///
/// # Returns
///
/// The total carrying cost over `periods`, in the same unit as the two
/// per-period inputs.
///
/// # Examples
///
/// ```rust
/// use software_engineering::technical_debt::debt_carrying_cost;
///
/// // $2,000/month velocity tax + $500/month elevated defect cost,
/// // carried for 12 months = $30,000.
/// assert_eq!(debt_carrying_cost(2_000.0, 500.0, 12.0), 30_000.0);
/// ```