skills/golang/go-style-core/SKILL.md
Core Go style principles and formatting guidelines from Google and Uber style guides. Use when writing any Go code to ensure clarity, simplicity, and consistency. This is the foundational skill - other Go style skills build on these principles.
npx skillsauth add HadiCherkaoui/opencode-config go-style-coreInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Normative: This guidance is required per Google's canonical Go style guide.
When writing readable Go code, apply these principles in order of importance:
The code's purpose and rationale must be clear to the reader.
// Good: Clear purpose
func (c *Config) WriteTo(w io.Writer) (int64, error)
// Bad: Unclear, repeats receiver
func (c *Config) WriteConfigTo(w io.Writer) (int64, error)
Code should accomplish goals in the simplest way possible.
Simple code:
Least Mechanism Principle: Prefer standard tools:
Code should have high signal-to-noise ratio.
// Good: Common idiom, high signal
if err := doSomething(); err != nil {
return err
}
// Good: Signal boost for unusual case
if err := doSomething(); err == nil { // if NO error
// ...
}
Code is edited many more times than written.
Maintainable code:
// Bad: Critical detail hidden
if user, err = db.UserByID(userID); err != nil { // = vs :=
// Good: Explicit and clear
u, err := db.UserByID(userID)
if err != nil {
return fmt.Errorf("invalid origin user: %s", err)
}
user = u
Code should look and behave like similar code in the codebase.
All Go source files must conform to gofmt output. No exceptions.
# Format a file
gofmt -w myfile.go
# Format all files in directory
gofmt -w .
Source: Effective Go
Go needs fewer parentheses than C and Java. Control structures (if, for, switch) don't have parentheses in their syntax. The operator precedence hierarchy is shorter and clearer, so x<<8 + y<<16 means what the spacing suggests—unlike in other languages.
Go uses MixedCaps or mixedCaps, never underscores:
// Good
MaxLength // exported constant
maxLength // unexported constant
userID // variable
// Bad
MAX_LENGTH // no snake_case
max_length // no underscores
Exceptions:
TestFoo_BarThere is no rigid line length limit in Go, but avoid uncomfortably long lines. Uber suggests a soft limit of 99 characters.
Combined: Google + Uber + Go Wiki CodeReviewComments guidance
Guidelines:
Break by semantics, not length:
Advisory: Go Wiki CodeReviewComments
Don't add line breaks just to keep lines short when they are more readable long (e.g., repetitive lines). Break lines because of what you're writing, not because of line length.
Long lines often correlate with long names. If you find lines are too long, consider whether the names could be shorter. Getting rid of long names often helps more than wrapping lines.
This advice applies equally to function length—there's no rule "never have a function more than N lines", but there is such a thing as too long. The solution is to change where function boundaries are, not to count lines.
// Bad: Arbitrary mid-line break
func (s *Store) GetUser(ctx context.Context,
id string) (*User, error) {
// Good: All arguments on own lines
func (s *Store) GetUser(
ctx context.Context,
id string,
) (*User, error) {
When the style guide is silent, be consistent with nearby code:
Valid local choices:
%s vs %v for error formattingInvalid local overrides:
Source: Uber Go Style Guide
Handle error cases and special conditions first. Return early or continue the loop to keep the "happy path" unindented.
// Bad: Deeply nested
for _, v := range data {
if v.F1 == 1 {
v = process(v)
if err := v.Call(); err == nil {
v.Send()
} else {
return err
}
} else {
log.Printf("Invalid v: %v", v)
}
}
// Good: Flat structure with early returns
for _, v := range data {
if v.F1 != 1 {
log.Printf("Invalid v: %v", v)
continue
}
v = process(v)
if err := v.Call(); err != nil {
return err
}
v.Send()
}
Source: Uber Go Style Guide
If a variable is set in both branches of an if, use default + override pattern.
// Bad: Setting in both branches
var a int
if b {
a = 100
} else {
a = 10
}
// Good: Default + override
a := 10
if b {
a = 100
}
Advisory: Go Wiki CodeReviewComments
A return statement without arguments returns the named return values. This is
known as a "naked" return.
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // returns x, y
}
// Good: Small function, naked return is clear
func minMax(a, b int) (min, max int) {
if a < b {
min, max = a, b
} else {
min, max = b, a
}
return
}
// Good: Larger function, explicit return
func processData(data []byte) (result []byte, err error) {
result = make([]byte, 0, len(data))
for _, b := range data {
if b == 0 {
return nil, errors.New("null byte in data")
}
result = append(result, transform(b))
}
return result, nil // explicit: clearer in longer functions
}
See go-documentation for guidance on Named Result Parameters.
| Principle | Key Question | |-----------|--------------| | Clarity | Can a reader understand what and why? | | Simplicity | Is this the simplest approach? | | Concision | Is the signal-to-noise ratio high? | | Maintainability | Can this be safely modified later? | | Consistency | Does this match surrounding code? |
go-naminggo-error-handlinggo-documentationgo-testinggo-defensivego-performancego-lintingdevelopment
Use when you have a spec or requirements for a multi-step task, before touching code
data-ai
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
tools
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
development
Use when implementing any feature or bugfix, before writing implementation code