Skip to content

Shipping a change

The dev branch is production. Pushing it deploys to stevendesignco.com. main is stale and deploys nothing.

There is a gate now. dev is protected and the Q&A check has to pass before a merge lands, so a change that breaks accessibility, reflow or contrast cannot reach the live site by accident. Force pushes and branch deletion are off.

The protection does not apply to repository admins, which is deliberate: this is a one-person repository and locking yourself out at the wrong moment is its own risk. The gate is there to stop a mistake, not to stop you.

Work happens on a branch. A branch deploys to a password-protected review URL. Merging into dev is the act of publishing.

  1. Build locally and check the page you changed.
  2. Run the Q&A pass.
  3. Push the branch. Read it on the review URL.
  4. Open a pull request. The template carries the checks; CI runs the Q&A pass and has to go green before the merge is allowed.
  5. Merge to dev only when asked to publish.
  6. Run the Q&A pass again against the live site, rather than assuming the deploy matched.

One command, and it is not optional for anything that touches the layout, the stylesheet or a component.

npm run dev # in one terminal
npm run qa # in another

Or npm run ship, which runs the documentation check, the build and the Q&A pass in order.

GitHub Actions runs the same pass on every push and every pull request, so a branch reports for itself. That is a second pair of eyes, not a replacement for the first: on dev the result lands after the deploy, because dev is production. Run it on your branch before you merge.

It covers reflow and target size across eleven pages and eleven widths, axe on every page and with every dialog open, the dialog’s keyboard behaviour, text spacing overrides, High Contrast Mode, and inline link distinction. --pixel adds a comparison against the live site and is worth the wait on any change that was supposed to be invisible.

Each check is in qa/checks/, and each one opens with the defect it was written for. They are all regressions that reached production or nearly did. The repository’s qa/README.md has the list and the reasoning.

Three habits sit underneath it:

Check the pages you did not touch. The worst regression in the component refactor landed on three pages nobody was editing.

Check where content is reused, not only where it lives. The case study pages were correct while the dialog showing the same content was broken.

Prove a check can fail. Break the thing deliberately, watch the check name it, then put it back. A check that has only ever passed is not evidence. This is also how we learned that axe passes a link with 1.02:1 contrast and no underline, which is why there is a separate check for that.

The resume PDF is the one thing that can drift

Section titled “The resume PDF is the one thing that can drift”

public/resume.pdf is a copy of the master built in the Job Hunt folder, not something this repository generates. The build there writes a .docx and converts it with LibreOffice; nothing in this project can reproduce that.

So when the master changes, the site does not follow. Someone has to copy the file across:

cp "~/Documents/claude/Projects/Job Hunt/resume-master-docx/Steven-Vanden-Heuvel-Resume-MASTER.pdf" public/resume.pdf

The resume page’s title line has to be changed by hand to match at the same time. It is the one place in this project where two copies of the same thing exist and nothing checks them, which is worth knowing rather than discovering.

It used to be worse. The PDF was a print of the resume page, which meant it had the site’s display font and brand styling instead of the plain ATS layout every other resume uses. A resume that looks different from the one attached to an application is not a style choice, it is a mistake.

Vercel keeps the previous production deployment as an instant rollback. Before a significant release, tag the current state so there is a named point to return to:

git tag -a live-backup-YYYY-MM-DD dev -m "state before <change>"
git push origin live-backup-YYYY-MM-DD

Rolling back is then a reset to that tag and a force-with-lease push.

The Storybook cannot drift: it regenerates its stylesheet from the running site every build. Prose can, so there is a check rather than a good intention.

npm run check:docs

It compares your branch against dev and fails when the layout, a component, a Storybook story or the Storybook’s own CSS changed and no documentation page did. It also fails when stories changed without the Storybook being rebuilt.

  1. Change the component.
  2. Change its page under docs-site/src/content/docs/components/. If the change alters how, when or where it is used, that page is part of the change, not a follow-up.
  3. Rebuild both: cd storybook && npm run build, then cd docs-site && npm run build. Both need the site running on :4321, because the Storybook pulls its CSS from it.
  4. Run npm run check:docs.
  5. Commit the component, the page and both builds together.

Some changes are invisible to the people the docs serve: a refactor with identical output, a typo in a comment. Set SKIP_DOCS_CHECK=1 and say why in the commit message. An override with a reason is a decision; a skipped step is an accident.

The Storybook generates its stylesheet from the running site. After a change that touches shared CSS, rebuild it so its rendering and its contrast figures stay true.

This documentation is prose and does not regenerate. If you change a component’s behaviour, the page describing it is part of the change, not a follow-up.