Skip to main content

software_engineering/
ai_assisted_development.rs

1//! # Measuring AI-Assisted Software Development
2//!
3//! AI coding assistance can feel dramatically faster at the point of initial
4//! code generation while showing no net cycle-time improvement once the
5//! full pipeline, including review and correction, is measured — code that
6//! is faster to produce but slower to review, or that requires more rework,
7//! can offset or reverse the apparent gain. A genuine productivity gain
8//! shows faster cycle time *with* stable or improved quality; a false gain
9//! shows faster cycle time *with* degrading quality, exactly the trade this
10//! book warns against throughout, discovered here through the same
11//! paired-metric discipline applied elsewhere to DORA and to flow metrics.
12//!
13//! ## Formula
14//!
15//! ```text
16//! Net cycle-time change (%) = (cycle time before − cycle time after) / cycle time before × 100
17//! Genuine gain                 when cycle time improved AND defect rate did not worsen
18//! ```
19//!
20//! ## Why it matters
21//!
22//! Developer self-report of "this saved me an hour" is a useful starting
23//! hypothesis, subject to the same recall and desirability biases as any
24//! self-reported data, and it says nothing about downstream review or
25//! correction cost. Measuring the full cycle-time chain, not just the
26//! coding stage, and checking it against change failure rate or escaped
27//! defect rate rather than trusting a felt sense of speed, is what
28//! distinguishes a genuine gain from a false one.
29//!
30//! ## Example
31//!
32//! A team's initial code-generation step feels dramatically faster, but the
33//! full pipeline, including a slower review and correction step, shows no
34//! net cycle-time improvement, and the defect rate has quietly worsened —
35//! the chapter's named false-gain pattern.
36//!
37//! ```rust
38//! use software_engineering::ai_assisted_development::{
39//!     net_cycle_time_change_percent, is_genuine_productivity_gain,
40//! };
41//!
42//! // Full cycle time (generation + review + correction) fell from 10 hours
43//! // to 8 hours: a 20% improvement.
44//! let change = net_cycle_time_change_percent(10.0, 8.0).unwrap();
45//! assert!((change - 20.0).abs() < 1e-9);
46//!
47//! // But if the defect rate rose from 2% to 5% alongside that speedup,
48//! // this is a false gain, not a genuine one.
49//! assert!(!is_genuine_productivity_gain(10.0, 8.0, 2.0, 5.0));
50//!
51//! // The same speedup with a stable or improved defect rate is genuine.
52//! assert!(is_genuine_productivity_gain(10.0, 8.0, 2.0, 2.0));
53//! ```
54//!
55//! ## Pitfalls
56//!
57//! - **Measuring only the generation-speed step**, ignoring full cycle time
58//!   — the chapter's central named pitfall; review and correction cost can
59//!   offset or reverse the apparent gain entirely.
60//! - **Treating self-reported time savings as a conclusion** rather than a
61//!   starting hypothesis to validate against objective cycle-time and
62//!   quality data.
63//! - **Comparing only a before-and-after snapshot**, without a genuine
64//!   comparison group or a longer historical baseline — cannot distinguish
65//!   AI assistance's effect from any other concurrent change.
66//! - **Reporting a single blended average across task types** — hides that
67//!   assistance may provide strong value for boilerplate work and little or
68//!   negative value for genuinely novel, complex problem-solving.
69//!
70//! ## Sources
71//!
72//! - Chapter 7.2, Measuring AI-assisted software development.
73//!
74//! Topic doc: software-engineering-metrics/locales/en-001/chapters/07-02-measuring-ai-assisted-software-development.md
75
76/// Net cycle-time change: the percentage change in full cycle time
77/// (generation plus review plus correction), positive meaning faster.
78///
79/// `(cycle_time_before_hours − cycle_time_after_hours) / cycle_time_before_hours
80/// × 100`.
81///
82/// # Arguments
83///
84/// * `cycle_time_before_hours` — full cycle time before AI assistance, in
85///   hours.
86/// * `cycle_time_after_hours` — full cycle time after AI assistance, in
87///   hours, measured across the same full pipeline.
88///
89/// # Returns
90///
91/// The percentage change (positive is faster, negative is slower), or
92/// `None` if `cycle_time_before_hours` is zero.
93///
94/// # Examples
95///
96/// ```rust
97/// use software_engineering::ai_assisted_development::net_cycle_time_change_percent;
98///
99/// assert!((net_cycle_time_change_percent(10.0, 8.0).unwrap() - 20.0).abs() < 1e-9);
100/// assert_eq!(net_cycle_time_change_percent(0.0, 1.0), None);
101/// ```
102#[must_use]
103pub fn net_cycle_time_change_percent(cycle_time_before_hours: f64, cycle_time_after_hours: f64) -> Option<f64> {
104    if cycle_time_before_hours == 0.0 {
105        return None;
106    }
107    Some(((cycle_time_before_hours - cycle_time_after_hours) / cycle_time_before_hours) * 100.0)
108}
109
110/// Whether an observed cycle-time speedup is a genuine productivity gain
111/// rather than a false one.
112///
113/// Genuine only if cycle time improved (`after < before`) **and** the
114/// defect rate did not worsen (`after <= before`). A speedup paired with a
115/// worsening defect rate is the chapter's named false-gain pattern and
116/// returns `false`, as does no speedup at all.
117///
118/// # Arguments
119///
120/// * `cycle_time_before_hours` — full cycle time before AI assistance, in
121///   hours.
122/// * `cycle_time_after_hours` — full cycle time after AI assistance, in
123///   hours.
124/// * `defect_rate_before_percent` — defect rate before AI assistance (e.g.
125///   change failure rate or escaped defect rate), as a percentage.
126/// * `defect_rate_after_percent` — defect rate after AI assistance, as a
127///   percentage.
128///
129/// # Returns
130///
131/// `true` only if cycle time improved and the defect rate did not worsen.
132///
133/// # Examples
134///
135/// ```rust
136/// use software_engineering::ai_assisted_development::is_genuine_productivity_gain;
137///
138/// // Faster, and quality held steady: genuine.
139/// assert!(is_genuine_productivity_gain(10.0, 8.0, 2.0, 2.0));
140/// // Faster, but quality got worse: the chapter's named false gain.
141/// assert!(!is_genuine_productivity_gain(10.0, 8.0, 2.0, 5.0));
142/// // No speedup at all: not a gain, genuine or otherwise.
143/// assert!(!is_genuine_productivity_gain(10.0, 10.0, 2.0, 2.0));
144/// ```
145#[must_use]
146pub fn is_genuine_productivity_gain(
147    cycle_time_before_hours: f64,
148    cycle_time_after_hours: f64,
149    defect_rate_before_percent: f64,
150    defect_rate_after_percent: f64,
151) -> bool {
152    cycle_time_after_hours < cycle_time_before_hours
153        && defect_rate_after_percent <= defect_rate_before_percent
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    // "track whether AI-assisted work moves faster through the cycle-time
161    // stages."
162    #[test]
163    fn net_cycle_time_change_computes_percentage_speedup() {
164        let change = net_cycle_time_change_percent(10.0, 8.0).unwrap();
165        assert!((change - 20.0).abs() < 1e-6);
166    }
167
168    #[test]
169    fn net_cycle_time_change_reports_a_slowdown_as_negative() {
170        let change = net_cycle_time_change_percent(8.0, 10.0).unwrap();
171        assert!(change < 0.0);
172    }
173
174    #[test]
175    fn net_cycle_time_change_is_none_for_zero_before() {
176        assert_eq!(net_cycle_time_change_percent(0.0, 1.0), None);
177    }
178
179    // "A genuine productivity gain shows faster cycle time with stable or
180    // improved quality."
181    #[test]
182    fn genuine_gain_requires_both_speed_and_stable_quality() {
183        assert!(is_genuine_productivity_gain(10.0, 8.0, 2.0, 2.0));
184    }
185
186    // "a false gain shows faster cycle time with degrading quality" — the
187    // chapter's named false-gain pattern.
188    #[test]
189    fn speedup_with_worsening_defect_rate_is_a_false_gain() {
190        assert!(!is_genuine_productivity_gain(10.0, 8.0, 2.0, 5.0));
191    }
192
193    #[test]
194    fn no_speedup_is_not_a_gain_at_all() {
195        assert!(!is_genuine_productivity_gain(10.0, 10.0, 2.0, 2.0));
196    }
197}