Agent Skills: Go Style Core Principles

Use when working with Go formatting, line length, nesting, naked returns, semicolons, or core style principles. Also use when a style question isn't covered by a more specific skill, even if the user doesn't reference a specific style rule. Does not cover domain-specific patterns like error handling, naming, or testing (see specialized skills). Acts as fallback when no more specific style skill applies.

UncategorizedID: cxuu/golang-skills/go-style-core

Repository

cxuuLicense: Apache-2.0
658

Install this agent skill to your local

pnpm dlx add-skill https://github.com/cxuu/golang-skills/tree/HEAD/skills/go-style-core

Skill Files

Browse the full folder contents for go-style-core.

Download Skill

Loading file tree…

skills/go-style-core/SKILL.md

Skill Metadata

Name
go-style-core
Description
Use when working with Go formatting, line length, nesting, naked returns, semicolons, or core style principles. Also use when a style question isn't covered by a more specific skill, even if the user doesn't reference a specific style rule. Does not cover domain-specific patterns like error handling, naming, or testing (see specialized skills). Acts as fallback when no more specific style skill applies.

Go Style Core Principles

Resource Routing

  • references/PRINCIPLES.md - Read when resolving conflicts between clarity, simplicity, concision, maintainability, and consistency.
  • references/FORMATTING.md - Read when handling gofmt, line breaks, whitespace, comments, or semicolons.

Style Principles (Priority Order)

When writing readable Go code, apply these principles in order of importance:

Priority Order

  1. Clarity — Can a reader understand the code without extra context?
  2. Simplicity — Is this the simplest way to accomplish the goal?
  3. Concision — Does every line earn its place?
  4. Maintainability — Will this be easy to modify later?
  5. Consistency — Does it match surrounding code and project conventions?

Formatting

Run gofmt — no exceptions. There is no rigid line length limit, but Uber suggests a soft limit of 99 characters. Break by semantics, not length — refactor rather than just wrap.


Reduce Nesting

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()
}

Unnecessary Else

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
}

Naked Returns

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
}

Guidelines for Naked Returns

  • OK in small functions: Naked returns are fine in functions that are just a handful of lines
  • Be explicit in medium+ functions: Once a function grows to medium size, be explicit with return values for clarity
  • Don't name results just for naked returns: Clarity of documentation is always more important than saving a line or two
// 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.


Semicolons

Go's lexer automatically inserts semicolons after any line whose last token is an identifier, literal, or one of: break continue fallthrough return ++ -- ) }.

This means opening braces must be on the same line as the control structure:

// Good: brace on same line
if i < f() {
    g()
}

// Bad: brace on next line — lexer inserts semicolon after f()
if i < f()  // wrong!
{           // wrong!
    g()
}

Idiomatic Go only has explicit semicolons in for loop clauses and to separate multiple statements on a single line.


Quick Reference

| 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? |

Related Skills

  • Naming conventions: See go-naming when applying MixedCaps, choosing identifier names, or resolving naming debates
  • Error flow: See go-error-handling when structuring error-first guard clauses or reducing nesting via early returns
  • Documentation: See go-documentation when writing doc comments, named return parameters, or package-level docs
  • Linting enforcement: See go-linting when automating style checks with golangci-lint or configuring CI
  • Code review: See go-code-review when applying style principles during a systematic code review
  • Logging style: See go-logging when reviewing logging practices, choosing between log and slog, or structuring log output