Skip to content

Standardised CI/CD Across a Repository Fleet

Lesson 3 of 8Advanced16 min readGit at Scale & Enterprise Engineering · Repository FleetVerified: GitHub Actions reusable workflow documentation, September 2026

Four hundred repositories with four hundred hand-written pipelines means every improvement is a campaign and every security fix is a project.

The alternative is that pipelines are called rather than written — and the difference is the single largest determinant of how expensive it is to change anything about how your organisation builds software.

A reusable workflow is called by another workflow, with inputs and secrets passed in. The caller’s file is a few lines; the logic lives centrally.

A composite action packages steps for use within a job. Finer-grained, and the right unit for a piece of a pipeline rather than a whole one.

Required workflows, through rulesets, can mandate that specified workflows pass before merging, at organisation and enterprise level. That is enforcement rather than convention.

The benefit is not consistency for its own sake — it is that a change made once reaches everything, which turns a fleet-wide security fix from a quarter into an afternoon.

The risk is a shared workflow that does not fit, which teams will bypass, and a bypassed standard is worse than none because it creates the appearance of coverage.

Making the case, because standardisation is a platform investment that competes with delivery work.

A security fix to a common pipeline pattern. With four hundred hand-written pipelines: identify the affected repositories, generate four hundred pull requests, chase four hundred reviews, track the tail for a quarter. With a shared workflow: one commit, move the tag, done in an afternoon.

An action reaching end of life. Every repository referencing it needs updating. The same arithmetic.

A required new scan. Four hundred pull requests, or one change to the shared workflow plus a ruleset.

A performance improvement. Nobody does this across four hundred repositories, which means it never happens — and the estate stays slow permanently.

Onboarding a new service. A day of pipeline authoring, repeated for every new service, forever.

The compounding effect is the real argument. It is not that any single change is expensive; it is that the expense discourages changes, so the estate’s pipelines are frozen at whatever they were when each repository was created. An organisation that cannot change its pipelines cheaply does not change them, and the accumulated cost of that is invisible because it consists of improvements that never happened.

Quantify one instance. Take the last fleet-wide pipeline change your organisation attempted and count the elapsed time and the pull requests. That number, next to “one commit”, is the business case.

The caller is short:

name: CI
on: [push, pull_request]
jobs:
build:
uses: example-org/workflows/.github/workflows/node-service.yml@v3
with:
node-version-file: '.nvmrc'
secrets: inherit

Everything that actually happens is in the called workflow, maintained by one team, improved once for everybody.

Design the interface carefully. Inputs are the contract, and a reusable workflow with thirty inputs is a configuration language nobody wants to learn. Aim for a handful, with sensible defaults.

Be explicit about secrets. secrets: inherit is convenient and passes everything the caller has; naming individual secrets is narrower and better where the shared workflow needs only one or two.

Cover the common case, not every case. A workflow that handles 80% of repositories cleanly, with a documented escape hatch for the rest, beats one that handles 100% through a configuration surface nobody can navigate.

Package a sequence of steps for use inside a job, rather than a whole workflow.

The right unit for a piece of pipeline — a standard checkout with the right filters, a login sequence, an artifact publication.

Compose better than reusable workflows for repositories that need their own job structure but want the standard steps.

And they are a good migration path. A team unwilling to adopt a whole shared workflow may readily adopt a shared checkout action, and that is a foothold.

Keep them focused. A composite action doing eleven things is a workflow wearing the wrong shape.

The shared workflow is a dependency of four hundred repositories. Treat it as one.

Callers reference a version, not the default branch. A tag or a release, so a change to the shared workflow does not immediately affect every pipeline.

Semantic versioning conventions apply. A breaking change to the input contract is a major version; everything else is not.

Support the previous major version for a stated period. Forcing four hundred repositories to migrate simultaneously is a campaign nobody wants.

But do not support versions forever. Three concurrent major versions of the shared workflow is three implementations to maintain, and it will be four before long.

