cli/templates/skills/backend-go/SKILL.md
Go (Golang) backend development conventions. Use when working on Go projects.
npx skillsauth add binhtranquoc/agent-kit-skill backend-goInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides specific conventions for Go backend development.
project-standards skillproject/
├── cmd/
│ └── api/
│ └── main.go # Entry point
├── internal/
│ ├── handler/ # HTTP handlers (Controllers)
│ ├── service/ # Business logic
│ ├── repository/ # Database access
│ ├── model/ # Domain models
│ └── dto/ # Request/Response objects
├── pkg/ # Shared libraries
├── api/ # OpenAPI/Swagger specs
├── config/ # Configuration
└── go.mod
Important: Interfaces should be defined where they are USED (Consumer), not where they are provided (Producer).
// In service package (consumer defines interface)
type UserRepository interface {
FindByID(ctx context.Context, id string) (*model.User, error)
Create(ctx context.Context, user *model.User) error
}
type UserService struct {
repo UserRepository
}
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}
Only panic during app setup failure (e.g., db disconnect).
// Bad - panicking in business logic
func GetUser(id string) User {
user, err := repo.Find(id)
if err != nil {
panic(err) // Never do this!
}
return user
}
// Good - return errors
func GetUser(id string) (User, error) {
user, err := repo.Find(id)
if err != nil {
return User{}, fmt.Errorf("get user %s: %w", id, err)
}
return user, nil
}
Use fmt.Errorf with %w to preserve context.
if err != nil {
return fmt.Errorf("create user failed: %w", err)
}
user, auth).er (e.g., Reader, Writer, UserRepository).userID, httpClient).// Good
package user
type Repository interface {
FindByID(ctx context.Context, id string) (*User, error)
}
var defaultTimeout = 30 * time.Second // unexported
var DefaultClient = &http.Client{} // exported
Always pass context as the first parameter.
func (s *UserService) GetUser(ctx context.Context, id string) (*User, error) {
// Check context cancellation
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
return s.repo.FindByID(ctx, id)
}
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
user, err := h.service.GetUser(r.Context(), id)
if err != nil {
if errors.Is(err, ErrNotFound) {
h.respondError(w, http.StatusNotFound, "user not found")
return
}
h.respondError(w, http.StatusInternalServerError, "internal error")
return
}
h.respondJSON(w, http.StatusOK, Response{
Success: true,
Data: user,
})
}
Use table-driven tests.
func TestUserService_GetUser(t *testing.T) {
tests := []struct {
name string
id string
want *User
wantErr bool
}{
{
name: "valid user",
id: "123",
want: &User{ID: "123", Email: "[email protected]"},
},
{
name: "not found",
id: "999",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test implementation
})
}
}
development
Activate Code Reviewer mode for code review and quality assurance. Use when reviewing code for bugs, security issues, or optimization opportunities.
development
Default Implementer mode for writing production code. Use for general coding tasks following project conventions.
development
Activate Debugger mode for systematic bug fixing. Use when debugging errors, investigating issues, or fixing bugs.
testing
Activate Architect mode for system design and architecture decisions. Use when planning features, designing systems, or making architectural choices.