go

Write Go code following best practices. Use when developing Go applications. Covers error handling, concurrency, and project structure.

allowed_tools: Read, Write, Edit, Bash, Glob, Grep

$ 安裝

git clone https://github.com/dralgorhythm/claude-agentic-framework /tmp/claude-agentic-framework && cp -r /tmp/claude-agentic-framework/.claude/skills/languages/go ~/.claude/skills/claude-agentic-framework

// tip: Run this command in your terminal to install the skill


name: go description: Write Go code following best practices. Use when developing Go applications. Covers error handling, concurrency, and project structure. allowed-tools: Read, Write, Edit, Bash, Glob, Grep

Go Development

Project Structure

myproject/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── handler/
│   ├── service/
│   └── repository/
├── pkg/
│   └── shared/
├── go.mod
└── go.sum

Error Handling

// Custom error types
type NotFoundError struct {
    Resource string
    ID       string
}

func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s not found: %s", e.Resource, e.ID)
}

// Error wrapping
func GetUser(id string) (*User, error) {
    user, err := db.FindUser(id)
    if err != nil {
        return nil, fmt.Errorf("GetUser(%s): %w", id, err)
    }
    return user, nil
}

// Error checking
if errors.Is(err, sql.ErrNoRows) {
    return nil, &NotFoundError{Resource: "user", ID: id}
}

Concurrency

// Goroutines with errgroup
func fetchAll(ctx context.Context, urls []string) ([]Response, error) {
    g, ctx := errgroup.WithContext(ctx)
    results := make([]Response, len(urls))

    for i, url := range urls {
        i, url := i, url // capture loop variables
        g.Go(func() error {
            resp, err := fetch(ctx, url)
            if err != nil {
                return err
            }
            results[i] = resp
            return nil
        })
    }

    if err := g.Wait(); err != nil {
        return nil, err
    }
    return results, nil
}

// Channels
func producer(ch chan<- int) {
    for i := 0; i < 10; i++ {
        ch <- i
    }
    close(ch)
}

func consumer(ch <-chan int) {
    for v := range ch {
        fmt.Println(v)
    }
}

HTTP Handler

func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    id := chi.URLParam(r, "id")

    user, err := h.service.GetUser(ctx, id)
    if err != nil {
        var notFound *NotFoundError
        if errors.As(err, &notFound) {
            http.Error(w, err.Error(), http.StatusNotFound)
            return
        }
        http.Error(w, "Internal error", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

Testing

func TestGetUser(t *testing.T) {
    t.Parallel()

    tests := []struct {
        name    string
        id      string
        want    *User
        wantErr bool
    }{
        {
            name: "existing user",
            id:   "123",
            want: &User{ID: "123", Email: "test@example.com"},
        },
        {
            name:    "non-existent user",
            id:      "999",
            wantErr: true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := service.GetUser(context.Background(), tt.id)
            if (err != nil) != tt.wantErr {
                t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
            }
            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("got = %v, want %v", got, tt.want)
            }
        })
    }
}

Tooling

# Format
gofmt -w .
goimports -w .

# Lint
golangci-lint run

# Test
go test -v -race -cover ./...