Boring on purpose: how we choose versions
Stable releases only, exact versions and fewer moving parts: the unglamorous rules behind software that still builds a year later.
Every project starts with a list of choices that nobody will notice if we get them right: which runtime, which database, which version of each library. We decide them the same way each time, and we wrote the rules down. This site was built with them.
Stable releases only
We use the latest stable release of each tool, not the newest one. When this site was built, a new major version of our database toolkit was available as a release candidate, and a new major version of TypeScript had just shipped without support from the linters and test runners we rely on. We stayed on the previous stable versions of both, and we will move when the tooling around them is ready.
The runtime follows the same rule: the current long-term support version of Node.js, not the release that is still waiting to become one.
Exact versions, no ranges
Every dependency is pinned to an exact version. No ^, no ~. A range means the code that runs on the server can differ from the code that was tested last week without anyone having changed anything. With exact versions and a committed lockfile, an upgrade is always a deliberate change with its own review, and a build from six months ago can be reproduced.
Fewer moving parts
For styling we use plain CSS modules and a short list of design tokens instead of a CSS framework. For validation, one schema describes each piece of data, and the same schema gives us the runtime check, the TypeScript type and the API documentation.
Each of these removes something that could drift out of sync.
Why it matters to a client
None of this shows up in a demo. It shows up a year later, when the application needs a new feature and the person adding it finds a codebase that still installs, still builds and still behaves the way the tests say it does. That is what we mean when we say we build things to last.