Stage 1 · Code
Capstone — Ship a Real Tool
Capstone Scope
Define the scope, users, acceptance criteria, and skeleton for your final Go project before writing the first function.
Choose Your Project
For the capstone you will build either a JSON HTTP service or an enhanced version of the task tracker CLI from Module 06. Both are real, deployable programs.
| Option | What you build | New skills practiced |
|---|---|---|
| Task tracker (CLI) | Full CRUD CLI with file storage, tests, and a release binary | Flags, file I/O, JSON, testing, cross-compilation |
| JSON HTTP service | REST API with handlers, middleware, JSON responses, and graceful shutdown | net/http, JSON encoding, context, signal handling |
Both projects cover the same Go fundamentals. The HTTP service is closer to real SRE tooling (think exporters, health-check servers, webhook handlers). The CLI is simpler to test and distribute. Either choice is valid.
Define Users and Commands
Before writing code, write down who uses your tool and what they can do. A one-page spec prevents feature creep and gives you a clear finishing line.
# User: any engineer on a Unix/Windows machine
# Purpose: manage personal tasks from the terminal
# Commands:
tasker add -title "Deploy service" [-tag ops] [-priority high]
tasker list [-tag ops] [-priority high] [-done false]
tasker done -id <task-id>
tasker delete -id <task-id>
tasker help
# Output: clean terminal table, JSON with --json flagAcceptance Criteria
Acceptance criteria are the bar that defines done. Write them before coding so you know when to stop adding features.
- All five commands work end-to-end against a real
~/.tasker/tasks.json. - Invalid input (missing -title, unknown command) prints a helpful error and exits with code 1.
- Concurrent writes are safe — the file is written atomically.
- Test coverage ≥ 80% on
internal/packages. - Cross-compiled binaries exist for linux/amd64, darwin/arm64, windows/amd64.
- Binary includes embedded version, commit, and build date.
Project Skeleton
mkdir -p tasker/{cmd/tasker,internal/{task,cli},testdata}
cd tasker
go mod init github.com/thesyscoder/tasker
# Create placeholder files
touch cmd/tasker/main.go
touch internal/task/task.go internal/task/store.go internal/task/store_test.go
touch internal/cli/commands.go internal/cli/commands_test.go
touch Makefile
cat Makefile
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo none)
DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
LDFLAGS = -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(DATE)
.PHONY: build test lint release
build:
go build -ldflags "$(LDFLAGS)" -o tasker ./cmd/tasker
test:
go test -race -cover ./...
lint:
go vet ./...
go fmt ./...
release:
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o dist/tasker_linux_amd64 ./cmd/tasker
GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o dist/tasker_darwin_arm64 ./cmd/tasker
GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o dist/tasker_windows_amd64.exe ./cmd/taskerMark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.