@rmrdeveloper/sideroom-pi 7.0.1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,27 +1,377 @@
1
- # Go
1
+ # Go Coding Guidelines
2
2
 
3
- Delta only. Keep the shared table.
3
+ Rules every Go change must follow.
4
4
 
5
- - Braces on every `if`. Keep `err` checks as flat guard clauses; do not nest the happy path.
6
- - Return errors with context. Do not `panic` for expected business failure, and do not ignore `err`.
7
- - Do not mutate slices or maps passed in as input; return a new value.
5
+ ## Index
6
+
7
+ | # | Rule | Tier |
8
+ | --- | ------ | ------ |
9
+ | 1 | [Braced conditionals](#1-braced-conditionals) | mechanical |
10
+ | 2 | [No magic strings](#2-no-magic-strings) | mechanical |
11
+ | 3 | [Guard clauses](#3-guard-clauses) | structural |
12
+ | 4 | [Fail fast](#4-fail-fast) | structural |
13
+ | 5 | [Explicit error handling](#5-explicit-error-handling) | structural |
14
+ | 6 | [Clear names](#6-clear-names) | structural |
15
+ | 7 | [Command Query Separation](#7-command-query-separation-cqs) | structural |
16
+ | 8 | [Null/undefined handling](#8-nullundefined-handling) | structural |
17
+ | 9 | [Immutability by default](#9-immutability-by-default) | structural |
18
+ | 10 | [Validate once](#10-validate-once) | structural |
19
+ | 11 | [Single Responsibility](#11-single-responsibility-srp) | architectural |
20
+ | 12 | [DRY (real duplication only)](#12-dry-real-duplication-only) | architectural |
21
+ | 13 | [KISS](#13-kiss) | architectural |
22
+ | 14 | [YAGNI](#14-yagni) | architectural |
23
+ | 15 | [Law of Demeter](#15-law-of-demeter) | architectural |
24
+ | 16 | [Composition over inheritance](#16-composition-over-inheritance) | architectural |
25
+ | 17 | [Dependency direction](#17-dependency-direction) | architectural |
26
+ | 18 | [Testability](#18-testability-as-a-design-constraint) | architectural |
27
+ | 19 | [Comments](#19-comments) | architectural |
28
+
29
+ ---
30
+
31
+ ### 1. Braced conditionals
32
+
33
+ RULE: Go requires braces for every `if`/`else` body. Keep even a one-line guard as a full, formatted block rather than compressing it onto one line.
34
+
35
+ ```go
36
+ // bad
37
+ if user == nil {
38
+ return 0
39
+ }
40
+
41
+ // good
42
+ if user == nil {
43
+ return 0
44
+ }
45
+ ```
46
+
47
+ ### 2. No magic strings
48
+
49
+ RULE: Name domain/protocol literals once (const, enum, shared contract) and reuse the name. One-off strings with no reuse and no domain meaning (a single log label) may stay inline.
8
50
 
9
51
  ```go
10
- // Bad
11
- func loadUser(id string) (*User, error) {
12
- user, err := repo.Get(id)
13
- if err == nil {
14
- return user, nil
15
- }
16
- return nil, nil
52
+ // bad
53
+ if order.Status == "pending_payment" {
54
+ charge(order)
17
55
  }
18
56
 
19
- // Good
20
- func loadUser(id string) (*User, error) {
21
- user, err := repo.Get(id)
22
- if err != nil {
23
- return nil, fmt.Errorf("load user %s: %w", id, err)
24
- }
25
- return user, nil
57
+ // good
58
+ const PendingPayment OrderStatus = "pending_payment"
59
+
60
+ if order.Status == PendingPayment {
61
+ charge(order)
26
62
  }
27
63
  ```
64
+
65
+ ### 3. Guard clauses
66
+
67
+ RULE: Validate and exit early. Keep the happy path last, at the shallowest indent. No pyramid nesting.
68
+
69
+ ```go
70
+ // bad
71
+ if user != nil {
72
+ if user.Active {
73
+ return 0.2
74
+ }
75
+ }
76
+ return 0
77
+
78
+ // good
79
+ if user == nil || !user.Active || !user.HasSubscription {
80
+ return 0
81
+ }
82
+ return 0.2
83
+ ```
84
+
85
+ ### 4. Fail fast
86
+
87
+ RULE: Invalid input, impossible state, or a broken dependency returns an error immediately with a clear message. No fallback chains or silent defaults.
88
+ WHY: This is for invariants that should never happen if upstream contracts hold — not license to re-check business rules a boundary already validated (see rule 10).
89
+
90
+ ```go
91
+ // bad
92
+ zone, ok := zones[order.ZoneID]
93
+ if !ok {
94
+ zone = zones["default"]
95
+ }
96
+
97
+ // good
98
+ zone, ok := zones[order.ZoneID]
99
+ if !ok {
100
+ return Zone{}, fmt.Errorf("unknown zone ID: %s", order.ZoneID)
101
+ }
102
+ ```
103
+
104
+ ### 5. Explicit error handling
105
+
106
+ RULE: Never ignore an error. Return it with context, or let it propagate.
107
+
108
+ ```go
109
+ // bad
110
+ user, _ := api.GetUser(id)
111
+
112
+ // good
113
+ user, err := api.GetUser(id)
114
+ if err != nil {
115
+ return User{}, fmt.Errorf("load user %s: %w", id, err)
116
+ }
117
+ ```
118
+
119
+ ### 6. Clear names
120
+
121
+ RULE: Names reveal role or domain meaning. No `data`, `info`, `temp`, `result`, `obj`, `val`, `x`. A long honest name beats a short vague one.
122
+
123
+ ```go
124
+ // bad
125
+ func process(data []Subscription) []Subscription {
126
+ return filter(data, func(x Subscription) bool {
127
+ return x.Days > 0
128
+ })
129
+ }
130
+
131
+ // good
132
+ func activeSubscriptions(subscriptions []Subscription) []Subscription {
133
+ return filter(subscriptions, func(subscription Subscription) bool {
134
+ return subscription.RemainingDays > 0
135
+ })
136
+ }
137
+ ```
138
+
139
+ ### 7. Command Query Separation (CQS)
140
+
141
+ RULE: A function either does something (side effect) or returns something (query) — never both. A "getter" must never mutate.
142
+
143
+ ```go
144
+ // bad
145
+ func nextID(counter *Counter) int {
146
+ counter.Value++
147
+ return counter.Value
148
+ }
149
+
150
+ // good
151
+ func peekNextID(counter Counter) int {
152
+ return counter.Value + 1
153
+ }
154
+
155
+ func incrementCounter(counter *Counter) {
156
+ counter.Value++
157
+ }
158
+ ```
159
+
160
+ ### 8. Null/undefined handling
161
+
162
+ RULE: Give `nil` one meaning for a type and never overload it for several business states. Use explicit values or the `(value, ok)` idiom for distinct states.
163
+
164
+ ```go
165
+ // bad
166
+ func discount(user *User) *int {
167
+ if user == nil || user.Plan == nil || user.Plan.Discount == 0 {
168
+ return nil
169
+ }
170
+ return &user.Plan.Discount
171
+ }
172
+
173
+ // good
174
+ func discount(user *User) int {
175
+ if user == nil || user.Plan == nil {
176
+ return 0
177
+ }
178
+ return user.Plan.Discount
179
+ }
180
+ ```
181
+
182
+ ### 9. Immutability by default
183
+
184
+ RULE: Return new values instead of mutating parameters. Never hide a side effect inside what looks like a pure transform.
185
+
186
+ ```go
187
+ // bad
188
+ func addItem(items []Item, item Item) []Item {
189
+ return append(items, item)
190
+ }
191
+
192
+ // good
193
+ func addItem(items []Item, item Item) []Item {
194
+ updated := append([]Item(nil), items...)
195
+ return append(updated, item)
196
+ }
197
+ ```
198
+
199
+ ### 10. Validate once
200
+
201
+ RULE: Validate at the boundary that owns the input (decoder, schema, DTO). Downstream code trusts that contract — no re-checking the same required/type/range rule in a helper, use case, or adapter further in.
202
+
203
+ ```go
204
+ // bad
205
+ func update(profile ValidatedProfile) error {
206
+ if profile.Email == "" {
207
+ return errors.New("email required")
208
+ }
209
+ return repo.Update(profile)
210
+ }
211
+
212
+ // good
213
+ func update(profile ValidatedProfile) error {
214
+ return repo.Update(profile)
215
+ }
216
+ ```
217
+
218
+ ### 11. Single Responsibility (SRP)
219
+
220
+ RULE: A function, class, or module has one reason to change. If it validates AND transforms AND persists AND notifies, split it.
221
+
222
+ ```go
223
+ // bad
224
+ func save(input Input) error {
225
+ validate(input)
226
+ user := normalize(input)
227
+ repo.Save(user)
228
+ return mailer.Welcome(user)
229
+ }
230
+
231
+ // good
232
+ func create(input ValidatedInput) (User, error) {
233
+ user := normalize(input)
234
+ return user, repo.Save(user)
235
+ }
236
+ ```
237
+
238
+ ### 12. DRY (real duplication only)
239
+
240
+ RULE: Extract when at least 2 of 3 hold: (a) the logic appears 3+ times, (b) a future rule change would need to touch all occurrences at once, (c) the copies represent the same domain concept. Do not extract on the first or second occurrence, and do not extract look-alike code that belongs to different concepts (a user's email validation vs a vendor's) — they will diverge.
241
+
242
+ ```go
243
+ // bad — extracted after one use
244
+ func formatName(value string) string {
245
+ return strings.ToUpper(strings.TrimSpace(value))
246
+ }
247
+
248
+ // good — third occurrence confirms one domain concept
249
+ func normalizeTicketStatus(status string) TicketStatus {
250
+ return ParseTicketStatus(strings.TrimSpace(status))
251
+ }
252
+ ```
253
+
254
+ ### 13. KISS
255
+
256
+ RULE: Ship the simplest solution for the current, real requirement. No configurability, hooks, or layers added "just in case."
257
+
258
+ ```go
259
+ // bad
260
+ func formatPrice(value float64, options FormatOptions) string {
261
+ return formatter.Format(value, options)
262
+ }
263
+
264
+ // good
265
+ func formatPrice(value float64) string {
266
+ return fmt.Sprintf("$%.2f", value)
267
+ }
268
+ ```
269
+
270
+ ### 14. YAGNI
271
+
272
+ RULE: Don't add fields, params, or branches for a use case that hasn't arrived. (Differs from KISS: KISS simplifies the chosen solution; YAGNI stops you building the unrequested one.)
273
+
274
+ ```go
275
+ // bad
276
+ func createInvoice(order Order, recurring, multiCurrency bool) Invoice {
277
+ return build(order, recurring, multiCurrency)
278
+ }
279
+
280
+ // good
281
+ func createInvoice(order Order) Invoice {
282
+ return Invoice{
283
+ Total: order.Total,
284
+ Items: order.Items,
285
+ }
286
+ }
287
+ ```
288
+
289
+ ### 15. Law of Demeter
290
+
291
+ RULE: Talk only to immediate collaborators. Never reach through an object to grab something several levels deep inside its internal structure.
292
+
293
+ ```go
294
+ // bad
295
+ func cityName(user User) string {
296
+ return user.Address.City.Name
297
+ }
298
+
299
+ // good
300
+ func cityName(user User) string {
301
+ return user.CityName()
302
+ }
303
+ ```
304
+
305
+ ### 16. Composition over inheritance
306
+
307
+ RULE: Compose small, focused behaviors instead of building inheritance chains that force unrelated methods onto every subtype.
308
+
309
+ ```go
310
+ // bad
311
+ type Animal interface {
312
+ Sound() string
313
+ Fly()
314
+ }
315
+
316
+ // good
317
+ type Dog struct {
318
+ Sound SoundBehavior
319
+ }
320
+
321
+ type Bird struct {
322
+ Sound SoundBehavior
323
+ Flight FlightBehavior
324
+ }
325
+ ```
326
+
327
+ ### 17. Dependency direction
328
+
329
+ RULE: Inner layers (domain/business logic) never import outer layers (framework, DB, HTTP). Outer layers depend inward, never the reverse.
330
+
331
+ ```go
332
+ // bad
333
+ func total(id string) Money {
334
+ return mysql.NewInvoiceRepository().Get(id).Total()
335
+ }
336
+
337
+ // good
338
+ func total(invoice Invoice) Money {
339
+ return invoice.Total()
340
+ }
341
+ ```
342
+
343
+ ### 18. Testability as a design constraint
344
+
345
+ RULE: Inject dependencies (clock, network, random) instead of hardcoding them. If a function is hard to test, redesign it — don't skip the test.
346
+
347
+ ```go
348
+ // bad
349
+ func expired(subscription Subscription) bool {
350
+ return subscription.ExpiresAt.Before(time.Now())
351
+ }
352
+
353
+ // good
354
+ func expired(subscription Subscription, now time.Time) bool {
355
+ return subscription.ExpiresAt.Before(now)
356
+ }
357
+ ```
358
+
359
+ ### 19. Comments
360
+
361
+ RULE: Comment only non-obvious intent — a trade-off, a constraint, a hazard names can't carry. Delete narration, restated logic, and stale TODOs. Test: if deleting the comment leaves the code equally clear, delete it.
362
+
363
+ ```go
364
+ // bad
365
+ // Increment the counter.
366
+ counter++
367
+
368
+ // good
369
+ // Poll because the provider cannot guarantee single webhook delivery.
370
+ ticker := time.NewTicker(5 * time.Second)
371
+ ```
372
+
373
+ ---
374
+
375
+ **When in doubt:** fail fast, keep it flat, keep it small.
376
+
377
+ **Enforcement note:** rules 1–2 are mechanical and should be backed by Go formatting or linting. Rules 3–10 are structural checks. Rules 11–19 require review judgment.
@@ -1,24 +1,257 @@
1
- # Java
1
+ # Java Coding Guidelines
2
2
 
3
- Delta only. Keep the shared table.
3
+ Rules every Java change must follow.
4
4
 
5
- - Braces on every `if` / `else`, including one-line guards.
6
- - Fail fast with a clear exception. Do not hide a missing value behind a silent default.
7
- - Domain code depends on ports, not `new` of infrastructure types.
5
+ ## Index
6
+
7
+ | # | Rule | Tier |
8
+ | --- | ------ | ------ |
9
+ | 1 | [Braced conditionals](#1-braced-conditionals) | mechanical |
10
+ | 2 | [No magic strings](#2-no-magic-strings) | mechanical |
11
+ | 3 | [Guard clauses](#3-guard-clauses) | structural |
12
+ | 4 | [Fail fast](#4-fail-fast) | structural |
13
+ | 5 | [Explicit error handling](#5-explicit-error-handling) | structural |
14
+ | 6 | [Clear names](#6-clear-names) | structural |
15
+ | 7 | [Command Query Separation](#7-command-query-separation-cqs) | structural |
16
+ | 8 | [Null/undefined handling](#8-nullundefined-handling) | structural |
17
+ | 9 | [Immutability by default](#9-immutability-by-default) | structural |
18
+ | 10 | [Validate once](#10-validate-once) | structural |
19
+ | 11 | [Single Responsibility](#11-single-responsibility-srp) | architectural |
20
+ | 12 | [DRY (real duplication only)](#12-dry-real-duplication-only) | architectural |
21
+ | 13 | [KISS](#13-kiss) | architectural |
22
+ | 14 | [YAGNI](#14-yagni) | architectural |
23
+ | 15 | [Law of Demeter](#15-law-of-demeter) | architectural |
24
+ | 16 | [Composition over inheritance](#16-composition-over-inheritance) | architectural |
25
+ | 17 | [Dependency direction](#17-dependency-direction) | architectural |
26
+ | 18 | [Testability](#18-testability-as-a-design-constraint) | architectural |
27
+ | 19 | [Comments](#19-comments) | architectural |
28
+
29
+ ---
30
+
31
+ ### 1. Braced conditionals
32
+
33
+ RULE: Every `if`/`else` body uses braces, even a one-line guard. Never bind a statement to a conditional by indentation alone.
8
34
 
9
35
  ```java
10
- // Bad
11
- public int shippingCost(Order order) {
12
- Zone zone = zones.getOrDefault(order.zoneId(), Zone.DEFAULT);
13
- return zone.baseCost();
36
+ // bad
37
+ if (user == null) return 0;
38
+ // good
39
+ if (user == null) {
40
+ return 0;
14
41
  }
42
+ ```
43
+
44
+ ### 2. No magic strings
45
+
46
+ RULE: Name domain/protocol literals once (const, enum, shared contract) and reuse the name. One-off strings with no reuse and no domain meaning (a single log label) may stay inline.
47
+
48
+ ```java
49
+ // bad
50
+ if (order.status().equals("pending_payment")) { charge(order); }
51
+ // good
52
+ if (order.status() == OrderStatus.PENDING_PAYMENT) { charge(order); }
53
+ ```
54
+
55
+ ### 3. Guard clauses
56
+
57
+ RULE: Validate and exit early. Keep the happy path last, at the shallowest indent. No pyramid nesting.
58
+
59
+ ```java
60
+ // bad
61
+ if (user != null) { if (user.isActive()) { return user.hasSubscription() ? .2 : 0; } }
62
+ return 0;
63
+ // good
64
+ if (user == null || !user.isActive() || !user.hasSubscription()) { return 0; }
65
+ return .2;
66
+ ```
67
+
68
+ ### 4. Fail fast
69
+
70
+ RULE: Invalid input, impossible state, or a broken dependency throws immediately with a clear message. No fallback chains or silent defaults.
71
+ WHY: This is for invariants that should never happen if upstream contracts hold — not license to re-check business rules a boundary already validated (see rule 10).
72
+
73
+ ```java
74
+ // bad
75
+ Zone zone = zones.getOrDefault(order.zoneId(), Zone.DEFAULT);
76
+ // good
77
+ Zone zone = Optional.ofNullable(zones.get(order.zoneId()))
78
+ .orElseThrow(() -> new IllegalArgumentException("Unknown zoneId: " + order.zoneId()));
79
+ ```
80
+
81
+ ### 5. Explicit error handling
82
+
83
+ RULE: Never swallow an error in an empty or generic `catch`. Re-throw with context, or let it propagate.
84
+
85
+ ```java
86
+ // bad
87
+ try { return api.getUser(id); } catch (Exception error) { return null; }
88
+ // good
89
+ try { return api.getUser(id); }
90
+ catch (ApiException error) { throw new UserLoadException("Failed to load user " + id, error); }
91
+ ```
92
+
93
+ ### 6. Clear names
94
+
95
+ RULE: Names reveal role or domain meaning. No `data`, `info`, `temp`, `result`, `obj`, `val`, `x`. A long honest name beats a short vague one.
15
96
 
16
- // Good
17
- public int shippingCost(Order order) {
18
- Zone zone = zones.get(order.zoneId());
19
- if (zone == null) {
20
- throw new IllegalArgumentException("Unknown zoneId: " + order.zoneId());
21
- }
22
- return zone.baseCost();
97
+ ```java
98
+ // bad
99
+ List<Subscription> process(List<Subscription> data) { return data.stream().filter(x -> x.days() > 0).toList(); }
100
+ // good
101
+ List<Subscription> activeSubscriptions(List<Subscription> subscriptions) {
102
+ return subscriptions.stream().filter(subscription -> subscription.remainingDays() > 0).toList();
23
103
  }
24
104
  ```
105
+
106
+ ### 7. Command Query Separation (CQS)
107
+
108
+ RULE: A function either does something (side effect) or returns something (query) — never both. A "getter" must never mutate.
109
+
110
+ ```java
111
+ // bad
112
+ int getNextId(Counter counter) { counter.increment(); return counter.value(); }
113
+ // good
114
+ int peekNextId(Counter counter) { return counter.value() + 1; }
115
+ void incrementCounter(Counter counter) { counter.increment(); }
116
+ ```
117
+
118
+ ### 8. Null/undefined handling
119
+
120
+ RULE: Pick one convention for `null` and never mix meanings. Never overload null to mean several business states — give each state its own explicit value, using `Optional` only for genuine absence.
121
+
122
+ ```java
123
+ // bad
124
+ Integer discount(User user) { return user == null || user.plan() == null || user.plan().discount() == 0 ? null : user.plan().discount(); }
125
+ // good
126
+ int discount(User user) { return user == null || user.plan() == null ? 0 : user.plan().discount(); }
127
+ ```
128
+
129
+ ### 9. Immutability by default
130
+
131
+ RULE: Return new values instead of mutating parameters. Never hide a side effect inside what looks like a pure transform.
132
+
133
+ ```java
134
+ // bad
135
+ Cart addItem(Cart cart, Item item) { cart.items().add(item); return cart; }
136
+ // good
137
+ Cart addItem(Cart cart, Item item) { return cart.withItems(Stream.concat(cart.items().stream(), Stream.of(item)).toList()); }
138
+ ```
139
+
140
+ ### 10. Validate once
141
+
142
+ RULE: Validate at the boundary that owns the input (schema, request DTO). Downstream code trusts that contract — no re-checking the same required/type/range rule in a helper, use case, or adapter further in.
143
+
144
+ ```java
145
+ // bad
146
+ void update(ValidatedProfile profile) { if (profile.email().isBlank()) { throw new IllegalArgumentException(); } repository.update(profile); }
147
+ // good
148
+ void update(ValidatedProfile profile) { repository.update(profile); }
149
+ ```
150
+
151
+ ### 11. Single Responsibility (SRP)
152
+
153
+ RULE: A function, class, or module has one reason to change. If it validates AND transforms AND persists AND notifies, split it.
154
+
155
+ ```java
156
+ // bad
157
+ void save(UserInput input) { validate(input); User user = normalize(input); repository.save(user); mailer.welcome(user); }
158
+ // good
159
+ User create(ValidatedUser input) { User user = normalizer.normalize(input); repository.save(user); return user; }
160
+ ```
161
+
162
+ ### 12. DRY (real duplication only)
163
+
164
+ RULE: Extract when at least 2 of 3 hold: (a) the logic appears 3+ times, (b) a future rule change would need to touch all occurrences at once, (c) the copies represent the same domain concept. Do not extract on the first or second occurrence, and do not extract look-alike code that belongs to different concepts (a user's email validation vs a vendor's) — they will diverge.
165
+
166
+ ```java
167
+ // bad — extracted after one real use
168
+ String formatName(String value) { return value.trim().toUpperCase(); }
169
+ // good — same domain concept, third occurrence confirms the pattern
170
+ TicketStatus normalizeTicketStatus(String status) { return TicketStatus.parse(status.trim()); }
171
+ ```
172
+
173
+ ### 13. KISS
174
+
175
+ RULE: Ship the simplest solution for the current, real requirement. No configurability, hooks, or layers added "just in case."
176
+
177
+ ```java
178
+ // bad
179
+ String formatPrice(BigDecimal value, Currency currency, Locale locale, boolean symbol) { return configurableFormatter.format(value); }
180
+ // good
181
+ String formatPrice(BigDecimal value) { return "$" + value.setScale(2); }
182
+ ```
183
+
184
+ ### 14. YAGNI
185
+
186
+ RULE: Don't add fields, params, or branches for a use case that hasn't arrived. (Differs from KISS: KISS simplifies the chosen solution; YAGNI stops you building the unrequested one.)
187
+
188
+ ```java
189
+ // bad
190
+ Invoice createInvoice(Order order, boolean recurring, boolean multiCurrency) { return builder.build(order, recurring, multiCurrency); }
191
+ // good
192
+ Invoice createInvoice(Order order) { return new Invoice(order.total(), order.items()); }
193
+ ```
194
+
195
+ ### 15. Law of Demeter
196
+
197
+ RULE: Talk only to immediate collaborators. Never reach through an object to grab something several levels deep inside its internal structure.
198
+
199
+ ```java
200
+ // bad
201
+ String cityName(User user) { return user.address().city().name(); }
202
+ // good
203
+ String cityName(User user) { return user.cityName(); }
204
+ ```
205
+
206
+ ### 16. Composition over inheritance
207
+
208
+ RULE: Compose small, focused behaviors instead of building inheritance chains that force unrelated methods onto every subclass.
209
+
210
+ ```java
211
+ // bad
212
+ abstract class Animal { abstract String sound(); abstract void fly(); }
213
+ final class Dog extends Animal { public void fly() { throw new UnsupportedOperationException(); } }
214
+ // good
215
+ final class Dog { private final SoundBehavior sound = new Bark(); }
216
+ ```
217
+
218
+ ### 17. Dependency direction
219
+
220
+ RULE: Inner layers (domain/business logic) never import outer layers (framework, DB, HTTP). Outer layers depend inward, never the reverse.
221
+
222
+ ```java
223
+ // bad
224
+ Money total(UUID id) { return new MysqlInvoiceRepository().get(id).total(); }
225
+ // good
226
+ Money total(Invoice invoice) { return invoice.total(); }
227
+ ```
228
+
229
+ ### 18. Testability as a design constraint
230
+
231
+ RULE: Inject dependencies (clock, network, random) instead of hardcoding them. If a function is hard to test, redesign it — don't skip the test.
232
+
233
+ ```java
234
+ // bad
235
+ boolean expired(Subscription subscription) { return subscription.expiresAt().isBefore(Instant.now()); }
236
+ // good
237
+ boolean expired(Subscription subscription, Clock clock) { return subscription.expiresAt().isBefore(clock.instant()); }
238
+ ```
239
+
240
+ ### 19. Comments
241
+
242
+ RULE: Comment only non-obvious intent — a trade-off, a constraint, a hazard names can't carry. Delete narration, restated logic, and stale TODOs. Test: if deleting the comment leaves the code equally clear, delete it.
243
+
244
+ ```java
245
+ // bad
246
+ // Increment the counter.
247
+ counter.increment();
248
+ // good
249
+ // Poll because the provider cannot guarantee single webhook delivery.
250
+ scheduler.scheduleAtFixedRate(this::checkPaymentStatus, 0, 5, SECONDS);
251
+ ```
252
+
253
+ ---
254
+
255
+ **When in doubt:** fail fast, keep it flat, keep it small.
256
+
257
+ **Enforcement note:** rules 1–2 are mechanical and should be backed by Java formatting or linting. Rules 3–10 are structural checks. Rules 11–19 require review judgment.