5.8 KiB
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
- Go 1.25+
- Git
- (Optional) VSCode + Go extension
- (Optional) Docker for devcontainer
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. For dependencies on interfaces, write
a manual fake inline in the test file — no code generation required:
type fakeGreeter struct {
greetFn func(name string) (string, error)
}
func (f *fakeGreeter) Greet(name string) (string, error) {
return f.greetFn(name)
}
func TestSomething(t *testing.T) {
fake := &fakeGreeter{
greetFn: func(name string) (string, error) {
return "Hello, " + name + "!", nil
},
}
// 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.