go-architecture-review
Skill85 starsChanged 18 days agoMITView on GitHub
Review Go project architecture: package structure, dependency direction, layering, separation of concerns, domain modeling, and module boundaries. Use when reviewing architecture, designing package layout, evaluating dependency graphs, or refactoring monoliths into modules. Trigger examples: "review architecture", "package structure", "project layout", "dependency direction", "clean architecture Go", "module boundaries". Not for: code-level style (go-coding-standards), endpoint design (go-api-design).
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 5 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 30
Read a reference file only when the summary below is not enough.
- Distinctive: line 67
Run independent passes: (a) layout vs section 1, (b) dependency direction vs section 2, (c) wiring and config vs sections 3+5, (d) package …
- Distinctive: line 70
If your environment supports delegating work to parallel sub-agents or tasks, assign each pass to one.
- Very specific: line 130
Read config, build the logger, open connections, construct stores → services → handlers in that order, mount routes, run the server.
- Very specific: line 134
Avoid dependency injection frameworks.
- Very specific: line 135
If wiring gets complex, use Google's wire for compile-time DI code generation.
- Distinctive: line 182
All config from environment variables (12-factor).
- Very specific: line 184
No config scattered across packages — centralize in internal/config.
- Distinctive: line 185
Never hardcode values. Not even "just for now."
- Very specific: line 189
Avoid init(). It runs implicitly, makes testing harder, and creates hidden dependencies.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Go Architecture Review
Good architecture makes the next change easy. Bad architecture makes every change scary.
Detailed reference material, loaded on demand:
references/layout-and-wiring.md— the full reference directory tree and a composition-rootmain.go.
Read a reference file only when the summary below is not enough.
Operating Modes
Pick the mode that matches the request before starting:
- Layout review (default) — assess an existing codebase against the sections below and report violations with severity.
- Refactor plan — same assessment, but the deliverable is an ordered migration plan (smallest safe steps first), not just findings.
- New service consultation — asked "how should I structure X": apply sections 1-3 as prescriptive guidance instead of review checks.
Match the Architecture to the Problem
Judge a codebase against the structure its problem needs, not the most elaborate one available. Over-architecture is a finding too.
| Situation | Fitting structure |
| CLI, small service, one data store, little business logic | Flat main + a few packages; no layers for their own sake |
| Typical service: HTTP/gRPC in front, a database behind | Handler → service → store, interfaces at the consumer (sections 1-2) |
| Several adapters (HTTP, gRPC, queue, CLI) over the same rules, or infrastructure expected to change | Ports and adapters: a pure core, adapters on the edge |
| Large domain, several teams, rules that differ by sub-domain | Bounded contexts as top-level packages, each layered inside, talking through explicit APIs or events |
Signs of too much: interfaces with one implementation per layer, mapping structs copied unchanged through three layers, a "domain" package with no behavior. Signs of too little: SQL in handlers, business rules spread across transport code, packages that import each other in both directions.
Auditing Large Codebases
For repositories with many packages, build the dependency picture before judging it:
- Map the module:
go list ./...for packages, then import statements to trace dependency direction. - Run independent passes: (a) layout vs section 1, (b) dependency direction vs section 2, (c) wiring and config vs sections 3+5, (d) package design vs section 4.
- If your environment supports delegating work to parallel sub-agents or tasks, assign each pass to one; synthesize at the end — dependency findings often explain layout findings.
- Cite package paths and
file.go:linein every finding.
1. Standard Project Layout
The reference layout: cmd/<binary>/main.go per binary, project-private
code under internal/ (domain, service, store/<backend>,
handler, config), pkg/ only for genuinely reusable code, plus
api/ for OpenAPI/proto and migrations/. Full tree:
references/layout-and-wiring.md. For scaffolding a new project, the
go-project-layout skill owns the procedure.
Key Rules:
internal/enforces encapsulation at the compiler level. Use it aggressively.pkg/is for genuinely reusable packages. When in doubt, useinternal/.cmd/main packages should be thin — wire dependencies and callRun().- One
main.goper binary, minimal logic inside.
2. Dependency Direction
Dependencies MUST flow inward. Domain core has zero external dependencies:
handlers → services → domain ← stores
↓ ↓ ↓
(net/http) (pure Go) (database/sql)Rules:
domain/imports NOTHING from the project. Nostore, nohandler, noconfig.service/depends ondomain/types and interfaces, NOT on concrete stores.handler/depends onservice/interfaces.store/implements interfaces defined inservice/ordomain/.- Circular dependencies are a 🔴 BLOCKER. The compiler catches them, but design should prevent them.
// ✅ Good — service defines the interface it needs
// internal/service/user.go
type UserStore interface {
GetByID(ctx context.Context, id string) (*domain.User, error)
Create(ctx context.Context, user *domain.User) error
}
type UserService struct {
store UserStore // depends on interface, not postgres.Store
}
// internal/store/postgres/user.go
type Store struct { db *sql.DB }
// Implements service.UserStore without importing the service package
func (s *Store) GetByID(ctx context.Context, id string) (*domain.User, error) { ... }3. Main Package Wiring
main.go is the composition root. Wire everything here:
Read config, build the logger, open connections, construct stores →
services → handlers in that order, mount routes, run the server. No
business logic. Example: references/layout-and-wiring.md.
Avoid dependency injection frameworks. Go's explicit wiring is a feature.
If wiring gets complex, use Google's wire for compile-time DI code generation.
4. Package Design Principles
One package = one purpose
// ✅ Good — clear purpose
package orderservice // business rules for orders
package postgres // PostgreSQL data access
package httphandler // HTTP transport layer
// ❌ Bad — grab-bag packages
package utils // what ISN'T a util?
package common // everything and nothing
package models // types without behaviorAvoid package stuttering
// ❌ Bad — package name repeated in type
package user
type UserService struct{} // user.UserService
// ✅ Good
package user
type Service struct{} // user.ServicePackage cohesion over size
A package with 20 related files is better than 20 packages with 1 file each. Split packages when they have distinct responsibilities, not when they get big.
5. Configuration
type Config struct {
Addr string `env:"ADDR" envDefault:":8080"`
DatabaseURL string `env:"DATABASE_URL,required"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
Timeout time.Duration `env:"TIMEOUT" envDefault:"30s"`
}Rules:
- All config from environment variables (12-factor).
- Validate at startup, fail fast with clear messages.
- No config scattered across packages — centralize in
internal/config. - Never hardcode values. Not even "just for now."
6. Init Functions
Avoid init(). It runs implicitly, makes testing harder, and creates hidden dependencies.
// ❌ Bad — hidden side effects
func init() {
db, _ = sql.Open("postgres", os.Getenv("DB_URL"))
}
// ✅ Good — explicit initialization
func NewStore(dsn string) (*Store, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, fmt.Errorf("open db: %w", err)
}
return &Store{db: db}, nil
}Exception: registering drivers or codecs is acceptable in init():
func init() {
sql.Register("custom", &CustomDriver{})
}Architecture Review Checklist
- 🔴 No circular dependencies between packages
- 🔴 Domain types have zero infrastructure dependencies
- 🔴 No business logic in
cmd/main packages - 🔴 No
init()with side effects (DB connections, HTTP calls) - 🟡
internal/used for project-private packages - 🟡 Interfaces defined at the consumer, not the producer
- 🟡 Configuration centralized and validated at startup
- 🟡 Dependency direction flows inward (handlers → services → domain)
- 🟢 Package names are short, singular, descriptive
- 🟢 No
utils/,common/,helpers/packages - 🟢 Main package is a thin composition root
- 🟢 Structure is proportional to the domain — no layers or interfaces without a second reason to exist
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- rjansen/skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository38 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- .cursor/rules/go-skills.mdcCursor rule
- go-api-designSkill
- go-cliSkill
- go-dependency-injectionSkill
- go-design-patternsSkill
- go-graphqlSkill
- go-grpcSkill
- go-interface-designSkill
- go-openapiSkill
- go-project-layoutSkill
- go-code-reviewSkill
- go-coding-standardsSkill
- go-contextSkill
- go-data-structuresSkill
- go-documentationSkill
- go-error-handlingSkill
- go-modernizeSkill
- go-namingSkill
- go-databaseSkill
- go-concurrency-reviewSkill
- go-defensive-codingSkill
- go-observabilitySkill
- go-performance-reviewSkill
- go-security-auditSkill
- go-troubleshootingSkill
- go-test-qualitySkill
- go-test-table-drivenSkill
- git-commitSkill
- go-binary-sizeSkill
- go-build-releaseSkill
- go-ciSkill
- go-dependency-auditSkill
- go-refactoringSkill
- go-semantic-toolsSkill
- go-skills-routerSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

