Go¶
Go's philosophy is radical simplicity: small interfaces, explicit errors, flat hierarchies. These 12 principles come from Effective Go, the Go Proverbs, and the collective wisdom of the Go community. They catch the patterns where Go's simplicity gets undermined.
Optional External Tool Augmentation¶
Consent-first external tooling
External tool execution is optional and disabled by default. Use
--enable-external-tools (CLI) or enable_external_tools=true (MCP)
to opt in. Missing tools should return recommendations; no automatic
installs occur during analysis.
| Tool | Default invocation | Output |
|---|---|---|
go |
go vet ./... |
Text / structured stderr |
Zen Principles¶
20 principles across 12 categories, drawn from Effective Go & The Zen of Go.
Architecture · 1 principle Complexity · 1 principle Concurrency · 3 principles Correctness · 1 principle Design · 5 principles Error Handling · 1 principle Idioms · 1 principle Initialization · 1 principle Organization · 2 principles Performance · 1 principle Readability · 2 principles Structure · 1 principle
| Rule ID | Principle | Category | Severity | Dogma |
|---|---|---|---|---|
go-001 |
Errors are values | Error Handling | 9 | ZEN-FAIL-FAST, ZEN-EXPLICIT-INTENT |
go-002 |
Accept interfaces, return structs | Design | 7 | ZEN-RIGHT-ABSTRACTION |
go-003 |
Make the zero value useful | Design | 6 | ZEN-RIGHT-ABSTRACTION, ZEN-EXPLICIT-INTENT |
go-004 |
Use short variable names | Readability | 5 | ZEN-UNAMBIGUOUS-NAME |
go-005 |
Don't use pointer to interface | Design | 8 | ZEN-RIGHT-ABSTRACTION |
go-006 |
Avoid goroutine leaks | Concurrency | 9 | ZEN-VISIBLE-STATE |
go-007 |
Use defer for cleanup | Idioms | 7 | ZEN-RIGHT-ABSTRACTION |
go-008 |
Package names are singular | Organization | 5 | ZEN-STRICT-FENCES, ZEN-UNAMBIGUOUS-NAME |
go-009 |
Avoid package-level state | Architecture | 7 | ZEN-RIGHT-ABSTRACTION, ZEN-VISIBLE-STATE |
go-010 |
Keep interfaces small | Design | 7 | ZEN-RIGHT-ABSTRACTION |
go-011 |
Use context for cancellation | Concurrency | 8 | ZEN-VISIBLE-STATE |
go-012 |
Avoid init() when possible | Initialization | 6 | ZEN-EXPLICIT-INTENT |
go-013 |
Organize by responsibility | Organization | 6 | ZEN-STRICT-FENCES, ZEN-RETURN-EARLY, ZEN-UNAMBIGUOUS-NAME |
go-014 |
Embed for composition, not inheritance | Structure | 7 | ZEN-RETURN-EARLY, ZEN-UNAMBIGUOUS-NAME |
go-015 |
Communicate by sharing memory through channels | Concurrency | 8 | ZEN-VISIBLE-STATE |
go-016 |
Avoid unnecessary complexity | Complexity | 7 | ZEN-PROPORTIONATE-COMPLEXITY, ZEN-RETURN-EARLY |
go-017 |
Handle every error path | Correctness | 9 | ZEN-EXPLICIT-INTENT, ZEN-FAIL-FAST |
go-018 |
Avoid premature optimization | Performance | 5 | ZEN-PROPORTIONATE-COMPLEXITY |
go-019 |
Design for testability | Design | 7 | ZEN-RIGHT-ABSTRACTION, ZEN-VISIBLE-STATE |
go-020 |
Write self-documenting code | Readability | 5 | ZEN-UNAMBIGUOUS-NAME, ZEN-EXPLICIT-INTENT, ZEN-VISIBLE-STATE |
go-001 — Errors are values
Handle errors explicitly, don't panic
Universal Dogmas: ZEN-FAIL-FAST, ZEN-EXPLICIT-INTENT
Common Violations:
- Ignoring error returns
- _ assignment for errors
- Excessive panic usage
- Not checking error != nil
Detectable Patterns:
_, err := without checkingpanic() in libraries
go-002 — Accept interfaces, return structs
Function parameters should be interfaces, returns concrete types
Universal Dogmas: ZEN-RIGHT-ABSTRACTION
Common Violations:
- Returning interfaces from functions
- Accepting concrete types when interface would work
- Premature interface abstraction
Detectable Patterns:
interface{
go-003 — Make the zero value useful
Structs should be usable without explicit initialization
Universal Dogmas: ZEN-RIGHT-ABSTRACTION, ZEN-EXPLICIT-INTENT
Common Violations:
- Structs requiring explicit initialization
- Mandatory constructor functions
- Unusable zero values
Detectable Patterns:
func New
go-004 — Use short variable names
Prefer short, contextual names in limited scopes
Universal Dogmas: ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Long variable names in small scopes
- unnecessarilyLongVariableNames
- Unclear abbreviations in large scopes
go-005 — Don't use pointer to interface
**Interfaces are already pointers, don't add ***
Universal Dogmas: ZEN-RIGHT-ABSTRACTION
Common Violations:
- *Interface type parameters
- Pointer to interface return types
Detectable Patterns:
*io.Reader*SomeInterface
go-006 — Avoid goroutine leaks
Always ensure goroutines can terminate
Universal Dogmas: ZEN-VISIBLE-STATE
Common Violations:
- Goroutines without cancellation
- Channels without close
- Infinite loops in goroutines
- No context for cancellation
Detectable Patterns:
go func
go-007 — Use defer for cleanup
Defer resource cleanup immediately after acquisition
Universal Dogmas: ZEN-RIGHT-ABSTRACTION
Common Violations:
- Manual cleanup scattered in code
- Not using defer for file.Close()
- Not deferring mutex.Unlock()
go-008 — Package names are singular
Package names should be lowercase, singular nouns
Universal Dogmas: ZEN-STRICT-FENCES, ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Plural package names (utils → util)
- CamelCase package names
- Underscores in package names
Detectable Patterns:
package utilspackage helpers
go-009 — Avoid package-level state
Minimize global variables and package-level mutable state
Universal Dogmas: ZEN-RIGHT-ABSTRACTION, ZEN-VISIBLE-STATE
Common Violations:
- Mutable package-level variables
- Package init() with side effects
- Global singletons
Detectable Patterns:
var
go-010 — Keep interfaces small
Prefer many small interfaces over large ones
Universal Dogmas: ZEN-RIGHT-ABSTRACTION
Common Violations:
- Interfaces with > 3 methods
- God interfaces
- Interface segregation violations
Thresholds:
| Parameter | Default |
|---|---|
max_interface_methods |
3 |
go-011 — Use context for cancellation
Pass context.Context for cancellation and deadlines
Universal Dogmas: ZEN-VISIBLE-STATE
Common Violations:
- Long-running functions without context
- HTTP handlers without request context
- Not propagating context
go-012 — Avoid init() when possible
Prefer explicit initialization over init()
Universal Dogmas: ZEN-EXPLICIT-INTENT
Common Violations:
- Complex logic in init()
- Multiple init() functions
- Side effects in init()
Detectable Patterns:
func init(
go-013 — Organize by responsibility
Group code by domain responsibility, not by technical layer
Universal Dogmas: ZEN-STRICT-FENCES, ZEN-RETURN-EARLY, ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Organizing packages by type (models/, controllers/)
- Deeply nested package hierarchies
- Circular package dependencies
- Package names reflecting technical role instead of domain
Detectable Patterns:
package modelspackage controllers
go-014 — Embed for composition, not inheritance
Use struct embedding for composing behaviors, not faking OOP inheritance
Universal Dogmas: ZEN-RETURN-EARLY, ZEN-UNAMBIGUOUS-NAME
Common Violations:
- Deep embedding chains exceeding two levels
- Embedding structs solely to access their fields
- Name collisions from overlapping embedded methods
Detectable Patterns:
type Foo struct { Bar
Thresholds:
| Parameter | Default |
|---|---|
max_embedding_depth |
2 |
go-015 — Communicate by sharing memory through channels
Prefer channels over shared-memory synchronization primitives
Universal Dogmas: ZEN-VISIBLE-STATE
Common Violations:
- Using sync.Mutex where a channel would be clearer
- Shared mutable state accessed by multiple goroutines
- Missing synchronization around concurrent map access
Detectable Patterns:
sync.Mutexsync.RWMutex
go-016 — Avoid unnecessary complexity
Prefer straightforward solutions; complexity must be justified
Universal Dogmas: ZEN-PROPORTIONATE-COMPLEXITY, ZEN-RETURN-EARLY
Common Violations:
- Deeply nested control flow exceeding three levels
- Functions longer than 50 lines
- Cyclomatic complexity above 10 per function
Thresholds:
| Parameter | Default |
|---|---|
max_nesting_depth |
3 |
max_function_lines |
50 |
go-017 — Handle every error path
Ensure all error branches are explicitly handled, not just the happy path
Universal Dogmas: ZEN-EXPLICIT-INTENT, ZEN-FAIL-FAST
Common Violations:
- Returning early without wrapping or annotating the error
- Silently swallowing errors inside deferred calls
- Using log.Fatal in library code instead of returning an error
go-018 — Avoid premature optimization
Write clear code first; optimize only after profiling proves a bottleneck
Universal Dogmas: ZEN-PROPORTIONATE-COMPLEXITY
Common Violations:
- Unsafe pointer tricks without benchmark justification
- Manual memory pooling where sync.Pool is not warranted
- Replacing readable code with micro-optimizations lacking profiling data
go-019 — Design for testability
Structure code so that dependencies can be replaced in tests
Universal Dogmas: ZEN-RIGHT-ABSTRACTION, ZEN-VISIBLE-STATE
Common Violations:
- Hard-coded external service calls with no interface seam
- Global state that prevents parallel test execution
- Unexported helpers that duplicate test setup across packages
go-020 — Write self-documenting code
Let clear naming and structure replace the need for most comments
Universal Dogmas: ZEN-UNAMBIGUOUS-NAME, ZEN-EXPLICIT-INTENT, ZEN-VISIBLE-STATE
Common Violations:
- Comments that restate the code instead of explaining intent
- Exported symbols missing GoDoc-style comments
- Magic numbers without named constants
Detector Catalog¶
Architecture¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoPackageStateDetector | Flags mutable package-level variables that introduce hidden global state | go-009 |
Complexity¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoSimplicityDetector | Flags empty interface usage that weakens type safety | go-016 |
Concurrency¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoConcurrencyCallerDetector | Flags functions that spawn goroutines internally | go-015 |
| GoContextUsageDetector | Flags code that lacks context.Context for cancellation and deadline propagation |
go-011 |
| GoGoroutineLeakDetector | Flags goroutines launched without cancellation support and unclosed channels | go-006 |
Correctness¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoEarlyReturnDetector | Flags err == nil guards that nest the happy path instead of returning early |
go-017 |
Design¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoInterfacePointerDetector | Detects pointers to interfaces, which are almost always a mistake in Go | go-005 |
| GoInterfaceReturnDetector | Flags functions that return interface types instead of concrete structs | go-002 |
| GoInterfaceSizeDetector | Detects oversized interfaces that violate Go's preference for small, composable contracts | go-010 |
| GoTestPresenceDetector | Flags exported functions without accompanying test functions | go-019 |
| GoZeroValueDetector | Flags New* constructor functions where making the zero value usable would be simpler |
go-003 |
Error Handling¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoErrorHandlingDetector | Flags ignored errors, unchecked err variables, and panic() calls in Go code |
go-001 |
General¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoModerationDetector | Flags excessive goroutine spawning within a single file |
Idioms¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoDeferUsageDetector | Detects defer misuse inside loops and missing defer for resource cleanup |
go-007 |
Initialization¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoInitUsageDetector | Flags func init() usage that hides initialization logic from callers |
go-012 |
Organization¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoPackageNamingDetector | Flags package names that violate Go's singular, lowercase naming convention | go-008 |
| GoOrganizeResponsibilityDetector | Detects catch-all package names like util, common, or helper | go-013 |
Performance¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoBenchmarkDetector | Flags optimisation primitives used without benchmark proof | go-018 |
Readability¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoMaintainabilityDetector | Flags exported functions missing godoc comments | go-020 |
| GoNamingConventionDetector | Flags overly long variable names that violate Go's brevity conventions | go-004 |
Structure¶
| Detector | What It Catches | Rule IDs |
|---|---|---|
| GoEmbeddingDepthDetector | Flags structs with too many anonymously embedded types | go-014 |
Principle → Detector Wiring
%%{init: {"theme": "base", "flowchart": {"useMaxWidth": false, "htmlLabels": true, "nodeSpacing": 40, "rankSpacing": 60}}}%%
graph TD
go_001["go-001<br/>Errors are values"]
go_002["go-002<br/>Accept interfaces, return..."]
go_003["go-003<br/>Make the zero value usefu..."]
go_004["go-004<br/>Use short variable names"]
go_005["go-005<br/>Don't use pointer to inte..."]
go_006["go-006<br/>Avoid goroutine leaks"]
go_007["go-007<br/>Use defer for cleanup"]
go_008["go-008<br/>Package names are singula..."]
go_009["go-009<br/>Avoid package-level state"]
go_010["go-010<br/>Keep interfaces small"]
go_011["go-011<br/>Use context for cancellat..."]
go_012["go-012<br/>Avoid init() when possibl..."]
go_013["go-013<br/>Organize by responsibilit..."]
go_014["go-014<br/>Embed for composition, no..."]
go_015["go-015<br/>Communicate by sharing me..."]
go_016["go-016<br/>Avoid unnecessary complex..."]
go_017["go-017<br/>Handle every error path"]
go_018["go-018<br/>Avoid premature optimizat..."]
go_019["go-019<br/>Design for testability"]
go_020["go-020<br/>Write self-documenting co..."]
det_GoBenchmarkDetector["Go Benchmark"]
go_018 --> det_GoBenchmarkDetector
det_GoConcurrencyCallerDetector["Go Concurrency<br/>Caller"]
go_015 --> det_GoConcurrencyCallerDetector
det_GoContextUsageDetector["Go Context<br/>Usage"]
go_011 --> det_GoContextUsageDetector
det_GoDeferUsageDetector["Go Defer<br/>Usage"]
go_007 --> det_GoDeferUsageDetector
det_GoEarlyReturnDetector["Go Early<br/>Return"]
go_017 --> det_GoEarlyReturnDetector
det_GoEmbeddingDepthDetector["Go Embedding<br/>Depth"]
go_014 --> det_GoEmbeddingDepthDetector
det_GoErrorHandlingDetector["Go Error<br/>Handling"]
go_001 --> det_GoErrorHandlingDetector
det_GoGoroutineLeakDetector["Go Goroutine<br/>Leak"]
go_006 --> det_GoGoroutineLeakDetector
det_GoInitUsageDetector["Go Init<br/>Usage"]
go_012 --> det_GoInitUsageDetector
det_GoInterfacePointerDetector["Go Interface<br/>Pointer"]
go_005 --> det_GoInterfacePointerDetector
det_GoInterfaceReturnDetector["Go Interface<br/>Return"]
go_002 --> det_GoInterfaceReturnDetector
det_GoInterfaceSizeDetector["Go Interface<br/>Size"]
go_010 --> det_GoInterfaceSizeDetector
det_GoMaintainabilityDetector["Go Maintainability"]
go_020 --> det_GoMaintainabilityDetector
det_GoModerationDetector["Go Moderation"]
det_GoNamingConventionDetector["Go Naming<br/>Convention"]
go_004 --> det_GoNamingConventionDetector
det_GoOrganizeResponsibilityDetector["Go Organize<br/>Responsibility"]
go_013 --> det_GoOrganizeResponsibilityDetector
det_GoPackageNamingDetector["Go Package<br/>Naming"]
go_008 --> det_GoPackageNamingDetector
det_GoPackageStateDetector["Go Package<br/>State"]
go_009 --> det_GoPackageStateDetector
det_GoSimplicityDetector["Go Simplicity"]
go_016 --> det_GoSimplicityDetector
det_GoTestPresenceDetector["Go Test<br/>Presence"]
go_019 --> det_GoTestPresenceDetector
det_GoZeroValueDetector["Go Zero<br/>Value"]
go_003 --> det_GoZeroValueDetector
Detector Class Hierarchy
%%{init: {"theme": "base"}}%%
classDiagram
direction TB
class ViolationDetector {
<<abstract>>
+detect(context, config)
}
class det_01["Go Benchmark"]
ViolationDetector <|-- det_01
class det_02["Go Concurrency Caller"]
ViolationDetector <|-- det_02
class det_03["Go Context Usage"]
ViolationDetector <|-- det_03
class det_04["Go Defer Usage"]
ViolationDetector <|-- det_04
class det_05["Go Early Return"]
ViolationDetector <|-- det_05
class det_06["Go Embedding Depth"]
ViolationDetector <|-- det_06
class det_07["Go Error Handling"]
ViolationDetector <|-- det_07
class det_08["Go Goroutine Leak"]
ViolationDetector <|-- det_08
class det_09["Go Init Usage"]
ViolationDetector <|-- det_09
class det_10["Go Interface Pointer"]
ViolationDetector <|-- det_10
class det_11["Go Interface Return"]
ViolationDetector <|-- det_11
class det_12["Go Interface Size"]
ViolationDetector <|-- det_12
class det_13["Go Maintainability"]
ViolationDetector <|-- det_13
class det_14["Go Moderation"]
ViolationDetector <|-- det_14
class det_15["Go Naming Convention"]
ViolationDetector <|-- det_15
class det_16["Go Organize Responsibility"]
ViolationDetector <|-- det_16
class det_17["Go Package Naming"]
ViolationDetector <|-- det_17
class det_18["Go Package State"]
ViolationDetector <|-- det_18
class det_19["Go Simplicity"]
ViolationDetector <|-- det_19
class det_20["Go Test Presence"]
ViolationDetector <|-- det_20
class det_21["Go Zero Value"]
ViolationDetector <|-- det_21
Analysis Pipeline
%%{init: {"theme": "base", "flowchart": {"useMaxWidth": false, "htmlLabels": true, "nodeSpacing": 50, "rankSpacing": 70}}}%%
flowchart TD
Source(["Source Code"]) --> Parse["Parse & Tokenize"]
Parse --> Metrics["Compute Metrics"]
Metrics --> Pipeline{"21 Detectors"}
Pipeline --> Collect["Aggregate Violations"]
Collect --> Result(["AnalysisResult<br/>20 principles"])
Analysis States
%%{init: {"theme": "base"}}%%
stateDiagram-v2
[*] --> Ready
Ready --> Parsing : analyze(code)
Parsing --> Computing : AST ready
Computing --> Detecting : metrics ready
Detecting --> Reporting : 21 detectors run
Reporting --> [*] : AnalysisResult
Parsing --> Reporting : parse error (best-effort)
Configuration¶
languages:
go:
enabled: true
pipeline:
- type: go_context_usage
require_context: True
- type: go_defer_usage
detect_defer_in_loop: True
detect_missing_defer: True
- type: go_embedding_depth
max_embedding_depth: 2
- type: go_error_handling
max_ignored_errors: 0
- type: go_interface_size
max_interface_methods: 3
- type: go_moderation
max_goroutine_spawns: 5
- type: go_naming_convention
detect_long_names: True
- type: go_simplicity
max_nesting_depth: 3
max_function_lines: 50
See Also¶
- Rust — Complementary systems language with ownership model
- Configuration — Per-language pipeline overrides
- Understanding Violations — Severity scale reference