4.3.2. Starter vs. Reusable vs. Composite
💡 First Principle: Choose by granularity and binding: a few steps inside a job → composite action; a whole job or pipeline maintained centrally → reusable workflow; a starting point teams own thereafter → starter workflow.
| Dimension | Starter workflow | Reusable workflow | Composite action |
|---|---|---|---|
| Unit of reuse | Whole workflow file | Whole workflow (one or more jobs) | A sequence of steps |
| Invoked by | Nothing — copied once | jobs.<id>.uses: | steps: - uses: |
| Binding time | Creation (copy) | Every run (by ref) | Every run (by ref) |
| Updates propagate | ❌ No | ✅ Yes, per ref | ✅ Yes, per ref |
| Consumer can modify | ✅ Fully — it's their file | ⚠️ Only via declared inputs | ⚠️ Only via declared inputs |
Can set runs-on | ✅ | ✅ (its own jobs) | ❌ Inherits caller's runner |
Can use secrets | ✅ | ✅ via secrets: mapping | ⚠️ Must be passed as inputs |
| Nesting limit | n/a | 10 levels, 50 references | 10 levels |
| Lives in | Org .github/workflow-templates | Any repo's .github/workflows | Any repo (action.yml at root or subdir) |
The composite-vs-reusable line is the one exam questions probe most. A composite action runs inside an existing job, sharing that job's runner, filesystem, and environment — so it can interleave with your other steps, but it cannot choose a runner, define a matrix, or run in parallel. A reusable workflow brings its own jobs, its own runners, and its own matrix, but it lands as a whole job in the caller's graph and cannot be spliced between two steps.
A practical composition many organizations use: composite actions for the shared steps (auth, setup, publish), reusable workflows to assemble those into a standard pipeline, and a starter workflow whose only content is a call to the reusable workflow — carrot and stick together.
⚠️ Exam Trap: Composite actions inherit the caller's runner and therefore cannot specify runs-on, and they do not automatically receive the caller's secrets — secrets must be passed explicitly as inputs. Any answer giving a composite action its own runs-on or implicit secrets access is wrong.
Reflection Question: You need shared logic that (a) runs between two existing steps in a caller's job and (b) must run on a different OS than the caller. Explain why no single mechanism satisfies both, and how you'd restructure.