Master the GH-ACT

An interactive study guide built on 7 memory techniques to help you pass the GitHub Actions certification exam.

Author & maintain workflows 30-40%Consume workflows 15-20%Author & maintain actions 15-20%Manage Actions for enterprise 15-20%
What it covers

YAML workflow syntax, triggers (push/PR/schedule/workflow_dispatch), jobs/steps/actions, runners (GitHub-hosted/self-hosted), secrets/variables, environments, matrix strategies, reusable workflows, composite actions, artifacts, caching, OIDC, and permissions.

Ideal for

DevOps engineers, platform engineers, and developers automating CI/CD pipelines with GitHub Actions.

Aspire to this if

You’re a developer who runs CI/CD manually or uses another platform and wants to master GitHub’s native automation.

The Map

Tap any component to see what you need to know for the exam.

⚡ TriggersEvents · Schedules · Manual📄 WorkflowsYAML · .github/workflows/🏗️ JobsParallel · Needs · Matrix📋 StepsUses · Run · With📦 ActionsJavaScript · Docker · Composite🖥️ RunnersGitHub-hosted · Self-hosted🔒 Secrets & VariablesEncrypted · Scoped · OIDC🌍 EnvironmentsProtection · Approvals · Rules💾 Artifacts & CachingUpload · Download · Cache🔄 Reusable Workflowsworkflow_call · DRY🏢 Enterprise ManagementPolicies · Runners · Audit
⚡ Triggers
Events · Schedules · Manual
📄 Workflows
YAML · .github/workflows/
🏗️ Jobs
Parallel · Needs · Matrix
📋 Steps
Uses · Run · With
📦 Actions
JavaScript · Docker · Composite
🖥️ Runners
GitHub-hosted · Self-hosted
🔒 Secrets & Variables
Encrypted · Scoped · OIDC
🌍 Environments
Protection · Approvals · Rules
💾 Artifacts & Caching
Upload · Download · Cache
🔄 Reusable Workflows
workflow_call · DRY
🏢 Enterprise Management
Policies · Runners · Audit

The Story

Follow the narrative to build a mental model of how everything connects.

⚡

The Spark

Every automation begins with a trigger. A developer pushes code, opens a pull request, or a cron schedule fires at midnight UTC. GitHub detects the event and searches .github/workflows/ for any YAML file whose ‘on’ key matches. The workflow ignites.

Exam IntelKey triggers: push, pull_request, schedule (cron), workflow_dispatch (manual), repository_dispatch (API). Filters: branches, tags, paths, types. pull_request runs on the merge commit by default.
📄

The Blueprint

The workflow YAML is the blueprint. It declares which events trigger it, what permissions it needs, and how jobs are organised. Concurrency groups prevent wasteful duplicate runs. Defaults set the shell and working directory for every step.

Exam IntelTop-level keys: name, on, permissions, env, defaults, concurrency, jobs. Concurrency: cancel-in-progress stops redundant runs. permissions: least-privilege principle — set contents: read, not write unless needed.
🏗️

The Assembly Line

Jobs are the assembly stations. They run in parallel by default, but ‘needs’ creates a dependency chain: build → test → deploy. Each job gets a fresh runner — nothing carries over unless you explicitly share artifacts. Matrix strategies multiply jobs across OS, Node versions, or any custom axis.

Exam Inteljobs.<id>.needs creates DAG dependencies. Matrix: strategy.matrix with include/exclude. fail-fast: true (default) cancels siblings on failure. services: Docker sidecar containers for integration tests.
📋

The Workbench

Inside each job, steps execute one by one. A step either ‘uses’ a pre-built action or ‘run’s shell commands. The first step is almost always actions/checkout to clone the repository. Each step can set outputs that later steps and jobs consume via the steps context.

Exam Intelsteps[*].uses: action reference (owner/repo@ref). steps[*].run: inline commands. Shell options: bash (default Linux/macOS), pwsh, python, cmd. Outputs: echo "name=value" >> $GITHUB_OUTPUT. continue-on-error and timeout-minutes per step.
📦

The Toolbox

Actions are the reusable tools. JavaScript actions run fastest on any OS. Docker actions package complex dependencies but only work on Linux. Composite actions stitch multiple steps into one shareable unit. The Marketplace hosts thousands of community actions, but enterprise teams curate allow-lists.

