Workshop

When Code Costs Nothing

Published on: 2026-02-07

By: Ian McCutcheon

When Code Costs Nothing

The hard part of building software has always been writing the code. You spend days on plumbing—parsing config files, wiring up API clients, writing the same boilerplate you've written a hundred times before. Even when you write reusable code, even when you have libraries and patterns, they still need tweaks and special cases. By the time you get to the interesting part, you're tired. The mechanical work consumes the budget for the creative work.

I don't have that problem anymore.


The Shift

I work with Claude Code as my primary development environment. Not as a code generator I paste from. As a fleet of expert agents that have access to my filesystem, understand my project structure, run tests, and maintain continuity across sessions.

The change isn't speed, though that's part of it. It's that implementation stops being the thing I think about. When the cost of building drops far enough, you stop asking "can I afford to build this?" and start asking "should this exist?" That's a different question, and it leads to different outcomes.


Code-First

The most useful philosophy I've adopted: just build it.

Don't diagram it. Don't write a spec. Don't spend an afternoon evaluating five libraries. Describe what you want, build a working version, iterate. If the approach is wrong, you'll know in minutes, not days. Early versions cut corners deliberately—no input validation, minimal error handling—because the goal is to prove the concept, not ship it. Rigor comes when the code earns its way toward production.

A DNS zone audit tool started as "check if DNSSEC is enabled." Within a few sessions it had seven hygiene checks, three output formats, interactive HTML reports, and clickable badges linking out to live DNS lookups. None of that was planned. Each feature emerged from using the previous version and noticing what was missing.

This extends beyond applications. It's how I build the AI infrastructure itself.

When I define a skill for my agents—a capability like managing DNS records, or searching my memory system—I write it as code, not instructions. The skill calls the API directly. It runs, and the agent sees raw results: actual DNS records, actual search hits, actual error messages. Not my description of what the API does. Not a summary. The unfiltered output.

When an agent runs code that hits EasyDNS and gets back a JSON response, it doesn't need me to explain the API's data model. The structure is right there in the response. When a semantic search returns ranked results with file paths, the agent doesn't need me to explain where things are stored. The code did the explaining.

The code is the learning. Every skill encodes what worked. When something breaks—an API changes, a field gets renamed—we fix the code. Next session, every agent benefits. No retraining, no re-explaining. The code carries the institutional knowledge forward.

This creates a flywheel. Each session starts with the agents running code I trust against systems I understand. We're immediately aligned. No warmup. No context-setting. The code put us on the same page before the conversation started.


Structure Over Syntax

What I spend my time on now is structure. Not code structure—project structure, feature design, deciding what belongs where and why.

Should this audit check distinguish between "not applicable" and "absent but not required"? Is this report library generic enough for a shared package, or is it too coupled to one domain? Does this API client belong in the same project as the CLI, or should it be a separate dependency?

These are the questions that make a tool good or mediocre. The code that implements the answers is secondary. And they're exactly what used to get squeezed out by implementation fatigue.


The Last Three Weeks

Vague productivity claims are useless. Here's what I built in about three weeks, working some evenings and weekends. Basically stopped watching TV like a zombie.

MailTaste. A DMARC report processor that turns the XML reports Google and Microsoft send daily into a usable dashboard. Domain verification via DNS. Encrypted data at rest. Untrusted XML from external senders parsed and sanitized before storage. Timeline charts showing authentication trends. An API that feeds other tools in the ecosystem. Already had its own blog post.

Simmer. An e-ink display plugin for TRMNL that puts MailTaste stats on a physical device sitting on my desk. DMARC compliance at a glance without opening a browser. Demo mode, webhook integration, file-based logging.

DNSlurp. A DNS lookup tool that queries authoritative servers directly—bypassing local resolvers, split-brain DNS, and caching layers. Enterprise licensing with Ed25519-signed tokens. Collapsible API documentation. A companion CLI tool for terminal-based lookups.

A DNS provider client library. A Python API wrapper for a managed DNS provider. Auth, zone operations, record CRUD, error handling. Fully tested. Foundation for everything below.

A zone audit tool. Takes any DNS zone and produces a hygiene report: DNSSEC, nameserver consistency, MX, SPF, DMARC policy strength, CAA, expected TXT records. Configurable policy defines "healthy." Outputs to terminal, JSON, CSV, or interactive HTML.

A zone inventory tool. Every zone in an account with metadata—type, record counts, linked status. Same output formats. A --detail flag that pulls full zone data when the provider's list API returns incomplete results.

A standalone HTML report engine. The audit tool needed HTML export. Then the inventory tool needed one too. The third time I looked at the code, I realized the generator had nothing DNS-specific in it. A generic engine: data and a schema in, self-contained interactive HTML file out. Search, filter, sort, copy to clipboard. Zero external dependencies. Email it to someone. They open it. It works. It's becoming its own package. Does it exist yet? Not quite. But the working code is already powering two tools, and the name is picked out.

The eSoup homepage. Rebuilt with a four-theme system. Blog integration. Static site generation deployed to DreamHost.

A shared infrastructure library. Reusable packages for the eSoup ecosystem—stateless signed tokens, email sending via Mailgun—so tools stop duplicating common patterns.

A brand website. Design, content, interactive easter eggs, print stylesheets, mobile optimization. Start to finish in a few hours.

DNS hygiene for my own domains. While building the audit tool, I ran it against my own zones. Found one with no email protections at all. Fixed it in the same session—null MX, strict SPF, DMARC with reject policy and reporting, CAA for the right certificate authority.

