运维审查
- 作者仓库星标 0
- 作者更新于 实时读取
- 作者仓库 skills-registry
- 领域
- AI 智能
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @tomevault-io · 未声明 license
- Token 消耗评级
- 较高消耗
- 接入复杂程度
- 需手动接入
- 是否需要外部 API Key
- 不需要
- 兼容的系统
- Docker
- 底层运行要求
- Docker
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- Shell 执行
- 读取环境变量
- 网络行为
- 允许外网请求
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: golang-development
description: Write and review Go code with idiomatic project structure, errors, concurrency, testing, and per…
category: AI 智能
runtime: Docker
---
# golang-development 输出预览
## PART A: 任务判断
- 适用问题:提示词、Agent 工作流、模型评估或自动化推理。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“When to Use / Go Philosophy / Project Structure”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于提示词、Agent 工作流、模型评估或自动化推理,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“When to Use / Go Philosophy / Project Structure”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、执行终端命令、读取环境变量、会按任务需要访问外部网络、通常不需要额外 API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件、执行终端命令、读取环境变量;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文没有稳定的斜杠命令要求。安装验证后通常全局生效,直接在对话里点名这个 Skill 并描述任务即可。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件、执行终端命令、读取环境变量。
先用一个小任务确认它会围绕“When to Use / Go Philosophy / Project Structure”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: golang-development
description: Write and review Go code with idiomatic project structure, errors, concurrency, testing, and per…
category: AI 智能
source: tomevault-io/skills-registry
---
# golang-development
## 什么时候使用
- 用于审阅代码、文档或方案并给出可执行反馈 适合处理AI Agent、提示词、模型评估与自动化推理,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查、可继续迭代的步骤;通常不需要…
- 面向提示词、Agent 工作流、模型评估或自动化推理,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「When to Use / Go Philosophy / Project Structure」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件、执行终端命令、读取环境变量;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "golang-development" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> When to Use / Go Philosophy / Project Structure
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> Docker | 读取文件、写入/修改文件、执行终端命令、读取环境变量 | 会按任务需要访问外部网络
安全层 -> 通常不需要额外 API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} Go (Golang) - Patterns and Guidance
Use this skill when writing, reviewing, or debugging Go code. Covers idiomatic Go patterns, project structure, concurrency, testing, and production-ready practices.
When to Use
- Writing new Go packages, services, or CLI tools
- Reviewing or refactoring existing Go code for idiomatic style
- Debugging Go concurrency, error handling, or performance issues
- Setting up Go project structure, module layout, or test harness
Go Philosophy
Core principles:
- Simplicity over cleverness
- Explicit over implicit
- Composition over inheritance
- Clear is better than clever
- Errors are values
- Concurrency is not parallelism
Key idioms:
- "Don't communicate by sharing memory; share memory by communicating"
- "A little copying is better than a little dependency"
- "The bigger the interface, the weaker the abstraction"
- "Make the zero value useful"
- "Accept interfaces, return structs"
Project Structure
Standard Layout
myproject/
├── cmd/ # Main applications
│ ├── api/
│ │ └── main.go # API server entry point
│ └── worker/
│ └── main.go # Background worker entry point
├── internal/ # Private application code
│ ├── app/ # Application logic
│ ├── domain/ # Domain models
│ └── platform/ # Platform-specific code
├── pkg/ # Public libraries (reusable)
│ ├── auth/
│ └── logger/
├── api/ # API definitions (OpenAPI, protobuf)
├── web/ # Web assets (templates, static files)
├── scripts/ # Build, install, analysis scripts
├── configs/ # Configuration files
├── deployments/ # Deployment configs (docker, k8s)
├── test/ # Additional test data
├── docs/ # Documentation
├── examples/ # Example code
├── tools/ # Supporting tools
├── vendor/ # Vendored dependencies (optional)
├── go.mod # Module definition
├── go.sum # Dependency checksums
├── Makefile # Build automation
└── README.md
Key directories:
| Directory | Purpose | Public/Private |
|---|---|---|
cmd/ |
Main applications (one per binary) | Private |
internal/ |
Private application code (cannot be imported) | Private |
pkg/ |
Public libraries (can be imported by others) | Public |
api/ |
API definitions, schemas | Public |
Rules:
internal/cannot be imported by external packages (enforced by Go compiler)- One
main.gopercmd/<app>/subdirectory - Keep
pkg/minimal. Place application code ininternal/unless other modules must import it. - Flat is better than nested - avoid deep hierarchies
Minimal Project
For small projects:
myproject/
├── main.go
├── handler.go
├── service.go
├── repository.go
├── go.mod
├── go.sum
└── README.md
Package Naming
// ❌ BAD: Stuttering
auth.AuthService
user.UserRepository
http.HTTPClient
// ✅ GOOD: Concise
auth.Service
user.Repository
http.Client
Rules:
- Lowercase, single word
- No underscores or mixedCaps
- Avoid generic names (
util,common,base,helper) - Name by purpose, not type (
auth,logger, notstructs,interfaces)
Error Handling
Basic Pattern
// ✅ GOOD: Immediate error check
result, err := doSomething()
if err != nil {
return nil, err
}
// ❌ BAD: Deferred error check
result, err := doSomething()
// ... many lines ...
if err != nil {
return nil, err
}
Error Wrapping
// ✅ GOOD: Wrap errors with context
func processUser(id int) error {
user, err := fetchUser(id)
if err != nil {
return fmt.Errorf("failed to fetch user %d: %w", id, err)
}
if err := saveUser(user); err != nil {
return fmt.Errorf("failed to save user %d: %w", id, err)
}
return nil
}
// Check wrapped errors
err := processUser(123)
if errors.Is(err, sql.ErrNoRows) {
// Handle specific error
}
Use %w for wrapping, %v for opaque errors.
Custom Errors
// Define sentinel errors for expected conditions
var (
ErrUserNotFound = errors.New("user not found")
ErrInvalidInput = errors.New("invalid input")
ErrUnauthorized = errors.New("unauthorized")
)
// Use in code
func getUser(id int) (*User, error) {
if id <= 0 {
return nil, ErrInvalidInput
}
user := db.FindUser(id)
if user == nil {
return nil, ErrUserNotFound
}
return user, nil
}
// Check in caller
user, err := getUser(id)
if errors.Is(err, ErrUserNotFound) {
return http.StatusNotFound
}
Error Types
// For errors with additional context
type ValidationError struct {
Field string
Value interface{}
Err error
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed for field %s: %v", e.Field, e.Err)
}
func (e *ValidationError) Unwrap() error {
return e.Err
}
// Usage
func validateAge(age int) error {
if age < 0 {
return &ValidationError{
Field: "age",
Value: age,
Err: errors.New("must be non-negative"),
}
}
return nil
}
Error Handling Anti-Patterns
// ❌ BAD: Ignoring errors
result, _ := doSomething()
// ❌ BAD: Generic error messages
if err != nil {
return errors.New("error")
}
// ❌ BAD: Panic for expected errors
if err != nil {
panic(err) // Only use panic for truly unrecoverable situations
}
// ✅ GOOD: Explicit handling
result, err := doSomething()
if err != nil {
return fmt.Errorf("failed to do something: %w", err)
}
Interfaces
Accept Interfaces, Return Structs
// ❌ BAD: Returning interface
func NewService() ServiceInterface {
return &service{}
}
// ✅ GOOD: Returning struct
func NewService() *Service {
return &Service{}
}
// ✅ GOOD: Accepting interface
func ProcessData(r io.Reader) error {
// Can accept *os.File, bytes.Buffer, etc.
}
Small Interfaces
// ✅ GOOD: Single-method interfaces
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// ✅ GOOD: Compose when needed
type ReadWriter interface {
Reader
Writer
}
// ❌ BAD: Large interfaces
type UserService interface {
CreateUser(ctx context.Context, user *User) error
GetUser(ctx context.Context, id int) (*User, error)
UpdateUser(ctx context.Context, user *User) error
DeleteUser(ctx context.Context, id int) error
ListUsers(ctx context.Context, filter Filter) ([]*User, error)
SearchUsers(ctx context.Context, query string) ([]*User, error)
// ... 10 more methods
}
Rule: "The bigger the interface, the weaker the abstraction."
Interface Definition Location
// ✅ GOOD: Define interfaces where they're used (consumer-side)
package handler
type UserRepository interface {
GetUser(id int) (*User, error)
}
type Handler struct {
repo UserRepository
}
// Separate implementation
package postgres
type UserRepo struct {
db *sql.DB
}
func (r *UserRepo) GetUser(id int) (*User, error) {
// Implementation
}
Rule: Let consumers define interfaces, not providers.
Concurrency
Goroutines
// ✅ GOOD: Launch goroutine
go func() {
result := compute()
fmt.Println(result)
}()
// ✅ GOOD: Launch with closure
for _, item := range items {
item := item // Capture loop variable
go func() {
process(item)
}()
}
// ❌ BAD: Loop variable capture
for _, item := range items {
go func() {
process(item) // All goroutines will see the same 'item'
}()
}
Channels
// Unbuffered channel (synchronous)
ch := make(chan int)
// Buffered channel (asynchronous)
ch := make(chan int, 10)
// Send and receive
ch <- 42 // Send
value := <-ch // Receive
// Close channel (sender's responsibility)
close(ch)
// Check if closed
value, ok := <-ch
if !ok {
// Channel closed
}
// Range over channel (stops when closed)
for value := range ch {
process(value)
}
Select Statement
select {
case msg := <-ch1:
fmt.Println("Received from ch1:", msg)
case msg := <-ch2:
fmt.Println("Received from ch2:", msg)
case ch3 <- msg:
fmt.Println("Sent to ch3")
case <-time.After(1 * time.Second):
fmt.Println("Timeout")
default:
fmt.Println("No channel ready")
}
Worker Pool Pattern
func workerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {
var wg sync.WaitGroup
// Start workers
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
result := processJob(job)
results <- result
}
}()
}
// Wait for all workers to finish
wg.Wait()
close(results)
}
// Usage
jobs := make(chan Job, 100)
results := make(chan Result, 100)
go workerPool(jobs, results, 5)
// Send jobs
for _, job := range allJobs {
jobs <- job
}
close(jobs)
// Collect results
for result := range results {
fmt.Println(result)
}
Context for Cancellation
func doWork(ctx context.Context) error {
// Create channel for result
resultCh := make(chan Result)
errCh := make(chan error)
// Start work in goroutine
go func() {
result, err := expensiveOperation()
if err != nil {
errCh <- err
return
}
resultCh <- result
}()
// Wait for result or cancellation
select {
case result := <-resultCh:
return processResult(result)
case err := <-errCh:
return fmt.Errorf("operation failed: %w", err)
case <-ctx.Done():
return ctx.Err()
}
}
// Usage
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := doWork(ctx)
if errors.Is(err, context.DeadlineExceeded) {
// Handle timeout
}
Mutex for Shared State
type SafeCounter struct {
mu sync.RWMutex
count map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
defer c.mu.Unlock()
c.count[key]++
}
func (c *SafeCounter) Value(key string) int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.count[key]
}
Rule: Use sync.RWMutex when reads vastly outnumber writes.
Common Concurrency Patterns
Fan-out, fan-in:
func fanOut(input <-chan int, workers int) []<-chan int {
channels := make([]<-chan int, workers)
for i := 0; i < workers; i++ {
ch := make(chan int)
channels[i] = ch
go func() {
defer close(ch)
for v := range input {
ch <- process(v)
}
}()
}
return channels
}
func fanIn(channels []<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, ch := range channels {
wg.Add(1)
go func(c <-chan int) {
defer wg.Done()
for v := range c {
out <- v
}
}(ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
Pipeline:
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
out <- n
}
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range in {
out <- n * n
}
}()
return out
}
// Usage
nums := gen(2, 3, 4)
squares := sq(nums)
for s := range squares {
fmt.Println(s)
}
Testing
Table-Driven Tests
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive", 1, 2, 3},
{"negative", -1, -2, -3},
{"mixed", 1, -1, 0},
{"zero", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
})
}
}
Subtests
func TestUser(t *testing.T) {
t.Run("Create", func(t *testing.T) {
user := NewUser("alice")
if user.Name != "alice" {
t.Errorf("expected name alice, got %s", user.Name)
}
})
t.Run("Update", func(t *testing.T) {
user := NewUser("alice")
user.SetName("bob")
if user.Name != "bob" {
t.Errorf("expected name bob, got %s", user.Name)
}
})
}
Test Helpers
func TestMain(m *testing.M) {
// Setup
setup()
// Run tests
code := m.Run()
// Teardown
teardown()
os.Exit(code)
}
func setup() {
// Initialize test database, etc.
}
func teardown() {
// Cleanup
}
Mocking with Interfaces
// Define interface
type UserRepository interface {
GetUser(id int) (*User, error)
}
// Real implementation
type PostgresRepo struct {
db *sql.DB
}
func (r *PostgresRepo) GetUser(id int) (*User, error) {
// Real database query
}
// Mock for testing
type MockRepo struct {
GetUserFunc func(id int) (*User, error)
}
func (m *MockRepo) GetUser(id int) (*User, error) {
return m.GetUserFunc(id)
}
// Test
func TestService(t *testing.T) {
mockRepo := &MockRepo{
GetUserFunc: func(id int) (*User, error) {
return &User{ID: id, Name: "test"}, nil
},
}
service := NewService(mockRepo)
user, err := service.GetUser(1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Name != "test" {
t.Errorf("expected name test, got %s", user.Name)
}
}
Testing HTTP Handlers
func TestHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/users/1", nil)
w := httptest.NewRecorder()
handler := NewHandler()
handler.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("expected status 200, got %d", w.Code)
}
var user User
if err := json.NewDecoder(w.Body).Decode(&user); err != nil {
t.Fatalf("failed to decode response: %v", err)
}
if user.ID != 1 {
t.Errorf("expected user ID 1, got %d", user.ID)
}
}
Benchmarks
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
func BenchmarkAddParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Add(1, 2)
}
})
}
Run: go test -bench=.
Coverage
go test -cover
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
Common Patterns
Constructor Pattern
// ✅ GOOD: New function returns pointer
func NewService(repo Repository) *Service {
return &Service{
repo: repo,
}
}
// ✅ GOOD: Functional options
type ServiceOption func(*Service)
func WithTimeout(d time.Duration) ServiceOption {
return func(s *Service) {
s.timeout = d
}
}
func NewService(opts ...ServiceOption) *Service {
s := &Service{
timeout: 30 * time.Second, // default
}
for _, opt := range opts {
opt(s)
}
return s
}
// Usage
svc := NewService(
WithTimeout(1 * time.Minute),
WithRetries(3),
)
Builder Pattern (Rare in Go)
// Only use when construction is complex
type RequestBuilder struct {
method string
url string
headers map[string]string
body io.Reader
}
func NewRequestBuilder() *RequestBuilder {
return &RequestBuilder{
headers: make(map[string]string),
}
}
func (b *RequestBuilder) Method(m string) *RequestBuilder {
b.method = m
return b
}
func (b *RequestBuilder) URL(u string) *RequestBuilder {
b.url = u
return b
}
func (b *RequestBuilder) Build() (*http.Request, error) {
return http.NewRequest(b.method, b.url, b.body)
}
// Usage
req, err := NewRequestBuilder().
Method("POST").
URL("https://api.example.com").
Build()
Singleton (Use Sparingly)
var (
instance *Service
once sync.Once
)
func GetInstance() *Service {
once.Do(func() {
instance = &Service{}
})
return instance
}
Note: Prefer dependency injection over singletons.
Adapter Pattern
// Adapt third-party library to your interface
type Logger interface {
Info(msg string)
Error(msg string)
}
type ZapAdapter struct {
logger *zap.Logger
}
func (z *ZapAdapter) Info(msg string) {
z.logger.Info(msg)
}
func (z *ZapAdapter) Error(msg string) {
z.logger.Error(msg)
}
Struct Patterns
Composition Over Inheritance
// ✅ GOOD: Embedding for composition
type User struct {
ID int
Name string
}
type Admin struct {
User // Embedded
Permissions []string
}
// Admin has all User fields
admin := Admin{
User: User{ID: 1, Name: "alice"},
Permissions: []string{"read", "write"},
}
fmt.Println(admin.Name) // Directly accessible
Zero Value Usability
// ✅ GOOD: Usable with zero value
type Buffer struct {
data []byte
}
func (b *Buffer) Write(p []byte) (int, error) {
b.data = append(b.data, p...)
return len(p), nil
}
// Usage without initialization
var buf Buffer
buf.Write([]byte("hello")) // Works!
Private Fields, Public Methods
type Account struct {
balance int // private
}
func (a *Account) Balance() int {
return a.balance
}
func (a *Account) Deposit(amount int) error {
if amount <= 0 {
return errors.New("amount must be positive")
}
a.balance += amount
return nil
}
Performance Optimization
Preallocate Slices
// ❌ BAD: Append without capacity
var items []Item
for i := 0; i < 1000; i++ {
items = append(items, Item{})
}
// ✅ GOOD: Preallocate capacity
items := make([]Item, 0, 1000)
for i := 0; i < 1000; i++ {
items = append(items, Item{})
}
String Builder
// ❌ BAD: String concatenation
var s string
for i := 0; i < 1000; i++ {
s += "x"
}
// ✅ GOOD: strings.Builder
var sb strings.Builder
sb.Grow(1000) // Preallocate
for i := 0; i < 1000; i++ {
sb.WriteString("x")
}
s := sb.String()
Avoid Allocations
// ❌ BAD: Allocates on every call
func process(items []int) []int {
result := make([]int, 0)
for _, item := range items {
if item > 0 {
result = append(result, item)
}
}
return result
}
// ✅ GOOD: Reuse slice
func process(items []int, result []int) []int {
result = result[:0] // Reset length
for _, item := range items {
if item > 0 {
result = append(result, item)
}
}
return result
}
Sync.Pool for Temporary Objects
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func processData(data []byte) {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufferPool.Put(buf)
}()
buf.Write(data)
// Use buf
}
Profiling
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Your application code
}
Access profiles:
- CPU:
http://localhost:6060/debug/pprof/profile - Heap:
http://localhost:6060/debug/pprof/heap - Goroutines:
http://localhost:6060/debug/pprof/goroutine
Analyze:
go tool pprof http://localhost:6060/debug/pprof/profile
Common Pitfalls
Range Loop Variable Capture
// ❌ BAD: Captures loop variable
for _, item := range items {
go func() {
fmt.Println(item) // All goroutines print last item
}()
}
// ✅ GOOD: Capture explicitly
for _, item := range items {
item := item // Reassign
go func() {
fmt.Println(item)
}()
}
// ✅ GOOD: Pass as parameter
for _, item := range items {
go func(i Item) {
fmt.Println(i)
}(item)
}
Nil Interface Gotcha
// ❌ BAD: Interface is not nil
func returnsError() error {
var p *MyError = nil
return p // error interface is not nil!
}
err := returnsError()
if err != nil {
// This block executes!
}
// ✅ GOOD: Return explicit nil
func returnsError() error {
var p *MyError = nil
if p == nil {
return nil
}
return p
}
Defer in Loops
// ❌ BAD: Defer accumulates
for _, file := range files {
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close() // Won't run until function returns
}
// ✅ GOOD: Close immediately or use function
for _, file := range files {
if err := processFile(file); err != nil {
return err
}
}
func processFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
// Process file
return nil
}
Slice Append Gotcha
// ❌ BAD: Original slice affected
a := []int{1, 2, 3}
b := a[:2]
b = append(b, 4)
// a is now [1, 2, 4]
// ✅ GOOD: Copy slice
a := []int{1, 2, 3}
b := make([]int, len(a[:2]))
copy(b, a[:2])
b = append(b, 4)
// a is still [1, 2, 3]
Map Concurrent Access
// ❌ BAD: Concurrent map writes cause panic
m := make(map[string]int)
go func() {
m["key"] = 1
}()
go func() {
m["key"] = 2
}()
// ✅ GOOD: Use sync.Map or mutex
var mu sync.Mutex
m := make(map[string]int)
go func() {
mu.Lock()
m["key"] = 1
mu.Unlock()
}()
go func() {
mu.Lock()
m["key"] = 2
mu.Unlock()
}()
Production Guidance
Logging
// Use structured logging
import "go.uber.org/zap"
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("user logged in",
zap.Int("user_id", 123),
zap.String("ip", "192.168.1.1"),
)
logger.Error("failed to process request",
zap.Error(err),
zap.String("request_id", reqID),
)
Configuration
// Use environment variables or config files
import "github.com/spf13/viper"
func loadConfig() (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath("/etc/myapp/")
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
Graceful Shutdown
func main() {
srv := &http.Server{
Addr: ":8080",
Handler: handler,
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exiting")
}
Database Connections
import "database/sql"
func initDB() (*sql.DB, error) {
db, err := sql.Open("postgres", connStr)
if err != nil {
return nil, err
}
// Connection pool settings
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
// Verify connection
if err := db.Ping(); err != nil {
return nil, err
}
return db, nil
}
Context Propagation
// Always pass context as first parameter
func fetchUser(ctx context.Context, userID int) (*User, error) {
// Use context for cancellation, timeouts, and values
if err := ctx.Err(); err != nil {
return nil, err
}
// Pass context to downstream calls
return db.GetUser(ctx, userID)
}
// HTTP handler
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
user, err := fetchUser(ctx, 123)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(user)
}
Tools and Commands
Essential Commands
# Format code
go fmt ./...
# Lint code (requires golangci-lint)
golangci-lint run
# Run tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests with race detector
go test -race ./...
# Run benchmarks
go test -bench=. ./...
# Build binary
go build -o myapp ./cmd/myapp
# Install dependencies
go mod download
# Tidy dependencies
go mod tidy
# Vendor dependencies
go mod vendor
# Update dependencies
go get -u ./...
# View documentation
go doc fmt.Println
# Run code
go run main.go
Useful Tools
| Tool | Purpose | Install |
|---|---|---|
golangci-lint |
Comprehensive linter | go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest |
gofmt |
Code formatter | Built-in |
goimports |
Import formatter | go install golang.org/x/tools/cmd/goimports@latest |
staticcheck |
Static analysis | go install honnef.co/go/tools/cmd/staticcheck@latest |
govulncheck |
Vulnerability scanner | go install golang.org/x/vuln/cmd/govulncheck@latest |
dlv |
Debugger | go install github.com/go-delve/delve/cmd/dlv@latest |
Makefile Example
.PHONY: build test lint clean
build:
go build -o bin/myapp ./cmd/myapp
test:
go test -v -race -cover ./...
lint:
golangci-lint run
clean:
rm -rf bin/
install-tools:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install golang.org/x/tools/cmd/goimports@latest
run:
go run ./cmd/myapp
Rules
- ALWAYS check errors immediately - defer error checks only when a later check is intentional and documented.
- ALWAYS use
context.Contextas the first parameter of functions that do I/O or long-running work. - ALWAYS close resources with defer immediately after opening them (files, connections, etc.).
- ALWAYS use
go fmtbefore committing code - no exceptions. - ALWAYS run tests with
-raceflag to catch race conditions. - NEVER ignore errors - at minimum, log them; never use
_for error returns. - NEVER use
panicfor expected errors - panic only for truly unrecoverable situations (programmer errors). - NEVER share memory by communicating - communicate by sharing memory (use channels over mutexes when possible).
- NEVER mutate slices/maps without understanding capacity - be aware of shared underlying arrays.
- Accept interfaces, return structs - keep APIs flexible by accepting interfaces but returning concrete types.
- Define interfaces where they're used, not where they're implemented (consumer-side).
- Keep interfaces small - single-method interfaces are ideal.
- Use table-driven tests for comprehensive test coverage with minimal code.
- Preallocate slices when size is known - use
make([]T, 0, capacity). - Use
strings.Builderfor string concatenation in loops - never concatenate strings with+. - Pass context to downstream functions - never create new context in the middle of a call chain.
- Name packages by purpose, not type -
auth, notstructs;logger, notinterfaces. - Avoid stuttering in names -
user.Repository, notuser.UserRepository.
Quick Reference
Minimal HTTP Server
package main
import (
"encoding/json"
"log"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func main() {
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
users := []User{
{ID: 1, Name: "Alice"},
{ID: 2, Name: "Bob"},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Minimal CLI App
package main
import (
"flag"
"fmt"
"os"
)
func main() {
name := flag.String("name", "World", "name to greet")
flag.Parse()
fmt.Printf("Hello, %s!\n", *name)
}
Module Initialization
# Initialize module
go mod init github.com/myorg/myapp
# Add dependency
go get github.com/pkg/errors
# Remove unused dependencies
go mod tidy
Resources
Inputs
- Go source files, module (
go.mod), and description of the feature, bug, or refactoring goal - Target Go version and any relevant package constraints
Outputs
- Idiomatic Go code following standard project layout, explicit error handling, and table-driven tests
Examples
// Idiomatic error wrapping
func getUser(id int) (*User, error) {
u, err := db.QueryUser(id)
if err != nil {
return nil, fmt.Errorf("getUser %d: %w", id, err)
}
return u, nil
}
Source: brpaz/agent-skills — distributed by TomeVault.
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核