Test before moving a tag. A shared workflow with no test suite of its own is a shared workflow that will eventually break four hundred pipelines simultaneously.

Rulesets can require that specified workflows pass before merging, at organisation and enterprise level.

Which converts a shared workflow from something teams call to something they cannot avoid. A required security scan cannot be removed by editing the repository’s own workflow files.

Use it sparingly. A required workflow applies to every targeted repository, must run successfully in all of them, and a workflow that fails in an unusual repository blocks it permanently.

Target by property. A required security scan on production-tier repositories is a defensible scope; the same on every repository including documentation is friction.

Roll it out with the same discipline as any policy. Observe first, announce, then enforce. See policy rollout.

The input contract is the design, and it determines whether the shared workflow is usable or is a burden.

Fewer inputs, better defaults. Every input is a decision the caller must make and a case the workflow must handle. A workflow with four inputs and good defaults serves more repositories than one with twenty.

Derive rather than ask. If the workflow can read the Node version from .nvmrc, it should, rather than requiring an input. Conventions beat configuration.

Make the common case zero-configuration. A caller with no inputs at all should get correct behaviour for the standard repository. Inputs exist for the exceptions.

Name inputs for what they mean, not for the implementation detail they control. run-integration-tests is intelligible; enable-stage-2 is not.

Prefer booleans and enumerations to free strings. A string input becomes a place where callers put arbitrary things, and the workflow ends up parsing them.

Resist “just add an input”. Every request for a new input is a request for the workflow to handle another case, and a workflow handling forty cases is a configuration language. Sometimes the right answer is that this repository should not use the shared workflow.

Document the contract, with an example caller for each common scenario. A reusable workflow with no documented examples will be used incorrectly and then blamed.

A reusable workflow runs with the secrets the caller passes it, and the choice of how to pass them matters.

secrets: inherit passes everything the caller has access to. Convenient, and a broad grant — the shared workflow, maintained by another team, receives every secret in the calling repository.

Naming individual secrets is narrower and is the right default for a workflow needing one or two.

The trust question is real. A shared workflow is code from another team running with your credentials. That is usually fine — it is the platform team — and it is worth being conscious of, particularly if shared workflows accept contributions.

Prefer OIDC to passing cloud credentials. A shared deployment workflow that exchanges the job’s identity for short-lived cloud credentials never touches a stored secret. This is the pattern to aim for, and it resolves the trust question entirely for the cases it covers.

Restrict who can change the shared workflow. CODEOWNERS on the shared workflows repository, with review by the platform team. A change there runs with four hundred repositories’ secrets.

A shared workflow nobody uses standardises nothing. This is where the effort actually goes.

Make it better than what they have. Faster, with caching done properly, with the checkout optimised, with sensible defaults. A shared workflow that is simply a mandate will be resisted; one that makes their pipeline faster will be adopted.

Put it in the template, so new repositories use it without deciding.

Migrate the highest-volume repositories first, with help. A team that gets their pipeline migrated for them, and finds it faster, becomes an advocate.

Handle the objections honestly. “It doesn’t do X” is either a gap to fix or a genuine reason that team should not adopt. Both are legitimate answers; pretending the gap does not exist is not.

Provide the escape hatch explicitly. Some repositories genuinely need their own pipeline. Saying so, and documenting when it is appropriate, prevents teams feeling they must smuggle it past you.

Measure adoption, and treat a plateau as feedback rather than as non-compliance.

A structural decision with consequences for access, review and versioning.

One repository for shared workflows and actions is the common arrangement. Simple to find, one place to review, one release process.

It must be readable by every calling repository. Which for an enterprise generally means internal visibility — every enterprise member can read it, and callers in any organisation can reference it.

Write access should be narrow. The platform team, via CODEOWNERS, because a change there executes in four hundred repositories with their secrets.

Accept contributions through pull requests. The teams using it know what is missing, and a shared workflow that only its owners can improve will stop matching what people need. This is innersource applied to the most consequential shared code in the organisation.

Tag releases from it. Version references point at tags, and the release process is what moves them.

