Thaddius Gamueda
Part 4 — Process of BuildingDeployment

Cloudflare Pages and GitHub: Surviving Contact With the Internet

How This Site Was Built — Part 4 of 4

Three parts of decisions, corrections, and refusals all come down to one question: does any of it survive once it leaves my machine? A site that looks perfect in a local preview has proven nothing. The internet is the real reviewer, and this is the part where the build met it. It is also where the discipline of the whole series, challenging output instead of accepting it, had to work in both directions at once, because the most important pushback in this part was not against the AI. It was against my own near-mistake.

What Happens When You Push a Commit?

To begin, the deployment pipeline is the same pattern I would use shipping a real production service. The code lives in a GitHub repository. Every commit pushed to that repo automatically triggers a Cloudflare Pages build, and every deploy is independently rollback-able, so a bad change is one click from being undone rather than a panic. That is not student infrastructure. That is how teams ship software, and choosing it on purpose was part of the point.

One caution earned the hard way: Cloudflare offers to send its own pull request back to the repository to sync settings, and you accept that exactly once. The configuration is set after the first merge. Approve a second one and you can push the live site to a blank page. The tool's suggestion is not automatically safe just because the tool is the one suggesting it.

What Do You Do When a Build That Worked Locally Refuses to Ship?

Here the warning from Part 3 came true in the worst way. The site built cleanly inside the design platform's sandbox and failed the moment Cloudflare tried to build it from GitHub. The error was a wall of nonsense about being unable to resolve an injected script module. Everything worked locally. Nothing shipped.

The cause sat in the gap between two environments. The framework underneath, TanStack Start, needs a build step that the platform's own helper package runs automatically inside its sandbox, and only inside its sandbox. Cloudflare's independent build runner never got that helper, so it skipped the step and choked. The fix was to write the configuration out explicitly so it would run anywhere, not just in the place that babied it. "Works on my machine" is the oldest lie in software, and the only answer to it is to refuse the comfort of the local success and chase why the open environment disagrees. It cost real time. It also turned a build that only worked in one room into one that works in public.

How Does a Contact Form Actually Reach My Inbox?

A portfolio with a dead contact form is a business card with a fake phone number, so the messaging system was non-negotiable. When someone fills out the contact form, the submission hits a Cloudflare Worker, a small piece of server code running on the same infrastructure as the site, which hands the message to Resend, an email API, which delivers it straight to my inbox. No third-party form widget, no inbox I have to go check somewhere else. The site emails me directly.

That feature carried its own version of the two-environment trap. An email API runs on a secret key, and every backend feature actually lives in two places: my local machine and the live production server. Set the key in one and forget the other, and the form returns a clean 500 error on whichever side you neglected. The lesson from the failed deploy repeated itself here. Local success is not production success, and you have to verify the environment that strangers will actually touch, not the one that is easy to test.

Where Did Scrutiny Catch Me Instead of the AI?

This is the moment that completes the series, because the discipline finally pointed at me. An API key is a password. It belongs in Cloudflare's secrets manager, never in a configuration file that git is tracking, and absolutely never pasted into a chat window where it lives forever in a log. At one point I had a key sitting in a tracked file, one push away from being public in my repository. Claude Code caught it before that push, pulled the file out of tracking, and added it to the ignore list first.

Three parts of this series were about me challenging the machine's output. This part is the machine catching mine, and that is the honest shape of the whole thing. The skill was never distrusting the AI specifically. It was distrusting any unexamined output, including my own, and building a process where a mistake gets caught before it ships rather than after it leaks. Scrutiny that only runs one direction is not scrutiny. It is bias.

What Does the Whole Series Prove?

Anyone can generate a website now. I keep returning to that line because it is the entire reason this site documents its own construction. The generating was never the hard part or the valuable one. The value lived in four parts of judgment: reframing a redesign into an audit, cutting seven sections to five, refusing a color that failed in silence, and chasing a deploy that worked everywhere except where it counted. At every step the AI was fast, capable, and confidently wrong often enough to matter. My job was to know the difference and to push until the output earned the word done.

That is what this site is, underneath the Paper background and the quiet navy accent. It is not proof that I can use AI. Everyone can use AI. It is proof that I can direct it, argue with it, catch it, catch myself, and ship something that looks like a firm built it. The tools will keep getting better. The judgment is the part that stays mine.