docs: add when-not to style + smells (package-design + documentation already done)

This commit is contained in:
2026-04-30 13:31:47 +00:00
parent 11048ae73e
commit 733aa7d261
2 changed files with 137 additions and 0 deletions
+46
View File
@@ -44,6 +44,26 @@ client.Update(ctx, copy)
**Evidence:** The `runtime.Object` interface *mandates* `DeepCopyObject()`. Every API type has generated deep copy methods. The entire architecture assumes immutable reads.
### Exceptions (When This Rule Doesn't Apply)
**Acceptable to skip deep copy when:**
- You're only *reading* fields, never mutating the object
- The object was just created locally (not from a shared cache) and hasn't been shared yet
- You're inside a test where the cache is a mock/fake with no other consumers
- Performance profiling shows deep copy is a measurable bottleneck AND you can prove no concurrent readers exist
**Acceptable-use example:**
```go
// Reading only — no mutation, no copy needed
deployment, _ := dc.dLister.Deployments(ns).Get(name)
if deployment.Spec.Replicas != nil && *deployment.Spec.Replicas == 0 {
logger.Info("deployment scaled to zero", "name", name)
return nil // just reading, never modified the object
}
```
**Why this is fine:** Deep copy has a cost (allocation + GC pressure). If you're only reading fields to make decisions and never writing to the object, the cache stays unmodified and other consumers are unaffected.
---
## 2. Never Process the Same Key Concurrently
@@ -107,6 +127,32 @@ func (dc *DeploymentController) syncDeployment(ctx context.Context, key string)
}
```
### Exceptions (When This Rule Doesn't Apply)
**Edge-triggered logic is acceptable when:**
- You're implementing audit logging or event sourcing (recording *what happened* is the goal, not reconciling state)
- The event carries information that isn't available from current state (e.g., "who deleted this" from the delete event's user context)
- You're optimizing a hot path where full reconciliation is too expensive, AND you have a periodic full-sync as a safety net
**Acceptable-use example:**
```go
// Audit logging — we WANT to record the specific event, not just current state
func onPodDeleted(pod *v1.Pod) {
auditLog.Record(AuditEvent{
Action: "delete",
Resource: pod.Name,
DeletedBy: pod.Annotations["deleted-by"],
Timestamp: time.Now(),
})
}
// This is fine because:
// 1. The goal is recording history, not maintaining correct state
// 2. A missed event means a missing audit entry (acceptable, not corrupt state)
// 3. The actual replica count is still reconciled level-triggered elsewhere
```
**Why this is fine:** Level-triggered reconciliation is about *correctness of state*. Audit logs, metrics counters, and notification systems have different requirements — they genuinely need to know *what happened*, not just *what is*.
---
## 4. Never Forget to Call queue.Forget() on Success