Paul Welty, PhD AI, WORK, AND STAYING HUMAN

Dev Reflection - January 22, 2026

Hey, it's Paul. January 22nd, 2026. Today was a launch day, which means it was also a "things broke immediately" day. Dialex went live at dialex.io, and the first thing that happened was every request got blocked with a 403 Forbidden error. I talk about reasonable decisions accumulating into unreasonable situations, why iteration speed matters more than initial tool choice, and how dashboards make accumulated state visible.

Duration: 8:06 | Size: 7.38 MB

Hey, it’s Paul. January 22nd, 2026.

What I worked on

Today was a launch day, which means it was also a “things broke immediately” day. Dialex went live at dialex.io, and the first thing that happened was every request got blocked with a 403 Forbidden error. Turns out I’d written a Rack::Attack rule weeks ago to block scrapers—seemed reasonable for an internal tool—and it was now blocking all legitimate traffic to my public marketing site. Commented it out, redeployed, and we were back.

Beyond that crisis, I built out the Stripe subscription system for Dialex, created an editorial API endpoint in Authexis so my dashboard can pull pipeline metrics, fixed a bunch of iOS app bugs, shipped podcast infrastructure for the blog, and spent way too many iterations fixing Stellaris modding syntax for Phantasmagoria. Oh, and I built a print-ready HTML template for consulting 1-pagers because Pages wouldn’t cooperate.

Scattered day. But there’s a thread running through it.


What I learned

Here’s the thing about “reasonable” defaults: they’re reasonable for a context. And context changes.

That Rack::Attack rule blocking scrapers? It made perfect sense when I wrote it. I was building an internal tool, I didn’t want bots hitting my endpoints, so I blocked requests without user agents or from curl and wget. Done. Defensive programming. Good practice.

Except then I decided to launch publicly, and suddenly “block anything that looks like a script” meant “block half the internet.” The rule that protected yesterday became the blocker today.

This happens constantly in software, and I think we underestimate how much. You make a decision that’s correct given what you know, you encode it in code, and then circumstances shift and that decision becomes wrong. But the code doesn’t know circumstances shifted. It just keeps enforcing the old decision.

The real answer isn’t “be more careful about defaults” or “anticipate future use cases.” You can’t anticipate what you don’t know yet. The answer is monitoring and iteration. Ship fast, watch what breaks, fix it. That 403 error taught me more in five minutes than weeks of speculation would have.

What’s interesting is how this connects to something else I was working on today: the editorial dashboard. I built a command that aggregates content status across three systems—Notion for ideas, Authexis for writing in progress, and the Hugo blog for scheduled posts. And the first thing it showed me was that I have 83 overdue drafts.

Eighty-three. Posts from years ago with draft: true that were never cleaned up.

Each one represents a decision deferred. Publish it, delete it, or reschedule it. But without visibility, the problem stays invisible and grows. Those drafts accumulated one at a time, each one reasonable in the moment—“I’ll finish this later”—and collectively they became a mess.

So here’s the pattern I’m seeing: reasonable decisions accumulate into unreasonable situations. The Rack::Attack rule was one reasonable decision that blocked everything. The overdue drafts were 83 reasonable deferrals that became a maintenance burden. Neither problem was visible until something forced me to look.

This is why dashboards matter more than I used to think. Not because they tell you what to do—they don’t—but because they make the accumulated state visible. You can’t fix what you can’t see.

The other big insight from today came from building print materials. I needed to create a 1-pager for consulting services, and Pages wouldn’t do what I needed. InDesign wouldn’t launch. Affinity was inscrutable. So I built it in HTML and CSS.

And here’s what surprised me: HTML won not because it was the best tool, but because it was the most modifiable tool. “Make headlines Libre Baskerville” → add one line of CSS. “Logo outside the blue box” → restructure one div. “0.25 inch margins for print-on-demand” → change two numbers.

We didn’t know about print-on-demand margins until mid-design. We didn’t know the logo placement until seeing the first version. Each discovery would have cost ten minutes in Pages. In HTML it cost thirty seconds.

The iteration speed matters more than the initial tool choice. Pages would have been faster to start but slower to refine. And when you’re dialing in requirements that emerge during work—which is always—that refinement speed compounds.

This connects back to the Stellaris modding I was doing today. I spent four iterations fixing situation approach modifiers because I didn’t know the correct syntax. The documentation was wrong, my assumptions were wrong, and I only learned the right way by trying things and reading error logs.

Attempt one: “Invalid modifier situation_monthly_progress_speed.” Okay, that modifier doesn’t exist.

Attempt two: “Unexpected token: monthly_progress.” Okay, that syntax isn’t valid in approach blocks.

Attempt three: “Referring to a non-existent approach.” Okay, I’m generating modifiers for approaches that don’t exist in some situations.

Attempt four: finally clean.

