From gosdk
Use when reviewing Go code, generating new Go code, refactoring existing Go codebases, or creating any new Go file/package. Triggers on requests like "review this Go code", "refactor this Go file", "is this Go idiomatic", "apply SOLID". Keywords - simplicity, scalability, SOLID, package structure, error handling, context propagation, dependency injection.
How this skill is triggered — by the user, by Claude, or both
Slash command
/gosdk:golang-code-qualityThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
A senior Go code quality review workflow, with a focus on **simplicity (簡潔性)** and **scalability (可擴展性)** — NOT testing. Guiding philosophy: code should be obvious to read, hard to misuse, and easy to extend without modification.
A senior Go code quality review workflow, with a focus on simplicity (簡潔性) and scalability (可擴展性) — NOT testing. Guiding philosophy: code should be obvious to read, hard to misuse, and easy to extend without modification.
"Clear is better than clever." — Rob Pike
Two non-negotiable goals when reviewing or writing Go:
If a piece of code violates either, flag it.
Go has no inheritance — apply SOLID through composition, small interfaces, and package boundaries.
UserService that handles auth, billing, and email notifications.auth.Service, billing.Service, notify.Service.if paymentType == "stripe" { ... } else if paymentType == "paypal" { ... }type PaymentProvider interface { Charge(ctx, amount) error } — add new providers without touching existing code.Storage.Get returns nil, nil for missing keys; another panics.ErrNotFound in the interface's package and require all implementations to use them.io.Reader and io.Writer are the gold standard.type UserRepo interface { Get; Create; Update; Delete; Search; Export; Import } consumed by a handler that only needs Get.type userGetter interface { Get(ctx, id) (*User, error) } locally.func NewHandler() *Handler { return &Handler{db: postgres.New()} }func NewHandler(repo UserRepo, logger Logger) *Handler { ... }Enforce this layered architecture. Dependencies flow downward only — handler → service → model. No upward or sideways imports between siblings.
| Package | Responsibility | What MUST NOT be there |
|---|---|---|
config/ | Load configuration. Prefer config.Default() from github.com/bizshuk/gosdk, falling back to viper if not supported. Initialize external clients (DB, Redis, S3, HTTP clients). | Business logic, domain types |
handler/ | Aggregate domain logic. Orchestrate calls to services. All business rules live here. | Direct DB calls, raw HTTP calls, config loading |
service/ (or svc/) | Wrap external services. Basic error handling, retries, timeout enforcement. Thin adapters only. | Domain logic, business rules |
model/ | Data structures + conversions between them (DTO ↔ domain ↔ persistence). | Behavior beyond conversion, I/O |
validation/ | Common validation rules. Each validator must have an explicit, descriptive name. | Business decisions, side effects |
utils/ | Non-business, non-functional concerns: metrics emission, callbacks, helper closures. | Anything domain-specific |
Validators must be named for what they check, not where they're used:
validateInput, checkUser, validateValidateEmailFormat, ValidateAgeRange, ValidatePasswordComplexityservice/ import handler/? → Violation, flag it.model/ have any I/O? → Violation, models are pure data.service/? → Move to handler/.utils/ becoming a junk drawer? → Split into named packages (metrics/, callback/).Enforce these rules strictly:
userauth not user_auth or userAuth.user not users. (Use model (singular) as the default package for domain models, unless there are more than 30 models, in which case they must be split into domain-specific packages).util, common, base, helpers, misc, shared. These signal a missing abstraction.user, the type is User not user.UserModel. Callers write user.New() not user.NewUser(). (Exception: the model package can contain a model.User or similar domain structs).http not httpfunctions.utils/ per the user's convention: it stays narrowly for non-business cross-cutting concerns (metrics, callbacks). If it grows beyond that, split it.panic only for truly unrecoverable programmer errors (nil pointer in init, etc.).fmt.Errorf("doing X: %w", err). The %w verb preserves the chain for errors.Is / errors.As.var ErrNotFound = errors.New("not found").type ValidationError struct { Field string; Reason string }._ unless you've written a comment explaining why.// ❌ Bad
result, err := svc.Fetch(ctx, id)
log.Printf("fetch failed: %v", err)
return nil, err
// ✅ Good — at service layer, just wrap
result, err := svc.Fetch(ctx, id)
if err != nil {
return nil, fmt.Errorf("fetch user %s: %w", id, err)
}
context.Context) Conventionsctx is always the first parameter of any function that does I/O, blocks, or might be cancelled.
Never store ctx in a struct field. Pass it through explicitly.
Never pass nil context. Use context.TODO() if you genuinely don't have one yet (and add a comment).
context.Value is for request-scoped data only (request ID, auth user, trace span). Not for optional parameters.
Use typed keys for context values to avoid collisions:
type ctxKey struct{ name string }
var userKey = ctxKey{"user"}
Always honor cancellation: check ctx.Done() in loops, pass ctx down to every downstream call.
Go DI is constructor injection. No frameworks required.
type Handler struct {
repo UserRepo
mailer Mailer
logger Logger
}
func NewHandler(repo UserRepo, mailer Mailer, logger Logger) *Handler {
return &Handler{repo: repo, mailer: mailer, logger: logger}
}
type HandlerOptions struct {
Repo UserRepo
Mailer Mailer
Logger Logger
Metrics MetricsEmitter // optional
}
func NewHandler(opts HandlerOptions) *Handler { ... }
func NewHandler(repo UserRepo, opts ...Option) *Handler { ... }
main.go or a single wire.go / bootstrap.go. This is the only place concrete types meet.container.Get("UserService").When asked to review Go code:
git diff --name-only (if in a repo) to scope changes, otherwise read the file/package the user pointed to.🔴 Critical (must fix)
service/ or model/_ = err) without justificationpanic used for recoverable conditions🟡 Warnings (should fix)
util, common, helpers)user.UserService)%w wrappingSCREAMING_SNAKE_CASE🟢 Suggestions (consider)
if err != nil blocks that could become a helperAlways structure findings like this:
🔴 CRITICAL — handler/user.go:42
Issue: Direct DB query in handler bypasses service layer
Why: Violates layered architecture — handler should orchestrate, not access storage
Fix:
// before
rows, err := h.db.Query("SELECT ...")
// after
user, err := h.userSvc.GetByID(ctx, id)
Be specific, cite line numbers, and show the diff. Never say "this could be improved" without showing how.
model/ → service/ → handler/ → wiring in main.go.// NewHandler returns ...).Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
npx claudepluginhub bizshuk/gosdk