agent friendly template for a typical Golang project
Go to file Use this template
djmil 3403a8e168 result package
- usage tests as examples
- test utils
2026-04-01 18:43:28 +00:00
.devcontainer initial implementation 2026-03-05 21:52:10 +01:00
.githooks trim fat, use Gi philosophy 2026-03-29 19:24:55 +00:00
.vscode result package 2026-04-01 18:43:28 +00:00
cmd/app result package 2026-04-01 18:43:28 +00:00
examples/result result package 2026-04-01 18:43:28 +00:00
internal result package 2026-04-01 18:43:28 +00:00
pkg/result result package 2026-04-01 18:43:28 +00:00
.editorconfig initial implementation 2026-03-05 21:52:10 +01:00
.gitignore initial implementation 2026-03-05 21:52:10 +01:00
.golangci.yml trim fat, use Gi philosophy 2026-03-29 19:24:55 +00:00
CLAUDE.md result package 2026-04-01 18:43:28 +00:00
go.mod trim fat, use Gi philosophy 2026-03-29 19:24:55 +00:00
go.sum trim fat, use Gi philosophy 2026-03-29 19:24:55 +00:00
Makefile trim fat, use Gi philosophy 2026-03-29 19:24:55 +00:00
README.md result package 2026-04-01 18:43:28 +00:00
rename.sh trim fat, use Gi philosophy 2026-03-29 19:24:55 +00:00
tools.go trim fat, use Gi philosophy 2026-03-29 19:24:55 +00:00

go-template

A batteries-included Go project template optimised for PoC and hobby projects. Clone it, rename the module, run make init, and you're coding.


Features

Area Tool Purpose
Language Go 1.25 Modules, toolchain directive
Logging standard log/slog Structured JSON/text logging + WithField extension
Config standard flag CLI flags with defaults, no config files
Linting golangci-lint Aggregated linters, one config file
Security gosec + govulncheck SAST + dependency vulnerability scan
Tests standard testing Table-driven tests, manual fakes, no third-party test framework
Git hooks custom pre-push gofmt + go vet + golangci-lint + gosec on every push
Tool versions tools.go + go.mod Single source of truth for tool versions
Dev environment devcontainer Reproducible VSCode / GitHub Codespaces setup
IDE VSCode Launch configs, tasks, recommended settings

Getting started

Prerequisites

1. Clone and rename

git clone https://gitea.djmil.dev/djmil/go-template my-project
cd my-project

# Interactive rename — updates module path, config, devcontainer, and docs:
./rename.sh

2. Init (run once)

make init   # fetches deps, installs tools, configures git hooks

3. Build and run

make build  # compiles to ./bin/app
make run    # go run with default flags

Daily workflow

make test          # run all tests
make test-race     # … with race detector
make lint          # go vet + golangci-lint
make lint-fix      # go fix + golangci-lint auto-fix
make security      # gosec + govulncheck

Keyboard shortcut (VSCode): Ctrl+Shift+B → build, Ctrl+Shift+T → test.


Project structure

.
├── cmd/
│   └── app/
│       └── main.go          # Composition root (thin — just wiring)
├── internal/
│   ├── config/              # flag-based config (config.Load)
│   ├── logger/              # slog wrapper with WithField / WithFields
│   └── greeter/             # Example domain package (replace with yours)
├── .devcontainer/           # VSCode / Codespaces container definition
├── .githooks/               # pre-push hook (installed by `make setup`)
├── .vscode/                 # launch, tasks, editor settings
├── tools.go                 # Tool version pinning (build tag: tools)
├── .golangci.yml            # Linter configuration
└── Makefile                 # All development commands

Configuration

All configuration is via CLI flags with sensible defaults:

./bin/app -help
  -env string       environment: dev | staging | prod (default "dev")
  -log-level string log level: debug | info | warn | error (default "info")
  -name string      application name (default "go-template")
  -port int         listen port (default 8080)

Override at runtime:

./bin/app -port 9090 -env prod -log-level warn

Logging

The logger wrapper adds WithField and WithFields for ergonomic context chaining on top of the standard log/slog package:

log.WithField("request_id", rid).
    WithField("user", uid).
    Info("handling request")

log.WithFields(map[string]any{
    "component": "greeter",
    "name":      name,
}).Debug("generating greeting")

In production (app.env != dev) the output is JSON (slog.NewJSONHandler). In development, logger.NewDevelopment() uses the human-friendly text handler.


Testing

Tests use the standard testing package. Concrete types are returned from constructors — consumers (including tests) define their own minimal interfaces and satisfy them with manual fakes. No code generation required.

// Interface declared in the consumer (or _test.go), not in greeter package.
type greeter interface {
    Greet(name string) result.Expect[string]
}

type fakeGreeter struct {
    greetFn func(name string) result.Expect[string]
}

func (f *fakeGreeter) Greet(name string) result.Expect[string] {
    return f.greetFn(name)
}

func TestSomething(t *testing.T) {
    fake := &fakeGreeter{
        greetFn: func(name string) result.Expect[string] {
            return result.Ok("Hello, " + name + "!")
        },
    }
    // pass fake to the system under test
}

Git hooks

The pre-push hook runs gofmt + go vet + golangci-lint + gosec against the full codebase before every git push. Running on push (not commit) keeps the inner commit loop fast while still blocking bad code from reaching the remote.

govulncheck is intentionally excluded (it makes network calls and can be slow). Run it manually with make security.

To skip the hook in an emergency:

git push --no-verify

Tool version management

Tool versions are pinned via tools.go (a //go:build tools file) and tracked in go.mod/go.sum. This ensures every developer and CI run uses exactly the same versions.

To update a tool:

go get github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go mod tidy
make tools   # reinstall binaries

Devcontainer

Open this repo in VSCode and choose "Reopen in Container" — the Go toolchain and all tools (golangci-lint, gosec, govulncheck) are installed automatically via make init.

Works with GitHub Codespaces out of the box.


Next steps (not included)

  • HTTP server — add chi or gin
  • Database — add sqlx or ent
  • CI — add .github/workflows/ci.yml running make lint test security
  • Docker — add a multi-stage Dockerfile for the binary
  • OpenTelemetry — add tracing with go.opentelemetry.io/otel