Resolve it at build time
Every runtime dependency is a standing obligation to an API, a key, and a vendor's roadmap. Why we push everything we can into the build, and how we decide what earns a place at runtime.
There is a kind of system that looks healthy because it is busy. A CMS feeds the pages through a webhook. A GraphQL API sits over a database. Scheduled functions pull data from three third-party services every few hours. Every page view triggers a handful of calls. It all works, and the activity reads as a sign of life.
What it actually means is that the system has made a lot of promises. Each integration is an API that can change, a key that can expire, a vendor that can be acquired or shut down, and a scheduled job that can stop without telling anyone. None of those failures are dramatic. They are quiet, and they accumulate.
Every integration is a standing obligation
The cost of a runtime dependency is not its invoice. It is the attention it demands for as long as it exists. Someone has to rotate the key, track the breaking change, notice when the job stops returning data, and decide what the page should do when the upstream is down.
One obligation is cheap. A dozen is a part-time job that nobody was hired to do. On our own properties we have watched integrations outlive the reason they were added. The vendor behind a planned integration shuts down. A scheduled job fails silently for months. A link quietly starts resolving to the homepage. Systems with lots of moving parts are good at hiding exactly this kind of decay, because something is always moving.
Push it into the build
Our default is the opposite. Anything that can be resolved when the site or service is built gets resolved then. Content lives as files in the same repository as the code. The build reads it, renders it, and ships static output to S3 behind CloudFront. If the build needs data from somewhere else, it fetches it at build time, and a failure fails the build where an engineer will see it, not in front of a user.
The result fails in a different way. A static artifact either deployed or it did not, and once deployed it keeps working whether or not anyone touches it for a year. There is no key to expire at runtime, no upstream to go down, and no scheduled job to stop.
It is also smaller. When our founder rebuilt his personal site this way, the homepage went from 211KB of HTML to 57KB. Runtime dependencies went from seventeen to nine, and the nine that remain are the framework and the Markdown renderer. The old version had a CMS, a GraphQL API, scheduled ingestion, and three third-party integrations. The new one has none of those and does its job better.
What earns a place at runtime
This is not an argument for never running code. It is an argument for making every runtime component justify itself.
Our own site is the example. It is a static export, except for one thing: the contact form, which runs through API Gateway, Lambda, DynamoDB, and SES. That component earns its keep. It is how qualified inbound reaches us within the hour, and the business depends on it. The founder's personal site has no form at all, because a mailto link does the same job there without a security surface or a maintenance cost.
The test we apply is simple. For each runtime dependency, can you name the user-facing outcome that requires it to be live, and would you notice within a day if it broke? If the answer to either is no, it belongs in the build or it does not belong at all.
The usual objection
Content editors want to publish without a deploy, and a CMS gives them that. Sometimes that is worth paying for. A team of writers publishing several times a day has a real need, and a headless CMS behind a build webhook is a reasonable answer.
But many sites that run a CMS publish a few times a month, written by people who are perfectly capable of committing a Markdown file or asking someone who is. For them, the CMS is a runtime dependency bought to solve a problem they do not have. With a self-mutating pipeline shipping on every merge, a commit is already a publish button.
Resolve what you can at build time. Keep at runtime only what pays its way, and know exactly what that is.
Want an inventory of what your system is depending on right now? Start a conversation.