Skip to main content
All posts

Building and Hosting This Website

The architecture behind cbchalmers.com - how GitHub, Vercel, and Claude Code combine into branch-based deployments, a UAT subdomain, a dedicated blog subdomain, and a pull-request release flow.

GitHub
Vercel
Claude Code
DevOps
CI/CD
Web Hosting

This site is built and hosted with three components: GitHub for source control, Vercel for building and hosting, and Claude Code, which wrote the code. This post walks through how they fit together - the deployments, the domain setup, the firewall, and the release process I use to push changes from development to live.

The building blocks

Each component was chosen for a specific job. GitHub holds the source and, more importantly, governs how changes move - nothing reaches the live site without passing through it. Vercel is the platform: it builds the site on each push, serves it from a global edge network, manages the domains and the firewall in front of them, and reports on traffic. Claude Code is how the site was actually built - an agentic coding tool I directed through a series of role-based prompts. I kept the number of components small on purpose: fewer moving parts means less to secure and maintain, and a setup I can understand in full.

The three components: Claude Code commits to GitHub, which deploys via Vercel to the live siteClaude Codewrites the codeGitHubsource controlVercelbuild & hostingLive Sitecbchalmers.comcommitsdeploysserves
The three components and how they connect: Claude Code writes the code, GitHub stores and controls it, and Vercel builds, hosts, and serves the live site.

Source control with GitHub

GitHub is where the code lives and where every change is recorded. Git keeps the full history of the site as a series of commits, so I can see what changed, when, and why, and undo a change cleanly if I need to. Day-to-day work happens on branches - separate lines of development that keep unfinished changes away from the live site until they are ready. What controls when something becomes live is the pull request: a proposed merge of one branch into another that I review before accepting. That review is the gate every change passes through, and the rest of the setup is arranged around it.

Two branches, two deployments

The core of the architecture is a clean split between two environments, each driven by its own branch. The repository has two long-lived branches on GitHub: main and preview. Main is connected to the Vercel production deployment, and preview to the Vercel preview deployment. Every push to a branch triggers a build of the matching deployment, so there are always two live versions of the site - the one the public sees and the one I am working on. The two never mix, and the only route from preview into production is a pull request.

Two GitHub branches mapped to two Vercel deployments and their domains, with the production deployment also serving the blog on its own subdomainGITHUBVERCELDOMAINSpreview branchPreview deploymentuat.cbchalmers.commain branchProduction deploymentcbchalmers.comwww.cbchalmers.comblog.cbchalmers.comvia proxymerge via PR
Each branch maps to its own Vercel deployment and domain; the production build also serves the blog on its own subdomain, and changes reach production only by merging preview into main.

Domain setup

Domains are where the two deployments become real addresses. In Vercel I pointed the apex domain, cbchalmers.com, and its www variant at the production deployment, and a dedicated subdomain, uat.cbchalmers.com, at the preview deployment. That subdomain is the key detail: it gives me a genuine, production-faithful staging environment, built and served exactly the way production is but on its own URL, rather than something that only exists on my machine. Because both deployments are built the same way, reviewing on uat is a reliable check of what production will serve.

The production deployment also answers on a second subdomain, blog.cbchalmers.com, which is where this writing lives. Rather than a separate deployment, it is the same production build with a small proxy in front: a request to the blog subdomain is rewritten onto the site's blog routes. The result is a clean, memorable address for the blog without adding another thing to build, deploy, or secure.

Locked down with the firewall until go-live

Before launch I did not want either environment reachable by the outside world. Vercel's firewall covers this with a small set of rules. During the build I added firewall rules that blocked all inbound traffic to both the production and preview deployments, so the work stayed private while it took shape, with no half-finished pages exposed to crawlers or visitors. Go-live was then a single, controlled action: I removed the rule in front of production while leaving the preview deployment blocked, so the public site went up at a moment I chose and uat stayed private behind the firewall.

Firewall blocks both deployments during build, then opens production at go-liveDURING BUILDAT GO-LIVEVisitorsfirewallProductionPreview (uat)VisitorsfirewallProductionPreview (uat)
Firewall rules keep both deployments private during the build; go-live simply opens production while uat stays locked.

The release flow

With the architecture in place, day-to-day delivery is a short loop. I make changes on the preview branch, commit, and push; Vercel rebuilds the preview deployment; and I review the result on uat.cbchalmers.com. When I am happy, I open a pull request from preview to main and merge it, which promotes the change to production. Because that merge is the only path to the live site, every release is an explicit, reviewable event with a full history behind it - the same change control I would put around a client's production environment, on a smaller scale.

Release pipeline from a commit on the preview branch through to productionCommit onpreviewPush, VercelbuildsReview onuatOpen PRto mainMerge,go liveiterate until ready
The everyday loop: commit on preview, review on uat, then merge a pull request to ship to production.

Analytics

The last component is measurement. Vercel Web Analytics, switched on for the production deployment, reports which pages and posts draw attention and where visitors arrive from. It is privacy-friendly and needs no cookie banner, which keeps the site clean and compliant, and it lives inside the same platform as the hosting and the firewall, so there is no extra service to integrate or secure. For a site whose job is to start conversations, knowing what people actually read is worth having.

Built with Claude Code

The whole solution was built with Claude Code, and how I worked with it shaped the result. Instead of one long, open-ended chat, I directed it through distinct personas - a designer to set the visual direction, a front-end engineer to build the pages, a reviewer to check the work against the brief, and an architect to make the hosting and release decisions. Each persona stayed within its remit, which kept the output focused and the reasoning explicit. It reflects how I see AI in delivery: it does the bulk of the work, while a human stays accountable for the design, the security, and the outcome.

What I would take into the next engagement

The main thing I would carry into other work is that a small, well-chosen set of components is usually enough. Two branches mapped to two deployments, a staging subdomain, a firewall I control, and a pull-request gate add up to a release process that is safe, repeatable, and easy to explain, and almost all of it is standard platform configuration rather than custom plumbing. The same approach scales to larger estates: separate your environments, make production reachable only through a gate, and control when you go live.