Chapter 12Lesson 03~170 minutes

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.

Risk-based testingCost controlSupport policyReusable workflowsEvidence design

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-parallel from 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.

Dynamic does not mean uncontrolled

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?

What new evidence boundary does a dynamic matrix introduce?

Why can a matrixed reusable-workflow output be unsafe as an aggregate?

Which mechanism controls two unrelated workflows that deploy to the same target: max-parallel or concurrency?

What should happen before removing a supported cell to save CI minutes?

Next lesson

Diagnose matrix failures causally

Lesson 4 preserves the original graph and shows how to separate generator, scheduler, runner, test and aggregation failures.

Official references and version notes

Version and compatibility note

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.