Architecture area

Golden-path scaffolding

Every new service starts from a versioned template that already builds, ships, runs safely and shows up in monitoring and the portal. Good standards are where a team starts, not a checklist it works through later.

The shape

A versioned template in platform-scaffolds is rendered once by scaffold-operator into the new repository: service code, a Helm chart, a migrations directory, CI and a catalog file. Each part is what lets the rest of the platform pick the service up: the portal finds it, observability scrapes it, releases deploy it, bindings attach to it and schemas migrate its database. TEMPLATE THE NEW REPOSITORY WHAT THE PLATFORM GETS platform-scaffolds golang-service/v0.13.0 immutable tag render once one commit Service health · /metrics · JSON logs · landing page Helm chart 2 replicas · probes · limits · PDB · HTTPS Database slot binding off · migrations/ ready CI + catalog-info.yaml test · lint · image · schema package Observable scraped with platform labels Safe to roll out readiness-gated, reachable Bindable · migratable via Release · DatabaseSchema Deployable + in the portal images to release, auto-discovered
Scroll sideways to see all of it. Each part of the generated repository is what lets another part of the platform pick the service up.

What every service gets

Runs safely

Two replicas, readiness-gated rollouts that never drop below capacity, startup, readiness and liveness probes, CPU and memory requests and limits, and a PodDisruptionBudget.

Observable from the first deploy

Prometheus metrics (runtime, HTTP request count and latency by route, database pool) and a ServiceMonitor that adds the platform's identity labels. JSON logs with one access-log line per request. See Observability.

Reachable

A public HTTPS endpoint at <service>.<env>.gerodimos.dev with a landing page, while /metrics is answered with a 404 at the load balancer, so only in-cluster Prometheus sees it.

Ready for a database

The chart ships the database binding switched off. A Release switches it on per environment, without touching the service's code. See Service bindings. A migrations/ directory and a schema workflow that checks and packages it come with it. See Database schemas.

Shippable

CI runs Go tests, helm lint and chart unit tests, then pushes an image tagged with the commit, which is what a Release deploys. When migrations/ changes, a second workflow checks them and publishes a schema package the same way.

Discoverable

A catalog-info.yaml with the owning team, so the service appears in Backstage without anyone registering it.

How it works

  1. Pick a template and a version

    Backstage's Onboard Service form lists the template's release tags and defaults to the newest. The choice is recorded on the Component.

    spec:
      scaffold: {template: golang-service, version: "0.13.0"}
  2. Render once, as one commit

    component-operator creates the repository and a ScaffoldRequest; scaffold-operator renders the tagged template into it as a single commit and records exactly which template commit it used. It never touches the repository again.

  3. Deliberately simple templating

    Only files ending in .tpl are rendered, and only exact {{ parameterName }} placeholders are replaced. Helm templates and GitHub Actions expressions pass through untouched, because they belong to engines that run later.

  4. Versioned like a product

    Each template has its own SemVer tags, and a release check fails unless the template's declared version matches the tag. A change can be tried on a real generated repository (tests, chart lint, image build) before it's tagged.

Design choices

Conventions in the template, not the API

Probes, replicas, metrics and endpoints are template defaults, not Component fields. The API stays a small identity record, and a team can still change any default in its own repository.

Render once, then hand over

The team owns its repository from the first commit. Nothing reaches back in and overwrites its changes.

Trade-off: existing services don't get template improvements automatically; moving to a newer version is a pull request in each service's repository.

The platform contract is built in

The chart puts the platform's component and environment labels on every workload. Everything that finds services by those labels (dashboards, the portal, Argo CD lookups) works for a new service with no setup.

Dumb templating on purpose

No logic in the template engine means no surprises in generated files, and no collision with Helm's or GitHub Actions' own {{ }} syntax.

Not built yet

  • A second template. golang-service is the only one; adding another is adding a directory.
  • Assisted upgrades. Nothing yet opens the upgrade pull request for services on an older template version.

Code