
Go Functions
- 666 installs
- 137 repo stars
- Updated June 20, 2026
- cxuu/golang-skills
go-functions is a Claude Code skill that organizes Go functions, designs signatures, and applies Printf-style naming conventions for developers writing or refactoring functions in Go source files.
About
go-functions is a skill in cxuu/golang-skills for designing and organizing functions within Go files. It routes agents to references/SIGNATURES.md for parameters, return values, named results, and readability, plus references/PRINTF-STRINGER.md for fmt verbs, Stringer, GoStringer, Formatter, and Printf-style naming. The skill triggers when users add or refactor any Go function even without mentioning signature design. It explicitly does not cover functional options constructors, which belong to the separate go-functional-options skill. Developers reach for go-functions when cleaning up Go APIs, standardizing error returns, or aligning logging helpers with idiomatic Printf naming patterns.
- 4 core ordering rules: rough call order, group by receiver, exported functions first, constructors immediately after typ
- References SIGNATURES.md for parameter, return value, and named result design
- References PRINTF-STRINGER.md for fmt verbs, Stringer, and Printf-style naming
- Applies automatically when adding or refactoring any Go function
- Hard-gated exclusions for functional options, error handling, and naming (see related skills)
Go Functions by the numbers
- 666 all-time installs (skills.sh)
- Ranked #554 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cxuu/golang-skills --skill go-functionsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 666 |
|---|---|
| repo stars | ★ 137 |
| Last updated | June 20, 2026 |
| Repository | cxuu/golang-skills ↗ |
How do you design idiomatic Go function signatures?
Consistently organize, signature-design, and group functions when writing or refactoring Go code.
Who is it for?
Go developers refactoring or adding functions who want consistent signature design and Printf-style naming across backend packages.
Skip if: Developers implementing functional options constructors, which should use the go-functional-options skill instead of go-functions.
When should I use this skill?
User adds or refactors Go functions, asks about signature design, named returns, Stringer interfaces, or Printf-style function naming.
What you get
Consistently organized Go functions, readable signatures with named returns, and Printf-style Stringer method naming.
- Refactored Go function signatures
- Consistent Printf-style method naming
By the numbers
- Includes two reference guides: SIGNATURES.md and PRINTF-STRINGER.md
Files
Go Function Design
Resource Routing
references/SIGNATURES.md- Read when designing parameters, return values, named results, or signature readability.references/PRINTF-STRINGER.md- Read when using fmt verbs, Stringer, GoStringer, Formatter, or Printf-style function naming.
When this skill does NOT apply: For functional options constructors (WithTimeout,WithLogger), see go-functional-options. For error return conventions, see go-error-handling. For naming functions and methods, see go-naming.
---
Function Grouping and Ordering
Organize functions in a file by these rules:
1. Functions sorted in rough call order 2. Functions grouped by receiver 3. Exported functions appear first, after struct/const/var definitions 4. NewXxx/newXxx constructors appear right after the type definition 5. Plain utility functions appear toward the end of the file
type something struct{ ... }
func newSomething() *something { return &something{} }
func (s *something) Cost() int { return calcCost(s.weights) }
func (s *something) Stop() { ... }
func calcCost(n []int) int { ... }---
Function Signatures
Keep the signature on a single line when possible. When it must wrap, put all arguments on their own lines with a trailing comma:
func (r *SomeType) SomeLongFunctionName(
foo1, foo2, foo3 string,
foo4, foo5, foo6 int,
) {
foo7 := bar(foo1)
}Add /* name */ comments for ambiguous arguments, or better yet, replace naked bool parameters with custom types.
---
Pointers to Interfaces
You almost never need a pointer to an interface. Pass interfaces as values — the underlying data can still be a pointer.
// Bad: pointer to interface
func process(r *io.Reader) { ... }
// Good: pass the interface value
func process(r io.Reader) { ... }---
Printf and Stringer
Printf-style Function Names
Functions that accept a format string should end in f for go vet support. Declare format strings as const when used outside Printf calls.
Prefer %q over %s with manual quoting when formatting strings for logging or error messages — it safely escapes special characters and wraps in quotes:
return fmt.Errorf("unknown key %q", key) // produces: unknown key "foo\nbar"See go-functional-options when designing a constructor with 3+ optional parameters.
---
Quick Reference
| Topic | Rule |
|---|---|
| File ordering | Type -> constructor -> exported -> unexported -> utils |
| Signature wrapping | All args on own lines with trailing comma |
| Naked parameters | Add /* name */ comments or use custom types |
| Pointers to interfaces | Almost never needed; pass interfaces by value |
| Printf function names | End with f for go vet support |
---
Related Skills
- Error returns: See go-error-handling when designing error return patterns or wrapping errors in multi-return functions
- Naming conventions: See go-naming when naming functions, methods, or choosing getter/setter patterns
- Functional options: See go-functional-options when designing a constructor with 3+ optional parameters
- Formatting principles: See go-style-core when deciding line length, naked returns, or signature formatting
Printf, Stringer, and Custom Formatting
Deep reference for Go's fmt printing verbs, the Stringer and GoStringer interfaces, custom Format() methods, and common pitfalls.
Contents
- Printf Verbs
- Use `%q` for Strings
- Format Strings Outside Printf
- Naming Printf-style Functions
- The `fmt.Stringer` Interface
- The `fmt.GoStringer` Interface
- Custom Formatting with `fmt.Formatter`
- The Infinite Recursion Trap
- Quick Reference
Printf Verbs
General Verbs
| Verb | Use |
|---|---|
%v | Default format (struct fields, slice elements) |
%+v | Struct fields with names: {Name:alice Age:30} |
%#v | Go-syntax representation: main.User{Name:"alice", Age:30} |
%T | Type of the value: main.User |
%% | Literal percent sign |
String and Byte Verbs
| Verb | Use |
|---|---|
%s | Plain string or byte slice |
%q | Quoted string with Go syntax escaping: "hello\n" |
%x | Hex encoding, lowercase: 68656c6c6f |
%X | Hex encoding, uppercase: 68656C6C6F |
Integer Verbs
| Verb | Use |
|---|---|
%d | Decimal integer |
%b | Binary |
%o | Octal |
%O | Octal with 0o prefix |
%x | Hex, lowercase |
%X | Hex, uppercase |
Float Verbs
| Verb | Use |
|---|---|
%f | Decimal point, no exponent: 123.456 |
%e | Scientific notation: 1.23456e+02 |
%g | Compact: %e for large exponents, %f otherwise |
Width and Precision
fmt.Sprintf("%10d", 42) // " 42" (width 10, right-aligned)
fmt.Sprintf("%-10d", 42) // "42 " (width 10, left-aligned)
fmt.Sprintf("%.2f", 3.14159) // "3.14" (2 decimal places)
fmt.Sprintf("%010d", 42) // "0000000042" (zero-padded)---
Use %q for Strings
The %q verb prints strings inside double quotes, making empty strings and control characters visible:
fmt.Printf("value %q looks like English text", someText)
// Bad: manually adding quotes
fmt.Printf("value \"%s\" looks like English text", someText)Prefer %q in output intended for humans where the value could be empty or contain control characters.
---
Format Strings Outside Printf
When declaring format strings outside a Printf-style call, use const. This enables go vet to perform static analysis:
// Bad: variable format string — go vet can't check it
msg := "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)
// Good: const format string — go vet can validate
const msg = "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)---
Naming Printf-style Functions
Functions that accept a format string should end in f. This lets go vet check format strings automatically:
func Wrapf(err error, format string, args ...any) errorIf using a non-standard name, tell go vet:
go vet -printfuncs=wrapf,statusf---
The fmt.Stringer Interface
Implement fmt.Stringer to control how your type appears with %v and %s:
type fmt.Stringer interface {
String() string
}type Point struct{ X, Y int }
func (p Point) String() string {
return fmt.Sprintf("(%d, %d)", p.X, p.Y)
}
// fmt.Println(Point{1, 2}) → "(1, 2)"
// fmt.Sprintf("point: %v", p) → "point: (1, 2)"
// fmt.Sprintf("point: %s", p) → "point: (1, 2)"When to Implement Stringer
- Your type will appear in log messages or user-facing output
- The default
%voutput (field values only) isn't meaningful - You need a human-friendly representation separate from serialization
---
The fmt.GoStringer Interface
Implement fmt.GoStringer to control %#v output. This is useful for types where the default Go-syntax representation is misleading or too verbose:
type fmt.GoStringer interface {
GoString() string
}type Color struct{ R, G, B uint8 }
func (c Color) GoString() string {
return fmt.Sprintf("Color(%#02x, %#02x, %#02x)", c.R, c.G, c.B)
}
// fmt.Sprintf("%#v", Color{255, 128, 0})
// → "Color(0xff, 0x80, 0x00)" instead of "main.Color{R:0xff, G:0x80, B:0x00}"GoString() output should be valid Go syntax or close to it — it's meant for debugging, not user-facing display.
---
Custom Formatting with fmt.Formatter
For full control over all format verbs, implement fmt.Formatter:
type fmt.Formatter interface {
Format(f fmt.State, verb rune)
}type Point struct{ X, Y int }
func (p Point) Format(f fmt.State, verb rune) {
switch verb {
case 'v':
if f.Flag('#') {
// %#v — Go-syntax representation
fmt.Fprintf(f, "Point{X: %d, Y: %d}", p.X, p.Y)
return
}
if f.Flag('+') {
// %+v — verbose with field names
fmt.Fprintf(f, "X:%d Y:%d", p.X, p.Y)
return
}
// %v — default
fmt.Fprintf(f, "(%d, %d)", p.X, p.Y)
case 's':
fmt.Fprintf(f, "(%d, %d)", p.X, p.Y)
case 'q':
fmt.Fprintf(f, "%q", p.String())
default:
fmt.Fprintf(f, "%%!%c(Point=%d,%d)", verb, p.X, p.Y)
}
}fmt.State Methods
| Method | Returns |
|---|---|
Flag(c int) bool | Whether flag (+, -, #, 0, ) is set |
Width() (int, bool) | Width value and whether it was specified |
Precision() (int, bool) | Precision value and whether it was specified |
Write(b []byte) (int, error) | Writes output bytes |
Only implement fmt.Formatter when String() isn't sufficient — it's rarely needed. Common reasons: different output for %v vs %+v vs %#v, or respecting width/precision flags.
---
The Infinite Recursion Trap
Calling `fmt.Sprintf` with `%s` or `%v` on the receiver inside `String()` causes infinite recursion:
type MyString string
// BUG: infinite recursion — Sprintf calls String(), which calls Sprintf...
func (m MyString) String() string {
return fmt.Sprintf("MyString: %s", m) // CRASH: stack overflow
}The fix — convert the receiver to its underlying type to break the method set:
func (m MyString) String() string {
return fmt.Sprintf("MyString: %s", string(m)) // Safe: string has no String()
}This trap also applies to:
- Types whose underlying type is a string, []byte, or another Stringer
- Any
String()method that formatsselfusing%sor%v GoString()methods that formatselfusing%#v
type IPAddr [4]byte
// BUG: %v calls String(), infinite recursion
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
// Safe here — ip[0] is a byte (uint8), which has no String() method.
// But if ip were a named type wrapping a Stringer, this would recurse.
}Rule of thumb: inside String(), never pass the receiver (or the receiver directly re-typed as its own type) to a %s or %v verb. Convert to the underlying primitive type first.
---
Quick Reference
| Topic | Rule |
|---|---|
%q | Use for human-readable string output |
%+v | Struct fields with names |
%#v | Go-syntax representation; customize via GoStringer |
| Format string storage | Declare as const outside Printf calls |
| Printf function names | End with f for go vet support |
Stringer | Implement String() string for %v/%s output |
GoStringer | Implement GoString() string for %#v output |
Formatter | Implement Format(fmt.State, rune) for full verb control |
| Recursion trap | Never Sprintf("%s", receiver) inside String(); convert to underlying type |
Function Signatures
Detailed rules for formatting Go function signatures, avoiding naked parameters, and keeping call sites readable.
---
Single-Line vs Multi-Line
Keep the signature on a single line when it fits comfortably. When it must wrap, put all arguments on their own lines with a trailing comma:
Bad — partial wrapping makes alignment brittle:
func (r *SomeType) SomeLongFunctionName(foo1, foo2, foo3 string,
foo4, foo5, foo6 int) {
foo7 := bar(foo1)
}Good — full wrapping, trailing comma:
func (r *SomeType) SomeLongFunctionName(
foo1, foo2, foo3 string,
foo4, foo5, foo6 int,
) {
foo7 := bar(foo1)
}Return Values
When return values also need wrapping, follow the same pattern:
func (r *SomeType) LongName(
foo1, foo2, foo3 string,
foo4, foo5, foo6 int,
) (
*Result,
error,
) {
// ...
}For simpler cases, named return values can stay on the same line as the closing paren of parameters:
func (r *SomeType) LongName(
foo1, foo2, foo3 string,
) (result *Result, err error) {
// ...
}---
Shortening Call Sites
Factor out local variables instead of splitting function calls across lines:
// Bad: long inline call
result := foo.Call(
somePackage.ComplexFunction(arg1, arg2),
anotherPackage.Transform(data),
defaultOptions,
)
// Good: factor out locals for clarity
transformed := anotherPackage.Transform(data)
computed := somePackage.ComplexFunction(arg1, arg2)
result := foo.Call(computed, transformed, defaultOptions)This improves readability and makes intermediate values available for debugging.
---
Avoid Naked Parameters
Naked parameters in function calls hurt readability. Add C-style comments for ambiguous arguments:
// Bad: what do these booleans mean?
printInfo("foo", true, true)
// Good: inline comments clarify intent
printInfo("foo", true /* isLocal */, true /* done */)Better yet, replace naked bool parameters with custom types:
type Region int
const (
UnknownRegion Region = iota
Local
)
type Status int
const (
Pending Status = iota
Done
)
func printInfo(name string, region Region, status Status)When to Use Each Approach
| Approach | When |
|---|---|
| C-style comments | Quick fix; few call sites; third-party API you can't change |
| Custom types | Multiple call sites; public API; more than one bool/int parameter |
| Functional options | 3+ optional parameters; see go-functional-options |
---
Grouping Related Parameters
When a function takes several parameters of the same type, group them:
// Acceptable: group same-type params
func Copy(dst, src string) error
// Acceptable: separate when meaning differs despite same type
func Move(source string, destination string) errorUse grouping when the parameter names make the roles obvious; use separate declarations when they don't.
---
Method Receiver Placement
The receiver goes before the function name, formatted like a parameter:
// Short receiver — on the same line
func (s *Server) Start(ctx context.Context) error { ... }
// Long receiver type — consider wrapping if the whole line is too long
func (h *ComplicatedHandler) ServeHTTP(
w http.ResponseWriter,
r *http.Request,
) { ... }See go-naming for receiver naming conventions (short, one or two letter abbreviations).
---
Quick Reference
| Topic | Rule |
|---|---|
| Single-line | Keep on one line when it fits |
| Multi-line | All args on own lines, trailing comma |
| Return wrapping | Same pattern as parameters |
| Call sites | Factor out locals instead of splitting calls |
| Naked bools | Add /* name */ comments or use custom types |
| Grouped params | Group same-type when names make roles obvious |
| Receiver | Before function name; short abbreviation |
Related skills
How it compares
Choose go-functions over generic Go style guides when refactoring function signatures and Printf-style naming, not functional options constructors.
FAQ
What reference files does go-functions use?
go-functions routes agents to references/SIGNATURES.md for parameter and return value design, and references/PRINTF-STRINGER.md for fmt verbs, Stringer, GoStringer, and Printf-style naming conventions.
Does go-functions cover functional options in Go?
go-functions does not cover functional options constructors. For option-pattern APIs in Go, use the separate go-functional-options skill in the same cxuu/golang-skills repository.
When should go-functions trigger automatically?
go-functions should trigger when a user adds or refactors any Go function, even if they do not mention signature design. It standardizes organization, return values, and Printf-style naming across backend packages.