go-backend-init
Go Backend Repository Initialization Skill
Initialize a production-grade Go backend repository following battle-tested patterns from real microservice projects.
When to Use
- User asks to create a new Go backend project
- User wants to scaffold a Go microservice repo
- User needs a Go project template with CI/CD, linting, Docker, etc.
Initialization Checklist
When invoked, walk through these steps in order. Ask the user for project-specific details first:
- Project name (Go module path, e.g.,
github.com/org/my-service) - Service names (e.g.,
platform,api,worker— at least one) - Go version (default: latest stable, currently 1.25)
- Database (PostgreSQL with Ent ORM is default; ask if needed)
- Proto/gRPC (yes/no — if yes, set up buf + generated code structure)
- System dependencies (any C libraries like libsoxr, etc.)
Directory Structure
Create this structure, adapting service names to the user's input:
.
├── cmd/
│ └── <service>/
│ └── main.go # Uber FX app entry point
├── internal/
│ ├── configs/ # Config loading (.env + optional config center)
│ ├── datastore/ # Database client initialization (Ent)
│ ├── grpc/
│ │ ├── servers/ # gRPC + HTTP server lifecycle
│ │ ├── services/ # gRPC service implementations (business logic)
│ │ └── registers/ # Service registration with gRPC server
│ └── models/ # Data access layer (Ent ORM CRUD)
├── pkg/ # Reusable packages (errors, utils)
│ └── apierrors/ # Standardized error handling
├── libs/ # Shared test/logging utilities
├── schema/ # Ent ORM schema definitions
│ └── generate.go # go:generate directive for Ent
├── ent/ # (generated) Ent ORM code — DO NOT EDIT
├── api/
│ └── generated/ # (generated) Proto/gRPC code — DO NOT EDIT
├── hack/ # Dev scripts
├── .github/
│ ├── actions/
│ │ └── setup-go-env/
│ │ └── action.yml # Composite action: Go + system deps
│ └── workflows/
│ └── ci.yml # Lint + Test + Build + Autofix
├── .vscode/
│ ├── settings.json
│ └── extensions.json
├── .editorconfig
├── .gitignore
├── .golangci.yml
├── .tool-versions
├── .env.example
├── cspell.config.yaml
├── renovate.json
├── Dockerfile
├── go.mod
└── CLAUDE.md # AI assistant context
File Templates
1. go.mod
module {{MODULE_PATH}}
go {{GO_VERSION}}
require (
go.uber.org/fx v1.23.0
// Add Ent ORM if using database
entgo.io/ent v0.14.4
// Add gRPC if using proto
google.golang.org/grpc v1.72.0
google.golang.org/protobuf v1.36.6
// Add Echo if using HTTP
github.com/labstack/echo/v4 v4.13.4
)
After creating, run go mod tidy to resolve dependencies.
2. .editorconfig
root = true
[*.toml]
indent_size = 4
indent_style = space
max_line_length = 100
trim_trailing_whitespace = true
[*.rs]
indent_size = 2
indent_style = space
max_line_length = 100
trim_trailing_whitespace = true
[*.md]
# double whitespace at end of line
# denotes a line break in Markdown
trim_trailing_whitespace = false
[*.{js,ts,vue,tsx,jsx,html,css,json,yaml,yml}]
indent_size = 2
indent_style = space
trim_trailing_whitespace = true
[*.go]
indent_size = 2
indent_style = tab
trim_trailing_whitespace = true
[*.proto]
indent_size = 2
indent_style = space
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
3. .golangci.yml
See references/golangci.yml for the full config.
Key principles:
- Use
version: "2"format - Start with
default: all, then disable noisy/opinionated linters - Enable
gofmt+goimportsformatters - Exclude generated code paths (
api/,ent/) withgenerated: lax - Set sane thresholds:
dupl: 600,nestif: min-complexity: 9
4. .gitignore
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
bin/*
dist/*
out/*
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool
*.out
# Go workspace file
go.work
# Editor and IDE
.idea
*.swp
*.swo
*~
.DS_Store
# Config files (keep example)
config/*
!config/config.yaml
__debug*
.env
.env.local
# Binary outputs (add your service names)
./main
5. CI Workflow (.github/workflows/ci.yml)
See references/ci.yml for the full workflow.
Key jobs:
- Lint (push only): golangci-lint with 7m timeout
- Unit Test:
go test ./... - Nilaway (null safety): static analysis excluding generated code
- Build Test (PR only):
go build ./... - Autofix (PR only): auto-fix lint and commit with
[autofix]tag, with conflict guard
6. Composite Action (.github/actions/setup-go-env/action.yml)
name: setup-go-env
description: Setup Go environment with required dependencies
runs:
using: composite
steps:
- uses: actions/setup-go@v6
with:
go-version: "^{{GO_VERSION}}"
cache: true
# Add system dependencies if needed:
# - run: |
# sudo apt-get update -qq
# sudo apt-get install --no-install-recommends -y \
# libsoxr-dev \
# pkg-config
# shell: bash
7. .vscode/settings.json
{
"go.useLanguageServer": true,
"go.lintOnSave": "package",
"go.vetOnSave": "workspace",
"go.coverOnSave": false,
"go.lintTool": "golangci-lint-v2",
"go.lintFlags": [
"--fast-only",
"--config=${workspaceFolder}/.golangci.yml"
],
"go.formatTool": "gofmt",
"go.inferGopath": true,
"go.coverOnSingleTest": true,
"go.testTimeout": "900s",
"go.testFlags": ["-v", "-count=1"],
"go.toolsManagement.autoUpdate": true,
"gopls": {
"build.buildFlags": [],
"ui.completion.usePlaceholders": true,
"ui.semanticTokens": true
},
"[go]": {
"editor.insertSpaces": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "always"
}
},
"go.testEnvVars": {
"LOG_LEVEL": "debug"
}
}
8. .vscode/extensions.json
{
"recommendations": [
"streetsidesoftware.code-spell-checker",
"mikestead.dotenv",
"EditorConfig.EditorConfig",
"yzhang.markdown-all-in-one",
"redhat.vscode-yaml",
"golang.go",
"bufbuild.vscode-buf"
]
}
9. renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard",
":semanticPrefixFixDepsChoreOthers",
":prHourlyLimitNone",
":prConcurrentLimitNone",
":ignoreModulesAndTests",
"group:monorepos",
"group:recommended",
"group:allNonMajor",
"replacements:all",
"workarounds:all"
],
"rangeStrategy": "bump",
"labels": ["dependencies"],
"minimumReleaseAge": "3 days"
}
10. .tool-versions
golang {{GO_VERSION}}
11. cspell.config.yaml
version: "0.2"
ignorePaths: []
dictionaryDefinitions: []
dictionaries: []
words:
- entgo
- Ents
- entsql
- enttest
- fxevent
- nolint
- stretchr
- structpb
- timestamppb
# Add project-specific words as they arise
12. Dockerfile (multi-stage)
See references/Dockerfile for the full template.
Key patterns:
- Build stage:
golang:<version>-bookwormwith system deps - Go mod download first (layer caching)
- Build all services under
./cmd/*dynamically - Runtime stage:
debian:bookworm-slim(minimal image) - Use
--mount=type=cachefor Go build cache
13. Uber FX Entry Point (cmd/<service>/main.go)
package main
import (
"log/slog"
"os"
"go.uber.org/fx"
"go.uber.org/fx/fxevent"
"{{MODULE_PATH}}/internal/configs"
"{{MODULE_PATH}}/internal/datastore"
"{{MODULE_PATH}}/internal/models"
)
func main() {
app := fx.New(
fx.WithLogger(func(logger *slog.Logger) fxevent.Logger {
return &fxevent.SlogLogger{Logger: logger}
}),
fx.Provide(func() *slog.Logger {
return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
}),
configs.Module(),
datastore.Module(),
models.Modules(),
// Add service-specific modules here
)
app.Run()
}
14. Uber FX Constructor Pattern
All services and models MUST follow this pattern:
type NewXXXModelParams struct {
fx.In
Client *ent.Client
Logger *slog.Logger
Config *configs.Config
}
func NewXXXModel() func(NewXXXModelParams) *XXXModel {
return func(params NewXXXModelParams) *XXXModel {
return &XXXModel{
client: params.Client,
logger: params.Logger,
config: params.Config,
}
}
}
Register in module files:
// internal/models/models.go
func Modules() fx.Option {
return fx.Options(
fx.Provide(xxx.NewXXXModel()),
// ... other models
)
}
15. Ent ORM Schema (schema/generate.go)
package schema
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./ --target ../ent --feature=sql/execquery,sql/schemaconfig,sql/lock,sql/upsert
Schema conventions:
- UUID primary keys:
field.UUID("id", uuid.UUID{}).Default(uuid.New).Unique().Immutable() - Soft delete:
field.Time("deleted_at").Optional().Nillable() - Enum status fields:
field.Enum("status").Values("ACTIVE", "INACTIVE") - Timestamps:
field.Time("created_at").Default(time.Now).Immutable()
16. .env.example
# =============================================================================
# Application Configuration
# =============================================================================
# Log level: debug, info, warn, error
LOG_LEVEL=info
# =============================================================================
# Database
# =============================================================================
DATABASE_DSN=postgres://user:pass@localhost:5432/dbname?sslmode=disable
# =============================================================================
# Service Ports
# =============================================================================
HTTP_PORT=8080
GRPC_PORT=9090
# =============================================================================
# Authentication
# =============================================================================
# JWT_SECRET=your-secret-here
# =============================================================================
# External Services
# =============================================================================
# REDIS_URL=redis://localhost:6379/0
17. CLAUDE.md Template
Generate a CLAUDE.md that documents:
- Project overview (what it does, key capabilities)
- Technology stack
- Essential commands (run, test, lint, generate)
- Architecture (services, layers, communication)
- Directory structure
- Development workflow (schema changes, API changes, service implementation)
- DI patterns (Uber FX constructors)
- Code style and best practices
Use the reference project's CLAUDE.md as a structural template, but tailor content to the new project.
Post-Initialization Steps
After scaffolding, run these commands:
# Initialize Go module
go mod init {{MODULE_PATH}}
go mod tidy
# Initialize git
git init
# Generate Ent code (if using database)
go generate ./...
# Verify lint config
golangci-lint run
# Verify build
go build ./...
# Create initial commit
git add .
git commit -m "feat: initial project scaffold"
Key Principles
- Config via .env — no YAML config files; use environment variables with
.envfor local dev - Uber FX everywhere — all DI through FX constructors, never manual instantiation
- Generated code is sacred — never edit
ent/orapi/generated/, always regenerate - Soft delete by default —
deleted_atfield on user-facing entities - Structured logging —
*slog.Loggerinjected via FX, neverfmt.Println - Factory pattern for providers — when supporting multiple backends (TTS, storage, etc.)
- Layered architecture — Model (CRUD) → Service (business logic) → Registration → Server