$npx -y skills add samber/cc-skills-golang --skill golang-code-styleGolang code style conventions — line length and breaking, variable declarations, control flow clarity, when comments help vs hurt. Use when writing or reviewing Go code, asking about style or clarity, or establishing project coding standards. Not for naming conventions (→ See `sa
| 1 | **Orchestration mode:** Use `ultracode` when reviewing code style across a large codebase — orchestrate the sub-agents described in the "Parallelizing Code Style Reviews" section, each covering an independent style concern, and merge their findings. |
| 2 | |
| 3 | > **Community default.** A company skill that explicitly supersedes `samber/cc-skills-golang@golang-code-style` skill takes precedence. |
| 4 | |
| 5 | # Go Code Style |
| 6 | |
| 7 | Style rules that require human judgment — linters handle formatting, this skill handles clarity. For naming see `samber/cc-skills-golang@golang-naming` skill; for design patterns see `samber/cc-skills-golang@golang-design-patterns` skill; for struct/interface design see `samber/cc-skills-golang@golang-structs-interfaces` skill. |
| 8 | |
| 9 | > "Clear is better than clever." — Go Proverbs |
| 10 | |
| 11 | When ignoring a rule, add a comment to the code. |
| 12 | |
| 13 | ## Line Length & Breaking |
| 14 | |
| 15 | No rigid line limit, but lines beyond ~120 characters MUST be broken. Break at **semantic boundaries**, not arbitrary column counts. Function calls with 4+ arguments MUST use one argument per line — even when the prompt asks for single-line code: |
| 16 | |
| 17 | ```go |
| 18 | // Good — each argument on its own line, closing paren separate |
| 19 | mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) { |
| 20 | handleUsers( |
| 21 | w, |
| 22 | r, |
| 23 | serviceName, |
| 24 | cfg, |
| 25 | logger, |
| 26 | authMiddleware, |
| 27 | ) |
| 28 | }) |
| 29 | ``` |
| 30 | |
| 31 | When a function signature is too long, the real fix is often **fewer parameters** (use an options struct) rather than better line wrapping. For multi-line signatures, put each parameter on its own line. |
| 32 | |
| 33 | ## Variable Declarations |
| 34 | |
| 35 | SHOULD use `:=` for non-zero values, `var` for zero-value initialization. The form signals intent: `var` means "this starts at zero." |
| 36 | |
| 37 | ```go |
| 38 | var count int // zero value, set later |
| 39 | name := "default" // non-zero, := is appropriate |
| 40 | var buf bytes.Buffer // zero value is ready to use |
| 41 | ``` |
| 42 | |
| 43 | ### Slice & Map Initialization |
| 44 | |
| 45 | Slices and maps MUST be initialized explicitly, never nil. Nil maps panic on write; nil slices serialize to `null` in JSON (vs `[]` for empty slices), surprising API consumers. |
| 46 | |
| 47 | ```go |
| 48 | users := []User{} // always initialized |
| 49 | m := map[string]int{} // always initialized |
| 50 | users := make([]User, 0, len(ids)) // preallocate when capacity is known |
| 51 | m := make(map[string]int, len(items)) // preallocate when size is known |
| 52 | ``` |
| 53 | |
| 54 | Do not preallocate speculatively — `make([]T, 0, 1000)` wastes memory when the common case is 10 items. |
| 55 | |
| 56 | ### Composite Literals |
| 57 | |
| 58 | Composite literals MUST use field names — positional fields break when the type adds or reorders fields: |
| 59 | |
| 60 | ```go |
| 61 | srv := &http.Server{ |
| 62 | Addr: ":8080", |
| 63 | ReadTimeout: 5 * time.Second, |
| 64 | WriteTimeout: 10 * time.Second, |
| 65 | } |
| 66 | ``` |
| 67 | |
| 68 | ## Control Flow |
| 69 | |
| 70 | ### Reduce Nesting |
| 71 | |
| 72 | Errors and edge cases MUST be handled first (early return). Keep the happy path at minimal indentation: |
| 73 | |
| 74 | ```go |
| 75 | func process(data []byte) (*Result, error) { |
| 76 | if len(data) == 0 { |
| 77 | return nil, errors.New("empty data") |
| 78 | } |
| 79 | |
| 80 | parsed, err := parse(data) |
| 81 | if err != nil { |
| 82 | return nil, fmt.Errorf("parsing: %w", err) |
| 83 | } |
| 84 | |
| 85 | return transform(parsed), nil |
| 86 | } |
| 87 | ``` |
| 88 | |
| 89 | ### Eliminate Unnecessary `else` |
| 90 | |
| 91 | When the `if` body ends with `return`/`break`/`continue`, the `else` MUST be dropped. Use default-then-override for simple assignments — assign a default, then override with independent conditions or a `switch`: |
| 92 | |
| 93 | ```go |
| 94 | // Good — default-then-override with switch (cleanest for mutually exclusive overrides) |
| 95 | level := slog.LevelInfo |
| 96 | switch { |
| 97 | case debug: |
| 98 | level = slog.LevelDebug |
| 99 | case verbose: |
| 100 | level = slog.LevelWarn |
| 101 | } |
| 102 | |
| 103 | // Bad — else-if chain hides that there's a default |
| 104 | if debug { |
| 105 | level = slog.LevelDebug |
| 106 | } else if verbose { |
| 107 | level = slog.LevelWarn |
| 108 | } else { |
| 109 | level = slog.LevelInfo |
| 110 | } |
| 111 | ``` |
| 112 | |
| 113 | ### Complex Conditions & Init Scope |
| 114 | |
| 115 | When an `if` condition has 3+ operands, MUST extract into named booleans — a wall of `||` is unreadable and hides business logic. Keep expensive checks inline for short-circuit be |