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
179
180
//! # Cost and Unit Economics of Engineering
//!
//! Engineering cost has at least three distinct components with different
//! drivers and different levers: **people cost** (salaries and benefits,
//! largely fixed in the short term), **infrastructure cost** (cloud spend,
//! largely variable with usage), and **tooling and licensing cost** (often
//! fixed per-seat or per-usage-tier). Tracking them separately, rather than
//! as one blended total, matters because a rising total driven by
//! infrastructure scaling with genuine growth calls for a very different
//! response than the same rise driven by unmanaged tooling sprawl. Dividing
//! the total by a genuine unit of value delivered — cost per customer
//! served, per transaction, per deployment — turns that total into a
//! trackable, comparable trend.
//!
//! ## Formula
//!
//! ```text
//! Total engineering cost = people cost + infrastructure cost + tooling cost
//! Unit cost = total cost / units delivered
//! ```
//!
//! ## Why it matters
//!
//! Selecting a unit that genuinely tracks business or mission value, rather
//! than an easily inflated, internal, largely discretionary count, is what
//! keeps a unit-cost ratio honest. A single unit-cost snapshot is also less
//! useful than its trend: falling unit cost as the platform matures signals
//! genuine efficiency gains, while rising unit cost is often a direct,
//! measurable consequence of accumulated technical debt or complexity
//! hotspots elsewhere in the codebase.
//!
//! ## Example
//!
//! ```rust
//! use software_engineering::unit_economics::{total_engineering_cost, unit_cost};
//!
//! // $500k people cost, $120k infrastructure, $30k tooling, serving 10,000
//! // customers.
//! let total = total_engineering_cost(500_000.0, 120_000.0, 30_000.0);
//! assert_eq!(total, 650_000.0);
//!
//! let cost_per_customer = unit_cost(total, 10_000.0).unwrap();
//! assert_eq!(cost_per_customer, 65.0);
//! ```
//!
//! ## Money
//!
//! [`total_engineering_cost`] and [`unit_cost`] take plain `f64` amounts.
//! For currency-checked accounting, use [`rusty_money::Money`] directly
//! rather than through a wrapper this crate provides — its own `add`/`div`
//! already return `Result`, rejecting cost components quoted in different
//! currencies instead of silently treating them as the same unit:
//!
//! ```rust
//! use rusty_money::{Money, iso};
//! use software_engineering::unit_economics::unit_cost;
//!
//! let people = Money::from_major(500_000, iso::USD);
//! let infrastructure = Money::from_major(120_000, iso::USD);
//! let tooling = Money::from_major(30_000, iso::USD);
//! let total = people.add(infrastructure).unwrap().add(tooling).unwrap();
//! assert_eq!(total, Money::from_major(650_000, iso::USD));
//!
//! let cost_per_customer = unit_cost(total.to_f64_lossy(), 10_000.0).unwrap();
//! assert!((cost_per_customer - 65.0).abs() < 1e-9);
//!
//! // Dividing by zero units is rejected rather than producing infinity.
//! assert!(total.div(0).is_err());
//! ```
//!
//! ## Pitfalls
//!
//! - **Choosing an easily inflated denominator** that does not correspond to
//! any genuine external unit of value delivered — flatters the ratio
//! without informing anyone.
//! - **Tracking one blended cost total** instead of separating people,
//! infrastructure, and tooling cost — hides which lever actually needs
//! pulling when the total rises.
//! - **Reporting a unit-cost snapshot with no trend** — a single number
//! says nothing about whether efficiency is improving or degrading.
//! - **Never connecting rising unit cost back to technical debt or
//! complexity metrics** — misses a measurable, quantifiable case for debt
//! remediation investment.
//!
//! ## Sources
//!
//! - Chapter 5.4, Cost and unit economics of engineering.
//! - `FinOps` Foundation, *`FinOps` Framework*.
//!
//! Topic doc: software-engineering-metrics/locales/en-001/chapters/05-04-cost-and-unit-economics-of-engineering.md
/// The sum of engineering's three distinct cost components.
///
/// `people_cost + infrastructure_cost + tooling_cost`. This function is
/// only the sum used to compute a unit cost; callers should keep tracking
/// the three components separately elsewhere, per the chapter's explicit
/// recommendation, rather than discarding the breakdown once summed.
///
/// # Arguments
///
/// * `people_cost` — salaries and benefits, in any currency unit.
/// * `infrastructure_cost` — cloud and infrastructure spend, in the same
/// unit.
/// * `tooling_cost` — tooling and licensing cost, in the same unit.
///
/// # Returns
///
/// The total engineering cost, in the same unit.
///
/// # Examples
///
/// ```rust
/// use software_engineering::unit_economics::total_engineering_cost;
///
/// let total = total_engineering_cost(500_000.0, 120_000.0, 30_000.0);
/// assert_eq!(total, 650_000.0);
/// ```
/// Cost per genuine unit of value delivered, such as cost per customer
/// served, per transaction, or per deployment.
///
/// `total_cost / units_delivered`.
///
/// # Arguments
///
/// * `total_cost` — total engineering cost for the period, in any currency
/// unit (typically from [`total_engineering_cost`]).
/// * `units_delivered` — count of genuine value units delivered in the same
/// period.
///
/// # Returns
///
/// The cost per unit, or `None` if `units_delivered` is zero.
///
/// # Examples
///
/// ```rust
/// use software_engineering::unit_economics::unit_cost;
///
/// assert_eq!(unit_cost(650_000.0, 10_000.0), Some(65.0));
/// assert_eq!(unit_cost(1.0, 0.0), None);
/// ```