From predicate
Guides idiomatic Go programming with conventions for naming, formatting, error handling, and composition. Useful when writing or reviewing Go code.
How this skill is triggered — by the user, by Claude, or both
Slash command
/predicate:goThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Go favors simplicity, readability, and explicit behavior. Work with the language's conventions, not against them.
Go favors simplicity, readability, and explicit behavior. Work with the language's conventions, not against them.
Let gofmt handle it. Don't fight the formatter.
gofmt -w . # Format all files
go fmt ./... # Format package
All Go code in the ecosystem follows gofmt. There is no debate.
| Scope | Style | Example |
|---|---|---|
| Exported | PascalCase | ReadFile, HTTPClient |
| Unexported | camelCase | readFile, httpClient |
| Packages | lowercase, single word | bytes, http, bufio |
| Acronyms | All caps | HTTP, URL, ID (not Http, Url, Id) |
bufio.Reader, not bufio.BufReader// ❌ Don't prefix getters with "Get"
func (u *User) GetName() string
// ✅ Just use the field name
func (u *User) Name() string
// ✅ Setters use "Set" prefix
func (u *User) SetName(name string)
One-method interfaces use method name + -er suffix:
| Method | Interface |
|---|---|
Read | Reader |
Write | Writer |
Close | Closer |
String | Stringer |
Go functions return (result, error) instead of throwing:
func ReadFile(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
return io.ReadAll(f)
}
Use for documentation, but avoid naked returns in non-trivial functions:
// Named returns document what's being returned
func ParseDuration(s string) (duration time.Duration, err error) {
// ...
return duration, nil // Explicit, not naked
}
Use defer for cleanup. Defers execute LIFO at function exit:
func CopyFile(dst, src string) error {
r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return err
}
defer w.Close()
_, err = io.Copy(w, r)
return err
}
Accept interfaces, return concrete types:
// ✅ Function accepts interface (flexible)
func Process(r io.Reader) error
// ✅ Function returns concrete type (useful)
func NewClient() *Client
Interfaces are satisfied implicitly—no implements keyword:
type Stringer interface {
String() string
}
type User struct{ Name string }
// User satisfies Stringer automatically
func (u User) String() string {
return u.Name
}
Verify interface satisfaction at compile time:
var _ io.Reader = (*MyReader)(nil)
| Use Pointer | When |
|---|---|
*T | Method modifies the receiver |
*T | Struct is large (avoid copying) |
*T | Consistency (if any method uses pointer, all should) |
| Use Value | When |
|---|---|
T | Method doesn't modify receiver |
T | Type is small (int, small struct) |
T | Type is immutable by design |
Lightweight, start with go:
go func() {
// runs concurrently
}()
Use channels for communication between goroutines:
ch := make(chan int) // Unbuffered
ch := make(chan int, 100) // Buffered
ch <- value // Send
v := <-ch // Receive
"Don't communicate by sharing memory; share memory by communicating."
// ❌ Shared state with mutex
var count int
var mu sync.Mutex
// ✅ Channel-based coordination
type result struct{ value int }
results := make(chan result)
| Pattern | Use Case |
|---|---|
sync.WaitGroup | Wait for N goroutines to complete |
context.Context | Cancellation and timeouts |
select | Multiplex channel operations |
sync.Once | One-time initialization |
Check explicitly. Never discard with _:
// ❌ Never ignore errors
result, _ := doSomething()
// ✅ Always check
result, err := doSomething()
if err != nil {
return fmt.Errorf("doing something: %w", err)
}
Use %w to wrap errors with context:
if err != nil {
return fmt.Errorf("failed to parse config %s: %w", path, err)
}
For libraries, define sentinel errors or custom types:
// Sentinel errors
var ErrNotFound = errors.New("not found")
// Custom error types
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
| Context | Panic? | Alternative |
|---|---|---|
| Library code | ❌ Never | Return error |
| Impossible states | ✅ Acceptable | Assert invariants |
| Init failures | ✅ Acceptable | log.Fatal |
| Anti-Pattern | Description | Remedy |
|---|---|---|
| Ignoring errors | Using _ to discard errors | Always check or explicitly document why ignored |
| Naked returns | return without values in complex functions | Use explicit return values |
| Interface pollution | Defining interfaces before needed | Define interfaces at point of use |
| Premature channels | Using channels when mutex suffices | Start simple; add concurrency primitives as needed |
| Giant interfaces | Interfaces with many methods | Keep interfaces small; compose when needed |
go fmt ./... # Format code
go vet ./... # Static analysis
golangci-lint run # Comprehensive linting
go test ./... # Run tests
go test -race ./... # Detect race conditions
gofmt — no formatting debatesdefer for cleanup — runs at function exit, LIFOThese idioms refine but are subordinate to the Code-Edit Constraints.
npx claudepluginhub nrdxp/predicate --plugin predicateIdiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications.
Provides idiomatic Go patterns and best practices for writing, reviewing, refactoring Go code, and designing packages. Covers simplicity, zero values, interfaces, and error handling.
Applies Go best practices, idioms, and conventions from Effective Go guide for writing, reviewing, and refactoring idiomatic Go code.