Each of these is a real project with tests, a CLI or web interface, and version control. The ones facing the internet got security audits—dedicated agents that review for injection vulnerabilities, input validation gaps, and authentication flaws before anything goes live. Not prototypes.


My Actual Contribution

What I bring to these sessions isn't code.

Decisions. Should the DMARC check warn at p=none or fail? What does "healthy" mean for a zone that doesn't handle email? These require domain knowledge and opinions about what users need.

Architecture. This report library should be reusable. This client should be a separate package. This CLI flag should be --detail, not --verbose, because it changes what data we fetch, not how much we print.

Taste. The audit report shouldn't say "skip." It should say "n/a" when a check doesn't apply, and use a dash when a record is absent but not required. Small distinction. Matters to the person reading the report.

Direction. Those status badges in the HTML report—what if they were clickable? What if clicking "FAIL" on a DMARC check opened a live DNS lookup showing exactly what the record contains? Now the audit tool is driving traffic to DNSlurp. Two products that were built independently, suddenly feeding each other. That idea took ten seconds to articulate and twenty minutes to ship.

I'm not offloading coding. I'm offloading the tax on thinking that coding imposes. What's left is the work I care about: making things useful, coherent, and well-considered.


From Prototype to Production

Code-first means you move fast through the uncertain phase—the phase where you don't yet know if the idea is right, the API makes sense, or the data model holds up. You take wrong turns. You explore forks that dead-end. That's the point. Each dead end costs minutes, not days.

But once the concept proves out, the posture changes. The same agents that built the prototype now harden it. Are we catching errors, or letting them propagate? Is user input validated before it touches a database? Are we handling the case where an external API returns something unexpected? What happens when the DNS provider is down—does the tool crash or degrade gracefully?

This is where the agents earn their keep a second time. I point them at the codebase with a different lens: review this for production readiness. They walk through every endpoint, every external call, every place untrusted data enters the system. They add try-catch blocks, input sanitization, rate limiting, proper error messages. The same speed that made exploration cheap makes hardening cheap too.

The sequence matters. Harden too early and you're polishing code that might get thrown away tomorrow. Harden too late and you ship something fragile. The sweet spot is: iterate freely until the shape is right, then lock it down before it faces the world. Code-first doesn't mean code-only. It means code first—and safety follows, deliberately, before anything goes live.


Continuity

AI agents don't remember last week by default. Every session starts fresh unless you build systems for it.

So I did. A structured memory store for patterns and decisions. Project documentation that loads at session start. Skill definitions that encode how I work—not just what tools to use, but what my preferences are, what my voice sounds like, what mistakes to avoid repeating.

When I say "remember that standalone HTML reports should be single-file with zero dependencies," it gets filed in a pattern library that future sessions reference. Same discipline you'd apply to any engineering team: write things down. The difference is that the cost of maintaining this documentation is negligible when your agents do the writing.

It goes further. A hook fires every time I send a message. It runs a semantic search against a live index of every skill, pattern, and project file in the system. Before the agents even see what I typed, they've already been handed the most relevant context files—ranked and prioritized. When I mention a topic that isn't loaded yet, the search surfaces it. The agents don't start cold. They start with hints.

This is the code-first principle again, applied to memory. The hook is code. The index is code. The ranking is code. It runs automatically, it improves as the index grows, and no one has to remember to invoke it.

This infrastructure compounds—and I mean that in the financial sense. Each session deposits more context: another pattern documented, another preference encoded, another project indexed. The tenth session doesn't just have ten sessions of memory. It has ten sessions of refined memory, where early mistakes have been corrected and vague patterns sharpened into precise ones. The hundredth session will be something else entirely. The system gets smarter every time I use it, permanently.


The Knowledge Underneath

I want to be precise. This approach amplifies expertise. It does not replace it.

When I tell the agents to build a DNS audit tool, I'm not learning DNS as we go. I know what a properly delegated zone looks like. I know why DMARC at p=none is security theater. I know that linked zones inherit their parent's records, that CAA records for Amazon ACM need to reference amazon.com and not letsencrypt.org, that a provider's list API might return empty arrays where you expect record counts. I know these things because I've been debugging DNS resolution paths, tracing packet flows across firewalls, and reading RFCs for longer than a lot of people in this industry have been alive.

The agents are fast. They're thorough. They write clean code. But they don't have opinions about what "healthy" means for a DNS zone. They don't look at a network architecture and feel that something is wrong before they can articulate why. They don't push back when a technically correct solution will fail operationally because they've seen that exact failure mode in production at 2 AM.

That kind of knowledge—the deep vertical kind, where you understand something from the wire protocol to the business impact—becomes more valuable when implementation is cheap. The bottleneck shifts from "can you build it" to "do you know what to build." Every architectural decision, every policy choice, every judgment call about what matters and what doesn't—that's the work now. And it requires the kind of knowledge you can only get by doing it for a long time, getting it wrong, and learning from the wreckage.


The Compound Effect

The individual projects aren't the interesting part. It's what happens when they connect.

The DNS client library enables the audit tool. The audit tool reveals a need for HTML reports. The report code gets extracted into a generic library. The library gets planned as a standalone package. The audit report links out to DNSlurp, cross-pollinating tools that were built independently. MailTaste feeds Simmer, which puts DMARC data on a physical display. The shared infrastructure library means the next tool starts with authentication and email already solved.

None of this was planned upfront. It emerged from building things, using them, and noticing connections. That kind of organic growth only happens when the cost of building is low enough to be exploratory.

A career's worth of ideas about what tools should exist, what reports should look like, what "good enough" DNS hygiene means—all of it was stuck behind the implementation tax.

The clay is free. Now I sculpt.