Consider splitting once it is large. A repository containing forty workflows and thirty composite actions is one release cadence for everything, which means an unrelated change forces a version bump for all consumers. Splitting by domain is a reasonable second-stage move and unnecessary at the start.

And protect its default branch properly. It is the highest-consequence repository in the estate by blast radius, and it should have the strictest controls you apply anywhere.

Checkout, with the right depth, filter and cone. Small, universal, and a measurable improvement. See CI clone optimisation.

Security scanning. Uniform, required, and something teams do not want to maintain themselves.

Dependency and build caching. Frequently done badly per repository, and a shared implementation is immediately faster.

Artifact publication, including provenance and signing. See build provenance.

Deployment, where deployment targets are uniform. The highest-value and hardest, because deployment varies most.

What not to standardise: the build itself, in a polyglot estate. A shared workflow attempting to build every language becomes a configuration language, and teams are better served by a shared scaffold with a per-ecosystem implementation.

A shared workflow is code with four hundred consumers and no type system. Testing it is not optional.

A test repository per supported scenario. A repository that calls the workflow as a typical caller would, with a pipeline that runs on every change to the shared workflow. This is the integration test and it catches most breakage.

Test the failure paths. A workflow that behaves correctly when everything succeeds and hangs when a step fails is a workflow that will hang four hundred pipelines.

Test against the oldest supported major version as well as the newest, so a fix does not break the version people have not migrated from yet.

Run the tests before moving a mutable tag. This is the moment the change reaches everybody, and it is the moment to be certain.

Stage the rollout where you can. A canary set of repositories on the new version before the major tag moves. Not always practical, and worth doing for significant changes.

Have a rollback. Moving a tag back is quick; knowing in advance that you will do that, rather than debating it, is what keeps an incident short.

And monitor caller failures after a change. A spike in failed workflow runs across many repositories immediately after a shared workflow change is the signal, and it should reach the owning team quickly.

Changing a shared workflow’s contract is a fleet-wide migration, and it needs the same discipline as any other.

  1. Release the new major version alongside the old. Both supported.

  2. Document what changed and how to migrate. A short guide, with a before-and-after example.

  3. Announce a deprecation date for the old version. Six months is generous; three is usually enough.

  4. Migrate the easy callers automatically where the change is mechanical — a bulk pull request updating the version reference. See API automation.

  5. Track remaining callers on the old version. A query across workflow files, run weekly.

  6. Chase the tail individually. The remaining callers usually have a reason, and the reason is frequently a gap in the new version.

  7. Warn from within the old version as the date approaches — a step that logs a deprecation notice is seen by the people who need to see it.

  8. Then remove it, on the announced date, having told everybody twice.

Do not skip step 5. A deprecation without a list of who is affected is a deprecation that will surprise somebody on the day.

And be willing to extend. If half the callers have not migrated, the migration guide is inadequate or the new version has a gap. Extending the date while fixing the cause is better than breaking half the estate on principle.

Once shared workflows exist, a few numbers tell you whether the standardisation is real.

Adoption. Repositories calling the shared workflow, as a proportion of those that could. The headline metric.

Version distribution. How many callers on each major version. A long tail on an old version is a deprecation problem forming.

Distinct workflow files. Should fall as adoption rises. If it does not, teams are calling the shared workflow and keeping their own.

Distinct actions referenced across the estate, and the proportion pinned by SHA. Both should improve as the shared workflow becomes the path.

Median pipeline duration, before and after migration. The number that persuades teams, and the one to publish.

Failure rate attributable to the shared workflow. If it rises after a change, the change is the cause and the tag should move back.

Time from request to capability. How long a team waits for the shared workflow to support something they need. This is the responsiveness metric, and it predicts whether adoption holds.

Publish the adoption and duration numbers, and send the version distribution to the teams on old versions. The rest are for the owning team.

Referencing the shared workflow’s default branch. Every change lands everywhere immediately.

Thirty inputs. A configuration language nobody learns.

No tests on the shared workflow. It will break four hundred pipelines eventually.

