go-interface-design — скилл для Claude Code
Скилл помогает проектировать интерфейсы Go, разделять зависимости и проверять контракты на этапе компиляции. Пригодится при рефакторинге и подготовке к тестированию.
npx skills add eduardo-sl/go-agent-skills --skill go-interface-design -a claude-codeЧто делает go-interface-design
Скилл подсказывает, где объявлять интерфейс, почему его лучше держать небольшим и когда вместо интерфейса оставить конкретную структуру. Он разбирает композицию интерфейсов, принцип «принимать интерфейсы, возвращать структуры» и проверку соответствия типа контракту при компиляции.
Есть и разбор ошибок: указателей на интерфейсы, преждевременных абстракций и interface{} без ясной причины. Подробные справочные материалы подключаются по мере необходимости. Для работы с проектом нужна Go toolchain; без неё команды go и gofmt не выполнятся.
Скилл рассчитан на проектирование интерфейсов и границ пакетов, а не на паттерны HTTP-обработчиков или общий код-ревью. Если в проекте есть правило агента, которое явно задаёт другие соглашения, оно имеет приоритет.
Какие задачи решает
- определить интерфейс на стороне кода-потребителя
- разбить широкий контракт на небольшие интерфейсы
- проверить соответствие типа интерфейсу при компиляции
- убрать указатель на интерфейс из сигнатуры функции
- решить, нужна ли абстракция для единственной реализации
Примеры запросов агенту
- Спроектируй интерфейс для сервиса, которому нужно только читать пользователя из хранилища.
- Проверь, стоит ли разделить этот широкий интерфейс на несколько составных.
- Добавь проверку на этапе компиляции, что
PostgresStoreреализует интерфейс потребителя.
Как установить go-interface-design
1Через Skills CLI (Vercel)
npx skills add eduardo-sl/go-agent-skills --skill go-interface-design -a claude-code2Вручную: положить файлы
~/.claude/skills/go-interface-design/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/go-interface-design/
1Через Skills CLI (Vercel)
npx skills add eduardo-sl/go-agent-skills --skill go-interface-design -a codex2Вручную: положить файлы
.agents/skills/go-interface-design/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add eduardo-sl/go-agent-skills --skill go-interface-design -a cursor2Вручную: положить файлы
.cursor/skills/go-interface-design/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install eduardo-sl/go-agent-skills go-interface-design2Вручную: положить файлы
.github/skills/go-interface-design/SKILL.md1Через Skills CLI (Vercel)
npx skills add eduardo-sl/go-agent-skills --skill go-interface-design -a gemini-cli2Командой платформы
gemini skills install https://github.com/eduardo-sl/go-agent-skills --path 'skills/(architecture)/go-interface-design'Вручную: положить файлы
.devin/skills/go-interface-design/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add eduardo-sl/go-agent-skills --skill go-interface-design -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о go-interface-design
Как установить go-interface-design в Claude Code?
Выполните npx skills add eduardo-sl/go-agent-skills --skill go-interface-design -a claude-code. Файл скилла находится по пути ~/.claude/skills/go-interface-design/SKILL.md.
Что делает go-interface-design?
Помогает выбирать место и размер интерфейсов в Go, составлять их из меньших контрактов и проверять реализацию при компиляции. Также объясняет типичные ошибки проектирования.
Что нужно для работы go-interface-design?
Нужна Go toolchain. Скилл использует команды go и gofmt для работы с Go-проектом.
Исходный текст
SKILL.md · MIT
---
name: go-interface-design
description: >
Go interface design patterns: implicit interfaces, consumer-side
definition, interface compliance verification, composition, the
accept-interfaces-return-structs principle, and common pitfalls. Use when
designing interfaces, decoupling packages, defining contracts, reviewing
interface usage, or refactoring for testability. Trigger examples: "design
interface", "accept interfaces return structs", "interface compliance",
"consumer-side interface", "interface composition".
Not for: HTTP handler patterns (go-api-design), general review
(go-code-review).
user-invocable: true
license: MIT
compatibility: Designed for Claude Code or similar AI coding agents working on Go projects. Requires the Go toolchain.
allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(gofmt:*)
metadata:
author: eduardo-sl
version: "1.2.1"
---
# Go Interface Design
Go interfaces are implicit. This is the single most important design feature
of the language, and most people coming from Java or C# get it wrong at first.
> Community default. A project skill or agent-config rule that explicitly
> supersedes `go-interface-design` takes precedence over this one.
Detailed reference material, loaded on demand:
- `references/embedding-and-assertions.md` — embedding vs named fields,
interface-embedding test fakes, optional capabilities, sealed interfaces.
Read a reference file only when the summary below is not enough.
## 1. The Cardinal Rule: Define Interfaces at the Consumer
The consumer of a behavior defines the interface, NOT the provider:
```go
// ❌ Wrong — producer defines interface (Java thinking)
// package store
type UserStore interface { // defined alongside implementation
GetByID(ctx context.Context, id string) (*User, error)
Create(ctx context.Context, user *User) error
// ... 15 more methods
}
type PostgresStore struct { ... }
func (s *PostgresStore) GetByID(...) { ... }
func (s *PostgresStore) Create(...) { ... }
// ✅ Right — consumer defines what it needs
// package service
type UserReader interface { // only what THIS service needs
GetByID(ctx context.Context, id string) (*domain.User, error)
}
type UserService struct {
store UserReader // depends on narrow interface
}
// package store (no interface defined here)
type PostgresStore struct { db *sql.DB }
func (s *PostgresStore) GetByID(ctx context.Context, id string) (*domain.User, error) { ... }
func (s *PostgresStore) Create(ctx context.Context, user *domain.User) error { ... }
// PostgresStore satisfies service.UserReader implicitly — no declaration needed
```
Why this matters:
- Consumer depends only on what it uses (Interface Segregation Principle).
- Producer can add methods without breaking consumers.
- Testing requires only the methods the consumer calls.
- No import cycle: consumer doesn't import producer's package.
## 2. Keep Interfaces Small
The bigger the interface, the weaker the abstraction.
```go
// ✅ Good — focused, composable
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
// ❌ Bad — kitchen sink interface
type FileManager interface {
Read(path string) ([]byte, error)
Write(path string, data []byte) error
Delete(path string) error
List(dir string) ([]string, error)
Move(src, dst string) error
Copy(src, dst string) error
Stat(path string) (os.FileInfo, error)
Watch(path string) (<-chan Event, error)
}
```
Guideline: 1-3 methods is ideal. If you need more, compose smaller interfaces.
## 3. Accept Interfaces, Return Structs
```go
// ✅ Good — accepts interface, returns concrete type
func NewUserService(store UserReader, logger Logger) *UserService {
return &UserService{store: store, logger: logger}
}
// ❌ Bad — returns interface (hides the concrete type for no reason)
func NewUserService(store UserReader) UserServiceInterface {
return &UserService{store: store}
}
```
Return a concrete type so callers get full access to the type's methods.
Returning an interface only makes sense when the function genuinely
returns different concrete types based on input (factory pattern).
## 4. Verify Interface Compliance at Compile Time
Use the blank identifier assignment to catch broken contracts early:
```go
// Verify *PostgresStore implements service.UserReader at compile time
var _ service.UserReader = (*PostgresStore)(nil)
// Verify LogHandler implements http.Handler
var _ http.Handler = (*LogHandler)(nil)
// For value receivers:
var _ fmt.Stringer = Status(0)
```
Place these immediately after the type declaration. They cost nothing
at runtime and prevent silent contract breakage.
## 5. Don't Use Pointers to Interfaces
```go
// ❌ Bad — pointer to interface is almost never correct
func process(r *io.Reader) { ... }
// ✅ Good — interface is already a pointer internally
func process(r io.Reader) { ... }
```
An interface value is internally two pointers (type + data).
A pointer to an interface is a pointer to a pointer — needless indirection.
The only exception: when you need to replace the interface value itself
(swap the implementation at runtime), which is extremely rare.
## 6. The Empty Interface
`interface{}` (or `any` in Go 1.18+) means you've given up on type safety.
Use it sparingly:
```go
// ✅ Acceptable — generic container before generics / stdlib compatibility
func Marshal(v any) ([]byte, error)
// ✅ Better (Go 1.18+) — use generics instead of any
func Map[T, U any](slice []T, fn func(T) U) []U { ... }
// ❌ Bad — lazy interface design
func Process(data any) any { ... } // what does this even do?
```
## 7. Optional Configuration Is Not an Interface Problem
Optional constructor settings are not a reason to introduce an interface.
Use functional options or a config struct — the go-design-patterns skill
owns both and the trade-off between them.
## 8. Common Interface Anti-Patterns
### Premature interfaces:
```go
// ❌ Bad — interface defined before second implementation exists
type Processor interface {
Process(ctx context.Context, data []byte) error
}
type processor struct { ... } // only one implementation ever
// ✅ Good — use concrete type until you need the abstraction
type Processor struct { ... }
// Add interface when you have 2+ implementations or need testing seam
```
"Don't design with interfaces, discover them." — Rob Pike
### Interface pollution:
```go
// ❌ Bad — wrapping every struct in an interface "for testability"
type UserServiceInterface interface { ... }
type OrderServiceInterface interface { ... }
type PaymentServiceInterface interface { ... }
// 50 more interfaces with exactly one implementation each
// ✅ Good — define interfaces where they're consumed
// Each consumer declares only the methods IT needs
```
### Sealed interfaces vs enums:
A sealed interface — an unexported marker method, so only this package can
add variants — is the right tool for a closed set of variants that carry
different data, consumed with a type switch. When the variants carry no
data, use a typed constant enum instead. Example:
`references/embedding-and-assertions.md`.
## 9. Embedding and Type Assertions
- Embedding promotes the whole method set. Embedding `sync.Mutex` in an
exported struct publishes `Lock`/`Unlock`; use a named field unless the
outer type truly *is* the inner one, extended.
- An interface embedded in a struct satisfies it with methods that panic
when called — fine for test fakes, a hidden runtime failure elsewhere.
- Assert to an interface for optional capabilities
(`w.(http.Flusher)`), always with comma-ok.
- Generic methods (Go 1.27+) never satisfy an interface method; keep the
methods of anything you define as an interface non-generic.
## Decision Checklist
1. **Do I need an interface here?** — Only if you have 2+ implementations,
need a testing seam, or are crossing a package boundary.
2. **Where should it be defined?** — At the consumer, not the producer.
3. **How many methods?** — Fewer is better. 1-3 is ideal.
4. **Am I returning an interface?** — Probably shouldn't. Return concrete.
5. **Have I verified compliance?** — `var _ Interface = (*Type)(nil)`
6. **Am I embedding for convenience?** — named field unless every promoted method belongs on the outer type.
© eduardo-sl · MIT · eduardo-sl/go-agent-skills