Lesson 4 · Ship it — code → prod plumbing

Build system / Skaffold

How 30+ services become one binary in one image that runs as any of them — and the two very different local workflows that get it onto a cluster.

Your win: explain the single-binary model, and tell the two local build paths (Skaffold+kind vs docker-compose) apart.

One binary, all services

The monorepo compiles every service into a single server binary. Each service is pulled in by a blank import and self-registers; the runtime picks which service to be via a cobra subcommand:

cmd/server/main.go (shape)
import (
    _ ".../cmd/server/eureka"       // blank import → self-registers
    _ ".../cmd/server/notificationmgmt"
    // ... every service
)
// root cobra command "server" → two subtrees:
//   gserver  → run the gRPC server for a service
//   gjob     → run a job (sql_migrate, crons, data migrations)
The service is chosen at runtime, not at build time There is one binary and one image. "Being eureka" vs "being notificationmgmt" is a matter of which cobra subcommand runs and which config/secrets files are mounted. Kubernetes runs the same image many times with different args — that's why you saw gjob sql_migrate (Lesson 1) and gjob notificationmgmt_consumers (Part 3): same binary, different subcommand.1

Cross-compiled for linux

The local build script cross-compiles for the container platform, not your Mac:

local/build-code-golang.bash
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./build/server ./cmd/server/
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./build/bdd.test ./features/
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./build/stub ./features/stub/

build/server is the hot-swapped server; build/bdd.test is the BDD runner (into gandalf); build/stub is the Learnosity mock. They only run inside a linux container.1

Two Dockerfiles — dev copies, prod compiles

dev (development.Dockerfiledeveloper)prod (release.Dockerfilerunner)
basedebian:12.11alpine:3.21.7
binarycopies the pre-built ./build/servercompiles /server inside a build stage
whyfast local loop (build once, copy in)reproducible, self-contained prod image

Both end with ENTRYPOINT ["/server"] and bundle /migrations. The dev image trusts your local cross-compiled binary; the prod image rebuilds from source in CI.2

Two local paths — and only one is Skaffold

This trips people up: there are two independent ways to run the stack locally.

Skaffold + kinddocker-compose
launchdeployments/sk.bashskaffold.local.yaml./local/run.bash (the one CLAUDE.md documents)
howcustom build cmd → docker buildkind load → Helm-deploy every servicecompose services; hot-swap build/server into a running container
usefull Kubernetes-like stackthe fast inner dev loop
Skaffold's build hides in profiles In skaffold.backend.yaml the top level is only deploy.helm.releases (one per service). The actual build lives inside profiles — the local profile uses a custom.buildCommand (not a Docker artifact block) that builds the three binaries, docker build --target developer, tags backend:locally, and kind loads it. It even fails on purpose if you try to push a local image. And ./local/run.bash doesn't touch Skaffold at all.3
Backend use case Backend use case: this is the model behind cobra subcommands, cross-compiled local binaries, dev vs prod Dockerfiles, and Skaffold vs compose workflows.
Common mistake Common mistake: assuming each service has its own dedicated binary or that local compose and Skaffold are the same execution path.
Read this next

Skaffold — the docs

The build/tag/deploy loop, profiles, and custom build commands — match them to skaffold.backend.yaml.

skaffold.dev/docs
→ in-repo cmd/server/main.go, local/build-code-golang.bash, skaffold.backend.yaml, developments/*.Dockerfile

In plain English Plain English: the platform builds one binary that can wear many service and job roles depending on which subcommand and config you run.

Check yourself (from memory)

Q1. How does one image run as many different services?

One binary, all services; the runtime picks one via a cobra subcommand (gserver/gjob) and the mounted config/secrets.

Q2. How do the dev and prod Docker images differ?

Dev (developer, debian) copies your cross-compiled build/server; prod (runner, alpine) compiles from source in CI.

Q3. Which local path uses Skaffold?

Skaffold+kind is deployments/sk.bash; ./local/run.bash is the separate docker-compose hot-swap path.
Recall: the single-binary model, the two images, the two local paths.
one binary + cross-compile + images + paths, then reveal
One binary, all services (cmd/server/main.go, blank imports self-register); runtime picks the service via a cobra subcommand (gserver/gjob) + mounted config — same image run many times. Cross-compile (build-code-golang.bash, GOOS=linux GOARCH=amd64) → build/{server,bdd.test,stub}. Two images: dev (development.Dockerfile:developer, debian, copies prebuilt binary) vs prod (release.Dockerfile:runner, alpine, compiles inside). Two local paths: Skaffold+kind (deployments/sk.bash → custom build → kind load → Helm; build lives in profiles) vs docker-compose (./local/run.bash, hot-swap the binary — NOT Skaffold).
🎯 Interview one-liner "How is the monorepo built and deployed?" → "Every service compiles into one binary; the runtime picks which service to be via a cobra subcommand and mounted config, so Kubernetes runs one image many times. Local dev copies a cross-compiled binary into a debian image for speed; prod compiles a slim alpine image in CI. We use Skaffold with kind for a full local cluster, and a docker-compose hot-swap loop for fast iteration."
That's Part 1 — you can ship a change: migrate the schema, pass the gates, wire the secrets, and build the image. Take the four quizzes cold, then tell me "build Part 2" for the reliability patterns — idempotency, retries, and the transactional outbox. Ask me anything first.

1. In-repo: cmd/server/main.go (blank imports, servergserver/gjob), internal/golibs/bootstrap/command.go, local/build-code-golang.bash.

2. In-repo: developments/development.Dockerfile (developer, copies prebuilt), developments/release.Dockerfile (runner, compiles inside).

3. In-repo: skaffold.backend.yaml (deploy.helm top-level; build in profiles; custom.buildCommand; push guard :317-320), skaffold.local.yaml, deployments/sk.bash, local/run.bash.