Matrix Strategies, Dynamic Matrices, Fail-Fast, and Parallel Test Design: Configuration, Design Patterns, and Trade-Offs
A matrix is an interface for expressing support policy, not a convenient way to multiply jobs. This lesson compares broad and risk-based matrices, static and dynamic generation, fail-fast and full-evidence runs, experimental tolerance, and the separate roles of max-parallel and repository concurrency.
Learning objectives
- Choose matrix dimensions from a support/risk model instead of maximizing combinations.
- Select static or dynamic generation based on ownership, auditability and change frequency.
- Choose fail-fast or full-evidence behavior according to the decision the run must support.
- Separate experimental tolerance from mandatory support and document prerequisites.
-
Distinguish matrix
max-parallelfrom Chapter 11 repository/job concurrency.
1. Start with the compatibility claim
A matrix should answer a question that matters to users or operators: “does this library support these runtimes?”, “does this binary build on these operating systems?”, or “does this deployment template work in these modes?”. An axis without a policy question adds cost and failure surface without necessarily adding confidence.
2. Broad matrix versus risk-based sampling
| Choice | Use when | Benefit | Cost / risk | Evidence requirement |
|---|---|---|---|---|
| Broad Cartesian | Every combination is an explicit support promise | High coverage | Combinatorial cost and noisy failures | Per-cell result for every supported combination |
| Risk-based sample | Some dimensions are equivalent or low risk | Lower latency/cost | Can miss interaction defects | Written rationale for omitted combinations |
| Include-only cells | Support contract is irregular | Exact, readable set | More manual maintenance | Each include entry maps to a named support claim |
Do not use the 256-job limit as a target. It is a safety ceiling, not a quality recommendation.
3. Static versus dynamic matrix
A static matrix keeps policy visible in the workflow revision and is easiest to review. A dynamic matrix is useful when a prior job computes affected packages, supported versions, shards or environments. Dynamic generation adds a new trust and evidence boundary: the planner code and its JSON output now determine what work exists.
Validate generated JSON, cap the number of cells, and preserve the planner output. Never let untrusted event text expand directly into privileged runner labels, deployment targets or other sensitive execution choices without validation.
4. Fail-fast versus full evidence
| Policy | Good fit | Failure effect | Trade-off |
|---|---|---|---|
fail-fast: true |
Fast feedback where one mandatory failure invalidates the batch | Other queued/in-progress cells may cancel | Lower cost; incomplete compatibility evidence |
fail-fast: false |
Release qualification / compatibility census | All cells may finish | Higher cost; stronger evidence |
Experimental continue-on-error |
Advisory future/non-contract cell | Cell failure is tolerated by workflow policy | Must never be used to hide a required cell |
5. max-parallel versus concurrency groups
max-parallel limits how many generated jobs of one
matrix execute simultaneously. Chapter 11 concurrency groups
coordinate jobs/runs that share a named critical resource across the
repository. If ten matrix cells test against a rate-limited
simulator, max-parallel: 2 may be enough. If multiple
workflows deploy to the same environment, use
concurrency/serialization rather than hoping each matrix uses a
small parallel number.
6. Matrix with reusable workflows
GitHub supports a matrix strategy on a job that calls a reusable workflow. This is powerful for platform teams: the caller owns the dimension values while the called workflow owns the standardized implementation. However, outputs from a matrixed reusable workflow are not a natural “map of all cells”; current semantics choose the output from the last successful completing reusable-workflow execution that actually sets a value. Use artifacts or an explicit aggregation mechanism when you need all cell results.
jobs:
call-tests:
strategy:
matrix:
target: [linux-py312, linux-py313, windows-py313]
uses: ./.github/workflows/reusable-test.yml
with:
target: ${{ matrix.target }}
7. Worked decision: a small SDK
| Question | Decision | Prerequisite | Observable proof |
|---|---|---|---|
| Supported OS/runtime | Ubuntu 24.04 + Windows 2025; Python 3.12/3.13 | GitHub-hosted runner availability | Generated jobs + pinned runtime versions |
| Future runtime | One advisory cell | Explicit experimental:true |
continue-on-error policy + separate artifact |
| Failure policy | fail-fast:false on release qualification |
Budget for all cells | No mandatory cell cancelled because another failed |
| Capacity | max-parallel:2 |
acceptable latency | job timestamps show ≤2 matrix cells active |
| Aggregation | download per-cell artifacts | artifact service compatible with platform | support report links each verdict to cell evidence |
8. Change and rollback discipline
Changing a matrix changes the test contract. Review it like code: record the before/after cell set, predicted run-count impact and any support claim added or removed. Rollback means restoring the prior matrix definition/generator, not rerunning old evidence and pretending the new policy was tested.
Knowledge check
When is an include-only matrix often better than Cartesian axes?
When the support contract is irregular and only specific combinations are meaningful; include-only entries make the exact supported cells explicit.
What new evidence boundary does a dynamic matrix introduce?
The planner code and the exact JSON output that defines the later job graph.
Why can a matrixed reusable-workflow output be unsafe as an aggregate?
It does not automatically collect a value from every cell; current semantics select the output from the last successful completing matrixed reusable workflow that sets one.
Which mechanism controls two unrelated workflows that deploy to the same target: max-parallel or concurrency?
A concurrency group. max-parallel only bounds simultaneous jobs inside one matrix strategy.
What should happen before removing a supported cell to save CI minutes?
Document the support-policy change or risk-based rationale and review the resulting evidence gap; cost alone does not redefine compatibility.
Official references and version notes
- GitHub Docs — Running variations of jobs in a workflow — matrix expansion, contexts, include/exclude, dynamic outputs, failure handling and max-parallel.
- GitHub Docs — workflow syntax: strategy.matrix — current matrix limit and strategy semantics.
- GitHub Docs — expressions: fromJSON — converting job-output JSON into arrays/objects used by a later matrix.
- GitHub Docs — strategy context — fail-fast, job-index, job-total and max-parallel for the current generated job.
- GitHub Docs — matrix strategy with reusable workflows — matrix-driven reusable workflow calls and output caveats.
- actions/upload-artifact v7.0.1 and actions/download-artifact v8.0.1 — action releases pinned by full commit SHA in the checkpoint.
Version-sensitive behavior was rechecked against current
GitHub-maintained documentation on 2026-09-09. A
matrix can generate at most
256 jobs per workflow run.
strategy.fail-fast defaults to true;
continue-on-error is evaluated per generated job; and
max-parallel limits simultaneous matrix jobs but does
not redefine the matrix or create a repository-wide concurrency
lock. Current mandatory examples target GitHub.com with versioned
runner labels ubuntu-24.04 and
windows-2025. Official actions are pinned to full
immutable commit SHAs: checkout v7.0.1, setup-python v7.0.0,
upload-artifact v7.0.1 and download-artifact v8.0.1. GitHub
Enterprise Server users must verify artifact-action
backend/major-version compatibility for their appliance before
copying these examples. Artifact aggregation is demonstrated in
the checkpoint; the deeper artifact lifecycle is intentionally
deferred to Chapter 13.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.