Skip to content
Back to skills

Lang Go Library Dev

ASecurity

Go-specific library/package development patterns. Use when creating Go libraries, designing public APIs with Go idioms, configuring go.mod, managing module versioning, publishing packages, or writing package documentation. Extends meta-library-dev with Go tooling and ecosystem practices.

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added February 8, 2026
documentationgobashsqltestinggitapidatabasedocumentation

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned February 12, 2026

npx -y skills add aRustyDev/ai --skill lang-go-library-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Lang Go Library Dev?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Lang Go Library Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-lang-go-library-dev/badge)](https://www.skillsdirectory.com/skills/arustydev-lang-go-library-dev)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: lang-go-library-dev
description: Go-specific library/package development patterns. Use when creating Go libraries, designing public APIs with Go idioms, configuring go.mod, managing module versioning, publishing packages, or writing package documentation. Extends meta-library-dev with Go tooling and ecosystem practices.
---

# Go Library Development

Go-specific patterns for library/package development. This skill extends `meta-library-dev` with Go tooling, module management, and ecosystem practices.

## This Skill Extends

- `meta-library-dev` - Foundational library patterns (API design, versioning, testing strategies)

For general concepts like semantic versioning, module organization principles, and testing pyramids, see the meta-skill first.

## This Skill Adds

- **Go tooling**: go.mod, module versioning, workspaces, package documentation
- **Go idioms**: Interface design, error handling, API patterns, zero values
- **Go ecosystem**: pkg.go.dev, module proxies, major version strategies

## This Skill Does NOT Cover

- General library patterns - see `meta-library-dev`
- Basic Go syntax - see `lang-go-dev`
- CLI development - see `lang-go-cli-dev`
- Web services - see `lang-go-http-dev`

---

## Quick Reference

| Task | Command/Pattern |
|------|-----------------|
| New module | `go mod init github.com/user/repo` |
| Add dependency | `go get package@version` |
| Update dependencies | `go get -u ./...` |
| Tidy dependencies | `go mod tidy` |
| Run tests | `go test ./...` |
| Run tests with coverage | `go test -cover ./...` |
| Generate docs | `go doc -all` |
| Format code | `go fmt ./...` |
| Vet code | `go vet ./...` |
| Build | `go build ./...` |

---

## Module Structure

### go.mod Configuration

```go
module github.com/username/mylibrary

go 1.21  // Minimum Go version

require (
    github.com/pkg/errors v0.9.1
    golang.org/x/sync v0.5.0
)

require (
    // Indirect dependencies
    golang.org/x/sys v0.15.0 // indirect
)
```

### Best Practices

**Use semantic import versioning for v2+:**
```go
// v0 or v1
module github.com/user/lib

// v2+
module github.com/user/lib/v2
```

**Specify minimum Go version:**
```go
// Enables use of features from that version
go 1.21
```

**Organize indirect dependencies:**
```bash
# Let Go manage indirect deps automatically
go mod tidy
```

---

## Package Organization

### Standard Library Structure

```
mylibrary/
├── go.mod
├── go.sum
├── README.md
├── LICENSE
├── doc.go              # Package-level documentation
├── library.go          # Main public API
├── types.go            # Public types
├── options.go          # Configuration/options
├── errors.go           # Error types
├── internal/           # Internal implementation
│   └── helper.go
├── testdata/           # Test fixtures
│   └── sample.json
└── example_test.go     # Example tests for godoc
```

### Package Naming

**Good package names:**
- Short, lowercase, single word: `http`, `json`, `xml`
- No underscores or mixedCaps: `ioutil` not `io_util`
- Descriptive: `encoding/json` not `encoding/j`

**Avoid:**
```go
// Bad: Generic names
package util
package common
package helpers

// Good: Specific names
package httputil
package stringutil
package mathutil
```

### Internal Packages

```
mylibrary/
├── library.go          # Public API
└── internal/           # Cannot be imported by external packages
    ├── parser.go
    └── validator.go
```

**Use internal/ to prevent external dependencies on implementation details.**

---

## Public API Design

### Interface Design

**Keep interfaces small:**
```go
// Good: Single method interface
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Good: Composed from small interfaces
type ReadWriter interface {
    Reader
    Writer
}

// Avoid: Large interfaces
type DataStore interface {
    Read() error
    Write() error
    Delete() error
    Update() error
    // ... 10 more methods
}
```

**Accept interfaces, return concrete types:**
```go
// Good: Accept interface (flexible)
func Process(r io.Reader) (*Result, error)

// Good: Return concrete type (clear contract)
func NewClient(url string) *Client

// Avoid: Returning interface unnecessarily
func NewClient(url string) ClientInterface
```

**Define interfaces at point of use:**
```go
// Good: Define interface where you use it
package consumer

type DataFetcher interface {
    FetchData(id string) ([]byte, error)
}

func ProcessData(fetcher DataFetcher) error {
    // Use fetcher
}

// Not: Define interface in provider package
```

### Functional Options Pattern

**Preferred pattern for optional configuration:**
```go
type Client struct {
    baseURL    string
    timeout    time.Duration
    maxRetries int
}

type Option func(*Client)

func WithTimeout(d time.Duration) Option {
    return func(c *Client) {
        c.timeout = d
    }
}

func WithMaxRetries(n int) Option {
    return func(c *Client) {
        c.maxRetries = n
    }
}

func NewClient(baseURL string, opts ...Option) *Client {
    c := &Client{
        baseURL:    baseURL,
        timeout:    30 * time.Second,  // defaults
        maxRetries: 3,
    }
    for _, opt := range opts {
        opt(c)
    }
    return c
}

// Usage
client := NewClient("https://api.example.com",
    WithTimeout(10*time.Second),
    WithMaxRetries(5),
)
```

### Constructor Patterns

**New prefix for constructors:**
```go
// Returns pointer (can fail, stateful)
func NewClient(url string) (*Client, error) {
    if url == "" {
        return nil, errors.New("url required")
    }
    return &Client{url: url}, nil
}

// Must prefix for constructors that panic
func MustCompile(pattern string) *Regexp {
    re, err := Compile(pattern)
    if err != nil {
        panic(err)
    }
    return re
}
```

### Error Design

**Create specific error types:**
```go
// Error type
type ValidationError struct {
    Field string
    Err   error
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation failed for %s: %v", e.Field, e.Err)
}

func (e *ValidationError) Unwrap() error {
    return e.Err
}

// Sentinel errors
var (
    ErrNotFound     = errors.New("not found")
    ErrUnauthorized = errors.New("unauthorized")
)

// Check with errors.Is
if errors.Is(err, ErrNotFound) {
    // Handle not found
}

// Check type with errors.As
var validErr *ValidationError
if errors.As(err, &validErr) {
    fmt.Println("Field:", validErr.Field)
}
```

**Error wrapping:**
```go
// Wrap errors with context
if err != nil {
    return fmt.Errorf("failed to connect to %s: %w", url, err)
}

// Allows callers to unwrap
originalErr := errors.Unwrap(err)
```

### Zero Values

**Design types to have useful zero values:**
```go
// Good: Zero value is useful
type Buffer struct {
    buf []byte
}

var b Buffer
b.Write([]byte("hello"))  // Works without initialization

// Good: Document when zero value is not useful
type Client struct {
    baseURL string  // Must be set
}

func NewClient(baseURL string) *Client {
    return &Client{baseURL: baseURL}
}
```

---

## Testing Patterns

### Table-Driven Tests

**Standard Go testing pattern:**
```go
func TestParse(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        want    Result
        wantErr bool
    }{
        {
            name:  "valid input",
            input: "key=value",
            want:  Result{Key: "key", Value: "value"},
        },
        {
            name:    "empty input",
            input:   "",
            wantErr: true,
        },
        {
            name:  "multiple pairs",
            input: "a=1,b=2",
            want:  Result{Pairs: map[string]string{"a": "1", "b": "2"}},
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := Parse(tt.input)
            if (err != nil) != tt.wantErr {
                t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if !tt.wantErr && !reflect.DeepEqual(got, tt.want) {
                t.Errorf("Parse() = %v, want %v", got, tt.want)
            }
        })
    }
}
```

### Subtests

**Organize tests hierarchically:**
```go
func TestClient(t *testing.T) {
    t.Run("GET", func(t *testing.T) {
        t.Run("success", func(t *testing.T) {
            // Test successful GET
        })
        t.Run("not found", func(t *testing.T) {
            // Test 404
        })
    })

    t.Run("POST", func(t *testing.T) {
        t.Run("success", func(t *testing.T) {
            // Test successful POST
        })
    })
}
```

### Test Helpers

**Use t.Helper() to improve error messages:**
```go
func assertNoError(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
}

func assertEqual(t *testing.T, got, want interface{}) {
    t.Helper()
    if !reflect.DeepEqual(got, want) {
        t.Errorf("got %v, want %v", got, want)
    }
}
```

### Example Tests

**Generate documentation with runnable examples:**
```go
// Example tests appear in godoc
func ExampleParse() {
    result, _ := Parse("key=value")
    fmt.Println(result.Key)
    // Output: key
}

func ExampleParse_multiple() {
    result, _ := Parse("a=1,b=2")
    fmt.Println(len(result.Pairs))
    // Output: 2
}

func ExampleClient_Get() {
    client := NewClient("https://api.example.com")
    data, _ := client.Get("/endpoint")
    fmt.Printf("Got %d bytes\n", len(data))
    // Output: Got 42 bytes
}
```

### Test Coverage

```bash
# Run tests with coverage
go test -cover ./...

# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

# View coverage by function
go tool cover -func=coverage.out
```

---

## Documentation with godoc

### Package Documentation

**doc.go pattern:**
```go
// Package mylibrary provides utilities for parsing configuration files.
//
// This package supports JSON, YAML, and TOML formats with automatic
// format detection based on file extension.
//
// Basic usage:
//
//	config, err := mylibrary.Load("config.yaml")
//	if err != nil {
//		log.Fatal(err)
//	}
//
// The package provides three main types:
//
//   - Config: The main configuration structure
//   - Parser: Interface for custom parsers
//   - Validator: Interface for custom validation
//
// For more examples, see the examples/ directory.
package mylibrary
```

### Type and Function Documentation

**Standard documentation format:**
```go
// Client represents an HTTP client for the API.
// It is safe for concurrent use by multiple goroutines.
type Client struct {
    baseURL string
    client  *http.Client
}

// NewClient creates a new API client with the given base URL.
// It returns an error if the URL is invalid.
//
// Example:
//
//	client, err := NewClient("https://api.example.com")
//	if err != nil {
//		log.Fatal(err)
//	}
func NewClient(baseURL string) (*Client, error) {
    // Implementation
}

// Get performs a GET request to the specified path.
// The path should not include the base URL.
//
// Get returns the response body as bytes. If the request fails
// or returns a non-2xx status code, an error is returned.
func (c *Client) Get(path string) ([]byte, error) {
    // Implementation
}
```

### Documentation Best Practices

| Practice | Example |
|----------|---------|
| Start with type/function name | `// Client represents...` not `// This is...` |
| Use complete sentences | `// NewClient creates a new client.` |
| Document parameters | `// path should be relative to base URL` |
| Document return values | `// Returns nil if not found` |
| Include examples | Code blocks in comments |
| Mark deprecated items | `// Deprecated: Use NewClient instead.` |

---

## Versioning and Releases

### Semantic Versioning

**Go modules use semantic versioning:**

- `v0.x.x` - Initial development, no stability guarantees
- `v1.x.x` - Stable API, backward compatible changes only
- `v2.x.x+` - Breaking changes require new major version

### Major Version Strategy

**v2+ requires module path change:**

```go
// v1
module github.com/user/lib

// v2 - add /v2 suffix
module github.com/user/lib/v2
```

**In code:**
```go
// Import v2
import "github.com/user/lib/v2"

// Both v1 and v2 can coexist
import (
    libv1 "github.com/user/lib"
    libv2 "github.com/user/lib/v2"
)
```

### Tagging Releases

```bash
# Tag v1 release
git tag v1.0.0
git push origin v1.0.0

# Tag v2 release (after creating /v2 module path)
git tag v2.0.0
git push origin v2.0.0

# Pre-release
git tag v1.1.0-beta.1
git push origin v1.1.0-beta.1
```

### Retract Versions

**Retract broken versions in go.mod:**
```go
module github.com/user/lib

go 1.21

retract (
    v1.0.0 // Published accidentally
    v1.0.1 // Critical bug, use v1.0.2+
    [v1.5.0, v1.7.0] // Range retraction
)
```

---

## Publishing and Distribution

### Making Your Module Discoverable

**Requirements for pkg.go.dev:**

1. **Public repository** on GitHub, GitLab, or Bitbucket
2. **Valid go.mod** with module path matching repo
3. **Version tags** using semantic versioning
4. **LICENSE file** (required)
5. **Documentation** in godoc format

**Trigger indexing:**
```bash
# pkg.go.dev automatically indexes on first request
curl https://pkg.go.dev/github.com/user/lib@v1.0.0
```

### LICENSE

**Common licenses for Go libraries:**
- MIT - Permissive, simple
- Apache 2.0 - Permissive, patent grant
- BSD 3-Clause - Permissive, attribution required

**Include LICENSE file in repository root.**

### README Best Practices

**Include in README.md:**

```markdown
# My Library

Brief description of what the library does.

## Installation

```bash
go get github.com/user/lib
```

## Quick Start

```go
package main

import "github.com/user/lib"

func main() {
    // Example usage
}
```

## Documentation

Full documentation available at [pkg.go.dev](https://pkg.go.dev/github.com/user/lib).

## License

MIT License - see [LICENSE](LICENSE) file.
```

### Go Module Proxy

**By default, Go uses proxy.golang.org:**

```bash
# Verify module is available
go list -m -versions github.com/user/lib

# Force direct access (bypass proxy)
GOPRIVATE=github.com/user/* go get github.com/user/lib
```

---

## API Compatibility

### Backward Compatibility Rules

**Within major version v1:**

**OK (minor/patch):**
- Add new functions, types, methods
- Add new fields to structs (if not used in comparisons)
- Add new error types
- Make unexported symbols exported

**NOT OK (requires v2):**
- Remove or rename exported symbols
- Change function signatures
- Change error types returned
- Remove struct fields
- Change exported variable types

### Deprecation Pattern

```go
// Deprecated: Use NewClient instead.
// OldClient will be removed in v2.
func OldClient(url string) *Client {
    return NewClient(url)
}

// NewClient creates a new API client.
func NewClient(url string) *Client {
    // Implementation
}
```

---

## Common Patterns

### Config Struct with Defaults

```go
type Config struct {
    Timeout    time.Duration
    MaxRetries int
    Debug      bool
}

// DefaultConfig returns a Config with sensible defaults.
func DefaultConfig() *Config {
    return &Config{
        Timeout:    30 * time.Second,
        MaxRetries: 3,
        Debug:      false,
    }
}

// Usage
config := DefaultConfig()
config.Debug = true
```

### Graceful Initialization

```go
type Manager struct {
    once sync.Once
    db   *sql.DB
}

func (m *Manager) init() {
    m.once.Do(func() {
        m.db, _ = sql.Open("postgres", "connection_string")
    })
}

func (m *Manager) Query(q string) (*Result, error) {
    m.init()  // Lazy initialization
    return m.db.Query(q)
}
```

### Context for Cancellation

```go
// Accept context as first parameter
func (c *Client) Fetch(ctx context.Context, url string) (*Response, error) {
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
        return nil, err
    }

    resp, err := c.client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    // Process response
    return parseResponse(resp)
}

// Usage with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

response, err := client.Fetch(ctx, "https://api.example.com")
```

---

## Anti-Patterns

### 1. init() Functions

```go
// Avoid: Non-deterministic initialization
func init() {
    db = connectDatabase()  // What if this fails?
}

// Prefer: Explicit initialization
func New() (*Client, error) {
    db, err := connectDatabase()
    if err != nil {
        return nil, err
    }
    return &Client{db: db}, nil
}
```

### 2. Global State

```go
// Avoid: Global variables
var defaultClient *Client

func Get(url string) (*Response, error) {
    return defaultClient.Get(url)
}

// Prefer: Explicit dependencies
type Client struct { }

func (c *Client) Get(url string) (*Response, error) {
    // Implementation
}
```

### 3. Interface Pollution

```go
// Avoid: Interfaces for every type
type UserRepository interface {
    Save(u User) error
}

type userRepository struct {}

// Prefer: Concrete types unless abstraction needed
type UserRepository struct {}

func (r *UserRepository) Save(u User) error {
    // Implementation
}
```

### 4. Returning Pointers to Slices/Maps

```go
// Avoid: Unnecessary pointer
func GetUsers() *[]User {
    users := []User{ /* ... */ }
    return &users
}

// Prefer: Return slice directly (already a reference type)
func GetUsers() []User {
    return []User{ /* ... */ }
}
```

---

## Troubleshooting

### Module Path Issues

**Problem:** `go get` fails with "module not found"

**Solutions:**
1. Ensure module path matches repository URL
2. Check that repository is public
3. Verify version tag exists: `git tag v1.0.0`
4. Wait for pkg.go.dev to index (can take a few minutes)

### Import Cycle

**Problem:** "import cycle not allowed"

**Solutions:**
1. Extract shared code to a separate package
2. Use interfaces to break dependency
3. Restructure package organization

```go
// Before: Cycle between user and auth packages
package user
import "myapp/auth"

package auth
import "myapp/user"

// After: Extract common types to separate package
package types
type User struct { }

package user
import "myapp/types"

package auth
import "myapp/types"
```

### Breaking API Changes

**Problem:** Need to make breaking change in v1

**Solutions:**
1. Add new function, deprecate old
2. Create v2 module with /v2 suffix
3. Use options pattern for extensibility

```go
// Instead of changing signature
func Process(data string) error

// Add new function
func ProcessWithOptions(data string, opts Options) error
```

---

## References

- `meta-library-dev` - Foundational library patterns
- `lang-go-dev` - Basic Go syntax and patterns
- [Go Modules Reference](https://go.dev/ref/mod)
- [Effective Go](https://go.dev/doc/effective_go)
- [Go API Guidelines](https://go.dev/wiki/CodeReviewComments)
- [pkg.go.dev](https://pkg.go.dev) - Package documentation

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…