Each failure was a teacher. Each error message told me something the documentation didn’t. Production teaches faster than speculation.


What’s interesting

There’s a tension I keep bumping into between smart systems and dumb systems.

When I built the editorial API endpoint today, I had a choice. I could make a smart endpoint that returns pre-grouped counts—essays by stage, newsletters by status, all the aggregation done server-side. Or I could make a dumb endpoint that returns raw records and lets the client aggregate however it wants.

I went dumb. And here’s why: the client knows better what it needs than the server does.

Today the dashboard wants to group by stage and type. Tomorrow it might want to filter by date range or calculate velocity metrics or group by something I haven’t thought of yet. If the server decides the aggregation, the client has to either live with it or I build more endpoints. If the server stays dumb, the client stays flexible.

The tradeoff is bandwidth and client complexity. But for less than a thousand records, that’s not a real constraint yet.

This same pattern showed up in the iOS app work. The web UI can use ActionMailbox and form uploads for transcript submission because it controls the full stack. iOS needs a structured JSON API. When you build web-first, it’s easy to accidentally couple to web-specific infrastructure. Then when you add a second client, you’re retrofitting an API that feels like an afterthought.

The better pattern is API-first, but that requires discipline to build infrastructure you’re not using yet. And that’s hard to justify when you’re moving fast.

So you end up with this drift between what the web can do and what other clients need. And the gaps between them reveal assumptions about who controls the flow. The web UI assumed it was the only client. The API had to be retrofitted when that assumption broke.

What I find interesting is that these aren’t technical problems. They’re problems of premature commitment. You commit to a structure—a security rule, a draft status, an API design—and then circumstances change and the commitment becomes a constraint.

The only real solution is to stay aware that every decision is provisional. Not in a wishy-washy “nothing is certain” way, but in a practical “this will probably need to change” way. Build systems that are easy to modify. Make state visible so you notice when it drifts. Ship fast so you learn what’s actually true.


Looking forward

Tomorrow Dialex actually launches publicly—blog post, social media, the whole thing. The Stripe integration works, the trial system is live, and now we find out if anyone wants this enough to pay forty-nine dollars a month.

I set a decision point for end of week: zero to five signups means focus on consulting, twenty or more means keep building, a hundred plus means this is real. The trial timer starts the clock. In two weeks, either someone pays or the product doesn’t work. That’s intentional pressure—no more building in a vacuum.

The editorial dashboard needs work too. Those 83 overdue drafts need a review workflow—something that shows each one with context and prompts: publish, delete, or reschedule? Bulk operations on a filtered set.

And I should probably implement the /pipeline skill I designed today. The spec is done, the plan is written, it’s just execution now.

But honestly, the main thing I’m thinking about is whether the patterns I’m seeing hold up. Reasonable decisions accumulating into unreasonable situations. Iteration speed mattering more than initial tool choice. Dumb systems staying flexible while smart systems calcify.

These feel true, but I’ve been wrong before. We’ll see what tomorrow teaches.

· development · daily, reflection, podcast

Featured writing

When your brilliant idea meets organizational reality: a survival guide

Transform your brilliant tech ideas into reality by navigating organizational challenges and overcoming hidden resistance with this essential survival guide.

Server-Side Dashboard Architecture: Why Moving Data Fetching Off the Browser Changes Everything

How choosing server-side rendering solved security, CORS, and credential management problems I didn't know I had.

AI as Coach: Transforming Professional and Continuing Education

Transform professional and continuing education with AI-driven coaching, offering personalized support, accountability, and skill mastery at scale.

Books

The Work of Being (in progress)

A book on AI, judgment, and staying human at work.

The Practice of Work (in progress)

Practical essays on how work actually gets done.

Recent writing

Dev reflection - January 23, 2026

Daily reflection synthesizing work across eight projects. What does it mean for a system to be ready? When infrastructure is complete but operations aren't, when automation works invisibly, and when configuration becomes code without type safety.

Why customer tools are organized wrong

This article reveals a fundamental flaw in how customer support tools are designed—organizing by interaction type instead of by customer—and explains why this fragmentation wastes time and obscures the full picture you need to help users effectively.

Start, ship, close, sum up: rituals that make work resolve

Most knowledge work never finishes. It just stops. The start, ship, close, and sum-up methodology creates deliberate moments that turn continuous work into resolved units.

Notes and related thinking

Infrastructure shapes thought

The tools you build determine what kinds of thinking become possible. On infrastructure, friction, and building deliberately for thought rather than just throughput.

Dev reflection - January 23, 2026

Daily reflection synthesizing work across eight projects. What does it mean for a system to be ready? When infrastructure is complete but operations aren't, when automation works invisibly, and when configuration becomes code without type safety.

Start, ship, close, sum up: rituals that make work resolve

Most knowledge work never finishes. It just stops. The start, ship, close, and sum-up methodology creates deliberate moments that turn continuous work into resolved units.