How I Build Things

And why

← Back to index

Minimal dependencies

Every dependency gets pinned exactly and held still — vendored JS, exact Python versions, SHA-pinned GitHub Actions.

Every dependency is a promise to keep updating it, and a supply-chain surface someone else controls — so when one earns its place, pin it exactly and hold it still. "Pin it" means something different per component; each is its own section below.

JavaScript

Vendor the exact file into the repo — vendor/<package>@<version>/ — rather than loading it from a CDN at runtime. A vendored file can't silently change version, can't be swapped by whoever controls the CDN, and doesn't need an exception carved into the page's own CSP just to load. This is the vendoring the HTML tool pattern's current recommendation calls for.

Python (build tooling)

Pin exact versions in pyproject.toml (==, not >=), and commit the lockfile uv resolves from them. A range means the build installs whatever the latest matching release happens to be on the day it runs — the opposite of pinning. Exact pins plus a committed lockfile mean the same build script installs the same interpreter-level dependencies whether it runs today or in a year. This is why the Knowledge base pattern reaches for a small pinned script over an off-the-shelf generator whose hosted build doesn't offer that same control.

GitHub Actions

Reference every action by full commit SHA, never a version tag — uses: owner/action@<40-char-sha> # v4 at most, the tag as a comment for a human, never as the ref itself. A tag like @v4 is a pointer someone else's repository controls and can repoint at will; a SHA is the one thing in this list that's genuinely immutable. This applies equally to first-party (actions/*) and third-party actions — first-party cuts supply-chain risk, it doesn't remove the need to pin.