Exam IntelThree types: JavaScript (Node.js runtime), Docker (container), Composite (multi-step). action.yml: name, description, inputs, outputs, runs. Version pinning: use SHA for security, tag for convenience. Publish to Marketplace via release.
🔒

The Vault

Secrets never appear in logs — GitHub masks them automatically. Repository secrets serve one repo, organisation secrets span many, and environment secrets scope to deployment targets. For cloud deployments, OIDC eliminates long-lived credentials entirely — the runner exchanges a short-lived token with your cloud provider.

Exam IntelSecrets: encrypted at rest, masked in logs, never exposed in forks. GITHUB_TOKEN: automatic, scoped to the repo, configurable permissions. OIDC: keyless auth to AWS/Azure/GCP via trust policy. Environment secrets override repo-level. Variables (non-secret) for config values.
🌍

The Gates

Before code reaches production, environments impose gates. Required reviewers must approve. Wait timers add cooling periods. Branch policies restrict which branches can deploy. Each deployment is logged — who deployed what commit, when, and to which environment.

Exam IntelEnvironment protection rules: required reviewers, wait timers (0–43200 min), branch restrictions. Deployment branches: all, protected, or custom patterns. Environment URL shown in PR deploy status. Concurrency on environments prevents simultaneous deploys.
💾

The Cargo Bay

Artifacts and caches keep the pipeline efficient. Build outputs upload as artifacts for downstream jobs. Dependency caches avoid re-downloading node_modules or Maven jars on every run. Cache keys use lock file hashes so stale caches auto-evict when dependencies change.

Exam IntelArtifacts: upload-artifact/download-artifact, 90-day default retention. Cache: actions/cache, key + restore-keys, 10 GB per repo, LRU eviction. hashFiles() for cache-busting on lock file changes. Artifacts share between jobs; cache persists across runs.
🔄

The Template Factory

Reusable workflows are the template factory. A central repo defines standard CI/CD patterns — build, test, deploy — and other repos call them like functions. Inputs customise behaviour, secrets flow through securely, and outputs return results. Composite actions serve a similar purpose at the step level.

Exam IntelReusable workflows: workflow_call trigger, max 4 nesting levels. secrets: inherit passes all caller secrets. Outputs: jobs.<id>.outputs map to workflow outputs. Composite actions: action.yml with runs.using: composite, steps array. Both reduce duplication across repos.
🏢

The Command Centre

Enterprise admins govern from the command centre. Policies control which actions are permitted — allowing only verified creators or pinned SHA references. Runner groups distribute self-hosted capacity across teams. Audit logs capture every workflow run, secret access, and policy change for compliance.

Exam IntelOrganisation policies: allow all, verified creators only, or explicit allow-list. Required workflows: enforce CI on all repos in an org. Runner groups: scope to repos/orgs, labels for routing. Audit log: workflow runs, secret access, settings changes. Enterprise: manage policies across multiple organisations.

Mnemonic Wall

Memorable acronyms and phrases to anchor key exam concepts in your memory.

⚡
PPSRW
The five key trigger events. Push/PR for code changes, schedule for cron, dispatch for manual/API.
📄
NOPED
Top-level workflow keys before the jobs section. Name it, trigger it, secure it, configure it.
🏗️
NMSC
Job-level configuration. Needs = dependencies. Matrix = variants. Services = sidecars. Concurrency = dedup.
📦
JDC
Three action types. JS = fastest, cross-platform. Docker = Linux only, isolated. Composite = multi-step reuse.
🔒
ROES
Three scopes for secrets, from narrowest to broadest. Environment overrides repo; org shares across repos.
🔑
OIDC
Keyless cloud auth. Runner gets JWT, exchanges for short-lived cloud token. No stored credentials.
🖥️
GHLR
Runner tiers. Standard (2-core), Larger (4–64 core), Self-hosted (your infra, your labels).
🌍
RWBD
Environment protection rules. Gate production with approvals, delays, and branch policies.
💾
ACH
Pipeline efficiency trio. Artifacts share between jobs. Cache persists across runs. hashFiles() busts stale caches.
🔄
WCA
Reuse mechanisms. workflow_call for reusable workflows. Composite for multi-step actions. action.yml defines both.
🏢
PRAG
Enterprise management pillars. Policies restrict actions. Runner groups scope compute. Audit logs track everything.
🛡️
LP
Set permissions at the workflow and job level. Default to contents: read. Only grant write when needed.
🧱
MIFE
Matrix strategy options. Matrix generates combos. Include/exclude customise. Fail-fast cancels on first failure.
🚀
SHA
Pin actions to full commit SHA for supply chain security. Tags can be moved; SHAs cannot.

