Skip to main content

software_engineering/
performance_metrics.rs

1//! # Performance Metrics and Outcome Proxies
2//!
3//! **Performance**, the P in SPACE (chapter 3.1), is the dimension most
4//! often confused with activity: it asks whether work actually produced a
5//! good outcome, not how much motion occurred. A team can be highly active
6//! and low performing, shipping constant small changes that never move an
7//! outcome, and the reverse is equally possible. Outcome is also rarely
8//! attributable to a single person or team — it emerges from collaboration,
9//! from decisions made months earlier, from market conditions no engineer
10//! controls — so SPACE researchers were explicit that performance should be
11//! measured at the system or team level using multiple, converging
12//! signals, never reduced to a single number or attributed to an
13//! individual.
14//!
15//! ## Formula
16//!
17//! ```text
18//! Converging signal count      = count of independent signals indicating
19//!                                 positive performance (change failure
20//!                                 rate, defect-escape rate, adoption,
21//!                                 qualitative peer assessment, ...)
22//! Sufficient converging evidence = converging signal count >= 2
23//!     (no single signal is reliable alone)
24//! ```
25//!
26//! ## Why it matters
27//!
28//! No individual performance proxy is reliable enough to stand alone: a
29//! single metric can look good while quality quietly degrades, or look bad
30//! for reasons entirely outside a team's control. Requiring several
31//! independent signals to agree before drawing a conclusion is what
32//! resists both accidental misreading and deliberate gaming of any one
33//! proxy — the same discipline chapter 5.3 applies to business-outcome
34//! attribution, applied here to the SPACE performance dimension
35//! specifically.
36//!
37//! ## Example
38//!
39//! ```rust
40//! use software_engineering::performance_metrics::{
41//!     converging_signal_count, has_sufficient_converging_evidence,
42//! };
43//!
44//! // A single positive signal (adoption is up) is not enough on its own.
45//! let adoption_only = [true, false, false];
46//! assert_eq!(converging_signal_count(&adoption_only), 1);
47//! assert!(!has_sufficient_converging_evidence(&adoption_only));
48//!
49//! // Change failure rate down, defect-escape rate down, and adoption up
50//! // all agree: two or more converging signals are trustworthy together.
51//! let converging = [true, true, false, true];
52//! assert_eq!(converging_signal_count(&converging), 3);
53//! assert!(has_sufficient_converging_evidence(&converging));
54//! ```
55//!
56//! ## Pitfalls
57//!
58//! - **Reducing performance to a single number** — no individual proxy
59//!   (velocity, adoption, a single quality metric) is reliable enough to
60//!   stand alone.
61//! - **Attributing an outcome to one individual** — software outcomes
62//!   emerge from collaboration and prior work; individual attribution is
63//!   usually false precision that discourages collaboration.
64//! - **Treating quality as separate from performance** — a feature that
65//!   ships on time but causes a wave of incidents did not perform well,
66//!   even though an output-only view would count it as delivered.
67//! - **Ranking teams competitively on performance data** — invites gaming
68//!   and morale damage; the productive use is deciding where to invest or
69//!   investigate, never a competitive ranking.
70//! - **Forcing a direct-outcome metric onto platform or enabling teams** —
71//!   their contribution is often several steps removed from any single
72//!   customer-facing metric; measure their effect on the teams they
73//!   enable instead.
74//!
75//! ## Sources
76//!
77//! - Chapter 3.3, Performance metrics and outcome proxies.
78//! - Forsgren, Storey, Maddila, Zimmermann, Houck, and Butler, "The SPACE of
79//!   Developer Productivity," *ACM Queue* (2021).
80//!
81//! Topic doc: software-engineering-metrics/locales/en-001/chapters/03-03-performance-metrics-and-outcome-proxies.md
82
83/// The number of independent signals, out of those checked, that indicate
84/// positive performance.
85///
86/// Each `bool` in `signals` represents one independent signal already
87/// evaluated by the caller (e.g. "did change failure rate improve",
88/// "did defect-escape rate improve", "did adoption rise"), `true` if that
89/// signal points to good performance. This function only counts how many
90/// agree; it does not itself decide what counts as a signal.
91///
92/// # Arguments
93///
94/// * `signals` — one `bool` per independent performance signal checked,
95///   `true` if that signal is positive.
96///
97/// # Returns
98///
99/// The count of `true` signals.
100///
101/// # Examples
102///
103/// ```rust
104/// use software_engineering::performance_metrics::converging_signal_count;
105///
106/// let signals = [true, false, true, true];
107/// assert_eq!(converging_signal_count(&signals), 3);
108/// assert_eq!(converging_signal_count(&[]), 0);
109/// ```
110#[must_use]
111pub fn converging_signal_count(signals: &[bool]) -> usize {
112    signals.iter().filter(|signal| **signal).count()
113}
114
115/// Whether enough independent signals converge to trust a performance
116/// conclusion, per the chapter's "no single one is reliable alone"
117/// principle.
118///
119/// True iff [`converging_signal_count`] is at least 2 — a single positive
120/// signal is never treated as sufficient evidence on its own.
121///
122/// # Arguments
123///
124/// * `signals` — one `bool` per independent performance signal checked,
125///   `true` if that signal is positive.
126///
127/// # Returns
128///
129/// `true` if two or more signals are positive.
130///
131/// # Examples
132///
133/// ```rust
134/// use software_engineering::performance_metrics::has_sufficient_converging_evidence;
135///
136/// assert!(!has_sufficient_converging_evidence(&[true]));
137/// assert!(has_sufficient_converging_evidence(&[true, true]));
138/// assert!(has_sufficient_converging_evidence(&[true, false, true]));
139/// ```
140#[must_use]
141pub fn has_sufficient_converging_evidence(signals: &[bool]) -> bool {
142    converging_signal_count(signals) >= 2
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    // "Use multiple, converging signals, never a single performance
150    // number. No individual proxy is reliable enough to stand alone."
151    #[test]
152    fn a_single_positive_signal_is_not_sufficient_evidence() {
153        let adoption_only = [true, false, false];
154        assert_eq!(converging_signal_count(&adoption_only), 1);
155        assert!(!has_sufficient_converging_evidence(&adoption_only));
156    }
157
158    #[test]
159    fn two_or_more_positive_signals_are_sufficient_evidence() {
160        let converging = [true, true, false, true];
161        assert_eq!(converging_signal_count(&converging), 3);
162        assert!(has_sufficient_converging_evidence(&converging));
163    }
164
165    #[test]
166    fn no_signals_checked_counts_as_zero_and_is_not_sufficient() {
167        assert_eq!(converging_signal_count(&[]), 0);
168        assert!(!has_sufficient_converging_evidence(&[]));
169    }
170
171    #[test]
172    fn all_negative_signals_count_as_zero() {
173        assert_eq!(converging_signal_count(&[false, false, false]), 0);
174        assert!(!has_sufficient_converging_evidence(&[false, false, false]));
175    }
176}