Tools We Run
The first piece of dogfooding for a developer publication is the publication’s own stack. We cover tooling choices in the agentic stack market; we should be willing to tell you what we chose for the boring publishing layer that ships these pieces. This page is that.
A note on framing: nothing on this page is sponsored, paid, or affiliated. We picked these tools because they did the job at the price we wanted to pay, and we will tell you which ones we are watching for replacement.
Site generator: Hugo (extended)
We run Stack Quarterly on Hugo, the Go-based static site generator, in its “extended” build (the version with built-in support for SCSS, minification, and image processing). The site is built from Markdown files in content/ plus a small layer of HTML templates in layouts/.
We picked Hugo over the obvious alternatives — Eleventy, Astro, Next.js static — for three reasons. The first is build speed. Stack Quarterly is small at launch (twelve pieces, a handful of reference pages), and build speed barely matters at this size. But the publication is designed to grow into a multi-year archive. Hugo’s build time stays sub-second through tens of thousands of pages; the Node-based generators slow down meaningfully past a few thousand. We wanted to pick the tool that does not require migrating later.
The second is the surface area of the templating language. Hugo’s templating is Go’s html/template with a layer of pipeline helpers on top. It is verbose. It does what you tell it. There is no React-style component model and no JSX. For a publication site whose layouts are mostly “headline, byline, body, footer,” the lack of a component model is not a problem; it is a feature. Layouts that look the way the page looks are easier to debug than layouts that look the way a component graph wants you to think about them.
The third is the deployment surface. A Hugo site is public/, a folder of HTML files. We deploy that folder. Nothing else has to be running. No Node runtime, no edge function, no API route. The static-publication architecture and Hugo go together; picking a generator that wanted us to ship server-rendered routes would have been picking against the architecture.
What we would change about Hugo: the documentation is dense, the error messages on broken templates are cryptic, and the upgrade path between minor versions occasionally bites. We accept those costs.
Hosting: Cloudflare Pages (production) + local Hugo (development)
Stack Quarterly is served from Cloudflare Pages, Cloudflare’s static-hosting platform. The build runs on Cloudflare’s infrastructure when we push to the main branch of the publication’s Git repository; the resulting public/ directory is uploaded to Cloudflare’s edge network and served from the nearest data center.
The reason is uninteresting: Cloudflare Pages is fast, free at our scale, and integrates with the DNS we already used for the domain. Vercel, Netlify, and Fly.io would all have done equivalently well; we did not pick on the marginal differences.
Local development is hugo server -D. The dev server reloads on save and serves on localhost:1313. Editing is in plain Markdown. No staging environment exists at launch; previews happen on a Cloudflare Pages branch deploy when a piece is in late edit.
DNS: Cloudflare DNS
The domain is registered with a separate registrar (we do not name it on this page; the registration record is public for anyone who looks it up). DNS is hosted at Cloudflare, where the apex record points at the Cloudflare Pages deployment via a CNAME alias. The TTLs are set on the short side (60-300 seconds) because we want fast recovery if we have to repoint records during a host migration.
Editor: VS Code (writers) plus the terminal (editor)
The writers compose pieces in VS Code with the Hugo extension installed. The extension does Markdown linting, frontmatter validation, and a live-preview pane next to the source. We do not enforce VS Code; one of the contributors prefers Neovim and we accept the resulting linter-warning discrepancies in pre-publication review.
The editor reviews pieces in the terminal — bat for syntax-highlighted previews, git diff for tracking edits across drafts, and a small script that calls hugo --buildDrafts to render the draft in the same template the published piece will use. This is unusual for a publication; we lean into it. Editing in the same tool the writer wrote in mostly produces the same view of the piece the reader will see.
Version control and hosting: Git + GitHub
Stack Quarterly’s repository is in a private GitHub organization owned by Lumenwhite Media Holdings Pte Ltd. Every piece goes through a branch per draft, a pull request for review, and a squash-merge to main for publication. We treat the squash-merge as the publication event; the Cloudflare Pages build that follows is automatic.
We use the GitHub Actions runner for one job — a link-checker that runs over every published piece nightly and reports broken outbound links to an internal Slack channel. Beyond that, no CI/CD that affects the live site.
Why GitHub over GitLab, Gitea, sourcehut, or self-hosted Forgejo: the writers we work with were already on GitHub, the PR workflow was already in their muscle memory, and the marginal benefit of moving was zero. We are watching the trajectory of self-hosted Forgejo; if it becomes the default in the practitioner community we will reconsider.
Search: Pagefind
Search on the site is powered by Pagefind, the static-site search library built by CloudCannon. The Pagefind CLI runs after every Hugo build, scans the generated HTML in public/, and writes a search index to public/pagefind/. The index is loaded by the browser on demand when a reader opens the search overlay.
Pagefind is unusual in two ways that we appreciate. The first is that the index lives in the deployed static site; there is no search-as-a-service backend to keep running. The second is that the search runs in the browser, which means queries do not leave the reader’s device. We did not pick Pagefind for the privacy benefit, but we like it.
What we would change about Pagefind: the default UI styling is heavy, and we spent two evenings restyling it to match the publication’s brand. The library has hooks for that; the hooks could be better documented.
Newsletter: hosted provider + plain-text email
The newsletter list and delivery currently runs on [TKTK: provider]. The publication’s standing rule on newsletter providers is that the provider does not get to insert tracking pixels we have not approved. We do not run open-rate trackers in our email. The provider exposes aggregate open-rate data based on standard image-load tracking; we look at the aggregate but do not log the per-subscriber detail.
Every newsletter is plain text with light HTML formatting. There are no banners, no display ads, no native ads, no segmented A/B-tested subject lines. The subject line is “Stack Quarterly · Q-N 20YY · Issue N.” It will not change.
Analytics: server access logs and aggregate Pagefind queries
We do not run a third-party analytics product. We do not run Google Analytics, Plausible, Fathom, Simple Analytics, or any other reader-tracking product. The data we have on reader behavior is:
- Cloudflare’s access logs (retained 30 days), which give us aggregate request counts per path and per status code.
- Pagefind’s optional analytics, which logs aggregate search queries (no IP, no device fingerprint, no per-user trail). We use this to find topics readers are looking for that we have not covered.
The analytics posture is intentional. A publication that runs heavy tracking has different incentives than a publication that does not; we want our incentive structure to match the practitioner-first voice. Server logs and aggregate query data are enough to tell us which pieces are working.
Email: editorial inboxes
The publication’s editorial inboxes (editorial@, pitches@, tips@, press@, corrections@, subscribe@) run on [TKTK: mail-provider]. Each address is a real shared inbox read by a real human on the editorial team. We do not autoresponder pitches. We do not autoresponder corrections. We do not run automated triage that mis-routes tips into the spam folder.
For tips marked sensitive, the named editor reads the message directly; no shared-inbox client touches the thread. The full tips policy is at /editorial-guidelines/.
OG image generation: build-time Hugo template
Per-article open-graph images are generated at build time by a small Hugo template that renders an SVG with the article title, byline, and dek into the publication’s brand template, then converts the SVG to PNG using the rsvg-convert binary that lives in the build environment.
The setup is unsophisticated and works. We considered the React-based generators (next/og, satori, vercel-og), but their value proposition is real for sites that want per-request OG generation and we do not need that. We re-render OG images on every build; they are static.
The OG-image template is in layouts/_default/og.html and the conversion script is in scripts/build-og.sh. Both are intentionally short.
Code highlighting: Chroma (Hugo built-in)
Code blocks in articles are syntax-highlighted by Chroma, the Go-native syntax highlighter built into Hugo. We use the github-dark theme to match the publication’s dark default. The theme is overridden by CSS variables when the reader switches to light mode; the highlighting itself does not switch.
Chroma is reliable across the dozen languages we publish (Python, Go, TypeScript, JavaScript, Rust, Bash, JSON, YAML, SQL, and a few one-off DSLs). We have not needed to switch to client-side Prism or highlight.js, which would also be defensible.
What we are not running
A short list of things we deliberately do not run on this site, even though they are common defaults in 2026:
- A CMS. Editorial happens in Markdown plus Git. We considered headless CMS options (Strapi, Sanity, Contentful, Decap) and decided the additional moving piece was not worth the writer convenience. The writers we work with are comfortable in Markdown.
- A comments system. Discussed in the FAQ.
- An ad server. No display ads at launch and probably never.
- A paywall. No paid tier at launch and probably never.
- A user-account system. Bookmarks and the reading list live in
localStorage. No accounts. - A chat widget. No live chat. Email is the channel.
- A real-time analytics dashboard. Server logs and aggregate Pagefind data are enough.
When we would change a tool
The publication’s standing rule is to revisit each tool on this page once a year. The questions for each revisit:
- Does the tool still do what we asked it to do at launch?
- Has a credible alternative emerged that is meaningfully better at the same job?
- If we had to migrate, would the work be one engineer-week or three months?
Tools where the answer to (1) is “yes” and the answer to (3) is “three months” do not get migrated absent a much stronger reason than “the alternative is shinier.” A publication’s job is to publish. Switching the publishing tooling for marginal gains is not the publication’s job.
What this page is for
The point of this page is to ground the publication’s coverage of other peoples’ stack choices in the publication’s own stack choices. We do not have the right to ask a working team to be specific about their tooling decisions if we are not specific about ours. The disclosure here is also a fact-check: a reader who notices a contradiction between something we said in a piece and something we did on this page is welcome to write to corrections@ and ask us to reconcile.