One security engineer cannot review every service a hundred developers write. The arithmetic does not work, and no amount of process discipline fixes it.
What does work is deciding things once, in a template, so that every service created afterwards starts with those decisions already made. A team that generates services from a good scaffold gets authentication, logging, secret handling and tenant scoping for free, and nobody had to remember any of it.
This post is what belongs in that template and how to stop it rotting. For whoever is outnumbered.
Why defaults beat policies
A policy is a request for future attention from people who have other priorities. A default is a decision already made, and the effort is in deviating from it rather than in following it.
That inversion is the entire value. A developer creating a service from a template does not decide whether to wire up structured logging with redaction; it is there. They would have to remove it, which nobody does, because they were never thinking about logging in the first place.
The corollary is that a template is only a control if it is the path of least resistance. If generating a service takes longer than copying the last one, people copy the last one, and you have written documentation rather than built a control.
What goes in
Aim for the things that are tedious to add later and easy to get subtly wrong.
Authentication and authorisation wired, not documented. A middleware in place, a deny-by-default route configuration, and a failing test asserting that an unauthenticated request to a new endpoint gets rejected. That test is what catches the route somebody adds on a Friday.
Tenant scoping in the data layer. If you are multi-tenant, the repository layer should make an unscoped query difficult or impossible. This is the single highest-value item for a multi-tenant product, because the alternative is relying on every query being written correctly forever.
Structured logging with redaction, allowlisted. Fields named explicitly rather than whole objects serialised.
Secrets from the secret manager, with no path that reads a credential from a committed file. If the template makes the right way the easy way, the wrong way stops appearing.
Health, readiness and a graceful shutdown handler that drains rather than dropping work.
A CI pipeline with the gates already configured, running in the positions that make sense: secret scanning pre-commit, diff-scoped analysis at the pull request, image scanning post-merge.
Dependency manifest with a frozen install command, and a lockfile committed from the start.
A CODEOWNERS file requiring an owner before the service can merge anything, so no service is created without one.
An outbound HTTP client with timeouts, retries and a bounded connection pool. Not security on its own, and the absence of timeouts is how one service's slowness becomes everyone's outage.
What to leave out
Anything a team will need to customise heavily. If eight of ten services delete a component, it does not belong in the template; it belongs in a library they can choose.
Opinions about the domain. A template that assumes a data model will be fought and abandoned.
Every possible integration. A template that generates two thousand lines nobody reads is worse than a small one people understand, because the unread parts get copied forward unexamined for years.
Small and genuinely good beats comprehensive.
The rot problem
Templates decay in a specific way: they produce services that are correct on the day they are generated and never updated afterwards.
Two years in you have forty services, each carrying a snapshot of your standards from whenever it was created, and a vulnerability in the shared middleware means forty separate pull requests by forty different teams.
Three things help, in increasing order of effort:
Record the template version in the generated service. A file recording which version produced it. Then you can answer "which services predate the fix", which is the question you will have during an incident.
Put the shared parts in a library, not in copied code. Middleware, the logging configuration, the HTTP client. A version bump is then a dependency update, which your existing automation already handles, rather than a code change in every repository.
Regenerate and diff, periodically. A tool that generates a fresh scaffold and shows what has drifted. Even run manually once a quarter, it tells you where the estate has diverged.
The library point is the important one. Anything in the template as literal code is a copy you now maintain in every service. Anything in a library is a dependency you bump once.
Measure whether it is being used
A template nobody uses is not a control, and this is easy to check.
Count the services created in the last quarter, and how many came from the template. If the number is low, find out why: usually it is too slow, it is missing something people need, or nobody knows it exists. All three are fixable and none of them is a discipline problem.
Also track how long generation takes. If it is more than a couple of minutes from command to running service, you are competing with copy-and-paste and losing.
The concession
Templates encode today's understanding, and some of today's understanding is wrong. A mistake in a widely used scaffold propagates to every service built from it, and it propagates silently, because nobody re-examines generated code.
That is a real cost and it argues for keeping templates small, keeping the substantive parts in versioned libraries where a fix can be rolled out, and reviewing the template itself with the care you would apply to the most important service you have. Which, in effect, it is.
The implication
At one security engineer per hundred developers, the only interventions that scale are the ones applied before anyone writes code. Review does not scale. Training does not scale. Defaults do.
So if you have time to build one thing this quarter, build the thing that makes the next fifty services start correct, and spend your review attention on the small number that deviate from it.