Provides Go patterns for type-first development with custom types, interfaces, functional options, and error handling. Must use when reading or writing Go files.
83%
Does it follow best practices?
Impact
91%
1.10xAverage score across 3 eval scenarios
—
The risk profile of this skill
Types define the contract before implementation. Follow this workflow:
Use Go's type system to prevent invalid states at compile time.
Structs for domain models:
// Define the data model first
type User struct {
ID UserID
Email string
Name string
CreatedAt time.Time
}
type CreateUserRequest struct {
Email string
Name string
}
// Functions follow from the types
func CreateUser(req CreateUserRequest) (*User, error) {
// implementation
}Custom types for domain primitives:
// Distinct types prevent mixing up IDs
type UserID string
type OrderID string
func GetUser(id UserID) (*User, error) {
// Compiler prevents passing OrderID here
}
func NewUserID(raw string) UserID {
return UserID(raw)
}
// Methods attach behavior to the type
func (id UserID) String() string {
return string(id)
}Interfaces for behavior contracts:
// Define what you need, not what you have
type Reader interface {
Read(p []byte) (n int, err error)
}
type UserRepository interface {
GetByID(ctx context.Context, id UserID) (*User, error)
Save(ctx context.Context, user *User) error
}
// Accept interfaces, return structs
func ProcessInput(r Reader) ([]byte, error) {
return io.ReadAll(r)
}Enums with iota:
type Status int
const (
StatusActive Status = iota + 1
StatusInactive
StatusPending
)
func (s Status) String() string {
switch s {
case StatusActive:
return "active"
case StatusInactive:
return "inactive"
case StatusPending:
return "pending"
default:
return fmt.Sprintf("Status(%d)", s)
}
}
// Exhaustive handling in switch
func ProcessStatus(s Status) (string, error) {
switch s {
case StatusActive:
return "processing", nil
case StatusInactive:
return "skipped", nil
case StatusPending:
return "waiting", nil
default:
return "", fmt.Errorf("unhandled status: %v", s)
}
}Functional options for flexible construction:
type ServerOption func(*Server)
func WithPort(port int) ServerOption {
return func(s *Server) {
s.port = port
}
}
func WithTimeout(d time.Duration) ServerOption {
return func(s *Server) {
s.timeout = d
}
}
func NewServer(opts ...ServerOption) *Server {
s := &Server{
port: 8080, // sensible defaults
timeout: 30 * time.Second,
}
for _, opt := range opts {
opt(s)
}
return s
}
// Usage: NewServer(WithPort(3000), WithTimeout(time.Minute))Embed for composition:
type Timestamps struct {
CreatedAt time.Time
UpdatedAt time.Time
}
type User struct {
Timestamps // embedded - User has CreatedAt, UpdatedAt
ID UserID
Email string
}Prefer smaller files within packages: one type or concern per file. Split when a file handles multiple unrelated types or exceeds ~300 lines. Keep tests in _test.go files alongside implementation. Package boundaries define the API; internal organization is flexible.
sort.Slice, iterators).fmt.Errorf and %w for wrapping. This preserves the error chain for debugging.switch statements; include a default case that returns an error. Exhaustive handling prevents silent bugs.context.Context to external calls with explicit timeouts. Runaway requests cause cascading failures.panic for truly unrecoverable situations; prefer returning errors. Panics crash the program.Explicit failure for unimplemented logic:
func buildWidget(widgetType string) (*Widget, error) {
return nil, fmt.Errorf("buildWidget not implemented for type: %s", widgetType)
}Wrap errors with context to preserve the chain:
out, err := client.Do(ctx, req)
if err != nil {
return nil, fmt.Errorf("fetch widget failed: %w", err)
}
return out, nilExhaustive switch with default error:
func processStatus(status string) (string, error) {
switch status {
case "active":
return "processing", nil
case "inactive":
return "skipped", nil
default:
return "", fmt.Errorf("unhandled status: %s", status)
}
}Structured logging with slog:
import "log/slog"
var log = slog.With("component", "widgets")
func createWidget(name string) (*Widget, error) {
log.Debug("creating widget", "name", name)
widget := &Widget{Name: name}
log.Debug("created widget", "id", widget.ID)
return widget, nil
}os.Getenv scattered throughout code.Typed config struct:
type Config struct {
Port int
DatabaseURL string
APIKey string
Env string
}
func LoadConfig() (*Config, error) {
dbURL := os.Getenv("DATABASE_URL")
if dbURL == "" {
return nil, fmt.Errorf("DATABASE_URL is required")
}
apiKey := os.Getenv("API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("API_KEY is required")
}
port := 3000
if p := os.Getenv("PORT"); p != "" {
var err error
port, err = strconv.Atoi(p)
if err != nil {
return nil, fmt.Errorf("invalid PORT: %w", err)
}
}
return &Config{
Port: port,
DatabaseURL: dbURL,
APIKey: apiKey,
Env: getEnvOrDefault("ENV", "development"),
}, nil
}NeverSight/learn-skills.dev
a62ed51
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.