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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! # Activity Metrics and Their Limits
//!
//! **Activity**, the A in SPACE (chapter 3.1), counts the volume of
//! engineering work observable from system telemetry: commits, pull
//! requests opened, lines of code changed. It is the easiest SPACE
//! dimension to measure, because every one of these events is already
//! logged automatically — and that ease of measurement is exactly what
//! makes this dimension the most dangerous one to over-weight. Activity
//! measures motion, not value: a commit count does not distinguish
//! between a commit that solved a hard problem elegantly and a commit
//! that split one meaningful change into five to look more productive.
//!
//! ## Formula
//!
//! ```text
//! Commit substitution gaming signal =
//! commit_count rose AND average_commit_size shrank
//!
//! Activity rate = commits / engineers / weeks
//! (a contextual signal only, never a standalone productivity proxy)
//! ```
//!
//! ## Why it matters
//!
//! This is the single most historically misused metric family in
//! software engineering measurement. Once activity becomes an
//! incentivized individual metric, gaming follows almost immediately:
//! padding commits, splitting changes trivially, avoiding deep,
//! unglamorous work that produces few visible events. This module exists
//! to detect that specific gaming pattern and to compute an aggregate
//! rate for context — never to rank or score an individual. Commit
//! count, lines of code, and pull request count should never appear in
//! an individual performance review, a comparative ranking, or any
//! context where an engineer's compensation, standing, or reputation
//! depends on the number.
//!
//! ## Example
//!
//! ```rust
//! use software_engineering::activity_metrics::{
//! is_commit_substitution_gaming_signal, commits_per_engineer_per_week,
//! };
//!
//! // A team's commit count rose 40% while average commit size fell by
//! // more than half — meaningful changes were likely split into many
//! // trivial ones to inflate the count.
//! assert!(is_commit_substitution_gaming_signal(50.0, 70.0, 120.0, 50.0));
//!
//! // Both count and size rising together is ordinary growth, not gaming.
//! assert!(!is_commit_substitution_gaming_signal(50.0, 70.0, 120.0, 140.0));
//!
//! // Read only in aggregate, alongside the other SPACE dimensions —
//! // never as a standalone verdict on any one person or team.
//! let rate = commits_per_engineer_per_week(120.0, 6.0, 4.0).unwrap();
//! assert!((rate - 5.0).abs() < 1e-9);
//! ```
//!
//! ## Pitfalls
//!
//! - **Ranking or evaluating individuals by raw activity counts** — the
//! single hardest, most important rule this chapter states; the moment
//! activity becomes an incentivized individual metric, gaming follows
//! almost immediately.
//! - **Reading an activity number in isolation** — a sharp drop in
//! team-level commit activity alongside a rise in satisfaction might
//! mean the team finally had breathing room to pay down technical
//! debt, a positive pattern that looks alarming without that context.
//! - **Treating raw volume as a quality-adjacent signal** — prefer size
//! relative to review depth, or the ratio of new code to code removed,
//! over a bare count.
//! - **Missing the substitution-gaming pattern** — rising frequency
//! alongside sharply falling change size is the clearest sign activity
//! is being inflated rather than genuinely increasing.
//!
//! ## Sources
//!
//! - Chapter 3.4, Activity metrics and their limits.
//!
//! Topic doc: software-engineering-metrics/locales/en-001/chapters/03-04-activity-metrics-and-their-limits.md
/// Whether a change in commit count and average commit size matches the
/// chapter's named substitution-gaming pattern: splitting genuinely
/// meaningful work into many small, trivial commits to inflate a count.
///
/// True iff `commit_count_after > commit_count_before` (the count rose)
/// **and** `average_commit_size_after < average_commit_size_before` (the
/// average size shrank). Either condition alone is ordinary variation;
/// both together are the specific pattern this chapter warns about.
///
/// # Arguments
///
/// * `commit_count_before` — commit count in the earlier period.
/// * `commit_count_after` — commit count in the later period.
/// * `average_commit_size_before` — average commit size (e.g. lines
/// changed) in the earlier period.
/// * `average_commit_size_after` — average commit size in the later
/// period.
///
/// # Returns
///
/// `true` if the count rose while the average size shrank.
///
/// # Examples
///
/// ```rust
/// use software_engineering::activity_metrics::is_commit_substitution_gaming_signal;
///
/// // Count up, size down: the gaming pattern.
/// assert!(is_commit_substitution_gaming_signal(50.0, 70.0, 120.0, 50.0));
///
/// // Count up, size also up: ordinary growth, not gaming.
/// assert!(!is_commit_substitution_gaming_signal(50.0, 70.0, 120.0, 140.0));
///
/// // Count down: not the gaming pattern, regardless of size.
/// assert!(!is_commit_substitution_gaming_signal(70.0, 50.0, 50.0, 120.0));
/// ```
/// Commits per engineer per week — a simple activity rate, provided only
/// as a contextual signal to read alongside the other SPACE dimensions,
/// never as a standalone productivity proxy or an individual ranking.
///
/// `commits / engineers / weeks`.
///
/// # Arguments
///
/// * `commits` — total commit count across the period.
/// * `engineers` — number of engineers the commits are spread across.
/// * `weeks` — length of the observation period, in weeks.
///
/// # Returns
///
/// The commit rate per engineer per week, or `None` if `engineers` or
/// `weeks` is zero.
///
/// # Examples
///
/// ```rust
/// use software_engineering::activity_metrics::commits_per_engineer_per_week;
///
/// // 120 commits across 6 engineers over 4 weeks: 5 commits/engineer/week.
/// assert_eq!(commits_per_engineer_per_week(120.0, 6.0, 4.0), Some(5.0));
/// assert_eq!(commits_per_engineer_per_week(120.0, 0.0, 4.0), None);
/// assert_eq!(commits_per_engineer_per_week(120.0, 6.0, 0.0), None);
/// ```