Versus Arena

Side-by-side comparisons to sharpen your understanding of similar concepts.

vs
GitHub-hostedvsSelf-hosted Runners

GitHub-hosted vs Self-hosted Runners

AspectGitHub-hostedSelf-hosted Runners
InfrastructureManaged by GitHubYour own servers/VMs
MaintenanceZero — auto-updatedYou maintain OS, tools, agent
StateClean VM every jobPersistent (unless ephemeral)
CostFree tier + per-minute billingYour compute costs
NetworkPublic internet onlyPrivate network / VPN access
Best forStandard CI/CD, open sourcePrivate infra, GPU, compliance
vs
Reusable WorkflowsvsComposite Actions

Reusable Workflows vs Composite Actions

AspectReusable WorkflowsComposite Actions
ScopeEntire workflow (jobs)Single step (multi-step)
Triggerworkflow_calluses: in a step
RunnersOwn runs-on per jobRuns on caller’s runner
SecretsExplicit or secrets: inheritPassed via env or inputs
NestingMax 4 levels deepNo nesting limit
Best forStandard CI/CD pipelinesReusable step sequences
vs
JavaScriptvsDockervsComposite Actions

JavaScript vs Docker vs Composite Actions

AspectJavaScriptDockerComposite Actions
RuntimeNode.js | ContainerCaller’s runner shell
SpeedFastest | Slower (image pull)Fast (no container overhead)
OS supportAll | Linux onlyAll
DependenciesBundled JS | DockerfilePre-installed on runner
ComplexityMedium (JS code) | High (Docker)Low (YAML steps)
Best forAPI calls, fast ops | Isolated toolsComposing existing actions
vs
SecretsvsVariables

Secrets vs Variables

AspectSecretsVariables
EncryptedYes — at rest and in transitNo — plain text
Masked in logsYes — automaticallyNo
Use caseTokens, keys, passwordsFeature flags, URLs, config
Accesssecrets.NAME contextvars.NAME context
ScopesRepo, org, environmentRepo, org, environment
Readable after setNo — write-only after creationYes — visible in settings
vs
ArtifactsvsCache

Artifacts vs Cache

AspectArtifactsCache
PurposeShare build outputs between jobsPersist dependencies across runs
Lifetime90 days default (configurable)7 days unused; 10 GB repo limit
ScopeWithin a workflow runAcross workflow runs
Actionsupload-artifact / download-artifactactions/cache (save/restore)
Key strategyNamed by step (no key needed)hashFiles() on lock files
Best forBinaries, test reports, logsnode_modules, pip, Maven jars
vs
GITHUB_TOKENvsPATvsGitHub App

GITHUB_TOKEN vs PAT vs GitHub App

AspectGITHUB_TOKENPATGitHub App
ScopeSingle repo (auto) | User-wideOrg/repo installation
LifetimeJob duration | Long-lived1-hour token (refreshable)
SetupAutomatic | Manual creationApp registration + install
PermissionsConfigurable per workflow | BroadFine-grained per install
Cross-repoNo | YesYes
Best forSame-repo ops | Quick scriptsEnterprise/org automation
vs
pushvspull_request Triggers

push vs pull_request Triggers

Aspectpushpull_request Triggers
Fires onCommits pushed to branchPR opened, synced, or reopened
RefThe branch pushed toMerge commit (PR merge ref)
GITHUB_TOKENFull write (default)Read-only for fork PRs
SecretsAlways availableNot available for fork PRs
Common filtersbranches, tags, pathsbranches, paths, types
Best forDeploy on merge, release buildsPR checks, code review gates
vs
ConcurrencyvsMatrix Strategy

Concurrency vs Matrix Strategy

AspectConcurrencyMatrix Strategy
PurposeLimit parallel runsMultiply job variants
Key configconcurrency.group + cancel-in-progressstrategy.matrix + include/exclude
ScopeWorkflow or job levelJob level only
EffectQueues or cancels duplicate runsCreates N parallel job instances
FailureN/A — controls schedulingfail-fast cancels siblings (default)
Best forDeploy pipelines, single deploysMulti-OS/version CI testing

