go-style-core

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "go-style-core" with this command: npx skills add cxuu/golang-skills/cxuu-golang-skills-go-style-core

Go Style Core Principles

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?

Read references/PRINCIPLES.md when resolving conflicts between clarity, simplicity, and concision, or when you need concrete examples of how each principle applies in real Go code.


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.

Read references/FORMATTING.md when configuring gofmt, deciding on line breaks, applying MixedCaps rules, or resolving local consistency questions.


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

PrincipleKey Question
ClarityCan a reader understand what and why?
SimplicityIs this the simplest approach?
ConcisionIs the signal-to-noise ratio high?
MaintainabilityCan this be safely modified later?
ConsistencyDoes 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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

go-linting

No summary provided by upstream source.

Repository SourceNeeds Review
232-cxuu
General

go-documentation

No summary provided by upstream source.

Repository SourceNeeds Review
214-cxuu
General

go-naming

No summary provided by upstream source.

Repository SourceNeeds Review
209-cxuu
General

go-concurrency

No summary provided by upstream source.

Repository SourceNeeds Review
201-cxuu