Skip to content

Workshop 3: Live demo script

Share-screen guide. Each step has a measurable before/after. Students mirror in their own forks and commit alongside you. The demo runs across blocks 3, 4, and 5.

Frame the numbers up front. The cold baseline is ~13 min, and that is deliberately, uncomfortably slow. ~95% of it (~12 min) is one stage: the serial real-HTTP test:api suite. The W3 steps below (caching, incremental tsc, no coverage, parallel workers, matrix/sharding, faster deploy) get the pipeline to roughly ~5–7 min, real progress, but they can't collapse test:api because the network round-trips are still real. The order-of-magnitude collapse to ~30s–2 min is Workshop 5, when we mock the SDK clients. Say that explicitly so today's win isn't oversold.

Setup before the demo

  • Repo is forked and visible at github.com/<my-handle>/orbittasks.
  • The slow baseline .github/workflows/ci.yml has run at least once on my fork. I have the duration.
  • Editor is open on .github/workflows/ci.yml.
  • Browser tab is on the Actions tab of my fork.

Step 1: Add dependency caching (Block 4, ~3 min)

"We're going to apply each optimization from the recipe, in order, and watch the duration drop. First up: dependency caching."

  1. Edit .github/workflows/ci.yml:

    yaml
    - name: Set up Node
      uses: actions/setup-node@v4
      with:
        node-version: 20
        cache: npm   # <- add this line
  2. Commit on screen: git commit -am "ci: cache npm dependencies" and push.

  3. Switch to the Actions tab. Watch the workflow run.

  4. When it finishes, point at the duration: expected drop of ~30–60 s on the install step.

  5. Students follow along. Take 2 minutes for them to land their own commit.


Step 2: Enable incremental TypeScript (Block 4, ~3 min)

  1. Edit apps/api/tsconfig.json:

    json
    {
      "compilerOptions": {
        "incremental": true,
        "tsBuildInfoFile": "./.tsbuildinfo"
      }
    }
  2. Add a cache step in the workflow for .tsbuildinfo:

    yaml
    - uses: actions/cache@v4
      with:
        path: '**/.tsbuildinfo'
        key: tsbuildinfo-${{ hashFiles('**/*.ts') }}
        restore-keys: |
          tsbuildinfo-
  3. Commit, push, watch. Expected drop: 30–60 s on typecheck on subsequent runs.


Step 3: Move coverage out of the PR path (Block 4, ~5 min)

"Coverage instrumentation costs ~30% of every test run. We don't need it on every PR; we need it nightly."

  1. Edit apps/api/jest.config.js: change collectCoverage: truecollectCoverage: false.
  2. Edit apps/web/vite.config.ts: change coverage.enabled: truefalse.
  3. (Optional, save for later) Add .github/workflows/coverage.yml that runs on a nightly schedule.
  4. Commit, push, watch. Expected drop: 10–20 s on test:api, 5–10 s on test:web.

Step 4: Parallelize Jest and Vitest (Block 5, ~5 min)

  1. Edit apps/api/jest.config.js: change maxWorkers: 1maxWorkers: '50%' (or delete the line entirely).
  2. Edit apps/web/vite.config.ts: the config advertises two serial knobs, so set both singleFork: false AND isolate: false (it was singleFork: true and isolate: true). Flipping only singleFork leaves web isolating every file.
  3. Commit, push, watch. Expected drop: test:api fans out across workers (the biggest W3 lever on the dominant stage); test:web drops a few seconds. It shortens test:api, it does not collapse it; the round-trips are still real.

"Some tests will now run in parallel. If anything fails that didn't before, that's the lesson: find the shared state."

If a flaky failure shows up here, good. Pull up the failing test, walk through what's happening (probably a shared db instance not reset properly), and fix it on the spot. That's authentic engineering.


Step 5: Defer to Workshop 5 (~30 sec)

"Step 5 in the recipe, mocking all six external SDK clients, we save for Workshop 5. That's the one that takes test:api from ~12 minutes to ~30 seconds, because it's the only step that stops the tests making real HTTP calls. Today's steps get us to ~5–7 min; Workshop 5 is the collapse. Skip for now."

This sets up Workshop 5's payoff. Don't try to do it here; the contrast is the point.


Step 6: Split CI into parallel matrix jobs (Block 5, ~5 min)

  1. Restructure .github/workflows/ci.yml:

    yaml
    jobs:
      test:
        strategy:
          matrix:
            app: [api, web]
        runs-on: ubuntu-latest
        steps:
          # ... (full structure from starter/ci-optimized-reference.yml)
  2. Walk through the matrix structure live. Note that GitHub will now run two jobs in parallel.

  3. Commit, push, watch. Expected drop: ~30 s wall-clock time.


Step 7 (optional polish, advanced): Shard the api tests

This one is optional and never required to keep up. If we have time and the cohort is moving well, we walk through --shard=N/M together. Otherwise skip; it's icing.


Step 8: Fix the slow deploy script (Block 5, ~2 min)

  1. Open scripts/deploy.sh. Show the per-file copy loop with sleep 0.3.
  2. Replace with rsync -a "${SRC_DIRS[@]}" "$DEST".
  3. Drop the smoke-check sleep 5 to sleep 0.5.
  4. Commit, push, watch. Expected drop: ~25–30 s on deploy.

After the live demo

Open the Actions tab one more time. Show the side-by-side: original duration vs current. We record the two numbers in the W3 section of the logbook together; we reuse them in Workshop 6.

"From X minutes down to Y minutes. That's the win. Every step had a real reason."

Optional bonus: ask the room which step had the biggest impact for their fork. The answers vary based on the runner GitHub assigned: good teaching moment about why "your mileage may vary."