hunter-party-go/solid-hunter-go/SKILL.md
Audit Go code for design principle violations — god packages, rigid extension points, broken interface contracts, fat interfaces, and concrete dependency chains. Adapted from SOLID for Go's composition-over-inheritance model. Use when: reviewing package structure, preparing for extension with new variants, reducing coupling between packages, or improving testability.
npx skillsauth add skyosev/agent-skills solid-hunter-goInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Audit Go code for design principle violations at the package, struct, and interface level — places where responsibilities are misassigned, extension requires modification, interface contracts are broken, interfaces force unused dependencies, or structs are wired to concrete implementations. The goal: packages have clear responsibilities, extend without modification, honor interface contracts, expose cohesive interfaces, and depend on abstractions.
Go does not have classes or inheritance, so SOLID applies differently than in object-oriented languages. SRP applies to packages and structs. OCP applies through interfaces and composition. LSP applies to interface implementations. ISP is already a Go strength ("accept interfaces, return structs") — but fat interfaces still occur. DIP applies through constructor injection of interfaces.
Single Responsibility (SRP). A package should have one reason to change — one domain concern. When a package serves multiple actors (e.g., persistence AND HTTP handling AND business rules), changes for one concern risk breaking another. For structs: a struct with 10+ methods spanning different concerns is a god struct. This is about responsibility assignment, not function count.
Open/Closed (OCP). A package should be open for extension but closed for modification. Adding a new variant or behavior should not require editing existing code. In Go, this is achieved through interfaces, function types, and composition — not inheritance hierarchies.
Liskov Substitution (LSP). Any type implementing an interface must fulfill the full contract implied by that interface. If a type implements an interface but panics on certain methods or returns hardcoded zero values, the contract is broken. Go's implicit interface satisfaction makes this easy to violate — a type can satisfy an interface syntactically without honoring its semantics.
Interface Segregation (ISP). No consumer should be forced to depend on methods it does not use. Go's idiom of
small, focused interfaces (io.Reader, io.Writer, fmt.Stringer) is the standard. Fat interfaces that bundle
unrelated capabilities force implementors into stub methods and callers into unnecessary coupling.
Dependency Inversion (DIP). High-level policy should not depend on low-level detail — both should depend on
abstractions (interfaces). When a struct creates its own dependencies with &ConcreteService{} or accepts concrete
types in its constructor, it becomes untestable and unchangeable.
SOLID principles are guidelines for managing change, not rules to apply universally. Do not flag:
main(), RegisterEndpoints,
cmd/ setup functions) is correct wiring, not a DIP violation. DIP applies to business logic and domain packages,
not to the place where dependencies are assembled. Do not recommend interfaces or DI containers for composition roots
with a single implementation per dependency.Packages or structs that accumulate responsibilities from multiple domain concerns.
Signals:
user.go, email.go, billing.go in same package)New*) with 5+ dependencies (high fan-in = multiple reasons to change)utils, helpers, common, misc, service with broad scopeAction: Identify distinct responsibilities. Extract each into a focused package. The original package becomes a coordinator that delegates, or is dissolved entirely.
Code that must be modified — not extended — when a new variant, strategy, or behavior is added.
Signals:
switch/if-else chains on a type discriminant that appear in 3+ locations across the codebaseif typ == "email" { sendEmail() } else if typ == "sms" { sendSMS() })switch statements and no registration mechanismAction: Introduce an interface for the variant behavior, implement per variant, dispatch polymorphically via a map or registry. The core package should not know about individual variants.
Types that implement an interface syntactically but violate its semantic contract.
Signals:
Action: If the type genuinely cannot support the interface contract, it shouldn't implement it. Use composition or a narrower interface instead.
Interfaces that bundle unrelated capabilities, forcing implementors to depend on methods they don't use.
Signals:
Action: Split into role-specific interfaces. Go's idiom: define interfaces where they are consumed, not where they
are implemented. io.Reader and io.Writer exist separately for a reason.
Structs that directly depend on concrete implementations instead of interfaces.
Signals:
&ConcreteService{} inside a constructor or method (not a composition root)var db = sql.Open(...) used directly in business logicAction: Define a small interface in the consumer package. Accept the interface in the constructor. Wire the concrete
implementation at the composition root (main, cmd/, wire setup).
Note: Direct instantiation of value types, slices, maps, and builders is fine — DIP applies to service dependencies that represent behavior, not data construction.
main/master)BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)
SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)
Constrain all subsequent scans to the resolved surface.main(), cmd/, wire/fx setup).EXCLUDE='--glob !**/*_test.go --glob !**/vendor/** --glob !**/testdata/**'
# Structs (starting point for SRP, DIP analysis)
rg 'type\s+\w+\s+struct' --type go $EXCLUDE
# Interfaces (starting point for ISP analysis)
rg 'type\s+\w+\s+interface' --type go $EXCLUDE
# Embedding (composition relationships)
rg '^\s+\w+\.\w+$|^\s+\*?\w+$' --type go $EXCLUDE
# Direct instantiation in non-factory code (DIP signal)
rg '&\w+\{|new\(\w+\)' --type go $EXCLUDE
# Type assertions/switches (LSP signal — consumer sniffing implementations)
rg '\.\(\w+\)|switch\s+\w+\.\(type\)' --type go $EXCLUDE
# switch chains on discriminants (OCP signal)
rg 'switch\s+\w+' --type go $EXCLUDE
# Not-implemented panics (LSP signal)
rg -i 'not.?implemented|unsupported|panic\(' --type go $EXCLUDE
For each struct found in Phase 2:
For each interface:
For each interface implementation:
Save as YYYY-MM-DD-solid-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs folder exists).
# SOLID Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
- Architecture style: {struct-based / mixed / functional (limited findings)}
## SRP Violations — God Packages/Structs
| # | Package/Struct | Location | Responsibilities | Dependencies | Action |
| - | -------------- | -------- | ---------------- | ------------ | ------ |
| 1 | `OrderService` | file:line | order CRUD + email + invoicing | 7 injected | Split into 3 focused packages |
## OCP Violations — Rigid Extension Points
| # | Location | Pattern | Occurrences | Action |
| - | -------- | ------- | ----------- | ------ |
| 1 | file:line | `switch typ` with 5 variants in 4 files | 4 | Extract strategy interface |
## LSP Violations — Broken Interface Contracts
| # | Type | Interface | Location | Violation | Action |
| - | ---- | --------- | -------- | --------- | ------ |
| 1 | `ReadOnlyStore` | `Store` | file:line | `Save()` panics | Use narrower interface |
## ISP Violations — Fat Interfaces
| # | Interface | Location | Methods | Avg Used by Consumers | Action |
| - | --------- | -------- | ------- | --------------------- | ------ |
| 1 | `DataStore` | file:line | 12 | 3 | Split into `Reader` + `Writer` + `Admin` |
## DIP Violations — Concrete Dependencies
| # | Struct | Location | Concrete Dependency | Action |
| - | ------ | -------- | ------------------- | ------ |
| 1 | `UserService` | file:line | `&EmailClient{}` | Inject via constructor interface |
## Recommendations (Priority Order)
1. **Must-fix**: {god packages with 5+ responsibilities, broken interface contracts causing panics}
2. **Should-fix**: {rigid extension points touched by every new variant, fat interfaces with no-op methods}
3. **Consider**: {concrete dependency chains limiting testability, speculative interface splits}
file/path.go:line with the exact code.main(). Calibrate findings to Go's design philosophy, not Java/C# SOLID patterns.development
Transforms vague feature ideas into precise, codebase-grounded technical requirements. Use when requirements are ambiguous/incomplete, the user struggles to describe behavior, terminology is unclear, or multiple concepts are mixed. Output is a requirements spec—NOT an implementation plan.
tools
Audit TypeScript type definitions for design debt — duplicated shapes, missing derivations, over-engineered generics, under-constrained type parameters, reinvented utility types, and disorganized type architecture. Type structure and maintainability, not type enforcement. Use when: reviewing type definitions for maintainability, reducing type duplication, simplifying over-engineered type-level logic, or reorganizing type architecture after growth.
development
Audit TypeScript test code for quality gaps — missing coverage on critical paths, brittle tests coupled to implementation, over-mocking, assertion-free tests, missing edge cases, and duplicated test setup. Focuses on test effectiveness, not production code structure. Use when: reviewing TypeScript test suites for reliability, reducing false-positive test failures, improving coverage of critical business logic, or cleaning up test debt.
tools
Audit TypeScript class and interface design for SOLID violations — god classes, rigid extension points, broken substitutability, fat interfaces, and concrete dependency chains. Focuses on responsibility assignment and abstraction fitness. Use when: reviewing class hierarchies, preparing for extension with new variants, reducing coupling between services, or improving testability of class-heavy code.