Cheat Sheet

Organised reference grouped by exam domain — everything you need on one page.

Author & Maintain Workflows

30-40%

Workflow Syntax

  • File location: .github/workflows/*.yml
  • Top-level keys: name, on, permissions, env, defaults, concurrency, jobs
  • on: push | pull_request | schedule | workflow_dispatch | repository_dispatch
  • Filters: branches, branches-ignore, tags, paths, paths-ignore, types
  • concurrency: { group: ${{ github.ref }}, cancel-in-progress: true }

Jobs & Steps

  • Jobs run in parallel by default; needs: [job_id] for dependencies
  • runs-on: ubuntu-latest | windows-latest | macos-latest | self-hosted
  • steps[*].uses: owner/repo@ref (action) or steps[*].run: command (shell)
  • Outputs: echo "key=value" >> $GITHUB_OUTPUT
  • Conditionals: if: github.event_name == 'push' or if: always()

Matrix Strategy

  • strategy.matrix: { os: [ubuntu, windows], node: [18, 20] }
  • include: add extra combos; exclude: remove specific combos
  • fail-fast: true (default) cancels sibling jobs on first failure
  • max-parallel: limit concurrent matrix jobs
  • Access values: ${{ matrix.os }}, ${{ matrix.node }}

Expressions & Contexts

  • Contexts: github, env, secrets, vars, steps, job, runner, matrix
  • Functions: contains(), startsWith(), toJSON(), hashFiles(), format()
  • Status checks: success(), failure(), always(), cancelled()
  • Ternary: ${{ condition && 'true-value' || 'false-value' }}
  • Environment files: $GITHUB_OUTPUT, $GITHUB_ENV, $GITHUB_PATH, $GITHUB_STEP_SUMMARY

Consume Workflows

15-20%

Using Actions

  • uses: actions/checkout@v4 — always check out code first
  • uses: actions/setup-node@v4 with: { node-version: 20 }
  • Pin to SHA for security: uses: actions/checkout@<full-sha>
  • Local actions: uses: ./.github/actions/my-action
  • Marketplace: browse and verify actions before adopting

Reusable Workflows

  • Call: uses: org/repo/.github/workflows/ci.yml@main
  • Pass inputs: with: { environment: production }
  • Pass secrets: secrets: inherit or secrets: { TOKEN: ${{ secrets.TOKEN }} }
  • Receive outputs: needs.called-workflow.outputs.result
  • Max 4 levels of nested reusable workflows

Starter Workflows

  • Organisation .github repo: workflow-templates/ directory
  • Provide starter YAML + metadata JSON for repos to adopt
  • Appear in Actions tab → New Workflow for org members
  • Include properties.json with name, description, iconName, categories

Workflow Commands

  • ::set-output name=key::value (deprecated → use $GITHUB_OUTPUT)
  • ::error file=app.js,line=10::Error message — annotations on PRs
  • ::group::Title / ::endgroup:: — collapsible log groups
  • ::add-mask::secret-value — dynamically mask values in logs
  • ::notice / ::warning / ::error — severity-level annotations

Author & Maintain Actions

15-20%

Action Types

  • JavaScript: runs.using: node20, runs.main: dist/index.js
  • Docker: runs.using: docker, runs.image: Dockerfile (Linux only)
  • Composite: runs.using: composite, runs.steps: [] (multi-step YAML)
  • All types defined in action.yml (or action.yaml)

action.yml Schema

  • name, description (required), author (optional)
  • inputs: { name: { description, required, default } }
  • outputs: { name: { description, value } }
  • branding: { icon, color } for Marketplace listing
  • runs: { using, main, pre, post } for lifecycle hooks

JavaScript Actions

  • @actions/core: getInput(), setOutput(), setFailed(), info()
  • @actions/github: context object, getOctokit() for API calls
  • @actions/exec: exec() for running shell commands
  • Bundle with ncc or esbuild — commit dist/ to repo
  • pre and post scripts for setup/cleanup lifecycle

Publishing & Versioning

  • Tag with semver: v1, v1.0.0 — move major tag on releases
  • Create GitHub Release to publish to Marketplace
  • README.md with usage examples and input/output docs
  • Use actions/toolkit packages for consistent behaviour
  • Test actions with act (local) or dedicated test workflows

Manage Actions for the Enterprise

15-20%

Organisation Policies

  • Allow all actions, verified creators only, or explicit allow-list
  • Required workflows: enforce CI checks across all org repos
  • Default GITHUB_TOKEN permissions: read-only (recommended)
  • Disable fork PR workflows to prevent abuse
  • Restrict workflow permissions at org level

Runner Management

  • Runner groups: scope self-hosted runners to specific repos/orgs
  • Labels: custom labels route jobs (e.g. gpu, arm64, production)
  • Ephemeral runners: --ephemeral flag for clean-state jobs
  • Auto-scaling: use webhook or KEDA to scale runner pools
  • Runner applications: Linux, macOS, Windows, ARM64 supported

Security Best Practices

  • Pin actions to full SHA — tags can be hijacked
  • Least-privilege permissions: set at workflow and job level
  • OIDC for cloud auth — no long-lived credentials
  • Dependabot for action version updates
  • OpenSSF Scorecard to audit action supply chain

Audit & Compliance

  • Audit log: workflow runs, secret access, policy changes
  • Deployment protection rules: required reviewers + wait timers
  • Environment branch policies: restrict which branches deploy
  • CODEOWNERS for .github/workflows/ changes require review
  • Billing: track minutes by runner type (standard, larger, macOS)

The Memory Palace

Walk through themed rooms — each object anchors a concept in spatial memory.

The Trigger Gate

Events — Where workflows begin

⚡
push / pull_request
Code-change triggers. Push fires on commits. PR fires on open/sync/reopen
⏰
schedule
POSIX cron syntax in UTC. Minimum 5-minute interval. Runs on default branch
🎮
workflow_dispatch
Manual trigger with custom inputs. UI button or API call to start
📡
repository_dispatch
External API trigger. Send event_type + client_payload from any system
🔍
Filters
branches, tags, paths, types — narrow when workflows fire
🔄
workflow_call
Makes a workflow reusable. Called by other workflows like a function

The Assembly Floor

Jobs & Steps — Where work is orchestrated

🏗️
Jobs
Parallel by default. needs: for dependencies. Each gets a fresh runner
🔢
Matrix Strategy
Generate combos: OS x Node version. include/exclude for custom rows. fail-fast
📋
Steps
Sequential within a job. uses: (action) or run: (shell command)
📤
Outputs
echo 'key=value' >> $GITHUB_OUTPUT. Share between steps and jobs
🐳
Service Containers
Sidecar containers (postgres, redis) for integration testing
🚦
Concurrency
Group + cancel-in-progress. Prevent duplicate runs on same branch/PR

The Action Workshop

Actions — Where reusable tools are built

📜
JavaScript Actions
Node.js runtime. @actions/core + github. Fastest, all platforms
🐳
Docker Actions
Run in container. Full dependency isolation. Linux runners only
🧱
Composite Actions
Multi-step YAML. No container overhead. Compose existing actions
📄
action.yml
Defines inputs, outputs, runs config, branding for Marketplace
🏷️
Versioning
Semver tags: v1, v1.0.0. Pin to SHA for security. Move major tag on release
🛒
Marketplace
Publish via GitHub Release. README with usage. Branding icon + colour

The Secure Vault

Secrets & Auth — Where credentials are protected

🔒
Secrets
Encrypted at rest, masked in logs. Repo, org, and environment scopes
🔑
GITHUB_TOKEN
Auto-generated per job. Configurable permissions. Scoped to repo
🌐
OIDC
Keyless cloud auth. JWT exchanged for short-lived AWS/Azure/GCP token
📝
Variables
Non-secret config values. vars.NAME context. Visible in settings UI
🌍
Environments
Required reviewers, wait timers, branch restrictions. Scoped secrets
🛡️
Least Privilege
Set permissions: at workflow/job level. Default contents: read only

The Command Centre

Enterprise — Where governance is enforced

🏢
Organisation Policies
Allow all, verified creators, or allow-list. Control what actions run
🖥️
Runner Groups
Scope self-hosted runners to repos/orgs. Labels for routing. Ephemeral mode
📋
Required Workflows
Enforce CI checks across all repos in an org. Cannot be skipped
📜
Audit Log
Workflow runs, secret access, policy changes. Export for compliance
💰
Billing
Per-minute by runner type. Free tier for public repos. Spending limits
🔏
Supply Chain Security
Pin actions to SHA. Dependabot updates. OpenSSF Scorecard. Fork PR restrictions

Pattern Spotter

Decision trees and trigger-answer pairs — see the pattern, know the answer.

Which Trigger?
Which Trigger?
  ├── Run on every push to main → on: push (branches: [main])
  ├── Run on pull request for code review → on: pull_request
  ├── Run nightly at midnight UTC → on: schedule (cron: '0 0 * * *')
  ├── Manual trigger with inputs → on: workflow_dispatch
  ├── Triggered by external API call → on: repository_dispatch
  └── Called by another workflow → on: workflow_call
Which Runner?
Which Runner?
  ├── Standard CI/CD, no special requirements → GitHub-hosted (ubuntu-latest)
  ├── Need macOS for iOS/Swift builds → GitHub-hosted (macos-latest)
  ├── Need Windows for .NET/MSBuild → GitHub-hosted (windows-latest)
  ├── Faster builds, more CPU/RAM → Larger GitHub-hosted runners
  ├── Access private network / VPN resources → Self-hosted runner
  └── GPU, ARM64, or specialised hardware → Self-hosted runner with labels
Which Authentication Method?
Which Authentication Method?
  ├── Same-repo operations (issues, PRs) → GITHUB_TOKEN (automatic)
  ├── Cross-repo access needed quickly → Personal Access Token (PAT)
  ├── Org-wide automation, fine-grained → GitHub App installation token
  ├── Deploy to AWS/Azure/GCP → OIDC (keyless cloud auth)
  └── Deploy to cloud with stored creds → Cloud credentials in secrets
Which Reuse Pattern?
Which Reuse Pattern?
  ├── Share a whole CI/CD pipeline across repos → Reusable workflow (workflow_call)
  ├── Share a sequence of steps within a job → Composite action
  ├── Complex logic with API calls → JavaScript action
  ├── Need isolated environment/dependencies → Docker action (Linux only)
  └── Provide starter templates for org repos → Starter workflow in .github repo

Decision Cards

"needs" or "job dependencies"→jobs.<id>.needs: [other-job]
"matrix" or "test across OS versions"→strategy.matrix with include/exclude
"cancel duplicate runs"→concurrency: group + cancel-in-progress
"keyless cloud auth" or "OIDC"→OpenID Connect token exchange
"share files between jobs"→upload-artifact / download-artifact
"speed up npm install" or "cache dependencies"→actions/cache with hashFiles()
"pin actions" or "supply chain security"→Use full commit SHA, not tags
"approval before deploy"→Environment protection rules + reviewers
"reuse workflow across repos"→Reusable workflow (workflow_call)
"restrict allowed actions"→Org policy: verified creators or allow-list
"service containers" or "integration tests"→services: postgres/redis sidecar
"fork PR" and "secrets not available"→Security: fork PRs have read-only token, no secrets
"continue even if step fails"→continue-on-error: true on the step
"run only on main branch push"→on: push: branches: [main]
"max 4 levels" or "nested workflows"→Reusable workflow nesting depth limit
"required reviewers" or "wait timer"→Environment deployment protection rules
"add annotation to PR"→::error / ::warning / ::notice workflow commands
"pass data between steps"→echo 'key=value' >> $GITHUB_OUTPUT

Train with practitioners, not presenters

Lucid Labs delivers Microsoft certification training led by Microsoft Certified Trainers (MCTs) and grounded in real-world project experience. We adapt every session to your team's environment, data stack, and business objectives — because the best exam prep comes from engineers who build these solutions every day.

🎯
Tailored Content
Training built around your actual repositories, workflows, and deployment targets — not generic slides.
🛠️
Hands-On Labs
Build real CI/CD pipelines, custom actions, and reusable workflows with expert guidance at every step.
📈
Exam + Capability
Pass the certification and build lasting skills your team can apply from day one.
Talk to us about GitHub Actions training

Custom training for teams & individuals — remote or on-site across Australia

Keith Oak
Keith Oak
Chief Technology Officer — Lucid Labs

Microsoft Solutions Partner architect specialising in Fabric, Azure Data & AI, and GitHub Enterprise. 18+ years delivering data platforms for Australian businesses — building the systems these exams test every day.