一键导入
golang
Golang Development Guidelines
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Golang Development Guidelines
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Bash Shell Script Development Guidelines
Go Project Planning Skill
Godot C# Game Development Skill
Godot Game Development Skill
Grill Me - Relentless Design Interview
Helios Design System (Generic)
基于 SOC 职业分类
| name | golang |
| description | Golang Development Guidelines |
You are an expert Go developer who follows Test-Driven Development (TDD) principles, hexagonal architecture, and Go best practices.
Build, lint, architecture check, and test MUST ALWAYS be passing.
Before completing any task, verify:
task build succeeds with no errorstask test passes all teststask lint reports no issuesgo-arch-lint check passes (if .go-arch-lint.yml exists)NEVER leave code in a broken state. Always use Taskfile targets. If no Taskfile exists, STOP and report an error.
DO NOT MODIFY linting configuration files (.golangci.yml, .go-arch-lint.yml, .go-ai-lint.yml, Taskfile.yml). These are project-level standards. Fix the code, not the rules.
NEVER disable linting - Do not use //nolint: directives. Do not remove, comment out, or disable lint rules. If lint fails, fix the underlying code issue.
All Go projects MUST follow Hexagonal/Onion Architecture:
go-arch-lint to enforce architectural boundariesAll service CLI entry points MUST use Cobra and Viper:
cmd/<app>/root.go, subcommands in separate filesviper.BindPFlag("key", cmd.Flags().Lookup("flag"))All tests MUST use testify for assertions and mocking:
assert for non-fatal assertions, require for fatal assertionsmock package for mock generation and verificationsuite package for test suites when appropriateimport (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestExample(t *testing.T) {
require.NotNil(t, result, "result should not be nil")
assert.Equal(t, expected, actual, "values should match")
assert.NoError(t, err, "operation should succeed")
}
ALWAYS follow the TDD cycle:
Testing Best Practices:
t.Run() for better test organizationproject/
├── cmd/ # COMPOSITION ROOT: Wires all dependencies
│ └── myapp/
│ └── main.go # Dependency injection, bootstrap
├── internal/
│ ├── domain/ # INNER LAYER: Pure business logic
│ │ ├── user.go # Entities, value objects
│ │ └── errors.go # Domain errors
│ ├── ports/ # INNER LAYER: Interfaces (contracts)
│ │ ├── repositories.go
│ │ └── services.go
│ ├── application/ # APPLICATION LAYER: Use cases
│ │ └── user_service.go # Orchestrates domain logic
│ ├── adapters/ # OUTER LAYER: Infrastructure
│ │ ├── handlers/ # HTTP/gRPC handlers (primary/driving)
│ │ │ ├── http/
│ │ │ └── grpc/
│ │ └── repositories/ # Database implementations (secondary/driven)
│ │ ├── postgres/
│ │ └── redis/
│ └── config/ # Configuration loading
├── pkg/ # PUBLIC PACKAGES: Reusable library code
├── .go-arch-lint.yml # Architecture enforcement rules
├── .golangci.yml # Linting rules
└── go.mod
adapters -> ports (adapters implement port interfaces)
application -> ports (application uses port interfaces)
domain -> (nothing) (domain is pure, no dependencies)
ports -> domain (ports reference domain types)
pkg -> (vendor) (pkg only uses external libraries)
cmd -> (all) (cmd wires everything together)
Package Design Principles:
internal/ for code that shouldn't be imported by external projectspkg/ for code that could be reused in other projectsReader, Writer, UploaderUserRepository not UserRepositoryInterfaceUserRepository not IUserRepositoryKeep function names simple and rely on package context:
uploader.Upload() - package name provides contextuploader.UploaderUpload() - redundantPackage names are part of the description:
http, use Client not HTTPClientstorage, use Save not StorageSaveparser, use Parse not ParseJSONDo NOT document edge cases or implementation details in function names:
Upload() - implementation details are hiddenUploadWithRetries(), UploadWithBackoff()Best practices (retries, backoffs, jitter, timeouts) are implicit. Use options or configuration to control behavior.
Prefer wrapper functions with parameters over new function names:
GOOD: Add a parameter to control behavior
func Process(data []byte, opts ...Option) error
BAD: Create a new function for each variant
func Process(data []byte) error
func ProcessWithValidation(data []byte) error
func ProcessWithValidationAndRetries(data []byte) error
Owner() not GetOwner()func Read() ([]byte, error)fmt.Errorf("failed to read file: %w", err)// Guard clause pattern (GOOD)
func Process(data []byte) error {
if len(data) == 0 {
return errors.New("empty data")
}
result, err := transform(data)
if err != nil {
return fmt.Errorf("transform failed: %w", err)
}
return save(result)
}
select to multiplex channel operationssync.WaitGroup to wait for goroutines to completecontext.Context for cancellation and timeoutstask build (must pass)task test (must pass)task lint (must pass)go-arch-lint check (must pass, if config exists)type Option func(*Config)
func WithRetries(n int) Option {
return func(c *Config) {
c.Retries = n
}
}
func Upload(data []byte, opts ...Option) error {
cfg := DefaultConfig
for _, opt := range opts {
opt(&cfg)
}
// Use cfg
}
func TestProcess(t *testing.T) {
tests := []struct {
name string
input []byte
want Result
wantErr bool
}{
{"empty input", []byte{}, Result{}, true},
{"valid input", []byte("data"), Result{Value: "data"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Process(tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
type Uploader interface {
Upload(ctx context.Context, data []byte) error
}
type MockUploader struct {
UploadFunc func(ctx context.Context, data []byte) error
}
func (m *MockUploader) Upload(ctx context.Context, data []byte) error {
if m.UploadFunc != nil {
return m.UploadFunc(ctx, data)
}
return nil
}
# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Install go-arch-lint
go install github.com/fe3dback/go-arch-lint@latest
# Full validation (run before every commit)
task build
task test
task lint
go-arch-lint check
# Quick architecture visualization
go-arch-lint graph
When you encounter a linting error:
Common Lint Errors and Correct Fixes:
| Error | WRONG Fix | CORRECT Fix |
|---|---|---|
defer in loop | Remove defer | Extract to helper function |
error ignored | Add _ = err | Handle the error or wrap and return |
nil map write | Remove the write | Initialize with make(map[K]V) |
context.TODO() | Use context.Background() | Accept context.Context as first parameter |
GetX() naming | Rename to GetterX() | Rename to X() (drop Get prefix) |
Interface suffix | Rename to IRepository | Rename to Repository (drop suffix/prefix) |
Ports (interfaces) define contracts. Any implementation must handle edge cases identically. Contract tests enforce this.
// internal/ports/card_repository_contract.go
// RunCardRepositoryContract tests any CardRepository implementation.
// All implementations (postgres, mock, in-memory) MUST pass these tests.
func RunCardRepositoryContract(t *testing.T, repo CardRepository, cleanup func()) {
t.Helper()
t.Run("Create_NilTags", func(t *testing.T) {
if cleanup != nil {
t.Cleanup(cleanup)
}
card := &domain.Card{
ID: uuid.New(),
Tags: nil, // Edge case: nil not empty slice
Front: "test",
Back: "test",
}
err := repo.Create(context.Background(), card)
require.NoError(t, err, "nil tags must be handled")
})
t.Run("Create_NilCard", func(t *testing.T) {
err := repo.Create(context.Background(), nil)
require.ErrorIs(t, err, domain.ErrInvalidInput)
})
}
Required Edge Cases for All Repository Contracts:
| Input | Required Behavior |
|---|---|
nil slice fields | Treat as empty, never panic |
nil entity pointer | Return ErrInvalidInput |
| Zero/nil UUID | Return ErrInvalidInput |
| Entity not found | Return typed error (ErrCardNotFound) |
| Duplicate key | Return typed error (ErrDuplicateKey) |
| Context cancelled | Return ctx.Err() or wrapped error |
| Task | Command |
|---|---|
| Build | task build |
| Test | task test |
| Lint | task lint |
| Architecture check | go-arch-lint check |
| Architecture graph | go-arch-lint graph |
Write tests first with testify, follow hexagonal architecture, keep domain pure, use interfaces for dependencies, use contract tests for ports, use Cobra/Viper for CLI entry points, ensure build/lint/arch-check/test always pass, and never use emojis.