Claude-skill-registry code-like-gopher
Provides Go programming expertise, including language syntax, idiomatic patterns, concurrency, and standard library usage. Use when generating, analyzing, refactoring, or reviewing Go code.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/code-like-gopher" ~/.claude/skills/majiayu000-claude-skill-registry-code-like-gopher && rm -rf "$T"
skills/data/code-like-gopher/SKILL.mdWhen to Use
Use this skill when:
- Writing, reviewing, or refactoring Go code
- Setting up Go project structure and tooling
- Debugging concurrency issues
- Configuring linters and formatters
- Writing idiomatic Go code
Prerequisites Check
Before starting any Go work:
# Check Go version go version # Check go.mod version requirement grep '^go ' go.mod 2>/dev/null | awk '{print $2}' # Check if golangci-lint is available command -v golangci-lint
Instructions
General Coding Approach
- All naming and comments must be in English
- Always check the Go version in
and use appropriate language featuresgo.mod - Starting with Go 1.22+, the loop variable capture issue is fixed
- Starting with Go 1.23+, range-over-integers is available:
// Go 1.23+ only for i := range 10 { fmt.Println(i) } // Go 1.22+ - loop variable capture is safe for i := range items { go func() { fmt.Println(i) // safe, no need for tt := tt }() }
Formatting and Linting
golangci-lint is the standard tool for Go code quality.
# Install if missing brew install golangci-lint # macOS # or go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
Check for existing config files:
/.golangci.yml.golangci.yaml
/.golangci.toml.golangci.json
If config version is not
"2", migrate:
golangci-lint migrate
Minimal .golangci.yml
Config
.golangci.ymlversion: "2" run: timeout: 5m tests: false linters: enable: - errcheck - govet - ineffassign - staticcheck - unused - misspell - unconvert - unparam - gosec - prealloc - revive - wrapcheck settings: govet: enable: - assign - appends - bools - defers - shadow - unmarshal - waitgroup - lostcancel - slog - unreachable errcheck: check-type-assertions: true exclude-functions: - fmt.Fprintln - fmt.Fprintf wrapcheck: ignore-package-globs: - encoding/* - github.com/pkg/* revive: enable-all-rules: true rules: - name: package-comments disabled: true - name: cognitive-complexity disabled: true - name: cyclomatic disabled: true - name: function-length disabled: true - name: line-length-limit arguments: [120] - name: add-constant arguments: - max-lit-count: "3" allow-strs: '""' allow-ints: "0,1,2,10,64" formatters: enable: - gofmt - gofumpt - goimports - golines settings: golines: max-len: 120
Usage
golangci-lint run ./... golangci-lint fmt ./...
All the details of the configuration file can be found here:
https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml
is your friend, use for formatting thegofmt
files*.go
helps you to sort and find required package importsgoimports
Naming Conventions
Variables
Name variables by what they hold, not their type:
// ❌ Bad var userString string var countInt int var usersMap map[string]*User var usersList []User // ✅ Good var username string var count int var users map[string]*User var users []User
Short variable conventions:
,i
,j
- loop indicesk
- counter, total, quantityn
,k
- map key/valuev
,a
- same-type comparisonsb
- string valuess
- errorserr
- contextctx
Collections always use plural names:
// ❌ Bad var userString string var countInt int var usersMap map[string]*User var companiesMap map[string]*Company var productsMap map[string]*Product var usersList []User // ✅ Good var username string var count int var users map[string]*User var users []User var companies map[string]*Company var companies []Company var products []Product
Functions vs Methods
Functions - name by the result they return:
// ❌ Bad - describes operation func Add(a, b int) int {} // ✅ Good - describes result func Sum(a, b int) int {}
Methods - name by the action they perform:
type User struct { email string } // Getter - no "Get" prefix func (u User) Email() string { return u.email } // Setter - "Set" prefix func (u *User) SetEmail(email string) { u.email = email }
Packages
- Lowercase, single-word names
- No underscores or mixedCaps
- Package name = base directory name
- Avoid
,util
,common
,misc
,helpers
,tools
,models
,api
ortypes
.interfaces
// ❌ Bad package string_utils func NewStringSet(...string) map[string]bool {} // ✅ Good package stringset func New(...string) Set {}
Constructor naming:
// When package exports one main type, use New() q := list.New() // returns *list.List r := ring.New(10) // returns *ring.Ring
Rules:
-
Don’t use the
notation, which can simplify tests that must run outside the package they are testing, but should otherwise be avoided.import . -
Package names may be abbreviated when the abbreviation is familiar to the programmer. stdlib includes:
strconv (string conversion) syscall (system call) fmt (formatted I/O) -
Avoid repetition. The HTTP server provided by the
package is calledhttp
, not HTTPServer. Client code refers to this type asServer
, so there is no ambiguity.http.Server -
Simplify function names. When a function in package
returns a value ofpkg
(ortype pkg.Pkg
), the function name can often omit the type name without confusion:*pkg.Pkgstart := time.Now() // start is a time.Time t, err := time.Parse(time.Kitchen, "6:06PM") // t is a time.Time ctx = context.WithTimeout(ctx, 10*time.Millisecond) // ctx is a context.Context ip, ok := userip.FromContext(ctx) // ip is a net.IP
Structs
-
Go doesn’t provide automatic support for getters and setters. You can provide getters and setters by your self, it's neither idiomatic nor necessary to put Get into the getter's name. If you have a field called
(lower case, unexported), the getter method should be calledowner
(upper case, exported), not GetOwner. The use of upper-case names for export provides the hook to discriminate the field from the method. A setter function, if needed, will likely be calledOwner
. Both names read well in practice:SetOwnerowner := obj.Owner() if owner != user { obj.SetOwner(user) }
Interfaces
One-method interfaces use -er suffix:
type Reader interface { Read(p []byte) (n int, err error) } type Stringer interface { String() string } // Combined interfaces type ReadWriteCloser interface { Reader Writer Closer }
Error Handling
Always wrap errors with context:
// ❌ Bad - loses context if err != nil { return err } // ✅ Good - stdlib way (Go 1.13+) if err != nil { return fmt.Errorf("authenticate user %s: %w", userID, err) }
Use
and errors.Is
for error checking:errors.As
// Check error type if errors.Is(err, os.ErrNotExist) { // handle not found } // Extract error type var pathErr *os.PathError if errors.As(err, &pathErr) { fmt.Println(pathErr.Path) }
Custom error types:
var ( ErrNotFound = errors.New("not found") ErrUnauthorized = errors.New("unauthorized") ) // Sentinel errors with context func GetUser(id string) (*User, error) { if user == nil { return nil, fmt.Errorf("user %s: %w", id, ErrNotFound) } return user, nil }
Functional Options Pattern
Use instead of config structs:
type Server struct { port string logger *slog.Logger } type Option func(*Server) error func WithPort(port string) Option { return func(s *Server) error { if port == "" { return errors.New("port cannot be empty") } s.port = port return nil } } func WithLogger(l *slog.Logger) Option { return func(s *Server) error { if l == nil { return errors.New("logger cannot be nil") } s.logger = l return nil } } func New(opts ...Option) (*Server, error) { s := &Server{ port: "8080", // default } for _, opt := range opts { if err := opt(s); err != nil { return nil, fmt.Errorf("apply option: %w", err) } } return s, nil } // Usage server, err := New( WithPort("3000"), WithLogger(slog.Default()), )
Concurrency
Golden Rule
Only the sender closes the channel, never the receiver.
The sender knows when work is finished; the receiver does not.
func produce(ch chan<- int) { defer close(ch) // sender closes for i := range 10 { ch <- i } } func consume(ch <-chan int) { for v := range ch { // receiver just reads fmt.Println(v) } }
Buffered Channels
Prefer buffered channel with capacity 1 for signals:
// Signal/done channel done := make(chan struct{}, 1) // Error propagation errCh := make(chan error, 1)
Concurrent-Safe Maps
Use
sync.Map for concurrent access:
var cache sync.Map // Store cache.Store("key", value) // Load if v, ok := cache.Load("key"); ok { // use v } // LoadOrStore actual, loaded := cache.LoadOrStore("key", newValue)
Best Practices
- Zero values - Make them useful (like
,bytes.Buffer
)sync.Mutex - Context - Always first parameter, never in struct fields
- Avoid naked returns - Use explicit returns
- Use
instead ofanyinterface{} - Generics - Last resort, prefer interfaces
- Struct field alignment - Order by size (8, 4, 2, 1 bytes)
- Pointer vs value receivers - Be consistent per type
// ❌ Bad - context in struct type Service struct { ctx context.Context } // ✅ Good - context as first param func (s *Service) Do(ctx context.Context, id string) error {}
Compile-time interface checks:
var _ io.Reader = (*MyReader)(nil) var _ http.Handler = (*MyHandler)(nil)
Testing
Naming
// ❌ Bad - describes input func TestTitleIllegalChar(t *testing.T) {} // ✅ Good - describes behavior func TestTitleEscapesSpecialCharacters(t *testing.T) {}
Table-Driven Tests
func TestSum(t *testing.T) { t.Parallel() tests := []struct { name string a, b int expected int }{ {"positive numbers", 2, 3, 5}, {"negative numbers", -1, -1, -2}, {"zero", 0, 0, 0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Go 1.22+ safe got := Sum(tt.a, tt.b) if got != tt.expected { t.Errorf("Sum(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.expected) } }) } }
Pre-Commit Hooks
brew install pre-commit pre-commit install
Minimal
.pre-commit-config.yaml:
repos: - repo: https://github.com/TekWizely/pre-commit-golang rev: v1.0.0-rc.1 hooks: - id: golangci-lint-mod - id: go-mod-tidy - id: go-test-mod
Commit Messages
Format:
[claude]: <verb> <description in lowercase> - Detail 1 - Detail 2 Fixes #123 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Example:
[claude]: add user authentication middleware - Implement JWT validation - Add rate limiting per user - Handle token refresh Fixes #42 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Quick Reference
| Task | Command |
|---|---|
| Check Go version | |
| Run linter | |
| Format code | |
| Run tests | |
| Tidy modules | |
| Build | |