Mandating before it is better. Resistance, and bypass.

No escape hatch. Teams smuggle their own pipelines rather than asking.

Supporting five major versions. Five implementations to maintain.

Required workflows targeted at everything. Friction where there is no benefit.

Standardising the build in a polyglot estate. Produces a configuration language.

No owner for the shared workflows. Everybody’s dependency, nobody’s responsibility.

Standardised CI works when both sides get something, and fails when it is presented as a requirement.

What the platform team gets: the ability to change how the organisation builds software with one commit. Security fixes, action version bumps, performance improvements, new required scans — each reaching four hundred repositories without four hundred pull requests.

What the teams get: a pipeline they did not have to write, that is faster than the one they would have written, that somebody else maintains, and that keeps working when Actions changes underneath it.

The bargain fails when the platform team takes the first without delivering the second — a mandated workflow that is slower, less flexible and unresponsive to requests. Teams then bypass it, and the coverage the platform team believes it has does not exist.

Which makes responsiveness the critical property. A team asking for a capability and getting it in a fortnight will stay. One waiting three months will fork the workflow into their own repository and never come back.

Fund it accordingly. Shared workflows are a product with four hundred internal users, and treating their maintenance as a background task guarantees the failure above.

And measure the right thing. Adoption percentage is the health metric, not the number of workflows published. A shared workflow used by 90% of repositories is a success; ten shared workflows used by nobody is a project that consumed a quarter.

The same reasoning applies to other shared build infrastructure, and it is worth stating because the mechanisms differ.

Base container images. Every service building from a shared base means a patched base reaches everything on rebuild. The same centralisation argument, with the same versioning and deprecation obligations.

Shared build configuration — linting rules, formatter settings, compiler options — distributed as a package rather than copied into every repository.

Deployment tooling. A shared deployment mechanism, called with parameters, rather than per-repository scripts.

Policy as code. Shared policy definitions consumed by every pipeline. See policy as code.

The pattern is identical in each case: define once, version, consume by reference, deprecate deliberately.

And the failure mode is identical: a shared thing that does not fit gets copied and modified, at which point you have four hundred variants and the appearance of standardisation.

Which suggests the general test. For anything you are considering centralising, ask what happens when a team needs it to be different. If the answer is “they copy it”, the centralisation will erode. If the answer is “they pass a parameter” or “they use the documented escape hatch”, it will hold.

Centralising pipeline logic converts a fleet-wide change from a campaign into a commit. The mechanism is straightforward; the difficulty is that a shared workflow is a product with four hundred users, and it succeeds on the same terms any product does — it must be better than the alternative, versioned responsibly, and owned by somebody.

  • A reusable workflow is called with inputs and secrets; a composite action packages steps within a job
  • Required workflows in rulesets mandate that specified workflows pass, at organisation and enterprise level
  • Callers should reference a version, and moving a mutable major tag affects everything at once
  • The shared workflow needs its own tests, because a bad change breaks every caller
  • Adoption is the hard part, and a shared workflow succeeds by being better rather than by being mandated
  • Standardise checkout, scanning, caching and publication; avoid standardising the build in a polyglot estate
  • Provide an explicit escape hatch, or teams will bypass without telling you
  • Support a bounded number of major versions, with a stated deprecation period
  • A shared workflow is a product with users and needs an owner

Use an organisation you administer.

  1. Count distinct workflow files across the estate. Predict: how many are near-identical copies?

  2. Pick the most common pattern. How many repositories implement it independently?

  3. Estimate the cost of a security fix to that pattern today, in pull requests.

  4. Design the reusable workflow that would replace it. List its inputs — aim for fewer than six.

  5. Identify what it would need to do to be faster than the current implementations.

  6. Find one repository that genuinely could not adopt it. Why?

  7. Design the versioning scheme and the deprecation period.

  8. Estimate adoption after six months, and what would drive it.

Engineering Team Onboarding SystemA 30-day Git and GitHub programme with standards templates, assessments and governance checklists.