Arrange Act Assert

Jag Reehals thinking on things, mostly product development

Secure AI Apps Start With Functions, Not SQL

13 May 2026

If you're building AI features into your product, the temptation is to hand the model a database connection and let it write SQL.

It feels powerful. It feels like the modern way.

It's the wrong place to start.

The same reasoning applies whether you're building a CLI, a Slack bot, an MCP server, or an agent.

Secure AI Apps Start With Functions, Not SQL

Read More →

node-env-resolver Makes Safe, Typed Node Config the Default

23 Apr 2026

Liran Tal is spot on in his Environment variables and configuration anti-patterns in Node.js applications post

You may inadvertently expose sensitive information like database credentials and API keys as part of error messages, stack traces, and other forms of data returned to consuming clients.

He explains why process.env feels safe right up until it isn't.

You add dotenv.config() on line one, scatter process.env.DB_PASSWORD across twelve files, then someone's error reporter serialises a request object and your Stripe key ends up in a third-party log.

If you've shipped a Node app, you've probably seen some version of this happen.

His anti-pattern example nails it:

const port = process.env.PORT || 3000;
const dbUsername = process.env.DB_USERNAME;
const dbPassword = process.env.DB_PASSWORD;
const dbHost = process.env.DB_HOST;
const apiBaseUrl = process.env.API_BASE_URL;
const apiToken = process.env.API_TOKEN;

Untyped. Unvalidated. Globally readable. One step away from leaking through logs, traces, or error reporters.

But there's still a gap...

Read More →

Your .env File Is Not a Secret Store

21 Apr 2026

Most teams make one of two mistakes with secrets.

The first is obvious.

The second is more common in teams that have already fixed the first one.

Both come from the same misunderstanding about what a .env file is supposed to do.

Your .env File Is Not a Secret Store

Read More →

Log the Fingerprint, Not the Secret

21 Apr 2026

When something goes wrong with your application's configuration, you need to know what was loaded and where it came from.

Most tools for answering that question either tell you too much or nothing at all.

There's a third option most people haven't considered.

Log the Fingerprint, Not the Secret

Read More →

The package manager settings that would have blocked the Axios attack

07 Apr 2026

On the 31st of March 2026, attackers hijacked an npm maintainer account and published malicious versions of axios with a remote access trojan baked in. npm pulled the bad releases after about two or three hours, but that was enough. Anyone who ran npm install axios during that window could have installed the trojan. The article Post Mortem: axios npm supply chain compromise has all the details.

This kind of attack keeps happening and the playbook barely changes: compromise an account, push a malicious update, hope people install it before anyone notices, get removed a few hours later.

Every major package manager now lets you defend against this. In this post I'll show you the setup for npm, pnpm, Bun and Yarn.

Read More →

Engineering decisions are bets, not proofs

06 Apr 2026

Most engineering decisions are not proofs.

They are bets.

You choose a direction with incomplete information, under time pressure, and with trade-offs you cannot fully test in advance.

That does not make the decision weak.

It makes it real.

Engineering decisions are bets, not proofs

In a previous post, I wrote about how design by committee leaves engineering change unfinished. The deeper reason is simple: many organisations treat technical decisions as though they should be certain before the work begins. But most meaningful engineering decisions are not certainties. They are bets.

Read More →

Why design by committee leaves engineering change unfinished

05 Apr 2026

Engineering change often gets stuck for the same reason: design by committee. Not because anyone has bad intentions, but because the pursuit of alignment quietly replaces the pursuit of learning.

Why design by committee leaves engineering change unfinished

It usually starts with good intentions. People want alignment, consistency, and lower risk. So a proposed change gets pulled into more meetings, more reviews, more stakeholders. Before long, the goal is no longer to try something, learn from it, and improve it. The goal becomes finding one perfect solution for every team.

That is usually where momentum dies.

Read More →

fn(args, deps) Is Not Anti-Mock

23 Mar 2026

People hear "don't use vi.mock" and assume the alternative is "don't mock anything." That is not the argument.

fn(args, deps) is not anti-mock

The real distinction is simpler: mock explicit collaborators, not implicit imports.

Read More →

Before You Change the Process, Understand the Problem

20 Mar 2026

Strong opinions should be earned, not borrowed. If you want to propose a change, do the work first.

Understand the trade-offs before you walk into the room.

Before You Change the Process, Understand the Problem

I see the same pattern again and again in software teams. Someone reads a blog post, watches a conference talk, picks up a new buzzword, or sees how a company like Spotify talks about working, and by the next meeting they are proposing that the whole team change how it works.

The idea often sounds compelling at first. But the moment you ask about the trade-offs, the case falls apart. There is no real research, no serious risk assessment, and no clear plan for what to do if the change creates new problems rather than solving the old ones.

That gap between enthusiasm and understanding is where teams get into trouble.

Read More →