← Back to list

go-expert-developer
by ALX99
here be dragons
⭐ 0🍴 0📅 Jan 23, 2026
SKILL.md
name: go-expert-developer description: Go best practices for writing clean, idiomatic, and maintainable Go code. This skill should be used when writing or reviewing Go code.
Go Expert Developer
Foreword
- You MUST follow these standards when writing Go code.
- Existing code SHOULD be refactored to comply when modified.
- Do NOT be mislead by existing code; ALWAYS remind yourself of these standards.
Principles
- KISS: Code should be as simple as possible; avoid premature abstractions and optimizations
- DRY: Extract shared patterns
- YAGNI: Don't build until needed
- Clear > Clever: Readability is EXTREMELY important. Do not sacrifice clarity for cleverness.
- Idiomatic Go: stdlib first; don't import other languages' idioms
- Follow Uber's Go Style Guide, Google's Go Style Guide, and Effective Go
Naming
Constructors
// ✓ package.New() when package name provides context
package server
func New() *Server { ... } // server.New()
// ✗ Redundant type name
func NewServer() *Server { ... }
Methods
// ✓ No Get prefix
func (u *User) Name() string { ... }
func (c *Client) FetchUser(id string) (*User, error) { ... }
// ✗ Get prefix
func (u *User) GetName() string { ... }
Variables
Rule: distance from declaration → name length.
// ✓ Short names for small scopes
for i := range len(items) { ... }
func parse(r io.Reader) error { ... }
// ✓ Descriptive for wider scopes
func (s *Server) sendNotifications(userID string) error {
user, err := s.db.GetUser(userID)
// ...
}
Receivers
// ✓ 1-2 letter abbreviation
func (c *Client) Connect() error { ... }
func (ns *Namespace) Name() string { ... }
Errors
Wrapping Format
// ✓ Imperative, lowercase, no "failed/error"
fmt.Errorf("connect to database: %w", err)
fmt.Errorf("parse config: %w", err)
// ✗ Bad patterns
fmt.Errorf("failed to connect: %w", err)
fmt.Errorf("Error parsing config: %w", err)
Naming
// ✓ Err prefix
var ErrNotFound = errors.New("not found")
var errInternal = errors.New("internal error")
Structure
Initialization
// ✓ Empty slice
var users []User
// ✓ Named fields
user := User{Name: "John", Email: "john@example.com"}
Struct Field Grouping
// ✓ Grouped logically, embedded types first
type Server struct {
httpSrv *http.Server
host string
port int
log *slog.Logger
metrics *Metrics
mu sync.Mutex
conns map[string]*Conn
}
Reduce Nesting & Early Returns
// ✓ Flat with early returns
func (s *Server) Handle(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
if id == "" {
http.Error(w, "missing id", http.StatusBadRequest)
return
}
user, err := s.db.GetUser(r.Context(), id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// happy path continues...
}
Handle Once
// ✓ Return OR log, never both
func loadConfig() (Config, error) {
data, err := os.ReadFile("config.json")
if err != nil {
return Config{}, fmt.Errorf("load config: %w", err)
}
// ...
}
Nil Handing
- Functions must never return (nil, nil) or when returning a single non-error value, never return nil.
It is the responsibility of functions to ensure
(nil, nil)or onlynilis ever returned. - When a function parameter is a pointer, it is the caller's responsibility to ensure it is non-nil unless otherwise documented.
// ✓ User is not nil-checked
func (s *Service) NotifyUser(user *User, msg string) error {
if user.Email != "" {
return s.mailer.Send(user.Email, msg)
}
return nil
}
Pass by Value
Default to value semantics. Use pointers only for:
- Types with pointer semantics (
sync.Mutex,sql.DB) - Types conventionally returned as pointers (
*bytes.Buffer) - Non-data structs such as servers, clients, handlers with a long lifecycle needing mutation
// ✓ Value for config, time
func (s *Server) Start(cfg Config) error { ... }
func formatTimestamp(t time.Time) string { ... }
// ✓ Pointer for mutation, semantics
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
Goroutines
- Goroutines must not leak. Use contexts or wait groups to manage lifecycle.
- Goroutines must not be unbounded in number.
func deleteUsers(ctx context.Context, userIDs []int) ([]User, error) {
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(5) // ✓ Bound concurrency
users := make([]User, len(userIDs))
for i, id := range userIDs {
eg.Go(func() error {
user, err := fetchUser(ctx, id)
if err != nil {
return fmt.Errorf("fetch user %d: %w", id, err)
}
users[i] = user
return nil
})
}
// ✓ Wait for all goroutines to finish
if err := eg.Wait(); err != nil {
return nil, err
}
return users, nil
}
Interfaces
Placement: Define interfaces in the consuming package, not the implementing package. Embedding: Prefer composition over type embedding to avoid surprises.
// ✓ Pass interface by value
func parseYAML(r io.Reader) (Config, error) { ... }
// ✗ Pointer to interface
func parseYAML(r *io.Reader) (Config, error) { ... }
Testability
Don't add interfaces just for testing. Code should be testable as-is. Accept interfaces and return structs.
Documentation
// Package server provides HTTP server functionality.
package server
// Config contains server configuration.
type Config struct {
Port int
}
// New creates a server instance.
func New(cfg Config) *Server { ... }
Miscellaneous
- API surface should be minimal; unexported by default.
- Use logging sparingly; log only when it adds important context.
- Prefer the standard library over third-party packages unless absolutely necessary.
- Avoid init functions; prefer explicit initialization.
- Use
iota(starting from1) for related constants.stringercan be used for generating string representations. - Use
deferfor resource cleanup and unlocking mutexes.
Additional References
- Refer to TESTING.md when writing tests.
- Refer to PKG_DESIGN.md when designing and creating packages.
Score
Total Score
50/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon