
- Installs
- 57
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/miles990/claude-software-skills --skill go- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads go automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Go
Overview
Go programming patterns including concurrency, error handling, and idiomatic Go code.
Basic Patterns
Structs and Methods
package main
import (
"encoding/json"
"fmt"
"time"
)
// Struct definition
type User struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
metadata map[string]interface{} // unexported (private)
}
// Constructor function
func NewUser(email, name string) *User {
return &User{
ID: generateID(),
Email: email,
Name: name,
CreatedAt: time.Now(),
metadata: make(map[string]interface{}),
}
}
// Value receiver (for read-only)
func (u User) FullName() string {
return u.Name
}
// Pointer receiver (for mutations or large structs)
func (u *User) SetMetadata(key string, value interface{}) {
u.metadata[key] = value
}
// Embedding (composition)
type Admin struct {
User // Embedded struct
Permissions []string
}
func (a *Admin) HasPermission(perm string) bool {
for _, p := range a.Permissions {
if p == perm {
return true
}
}
return false
}
Interfaces
// Interface definition
type Repository interface {
Find(id string) (*User, error)
FindAll() ([]*User, error)
Create(user *User) error
Update(user *User) error
Delete(id string) error
}
// Interface implementation (implicit)
type MemoryRepository struct {
users map[string]*User
}
func NewMemoryRepository() *MemoryRepository {
return &MemoryRepository{
users: make(map[string]*User),
}
}
func (r *MemoryRepository) Find(id string) (*User, error) {
user, ok := r.users[id]
if !ok {
return nil, ErrNotFound
}
return user, nil
}
func (r *MemoryRepository) Create(user *User) error {
r.users[user.ID] = user
return nil
}
// Compile-time interface check
var _ Repository = (*MemoryRepository)(nil)
// Empty interface (any type)
func PrintAny(v interface{}) {
fmt.Printf("%v\n", v)
}
// Type assertion
func ProcessValue(v interface{}) {
switch val := v.(type) {
case string:
fmt.Println("String:", val)
case int:
fmt.Println("Int:", val)
case *User:
fmt.Println("User:", val.Name)
default:
fmt.Println("Unknown type")
}
}
Error Handling
import (
"errors"
"fmt"
)
// Sentinel errors
var (
ErrNotFound = errors.New("not found")
ErrBadRequest = errors.New("bad request")
)
// Custom error type
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}
// Error wrapping
func GetUser(id string) (*User, error) {
user, err := repository.Find(id)
if err != nil {
return nil, fmt.Errorf("getting user %s: %w", id, err)
}
return user, nil
}
// Error checking
func ProcessUser(id string) error {
user, err := GetUser(id)
if err != nil {
if errors.Is(err, ErrNotFound) {
return fmt.Errorf("user not found: %s", id)
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
return fmt.Errorf("validation failed: %s", validationErr.Field)
}
return err
}
// Process user...
return nil
}
// Multi-error handling
type MultiError struct {
Errors []error
}
func (m *MultiError) Error() string {
var msgs []string
for _, err := range m.Errors {
msgs = append(msgs, err.Error())
}
return strings.Join(msgs, "; ")
}
func (m *MultiError) Add(err error) {
if err != nil {
m.Errors = append(m.Errors, err)
}
}
func (m *MultiError) HasErrors() bool {
return len(m.Errors) > 0
}
Concurrency
Goroutines and Channels
// Basic goroutine
func main() {
go func() {
fmt.Println("Hello from goroutine")
}()
time.Sleep(100 * time.Millisecond)
}
// Channel basics
func worker(jobs <-chan int, results chan<- int) {
for job := range jobs {
results <- job * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// Start workers
for w := 0; w < 3; w++ {
go worker(jobs, results)
}
// Send jobs
for j := 0; j < 9; j++ {
jobs <- j
}
close(jobs)
// Collect results
for r := 0; r < 9; r++ {
fmt.Println(<-results)
}
}
// Select for multiple channels
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(100 * time.Millisecond)
ch1 <- "one"
}()
go func() {
time.Sleep(200 * time.Millisecond)
ch2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("Received:", msg1)
case msg2 := <-ch2:
fmt.Println("Received:", msg2)
case <-time.After(500 * time.Millisecond):
fmt.Println("Timeout")
}
}
}
Concurrency Patterns
import (
"context"
"sync"
)
// Worker pool
type WorkerPool struct {
numWorkers int
jobs chan func()
wg sync.WaitGroup
}
func NewWorkerPool(numWorkers int) *WorkerPool {
pool := &WorkerPool{
numWorkers: numWorkers,
jobs: make(chan func(), numWorkers*2),
}
pool.Start()
return pool
}
func (p *WorkerPool) Start() {
for i := 0; i < p.numWorkers; i++ {
go func() {
for job := range p.jobs {
job()
p.wg.Done()
}
}()
}
}
func (p *WorkerPool) Submit(job func()) {
p.wg.Add(1)
p.jobs <- job
}
func (p *WorkerPool) Wait() {
p.wg.Wait()
}
func (p *WorkerPool) Close() {
close(p.jobs)
}
// Fan-out, fan-in
func FanOut(ctx context.Context, input <-chan int, workers int) []<-chan int {
outputs := make([]<-chan int, workers)
for i := 0; i < workers; i++ {
outputs[i] = worker(ctx, input)
}
return outputs
}
func FanIn(ctx context.Context, channels ...<-chan int) <-chan int {
var wg sync.WaitGroup
merged := make(chan int)
output := func(c <-chan int) {
defer wg.Done()
for v := range c {
select {
case merged <- v:
case <-ctx.Done():
return
}
}
}
wg.Add(len(channels))
for _, c := range channels {
go output(c)
}
go func() {
wg.Wait()
close(merged)
}()
return merged
}
// Rate limiter
type RateLimiter struct {
ticker *time.Ticker
tokens chan struct{}
}
func NewRateLimiter(rate int, burst int) *RateLimiter {
rl := &RateLimiter{
ticker: time.NewTicker(time.Second / time.Duration(rate)),
tokens: make(chan struct{}, burst),
}
// Fill initial burst
for i := 0; i < burst; i++ {
rl.tokens <- struct{}{}
}
// Refill tokens
go func() {
for range rl.ticker.C {
select {
case rl.tokens <- struct{}{}:
default:
}
}
}()
return rl
}
func (rl *RateLimiter) Wait(ctx context.Context) error {
select {
case <-rl.tokens:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
Context
import (
"context"
"time"
)
// Context with timeout
func FetchWithTimeout(url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
// Context with values
type contextKey string
const userIDKey contextKey = "userID"
func WithUserID(ctx context.Context, userID string) context.Context {
return context.WithValue(ctx, userIDKey, userID)
}
func GetUserID(ctx context.Context) (string, bool) {
userID, ok := ctx.Value(userIDKey).(string)
return userID, ok
}
// Passing context through layers
func Handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = WithUserID(ctx, r.Header.Get("X-User-ID"))
result, err := ProcessRequest(ctx)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(result)
}
func ProcessRequest(ctx context.Context) (*Result, error) {
// Check for cancellation
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
userID, ok := GetUserID(ctx)
if !ok {
return nil, errors.New("user ID not found in context")
}
return fetchData(ctx, userID)
}
Generics (Go 1.18+)
// Generic function
func Map[T, U any](items []T, fn func(T) U) []U {
result := make([]U, len(items))
for i, item := range items {
result[i] = fn(item)
}
return result
}
func Filter[T any](items []T, predicate func(T) bool) []T {
var result []T
for _, item := range items {
if predicate(item) {
result = append(result, item)
}
}
return result
}
func Reduce[T, U any](items []T, initial U, fn func(U, T) U) U {
result := initial
for _, item := range items {
result = fn(result, item)
}
return result
}
// Generic type constraint
type Number interface {
~int | ~int32 | ~int64 | ~float32 | ~float64
}
func Sum[T Number](items []T) T {
var sum T
for _, item := range items {
sum += item
}
return sum
}
// Generic struct
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
// Usage
stack := &Stack[int]{}
stack.Push(1)
stack.Push(2)
val, ok := stack.Pop() // val = 2, ok = true
Testing
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Basic test
func TestSum(t *testing.T) {
result := Sum([]int{1, 2, 3})
if result != 6 {
t.Errorf("expected 6, got %d", result)
}
}
// Table-driven tests
func TestSumTableDriven(t *testing.T) {
tests := []struct {
name string
input []int
expected int
}{
{"empty", []int{}, 0},
{"single", []int{5}, 5},
{"multiple", []int{1, 2, 3}, 6},
{"negative", []int{-1, 1}, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Sum(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
// Test with testify
func TestUser(t *testing.T) {
user := NewUser("test@example.com", "Test User")
require.NotNil(t, user)
assert.Equal(t, "test@example.com", user.Email)
assert.Equal(t, "Test User", user.Name)
assert.NotEmpty(t, user.ID)
}
// Benchmark
func BenchmarkSum(b *testing.B) {
items := make([]int, 1000)
for i := range items {
items[i] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
Sum(items)
}
}
Related Skills
- [[backend]] - Go web services
- [[cloud-platforms]] - Cloud-native Go
- [[system-design]] - System architecture
FAQ
What does the go skill do?
Go programming patterns and idioms
How do I install go?
Run npx skills add https://github.com/miles990/claude-software-skills --skill go in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/go/ (personal) or .claude/skills/go/ (project).
Which AI agents can use go?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is go safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains go?
It is published by miles990 in miles990/claude-software-skills. It has 57 recorded installs.
More from miles990/claude-software-skills
code-quality
miles990/claude-software-skills
Clean code principles, SOLID, and code review practices
devops-cicd
miles990/claude-software-skills
CI/CD pipelines, infrastructure as code, and deployment strategies
game-development
miles990/claude-software-skills
Game development patterns, architectures, and best practices
project-management
miles990/claude-software-skills
Open the skill to read its SKILL.md instructions.
e-commerce
miles990/claude-software-skills
E-commerce platforms, payment processing, and shopping cart patterns
saas-platforms
miles990/claude-software-skills
Open the skill to read its SKILL.md instructions.
Popular other skills
hyperframes-cli
heygen-com/hyperframes
The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures.
triage
mattpocock/skills
Move issues and external PRs through a state machine of triage roles, categorise, verify, grill if needed, and write agent-ready briefs.
ask-matt
mattpocock/skills
Ask which skill or flow fits your situation. A router over the skills in this repo.
to-questionnaire
mattpocock/skills
Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
loop-me
mattpocock/skills
Grill me about specs for the workflows I want to build, within this workspace.