docs: Go patterns vs official guidelines divergence analysis

This commit is contained in:
Rodin
2026-04-30 07:50:32 -07:00
commit ca07514d9c
2 changed files with 197 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# Patterns vs Official Guidelines
Divergence analyses comparing our extracted-from-source patterns against official coding guidelines.
## Reports
| Ecosystem | Our Patterns | Official Guidelines | Report |
|-----------|-------------|--------------------|---------|
| Go | [rodin/go-patterns](https://gitea.weiker.me/rodin/go-patterns) | Effective Go, Code Review Comments, Google Style | [go/](go/) |
| Elixir | [rodin/elixir-patterns](https://gitea.weiker.me/rodin/elixir-patterns) | CONTRIBUTING.md, Style Guide, HexDocs | [elixir/](elixir/) |
## Philosophy
Official guides tell you what to do. Source patterns show what mature code actually does.
When they diverge, neither is wrong — they serve different audiences and optimize for
different things.
This repo documents where they agree, where they differ, and why.
+179
View File
@@ -0,0 +1,179 @@
# Go Patterns vs Official Guidelines — Divergence Analysis
## Summary
- **Areas of agreement:** 12 (core patterns align with official guidance)
- **Divergences found:** 4 (our patterns make different emphasis or recommendations)
- **Our patterns go beyond official:** 8 (patterns we extracted that guides don't cover)
---
## Agreements (brief)
Our extracted patterns and the official Go guidelines **agree** on the following core principles:
| Area | Official Source | Agreement |
|------|---------------|-----------|
| Small interfaces (1-2 methods) | CodeReviewComments "Interfaces" | Both say interfaces belong in the consumer package, keep them small |
| Accept interfaces, return structs | CodeReviewComments "Interfaces" | Both recommend returning concrete types, accepting interfaces at boundaries |
| Don't define interfaces "for mocking" | CodeReviewComments "Interfaces" | Both explicitly warn against this |
| Error strings: lowercase, no punctuation | CodeReviewComments "Error Strings" | Perfect alignment |
| Handle errors, don't discard with `_` | CodeReviewComments "Handle Errors" | Identical advice |
| Indent error flow (happy path left-aligned) | CodeReviewComments "Indent Error Flow" | Our error-handling patterns show this |
| Receiver names: short, consistent, never this/self | CodeReviewComments "Receiver Names" + Google Style Decisions | Identical |
| MixedCaps / no underscores | Effective Go + Google Style Guide | Perfect alignment |
| Acronyms all-caps (URL, ID, HTTP) | CodeReviewComments "Initialisms" + Google Style Decisions | Identical, including edge cases (gRPC, DDoS) |
| gofmt is non-negotiable | Effective Go + CodeReviewComments + Google Style | All agree: format with gofmt, no exceptions |
| Context as first parameter, never in structs | CodeReviewComments "Contexts" | Our concurrency patterns match exactly |
| Don't Panic for normal errors | CodeReviewComments "Don't Panic" | Our Must pattern correctly limits panic to init-time programmer errors |
---
## Divergences
### 1. Interface Definition: "Don't define before they are used"
**Our pattern:** We teach interface design extensively upfront — 10 patterns covering composition, adapters, optional interfaces, runtime upgrades, driver patterns. The framing assumes you're *designing* interfaces as a primary activity.
**Official guide (CodeReviewComments):** "Do not define interfaces before they are used: without a realistic example of usage, it is too difficult to see whether an interface is even necessary, let alone what methods it ought to contain."
**Why they differ:** Our patterns were extracted from the *standard library*, which is mature code where interfaces emerged from years of real usage. The official guide targets the *development process* — advising engineers not to pre-design interfaces before concrete use cases exist. The stdlib already had those use cases. Our patterns describe the *outcome* of good design; the official guide describes the *process* to arrive there.
**Assessment:** The official guide is more trustworthy for *new code*. Our patterns are better as reference material for *how interfaces should look* once the design has crystallized.
---
### 2. Functional Options vs Config Structs
**Our pattern (package-design.md):** Documents both approaches but notes that the stdlib uses struct-based configuration (http.Server, tls.Config) and the functional options pattern "emerged from the community." Our table says config structs for "few options, all data (stdlib preference)" and functional options for "many options, some involve behavior, public API stability."
**Official guide (Google Style):** The Google Style Best Practices document does not mention functional options at all. The stdlib universally uses config structs with nil-means-default semantics. The Google style implicitly endorses config structs through its "least mechanism" principle: use the simplest tool that works.
**Why they differ:** Our patterns try to be balanced by acknowledging community practice (Dave Cheney, Rob Pike blog posts). The official guides — both Go team and Google — appear to view config structs as sufficient and functional options as unnecessary complexity for most cases.
**Assessment:** The official position (config structs) is simpler and covers 95% of cases. Functional options add value primarily for libraries with evolving APIs where binary compatibility matters (rare outside large organizations). Our patterns are correct but may over-emphasize functional options by giving them equal billing.
---
### 3. "When NOT to Use" Sections (Anti-pattern Depth)
**Our patterns:** Every pattern includes a detailed "When NOT to Use" section with over-application examples, showing what happens when the pattern is misapplied. This is ~40% of our content.
**Official guides:** Rarely discuss over-application. Effective Go and CodeReviewComments state the positive rule ("do X") without exploring what happens when you cargo-cult it. The Google Style Guide's "Best Practices" section occasionally shows bad/good contrasts but doesn't systematically cover misapplication.
**Why they differ:** Our patterns were designed as *teaching material* for engineers who might mechanically apply patterns without understanding their boundaries. The official guides assume the reader exercises judgment and don't attempt to enumerate all failure modes. The Google style achieves this through the "Least mechanism" principle — a general guard against over-engineering.
**Assessment:** Our "When NOT to Use" sections are genuinely valuable and fill a gap in official documentation. They're not contradicting official guidance; they're supplementing it with wisdom the guides assume you'll develop through experience.
---
### 4. Named Return Values
**Our pattern (style.md):** "Named returns for documentation and defer." Suggests using named returns when they add documentary value or enable `defer` modification.
**Official guide (CodeReviewComments "Named Result Parameters"):** More cautious: "Don't name result parameters just to avoid declaring a var inside the function; that trades off a minor implementation brevity at the cost of unnecessary API verbosity." Also warns that named returns create repetitive godoc (e.g., `func (n *Node) Parent1() (node *Node)` stutters).
**Why they differ:** Our pattern focuses on the positive case (when named returns help). The official guide focuses more on the negative case (when named returns hurt readability). The CodeReviewComments stance is: avoid named returns by default, use them only when the function returns same-typed values and the names genuinely disambiguate for the caller.
**Assessment:** The official guide's caution is correct. Named returns are over-used in practice. Our pattern should emphasize the "default to unnamed" stance more strongly.
---
## Beyond Official (patterns we extracted that guides don't cover)
### 1. Interface Upgrade Pattern (WriterTo/ReaderFrom in io.Copy)
**Our pattern:** Documented as Pattern #9 in interfaces.md — functions check for richer interfaces at runtime to unlock optimizations (zero-copy sendfile, etc.)
**Official guides:** Not mentioned in Effective Go, CodeReviewComments, or Google Style. This is a stdlib-specific advanced technique.
**Why it's beyond official:** This pattern requires deep understanding of type assertions, performance implications, and interface layering. The guides focus on code *most engineers write*; the upgrade pattern is for framework/infrastructure authors.
---
### 2. Zero-Value Usability (structs.md)
**Our pattern:** Structs should be designed so `var x T` immediately works. Nil fields fall back to sensible defaults at call time.
**Official guides:** Effective Go briefly mentions "the zero value of a sync.Mutex is an unlocked mutex" but doesn't elevate this to a design principle. CodeReviewComments doesn't address it. Google Style mentions it in passing ("zero value ready to use" in some library docs).
**Why it's beyond official:** This is a *design philosophy* extracted from repeated stdlib patterns (http.Client, bytes.Buffer, strings.Builder). Official docs demonstrate it but never articulate it as a rule.
---
### 3. Compile-Time Interface Satisfaction (`var _ I = (*T)(nil)`)
**Our pattern:** Dedicated section with examples from io, os, encoding/json, net/http.
**Official guides:** Not mentioned in Effective Go, CodeReviewComments, or Google Style Guide. It's a community idiom that the stdlib uses but no official document recommends.
**Why it's beyond official:** This is a defensive programming technique. The guides likely omit it because it's considered optional — the compiler already catches interface mismatches at usage sites. Our pattern correctly notes it's most valuable for exported types implementing external interfaces.
---
### 4. The Adapter Pattern (HandlerFunc)
**Our pattern:** Full treatment of how `type Func func(...)` with a method bridges functions to interfaces.
**Official guides:** Effective Go mentions it very briefly. Neither CodeReviewComments nor Google Style discuss it systematically.
**Why it's beyond official:** It's a specific implementation technique, not a style concern. The guides focus on *how to write* code (style), not *architectural patterns* (design).
---
### 5. errors.Join — Multi-Error Aggregation
**Our pattern:** Full treatment of Go 1.20's `errors.Join` with when-to-use and when-not-to-use.
**Official guides:** Not in any official style guide (too new — CodeReviewComments predates it). The Go blog covered it but style guides haven't incorporated it.
**Why it's beyond official:** Official guides lag behind language evolution. Our patterns are current (Go 1.24/1.25 features like `errors.AsType` and `WaitGroup.Go`).
---
### 6. Copy Protection (noCopy, copyCheck)
**Our pattern:** Documents both `noCopy` (vet-detected) and `copyCheck` (runtime panics, strings.Builder).
**Official guides:** CodeReviewComments mentions "Copying" briefly (don't copy a Buffer), but doesn't explain the implementation patterns for enforcing it.
**Why it's beyond official:** Implementation-level concern for library authors, not application developers.
---
### 7. Background Worker with Context Shutdown
**Our pattern:** Full concurrency pattern showing `OpenDB`-style background goroutine + context cancellation.
**Official guides:** CodeReviewComments "Goroutine Lifetimes" says "make it clear when - or whether - they exit" and "document when and why the goroutines exit." But doesn't show the full structural pattern.
**Why it's beyond official:** The guides state the principle; we show the implementation. Application engineers need both.
---
### 8. Layered API (Open/Create/OpenFile)
**Our pattern:** Full treatment of convenience wrappers over a configurable core function.
**Official guides:** Not discussed in any official Go style document.
**Why it's beyond official:** This is API design methodology, not code style. The Go team demonstrates it consistently but never writes it down as a rule.
---
## Assessment: Which is More Trustworthy When They Conflict?
**The official guides are more trustworthy for:**
- Day-to-day coding decisions (naming, formatting, error handling)
- Determining whether to create an interface at all
- Named returns (be more conservative than our patterns suggest)
- Config approach (prefer structs over functional options)
**Our patterns are more trustworthy for:**
- Understanding *why* stdlib code looks the way it does
- Knowing when a pattern is being misapplied (our "When NOT to Use" sections)
- Advanced patterns for library/framework authors (interface upgrades, adapters, driver patterns)
- Current Go features (errors.Join, WaitGroup.Go, errors.AsType)
**The fundamental difference:** Official guides tell you *what to do*. Our patterns tell you *what the result looks like and why*. The guides are prescriptive process; our patterns are descriptive reference. When they disagree, the guides are usually right about the *default behavior* (e.g., "don't create interfaces prematurely"), while our patterns are right about the *goal state* (e.g., "mature interfaces look like this").
**For Aaron's work codebase:** Follow official guides as the default. Reference our patterns when designing libraries, APIs, or when you've already determined an interface/pattern is needed and want to implement it idiomatically.