| name | golang-gocognit |
| description | Use when Go code is hard to follow, deeply nested, branch-heavy, or when analyzing, reviewing, or refactoring functions with high cognitive complexity — running gocognit, interpreting diagnostic output, or setting complexity thresholds in CI/CD. |
| metadata | {"author":"d.horkhover","version":"1.1.0"} |
gocognit — Go Cognitive Complexity Analyzer
Overview
gocognit measures cognitive complexity: how hard a function is for a human (or AI) to understand
intuitively. Unlike cyclomatic complexity which counts decision paths, cognitive complexity penalizes
deeply nested control flow and rewards idiomatic Go patterns like switch over if/else chains.
Key insight for AI agents: The -d (diagnostic) flag exposes the exact increments that raised
a function's score — giving a structured, line-by-line map of why code is complex, not just how much.
When to Use
- Go code that is hard to follow, deeply nested, or branch-heavy — even without a score yet
- Deciding where to focus refactoring on legacy or unfamiliar code (readability hotspot discovery)
- Auditing a codebase for complex functions before a code review or architectural review
- Setting a CI gate (
-over N) to prevent complexity regressions
- Comparing two implementations to confirm a refactor actually reduced complexity
- Reviewing a complex PR — validate that new code is not introducing cognitive debt
When NOT to Use
- Cyclomatic complexity analysis — use
gocyclo instead (different metric, different scores)
- Non-Go codebases — tool is Go-only
- Counting test coverage or detecting bugs —
gocognit is a readability tool, not a correctness tool
- Tiny changesets (a 3-line diff rarely benefits from a full complexity audit)
- Generated code files — mark with
//gocognit:ignore or use -ignore flag instead
Default Agent Workflow
Run this as a starting point for any complexity audit:
gocognit -json -d -over 15 ./...
gocognit -json -over 15 ./...
gocognit -avg ./...
Loop: inspect top offenders → open file at Pos.Line → apply one refactor → rerun → compare score.
Stop when -over 15 produces no output.
Complexity Score Guide
| Score | Interpretation | Typical action |
|---|
| 0–9 | Simple | No action needed |
| 10–15 | Moderate | Consider splitting if function has multiple responsibilities |
| 16–25 | High | Refactor — extract helpers, flatten nesting, use early returns |
| 25+ | Very high | High-priority refactor target; high review risk |
Common CI threshold: 15. SonarSource's original paper recommends investigating functions above 15.
Quick Reference
gocognit ./...
gocognit main.go
gocognit -over 15 ./...
gocognit -top 10 ./...
gocognit -avg ./...
gocognit -test ./...
gocognit -json ./...
gocognit -json -d ./...
gocognit -ignore "_generated|vendor" ./...
gocognit -f "{{.Complexity}}\t{{.FuncName}}\t{{.Pos}}" ./...
Scope note: gocognit . scans only the current directory's .go files (non-recursive).
Use ./... to scan all packages in the module tree. Prefer ./... for project-wide audits.
CLI Flags
| Flag | Description |
|---|
-over N | Show functions with complexity > N; exits 1 if output non-empty |
-top N | Show the N most complex functions |
-avg | Print average complexity across all functions |
-test | Include _test.go files (excluded by default) |
-json | Output as JSON array |
-d | Enable diagnostic output (per-increment details) |
-f format | Custom Go template format string |
-ignore expr | Skip files whose paths match this regexp |
Default output format: {{.Complexity}} {{.PkgName}} {{.FuncName}} {{.Pos}}
Understanding JSON Output
Top-level result fields
| Field | Type | Meaning |
|---|
PkgName | string | Go package name |
FuncName | string | Function or method name |
Complexity | int | Total cognitive complexity score |
Pos | object | Location of the function declaration |
Pos.Filename | string | File path |
Pos.Line | int | Line number — open file here to start reviewing |
Diagnostics | array | Present only when -d is used; see below |
Diagnostic fields (with -d)
| Field | Meaning |
|---|
Inc | Points added by this construct (1 base + nesting level) |
Nesting | Nesting depth at this increment |
Text | Construct name: if, for, switch, continue LABEL, etc. |
Pos.Line | Source line — jump directly to the problem |
Example diagnostic output
$ gocognit -json -d -over 15 ./pkg/...
[
{
"PkgName": "parser",
"FuncName": "ParseExpr",
"Complexity": 22,
"Pos": {
"Filename": "parser.go",
"Line": 45
},
"Diagnostics": [
{
"Inc": 1,
"Nesting": 0,
"Text": "for",
"Pos": {
"Line": 50
}
},
{
"Inc": 2,
"Nesting":
Reading diagnostic output as an AI agent:
- Sort by
Inc descending — entries with the highest Inc are deepest nesting; extracting them yields the largest score reduction per change
- Many
Inc=1 at Nesting=0 → sequential branching; check for multiple responsibilities — consider splitting into smaller functions
Inc=3+ entries → deep nesting; flatten with early returns or extract a helper function
continue LABEL / break LABEL entries → complex loop control; often extractable to a named helper with a clear return value
Increment Rules (Go-specific)
These constructs add to cognitive complexity:
| Construct | Increment |
|---|
if, else if, else | +1 each |
switch, select | +1 (not per case — the whole block is +1) |
for | +1 |
goto LABEL, break LABEL, continue LABEL | +1 each |
Sequence of binary logical operators (&&, ||) | +1 per sequence |
| Each method in a recursion cycle | +1 |
| Nesting | +depth for if, switch, select, for |
Key examples:
switch number { case 1: ... case 2: ... } → 1 total (one increment for the switch)
if ... else if ... else if ... → 3 (one per branch)
Responding to Diagnostic Patterns
Pattern: high Inc at deep nesting → flatten with early returns
func Process(s *State) error {
if s != nil {
if s.Ready {
if err := s.Run(); err != nil {
return err
}
}
}
return nil
}
func Process(s *State) error {
if s == nil {
return nil
}
if !s.Ready {
return nil
}
if err := s.Run(); err != nil {
return err
}
return nil
}
Pattern: if/else if chain → switch (fewer branches, lower score)
func Label(n int) string {
if n == 1 {
return "one"
} else if n == 2 {
return "two"
} else if n == 3 {
return "three"
} else {
return "many"
}
}
func Label(n int) string {
switch n {
case 1:
return "one"
case 2:
return "two"
case 3:
return "three"
default:
return "many"
}
}
Compare before/after: Always rerun gocognit -json -over 15 ./... after refactoring to confirm
the score dropped — and that you haven't accidentally shifted complexity into a helper function.
Ignoring Functions
Skip functions that are intentionally complex (generated parsers, large state machines, etc.):
func GeneratedParser() {
}
Or ignore whole files by path pattern:
gocognit -ignore "_generated|pb.go" ./...
CI Integration
- name: Check cognitive complexity
run: |
go install github.com/uudashr/gocognit/cmd/gocognit@latest
gocognit -over 15 ./...
.PHONY: complexity
complexity:
gocognit -over 15 -avg ./...
Common Mistakes
| Mistake | Reality |
|---|
| Treating score as a bug signal | gocognit measures readability, not correctness. A score of 30 means "hard to follow" — not "broken". |
| Optimizing the number, not the code | If a refactor reduces the score but makes the logic harder to follow, it's the wrong refactor. |
Scanning with . and assuming full coverage | . is non-recursive — it only checks the current directory. Use ./... for module-wide analysis. |
| Forgetting generated/vendored files | Generated code is legitimately complex; use -ignore "_generated|vendor" to exclude it. |
| Refactoring before checking diagnostics | Always run -d first — it tells you which lines to change, not just that the score is high. |
| Comparing gocognit scores to gocyclo scores | Cognitive and cyclomatic complexity are different metrics with different scales; don't mix thresholds. |
What gocognit Does NOT Tell You
- Correctness: A function with score 2 can still be buggy.
- Performance: Low complexity does not mean fast code.
- Test coverage: A simple function can be completely untested.
- Architecture: gocognit operates at function granularity; it cannot identify problematic module boundaries.
Use it alongside go vet, golangci-lint, and test coverage tools — not as a replacement.
References
Source: DimkaGorhover/agent-skills — distributed by TomeVault.