Why Code Golfing is the Ultimate Test for Multimodal LLMs (And a New Benchmark to Prove It)

Hi everyone,

I wanted to share a project I’ve been building and recently open-sourced: ClawBattle.

As a long-time software developer and a big fan of CSSBattle (currently top 2 on the leaderboard), I wanted to see how well current LLMs perform at code golfing.

It turns out this task is also excellent for benchmarking. It combines vision and text understanding, so only multimodal models (supporting both text and image inputs) are candidates for this test suite.

Right now, OpenAI’s GPT-5.5 is by far the best model on this benchmark. I also just added Gemini 3.5 Flash. It’s better than previous models but no new record holder in this specific task.

Most modern LLM benchmarks suffer from data contamination—the models have already seen the test solutions during training. ClawBattle solves this:

The benchmark runs on specific battle targets where the top-tier solutions are strictly confidential and not publicly available (with the sole exception of Target 1). There is absolutely no way for the models to have memorized or trained on the optimal code.

Having achieved a top rank in code golfing myself, I belong to a very small group of players who actually know these top-tier solutions. I used this exclusive knowledge to design the evaluation suite. This ensures the benchmark tests true problem-solving, visual understanding, and logic generation—not just memorization.

Check out the results here: https://beowolve.github.io/ClawBattle/

Github: https://github.com/Beowolve/ClawBattle

Enjoy!

Decoding Solana Account Data: Three Methods Compared

Methods Fetching preparation

From Hex to Human-Readable

At the end of Post 2, my account explorer was printing this for the Token Program:

Data:
Size: 48 bytes
First 48 bytes (hex):
7436346a506...

I could see the data existed. I had no idea what it meant.

That wall of hex bytes is actually structured information — token supply, decimals, mint authority. It’s just encoded in binary. This post is about the three ways I learned to decode it, what each method taught me, and when to use which.

The Problem

You’ve got an account. You can fetch it. But the data field is a binary blob:

data: [2, 0, 0, 0, 39, 241, 144, 177, 211, 175, 152, 184, 206, 113, 76, 68, ...]

How do you turn this into meaningful information?

This is where decoding comes in. And it turns out, there are three different ways to do it in Solana. Each has tradeoffs.

Methods Fetching preparation

Background: Account Data is Binary Borsh

SPL (Solana Program Library) accounts use Borsh, a binary serialization format. It’s deterministic, language-independent, and compact.

For example, a token mint account is 82 bytes:

Bytes 0-3:       Mint discriminator
Bytes 4-35:      Mint authority (32-byte public key)
Bytes 36-43:     Total supply (u64 = 8 bytes, little-endian)
Byte 44:         Number of decimals (u8)
Bytes 45-76:     Freeze authority (32-byte public key)

Raw bytes look like nonsense. Decoding means:

  1. Reading bytes at specific offsets
  2. Interpreting them as specific types
  3. Converting little-endian → numbers

We need a reliable way to do this.

Method 1: Using a Codec Library

The cleanest approach: use @solana-program/token with its built-in decoders.

npm install @solana-program/token
import { getMintDecoder } from "@solana-program/token";
import { base58Decoder } from "@solana/codecs";

async function decodeWithCodec() {
  const connection = new Connection("https://api.mainnet-beta.solana.com");

  // Fetch the token mint
  const WRAPPED_SOL = "So11111111111111111111111111111111111111112";
  const accountInfo = await connection.getAccountInfo(
    new PublicKey(WRAPPED_SOL)
  );

  // Decode using the official codec
  const mintDecoder = getMintDecoder();
  const decodedMint = await mintDecoder.decode(accountInfo.data);

  console.log("Method 1: Codec");
  console.log("Supply:", decodedMint.supply);
  console.log("Decimals:", decodedMint.decimals);
  console.log("Mint Authority:", decodedMint.mintAuthority);
  console.log("Freeze Authority:", decodedMint.freezeAuthority);
}

Method 1: Code LibraryThe rest of method 1

Pros:

  • ✅ Type-safe
  • ✅ Official/audited
  • ✅ Handles complex types automatically
  • ✅ No manual offset calculations

Cons:

  • ❌ Only works for SPL types
  • ❌ External dependency
  • ❌ Black box — you don’t see the binary

Method 2: Manual Byte-Level Parsing

Read raw bytes and extract fields yourself using DataView.

function decodeManually(data) {
  const view = new DataView(data.buffer);

  // Bytes 0-3: Skip discriminator

  // Bytes 4-35: Mint authority (public key = 32 bytes, stored as base58)
  const mintAuthBytes = data.slice(4, 36);

  // Bytes 36-43: Supply (u64, little-endian)
  const supply = view.getBigUint64(36, true);

  // Byte 44: Decimals (u8)
  const decimals = view.getUint8(44);

  // Bytes 45-76: Freeze authority
  const freezeAuthBytes = data.slice(45, 77);

  return {
    mintAuthority: base58Decoder.encode(mintAuthBytes),
    supply,
    decimals,
    freezeAuthority: base58Decoder.encode(freezeAuthBytes)
  };
}

Key gotcha: The true parameter means little-endian. Solana uses little-endian (LSB first). Get this wrong and your numbers are garbage.

// ❌ WRONG - big-endian
const supply = view.getBigUint64(36, false);  // Incorrect!

// ✅ CORRECT - little-endian
const supply = view.getBigUint64(36, true);   // Correct

Method 2: Manual byte-level parsing
The rest of method 2

Pros:

  • ✅ Complete control
  • ✅ Learn exactly how serialization works
  • ✅ Works for any binary format
  • ✅ No dependencies

Cons:

  • ❌ Easy to get offsets wrong
  • ❌ Tedious for complex structures
  • ❌ No type checking
  • ❌ Must handle endianness manually

Method 3: RPC’s jsonParsed Format

Let Solana’s RPC do the parsing for you:

async function decodeViaRpc() {
  const connection = new Connection("https://api.mainnet-beta.solana.com");

  const WRAPPED_SOL = "So11111111111111111111111111111111111111112";

  // Use getParsedAccountInfo instead of getAccountInfo
  const accountInfo = await connection.getParsedAccountInfo(
    new PublicKey(WRAPPED_SOL)
  );

  const parsed = accountInfo.value.data.parsed.info;

  console.log("Method 3: RPC jsonParsed");
  console.log("Supply:", parsed.supply);
  console.log("Decimals:", parsed.decimals);
  console.log("Mint Authority:", parsed.owner);
}

Method 3: RPC jsonParsed Format

Pros:

  • ✅ One RPC call
  • ✅ Automatic parsing
  • ✅ Type-safe JSON response
  • ✅ Easiest to implement

Cons:

  • ❌ Only works for known program types
  • ❌ Depends on RPC node supporting it
  • ❌ Can’t inspect raw data
  • ❌ Slower than local parsing

Comparison: Real Results

I tested all three methods on Wrapped SOL (So11111111...):

Method 1 (Codec):      Supply: 10234567890000000000n, Decimals: 9
Method 2 (Manual):     Supply: 10234567890000000000n, Decimals: 9
Method 3 (RPC):        Supply: "10234567890000000000", Decimals: 9

Status: ✅ All three match!

Comparison & Verification

Deciding Which to Use

Use Method 1 (Codec) if:

  • You’re working with SPL types (tokens, mints, associated accounts)
  • You want type safety
  • You’re okay with an external dependency

Use Method 2 (Manual) if:

  • You’re parsing custom program data
  • You want to understand the binary format
  • You need maximum control
  • You’re building low-level tools

Use Method 3 (RPC) if:

  • You’re building quick scripts
  • The RPC node supports parsing your account type
  • Performance isn’t critical
  • You just want the data, not the details

Real-World Example: Parsing Token Account Data

Here’s where it gets practical. A token account has a different structure than a mint. Let’s decode one:

async function decodeTokenAccount(address) {
  const connection = new Connection("https://api.mainnet-beta.solana.com");
  const accountInfo = await connection.getAccountInfo(new PublicKey(address));

  const view = new DataView(accountInfo.data.buffer);

  // Token account structure:
  // 0-31:   Mint (32 bytes)
  // 32-63:  Owner (32 bytes)
  // 64-71:  Amount (u64)
  // 72:     Decimals (u8)
  // 73-103: Delegate (32 bytes)
  // etc.

  const amount = view.getBigUint64(64, true);
  const decimals = view.getUint8(72);
  const realAmount = Number(amount) / Math.pow(10, decimals);

  console.log(`Token Balance: ${realAmount}`);
}

Summary Table

The Key Insight

Borsh serialization is deterministic: given the same account data, all three methods produce identical results. The difference is:

  • Implementation complexity (codec > RPC > manual)
  • Type safety (codec > manual > RPC)
  • Performance (manual > codec > RPC)
  • Flexibility (manual > codec > RPC)

Choose based on your use case.

Practical Debugging

When parsing fails, debug systematically:

console.log("Raw data length:", data.length);
console.log("First 10 bytes:", data.slice(0, 10));

const view = new DataView(data.buffer);
for (let i = 0; i < Math.min(64, data.length); i += 8) {
  const value = view.getBigUint64(i, true);
  console.log(`Offset ${i}: ${value} (0x${value.toString(16)})`);
}

Often the issue is:

  1. Wrong offset (miscounted bytes)
  2. Wrong endianness (used false instead of true)
  3. Wrong data type (u32 instead of u64)
  4. Missing discriminators or padding

Check the SPL source code for the exact account layout.

The Full Example

Here’s a complete script using all three methods:

import { Connection, PublicKey } from "@solana/web3.js";
import { getMintDecoder } from "@solana-program/token";
import { base58Decoder } from "@solana/codecs";

const WRAPPED_SOL = "So11111111111111111111111111111111111111112";

async function compareDecodingMethods() {
  const connection = new Connection("https://api.mainnet-beta.solana.com");
  const accountInfo = await connection.getAccountInfo(
    new PublicKey(WRAPPED_SOL)
  );

  // Method 1: Codec
  const mintDecoder = getMintDecoder();
  const m1 = await mintDecoder.decode(accountInfo.data);

  // Method 2: Manual
  const view = new DataView(accountInfo.data.buffer);
  const m2 = {
    supply: view.getBigUint64(36, true),
    decimals: view.getUint8(44)
  };

  // Method 3: RPC
  const parsed = await connection.getParsedAccountInfo(
    new PublicKey(WRAPPED_SOL)
  );
  const m3 = parsed.value.data.parsed.info;

  console.log("Supply Comparison:");
  console.log("Codec:", m1.supply);
  console.log("Manual:", m2.supply);
  console.log("RPC:", m3.supply);
}

compareDecodingMethods();

Challenge

Try parsing a token account you own:

  1. Get the account address
  2. Fetch it via RPC
  3. Decode using all three methods
  4. Verify they match

Understanding this deepens your grasp of Solana’s data model.

Next in this series: Post 4 covers Solana’s System Program — the kernel-level program that owns every wallet and enforces the rules that hold the whole network together.

Series: The Account Model Foundation

Which method do you prefer? Let me know in the comments!

Part of the 100 Days of Solana Epoch 1 Writing Challenge.

MCP Just Landed on Your Phone: What Google AI Edge Gallery Actually Does

This is a submission for the Google I/O Writing Challenge

I was already running MCP servers on my desktop — connected to Claude, wired into my daily workflow — when Google announced at I/O 2026 that AI Edge Gallery now supports MCP connections on Android. I pulled out my Pixel and started testing.

First attempt: “No eligible devices.” The app requires capable hardware. Second device — it opened.

What I found is more interesting than the announcement.

What Google AI Edge Gallery Is

Google AI Edge Gallery is an open-source Android app from Google Research. Large language models run entirely on your device — no internet required for inference, no data leaves your phone. Every prompt, every image, every audio clip stays local.

That part isn’t new. What changed at I/O 2026: the app now supports agents. Not a chat interface with a web search button — a proper agent runtime with toggleable skills, calendar integration, scheduled reminders, and experimental MCP connections. The same protocol the rest of the serious agent ecosystem runs on.

The Model List Surprised Me

Opening the Models panel, I expected a Gemma showcase. That’s not what this is.

Model Size Notes
Gemma-4-E2B-it 2.6 GB Recommended across most use cases, 32K context
Gemma-4-E4B-it 3.7 GB Multi-modal, 32K context
Gemma-3n-E2B-it 3.7 GB Text, vision, audio, 4096 context
Gemma-3n-E4B-it 4.9 GB Text, vision, audio, 4096 context
Gemma3-1B-IT 584 MB 4-bit quantized
Qwen2.5-1.5B-Instruct 1.6 GB Alibaba’s model, LiteRT-LM ready
DeepSeek-R1-Distill-Qwen-1.5B 1.8 GB Reasoning model, fully on-device
TinyGarden-270M 289 MB Fine-tuned FunctionGemma, task automation
MobileActions-270M 289 MB Fine-tuned FunctionGemma, device control

DeepSeek and Qwen sitting in a Google app isn’t accidental. The actual product here is LiteRT-LM — Google’s mobile inference runtime — not Gemma. Which model you run on top is your choice.

Gemma 4: The Number That Matters

Gemma 4 E2B runs in 2.6 GB and opens a 32K context window. Gemma 3n had 4096 tokens. Multi-modal input — text, images, and audio — in one model.

That context gap is what makes the agent use case real. Tool call outputs, calendar data, conversation history — there’s room to feed all of it back to the model without truncating. Running a 32K context window offline on a phone wasn’t viable when the best on-device options topped out under 2K.

All models run through LiteRT-LM — previously TensorFlow Lite, now meaningfully upgraded.

Agent Skills: What It Actually Looks Like

This is the part I came to test. Here’s what’s actually inside.

The Skills Architecture

Agent Skills runs on 12 skills total — split into two tiers:

Built-in skills (Google’s own):

  • calculate-hash — hash a given text
  • create-calendar-event — write to OS calendar
  • interactive-map — show a map view for a location

Community skills (user-created, same interface):

  • read-calendar-events — read OS calendar for a specific date
  • schedule-notification — schedule a one-time or repeating daily notification
  • query-wikipedia — pull a Wikipedia summary on a topic
  • qr-code — generate a QR code for a URL
  • mood-tracker — stores daily mood and comments, tracks history
  • send-email — send an email
  • learn-something-new — daily learning companion with image card and scheduled notification
  • kitchen-adventure — dungeon master RPG set in a world of sentient kitchen appliances
  • text-spinner — “Spin the given text on my head”

Two things worth noticing. First: calendar read and calendar write are separate skills with separate toggles. That’s a granular permissions model — the agent can write events without having read access, or vice versa. Second: send-email means an offline on-device model can send emails through a skill. That’s not a demo capability.

Every skill has a “View” button — inspect the full skill definition before enabling it. Each is individually toggleable. The chat bar shows a live count: Skills 8 | MCP 0. Skills and MCP are tracked separately in the same toolbar.

Four ways to add skills:

  1. Featured list — curated community contributions
  2. Load from URL — any web-hosted skill directory
  3. Import local skill — from the device directly, no server needed
  4. GitHub Discussions — browse the full community

Local import is the developer detail. You can build and test a skill entirely on-device without hosting anything. The iteration loop for custom skill development doesn’t require a deployed server.

Agent Skills only accepts two models — both Gemma 4. Gemma 3, DeepSeek, Qwen: available for AI Chat, locked out of agents. Google drew a capability line and stuck to it.

The MCP Setup

Tap the MCP counter in the toolbar → empty state with a single button: + Add MCP server.

The dialog asks for:

  1. Your MCP server URL
  2. Authorization: None, Request header, or OAuth (WIP)

OAuth isn’t ready yet. That’s the honest limitation for anyone planning to connect enterprise or authenticated MCP servers — you’re working with no auth or bearer token only, for now. Public MCP servers work today. Authenticated production servers will have to wait for OAuth to ship.

Worth noting what this architecture means even without OAuth: tool-selection logic runs on-device, and only the structured API call leaves the phone. For healthcare or legal tooling that can’t send raw queries to a server, that’s a meaningful trust boundary — not a workaround.

For developers already running public MCP servers: enter the URL, the app fetches the tool manifest, tool definitions load into the system prompt alongside your active skills. The model handles invocation from there.

The Rest of the App

Each use case — AI Chat, Ask Image, Audio Scribe, Prompt Lab — links directly to API documentation and example code from its own screen. This is a teaching environment, not just a demo. Google built it for developers to read, fork, and build on.

AI Chat supports Thinking Mode — Gemma 4’s step-by-step reasoning exposed inline. Useful before you wire a model into anything production-facing.

Mobile Actions runs on MobileActions-270M — 289 MB, fully offline. A 270M parameter model doing device automation. For context, Gemma 4 E2B is roughly 10x that size and handles general reasoning. The argument being made with that design: narrow fine-tunes at sub-300MB can do discrete tasks better than a general model, and they fit anywhere.

Tiny Garden — 289 MB, natural language gardening game — is the same point made playfully. Watch how function-calling works on-device in a consequence-free environment.

Getting Started

1. Install Google AI Edge Gallery (Play Store)
   Requires capable Android hardware — tested on Pixel

2. Download Gemma-4-E2B-it (2.6 GB)
   Only Gemma 4 models run Agent Skills

3. Open Agent Skills → tap the Skills or MCP button in the toolbar

4. For built-in skills: toggle on what you need
   For MCP: tap MCP → Add MCP server → enter URL + auth

5. Start chatting — the model sees your active skills and connected tools

Source code: github.com/google-ai-edge/gallery. Community skills shareable via GitHub Discussions.

What Developers Should Take From This

The Gemma 4-only lock on Agent Skills is the tell. This isn’t a checkbox feature. Google shipped agentic tool use where the model can handle it reliably, and locked out weaker models until that changes. That’s a better decision than letting anything run and degrading silently.

The OAuth (WIP) flag on MCP auth is the other honest signal. Public MCP servers work today. Enterprise-grade authenticated connections aren’t there yet. That’s not a failure — it’s a preview of where this is going, with the current edges visible rather than hidden.

The 270M fine-tuned models are the underrated part of this release. MobileActions and TinyGarden are evidence of a different architecture: specialized micro-models for narrow tasks, general models for reasoning, LiteRT-LM as the runtime connecting them. At 289 MB each, those models fit anywhere.

MCP being here matters because it’s the same protocol across Claude, Cursor, VS Code extensions, and now Google’s on-device runtime. Build a tool as an MCP server once and every compatible client picks it up. That’s not small.

Verdict

AI Edge Gallery is the most developer-forward release from Google I/O 2026. Not a consumer product — a reference implementation of what on-device agents look like when an open protocol, a capable model family, and a mobile inference runtime land in the same place.

If you’re building with MCP today, install the app and point it at your existing server. Your tools already work. That’s not a coincidence — it’s what a protocol looks like when it actually wins.

I Made My Website “Alive” using Physics (Vanilla JS Experiment Part 2) 🚀

A few days ago, I wrote about getting bored with flat, static websites and trying to build something that actually reacts to the user. I wanted the DOM to feel physical.

For my next update, my original plan was to go completely overboard — I’m talking HTML5 Canvas, complex math, Hooke’s Law for spring physics, and particles that react to cursor velocity.

Here is what I added to make the UI feel even more alive:

1.True Magnetic UI (Hooke’s Law in JS) 🧲
In the first version, elements just followed the cursor. Now, they have mass and tension.I implemented a physics engine for the buttons using Hooke’s Law ($F = -kx$). When your cursor gets within a certain radius, the button snaps towards it. But the magic happens when you pull away — it doesn’t just snap back; it wobbles and settles down with friction.

// The core kinematics engine making it happen
const forceX = (targetX - currentX) * stiffness;
velX = (velX + forceX) * friction;
currentX += velX;

2. Velocity-Reactive Particles (Canvas) ✨
Backgrounds shouldn’t just sit there. I added an HTML5 Canvas layer with a particle grid.
But they aren’t just floating randomly. The particles track the instantaneous velocity of your cursor.

  • If you move your mouse slowly, the particles gently move out of the way.
  • If you swipe your mouse aggressively, the particles calculate the momentum, scatter violently, and then spring back to their original grid positions.

It feels like swiping your hand through a pool of glowing water.
3. Integrating it into “Cyber-Hand OS” 💻
I’m taking all these micro-interactions and using them to build out the control panel for my larger project, Cyber-Hand OS. Starting with simple ON/OFF states for volume and notifications, but layering these fluid physics on top of them.

Sometimes, over-engineering a simple button is exactly what you need to remember why frontend development is so much fun.

Check it out!
You can play with the live demo here:
https://naman9104.github.io/Updated-experiment/
And the full code is on my repo: https://github.com/naman9104/Updated-experiment

Let me know what you guys think! Should I add collision detection next?

Stop Asking “Which Model?” and Start Fixing Your Team’s AI Supply Chain [Image Test C]

article visual 1

This week made one thing obvious: AI coding speed is up, but trust in code is now your real bottleneck.

Hook: the scary part isn’t wrong code anymore

The biggest AI risk in software teams right now is not bad output, it’s bad provenance.

Most senior devs I know can smell shaky code in a PR. We’ve trained that instinct for years.

What’s newer (and nastier) is code that looks fine, ships fast, and quietly breaks ownership, traceability, or security assumptions.

If your workflow still treats AI as “just a faster autocomplete,” you’re defending the wrong perimeter.

What changed this week (and why it’s not random noise)

A few trend signals lined up this week in a way that matters:

article visual 1

  • Anthropic acquired Stainless, which is a strong hint that “model + API lifecycle” is becoming one product surface, not two separate tools.
  • A repo maintainer story on HN showed AI bot spam getting blocked using Git’s --author flag strategy. Not glamorous, but very real: identity and attribution are now active attack surfaces.
  • Local-first and self-controlled tooling kept getting attention (for example, Files.md traction, Haiku-on-M1 discussions). That’s not nostalgia; it’s developers reclaiming control planes.

Different stories, same direction: we’re shifting from “Can AI write code?” to “Can our system verify who/what changed code, why, and under what guardrails?”

Why this matters in real teams

In a solo project, you can vibe-code and recover. In a team, ambiguity compounds.

Here’s what I’m seeing across real delivery environments:

  • PR volume grows faster than review depth.
  • Generated code lands without enough architectural context.
  • Ownership gets fuzzy: “the assistant wrote it” is not an accountability model.
  • Security and compliance teams get pulled in late, when the blast radius is already big.

The painful part: velocity looks great on paper right until one messy incident forces a freeze, and then everyone pretends this was unpredictable.

It was predictable.

AI didn’t remove software engineering constraints. It moved them.

You used to spend more time producing code; now you spend more time proving code deserves to exist.

The wrong question most people ask

“Which model should we standardize on?”

That’s not useless, but it’s not first-order anymore.

Model choice matters, yes. But teams are overfocusing on model IQ while underinvesting in workflow integrity. A stronger model in a weak workflow just lets you create ambiguity at higher throughput.

Contrarian angle: for most teams, upgrading process quality will outperform upgrading model quality.

Not forever, but definitely this quarter.

If your branch strategy is chaos, your review contract is vague, and your commit identity rules are loose, model gains are mostly cosmetic.

The better question: where does trust break in our AI workflow?

Ask this instead:

“At which exact handoffs can low-context AI output become high-impact production risk, and what lightweight controls close those gaps?”

Then run this playbook.

A practical playbook you can apply this sprint

1) Add a provenance contract to PRs

Every AI-assisted PR should include:

  • what was generated (files or modules)
  • what was human-authored
  • what checks were run
  • what remains unverified

Keep it short and mandatory. You’re not writing a thesis; you’re creating an audit trail.

2) Enforce commit identity hygiene

If bot noise or unclear authorship is possible in your flow, lock this down now:

  • require verified commit emails/domains where possible
  • define rules for bot/service authors
  • reject unexpected author patterns in CI

The HN bot-spam story is your warning shot: attribution is a security control now.

3) Split AI usage into lanes

Stop using one giant “AI helped” bucket.

Create 3 lanes:

  • drafting: scaffolding, boilerplate, test seed generation
  • transforming: refactors, migrations, repetitive edits
  • deciding: architecture, security-sensitive logic, data contracts

Lane 3 always gets human-first review. No exceptions.

4) Upgrade review prompts, not just generation prompts

Most teams polish generation prompts and ignore review prompts.

Use reviewer checklists tuned for AI-heavy diffs:

  • does this change preserve invariants?
  • did naming drift from domain language?
  • are edge cases tested or only happy path?
  • did hidden coupling increase?

Treat review as an explicit system, not heroics.

5) Track one metric that reveals workflow health

Pick one, start simple:

  • PR reopen rate
  • hotfixes within 7 days of merge
  • median review rounds per AI-assisted PR
  • percent of AI-assisted PRs with complete provenance note

Don’t build a dashboard empire. One honest metric beats ten vanity charts.

6) Keep a “human judgment list”

Document decisions AI should not make alone in your codebase:

  • auth boundaries
  • billing logic
  • destructive migrations
  • incident automation steps

This avoids vague arguments mid-PR and protects your senior engineers from becoming nonstop escalation points.

Closing line that sticks

AI didn’t kill software engineering fundamentals; it just made the fundamentals bill you daily.

Teams that win this year won’t be the ones with the flashiest model, they’ll be the ones with the cleanest trust pipeline from prompt to production.

How to Prompt AI Tools to Write Accurate SQL Queries (And Why Most Developers Get This Wrong)

If you’ve tried asking ChatGPT, Claude, or any AI SQL assistant to generate a query and gotten back something that looked plausible but was subtly wrong — you’re not alone. The frustrating part is it often runs. The database returns rows, the numbers look reasonable, and you ship it. Three days later, someone points out the totals are off by 20%.

The problem isn’t the AI. The problem is the prompt.

Text-to-SQL works remarkably well when you give the model what it actually needs. According to AWS’s benchmarking, GPT-4-class models achieve a 94% first-try success rate on ad-hoc analytics queries when the schema and foreign key constraints are properly provided. Without that context? You’re closer to 60%. The difference is entirely in how you prompt.

This guide covers the practical techniques — schema context, few-shot examples, business term definitions, and chain-of-thought decomposition — that separate accurate AI-generated SQL from the kind that silently lies to you.

Why AI Gets SQL Wrong (It’s Not the Model’s Fault)

When you ask an AI “give me last month’s revenue by plan tier,” the model has to make a series of guesses:

  • Which table holds revenue? orders? subscriptions? invoices?
  • What column tracks the amount? total? amount_cents? mrr?
  • What does “last month” mean in your timezone?
  • Is “revenue” recognized revenue, gross, or net of refunds?
  • What column stores “plan tier”?

Without your schema, the model invents plausible-sounding answers to all of these. It will write syntactically valid SQL that is semantically wrong for your specific database.

The key insight: From the model’s perspective, the schema is the problem space. You wouldn’t ask a contractor to remodel your kitchen without giving them the floor plan.

Technique 1: Always Include Your Schema (But Not All of It)

The most impactful single change you can make is providing your table definitions in the prompt.

The naive approach — dumping your entire database schema — backfires. Enterprise databases with 100+ tables drown the model in noise and push important tables out of its effective context window. The right move is to include only the tables relevant to the question.

Here’s a solid schema context block to include in your prompt:

You are a SQL expert working with a PostgreSQL database.

Relevant tables:

CREATE TABLE subscriptions (
  id          SERIAL PRIMARY KEY,
  user_id     INTEGER REFERENCES users(id),
  plan        TEXT,         -- 'starter', 'pro', 'enterprise'
  status      TEXT,         -- 'active', 'cancelled', 'trialing'
  mrr_cents   INTEGER,      -- monthly recurring revenue in cents
  started_at  TIMESTAMPTZ,
  cancelled_at TIMESTAMPTZ
);

CREATE TABLE users (
  id          SERIAL PRIMARY KEY,
  email       TEXT,
  created_at  TIMESTAMPTZ,
  country     TEXT
);

Write a SQL query to: [your question here]

Notice the inline comments on columns like plan and status. These are crucial — they tell the model the exact set of values to filter on. Without them, the model might write WHERE plan = 'professional' instead of WHERE plan = 'pro'.

Practical tip: If you don’t know which tables are relevant upfront, ask the model first: “Given this question, which tables from the following list would you need?” Then provide only those.

Technique 2: Define Your Business Terms

Business vocabulary rarely maps cleanly to column names. “Revenue” might mean mrr_cents. “Active users” might mean users who logged in within 30 days, not users with status = 'active'. “Churn” could be calculated six different ways.

Add a glossary block to your prompt for any domain-specific terms:

Business term definitions:
- "Active subscriber": a user with subscriptions.status = 'active'
- "MRR": SUM(mrr_cents) / 100.0, expressed in dollars
- "Last month": the full calendar month before the current month
  (e.g., if today is May 15 2026, last month = April 1–30 2026)
- "Churned": status changed from 'active' to 'cancelled' in the period

This is the step most developers skip. And it’s the reason queries that look right return numbers that don’t match your finance team’s spreadsheet.

Technique 3: Use Few-Shot Examples

Few-shot prompting — showing the model one or two example question/query pairs — dramatically improves accuracy on complex queries. It teaches the model your style, your naming conventions, and the kinds of JOINs your schema requires.

Here are two example queries to guide your output:

-- Question: How many new subscribers signed up in March 2026?
-- Query:
SELECT COUNT(*)
FROM subscriptions
WHERE started_at >= '2026-03-01'
  AND started_at < '2026-04-01';

-- Question: What is total MRR by plan for active subscriptions?
-- Query:
SELECT
  plan,
  SUM(mrr_cents) / 100.0 AS mrr_dollars
FROM subscriptions
WHERE status = 'active'
GROUP BY plan
ORDER BY mrr_dollars DESC;

-- Now answer this question: [your new question]

The examples serve as a template. The model learns that you use >= / < for date ranges (not BETWEEN), that you divide cents by 100.0, and that you prefer ORDER BY descending. You’ll get output that fits into your codebase without needing cleanup.

Technique 4: Break Complex Questions into Steps (Chain-of-Thought)

For multi-step analytical queries — cohort analysis, funnel calculations, retention — don’t ask for the whole query at once. Ask the model to reason through it first.

I need to calculate 30-day retention for users who signed up in April 2026.
Retention means: they had at least one active session in the 30-day window
after signup (tracked in the `events` table with event_type = 'session_start').

Before writing the SQL:
1. What intermediate steps are needed?
2. Which tables will you join?
3. How will you define the 30-day window?

Then write the final query.

When you ask the model to think through the steps explicitly, it catches its own mistakes before producing the final SQL. This mirrors what an experienced developer does mentally before writing a complex query. The chain-of-thought approach consistently outperforms direct generation on multi-table, multi-condition queries.

Here’s what the output might look like for that retention query:

WITH april_signups AS (
  SELECT id, created_at
  FROM users
  WHERE created_at >= '2026-04-01'
    AND created_at < '2026-05-01'
),
retained AS (
  SELECT DISTINCT u.id
  FROM april_signups u
  JOIN events e ON e.user_id = u.id
    AND e.event_type = 'session_start'
    AND e.occurred_at >= u.created_at + INTERVAL '1 day'
    AND e.occurred_at <= u.created_at + INTERVAL '30 days'
)
SELECT
  COUNT(DISTINCT a.id)                        AS total_signups,
  COUNT(DISTINCT r.id)                        AS retained_users,
  ROUND(
    COUNT(DISTINCT r.id)::NUMERIC /
    NULLIF(COUNT(DISTINCT a.id), 0) * 100, 1
  )                                           AS retention_pct
FROM april_signups a
LEFT JOIN retained r ON r.id = a.id;

A direct one-shot prompt rarely produces something this structurally sound. Decomposing the problem does.

Technique 5: Ask for Explanation and Validation

After receiving a query, always ask the model to explain it:

Explain what this query does, step by step.
Are there any edge cases (nulls, division by zero, time zone assumptions)
that could return incorrect results?

This serves two purposes: it helps you catch logical errors before running the query in production, and it forces the model to re-examine its own output. Models often catch their own mistakes during this review pass.

For example, the model might flag:

  • “This query uses CURRENT_DATE which assumes UTC. If your database runs in a different timezone, you may want CURRENT_DATE AT TIME ZONE 'America/New_York'.”
  • “If mrr_cents is NULL for any rows, SUM() will silently exclude them. You may want COALESCE(mrr_cents, 0).”

These are exactly the gotchas that cause silent data quality issues.

Common Mistakes to Avoid

Vague questions. “Show me user activity” will produce a vague, probably useless query. “Show me the count of distinct users who triggered at least one purchase event in the last 7 days, grouped by their plan” will produce something accurate and useful.

No schema, but expecting column-level accuracy. The model will guess, and its guesses will be syntactically valid. That’s what makes them dangerous.

Ignoring dialect differences. If you’re on BigQuery, asking a generic model for date functions might get you DATE_TRUNC when you need DATE_TRUNC(date, MONTH). Always tell the model your database flavor (PostgreSQL, MySQL, BigQuery, Snowflake, etc.).

Trusting output that executes without errors. A query that runs is not a query that’s correct. Always sanity-check with a small sample (LIMIT 100), compare a few rows to known-good data, and check totals against a source of truth.

Not iterating. The first query you get back is a first draft. Paste it back in with corrections: “This is close, but I need the results grouped by week, not month, and I only want users in the ‘pro’ plan.” Iteration gets you to accuracy faster than trying to write the perfect prompt on the first try.

Key Takeaways

The gap between 60% and 94% AI SQL accuracy comes down to what you put into the prompt:

  1. Include your schema — just the relevant tables, with column comments for values
  2. Define business terms — don’t let the model guess what “revenue” or “active” means
  3. Add 1–2 example queries — establish your style and naming conventions
  4. Decompose complex questions — ask for reasoning steps before the final query
  5. Ask for explanation and edge-case review — let the model catch its own mistakes
  6. Always specify your database flavor — PostgreSQL, MySQL, BigQuery, etc.

AI-powered SQL generation is genuinely useful once you treat prompting as a craft, not an afterthought. The developers who get the most out of these tools aren’t the ones using the fanciest models — they’re the ones who’ve learned to give the model what it actually needs to succeed.

What techniques have worked well for you when prompting AI for SQL? Have you found ways to handle large, complex schemas effectively? Drop your approach in the comments — I’d love to compare notes.

Stop Asking “Which Model?” and Start Fixing Your Team’s AI Supply Chain [Image Test B]

article visual 1

This week made one thing obvious: AI coding speed is up, but trust in code is now your real bottleneck.

Hook: the scary part isn’t wrong code anymore

The biggest AI risk in software teams right now is not bad output, it’s bad provenance.

Most senior devs I know can smell shaky code in a PR. We’ve trained that instinct for years.

What’s newer (and nastier) is code that looks fine, ships fast, and quietly breaks ownership, traceability, or security assumptions.

If your workflow still treats AI as “just a faster autocomplete,” you’re defending the wrong perimeter.

What changed this week (and why it’s not random noise)

A few trend signals lined up this week in a way that matters:

article visual 1

  • Anthropic acquired Stainless, which is a strong hint that “model + API lifecycle” is becoming one product surface, not two separate tools.
  • A repo maintainer story on HN showed AI bot spam getting blocked using Git’s --author flag strategy. Not glamorous, but very real: identity and attribution are now active attack surfaces.
  • Local-first and self-controlled tooling kept getting attention (for example, Files.md traction, Haiku-on-M1 discussions). That’s not nostalgia; it’s developers reclaiming control planes.

Different stories, same direction: we’re shifting from “Can AI write code?” to “Can our system verify who/what changed code, why, and under what guardrails?”

Why this matters in real teams

In a solo project, you can vibe-code and recover. In a team, ambiguity compounds.

Here’s what I’m seeing across real delivery environments:

  • PR volume grows faster than review depth.
  • Generated code lands without enough architectural context.
  • Ownership gets fuzzy: “the assistant wrote it” is not an accountability model.
  • Security and compliance teams get pulled in late, when the blast radius is already big.

The painful part: velocity looks great on paper right until one messy incident forces a freeze, and then everyone pretends this was unpredictable.

It was predictable.

AI didn’t remove software engineering constraints. It moved them.

You used to spend more time producing code; now you spend more time proving code deserves to exist.

The wrong question most people ask

“Which model should we standardize on?”

That’s not useless, but it’s not first-order anymore.

Model choice matters, yes. But teams are overfocusing on model IQ while underinvesting in workflow integrity. A stronger model in a weak workflow just lets you create ambiguity at higher throughput.

Contrarian angle: for most teams, upgrading process quality will outperform upgrading model quality.

Not forever, but definitely this quarter.

If your branch strategy is chaos, your review contract is vague, and your commit identity rules are loose, model gains are mostly cosmetic.

The better question: where does trust break in our AI workflow?

Ask this instead:

“At which exact handoffs can low-context AI output become high-impact production risk, and what lightweight controls close those gaps?”

Then run this playbook.

A practical playbook you can apply this sprint

1) Add a provenance contract to PRs

Every AI-assisted PR should include:

  • what was generated (files or modules)
  • what was human-authored
  • what checks were run
  • what remains unverified

Keep it short and mandatory. You’re not writing a thesis; you’re creating an audit trail.

2) Enforce commit identity hygiene

If bot noise or unclear authorship is possible in your flow, lock this down now:

  • require verified commit emails/domains where possible
  • define rules for bot/service authors
  • reject unexpected author patterns in CI

The HN bot-spam story is your warning shot: attribution is a security control now.

3) Split AI usage into lanes

Stop using one giant “AI helped” bucket.

Create 3 lanes:

  • drafting: scaffolding, boilerplate, test seed generation
  • transforming: refactors, migrations, repetitive edits
  • deciding: architecture, security-sensitive logic, data contracts

Lane 3 always gets human-first review. No exceptions.

4) Upgrade review prompts, not just generation prompts

Most teams polish generation prompts and ignore review prompts.

Use reviewer checklists tuned for AI-heavy diffs:

  • does this change preserve invariants?
  • did naming drift from domain language?
  • are edge cases tested or only happy path?
  • did hidden coupling increase?

Treat review as an explicit system, not heroics.

5) Track one metric that reveals workflow health

Pick one, start simple:

  • PR reopen rate
  • hotfixes within 7 days of merge
  • median review rounds per AI-assisted PR
  • percent of AI-assisted PRs with complete provenance note

Don’t build a dashboard empire. One honest metric beats ten vanity charts.

6) Keep a “human judgment list”

Document decisions AI should not make alone in your codebase:

  • auth boundaries
  • billing logic
  • destructive migrations
  • incident automation steps

This avoids vague arguments mid-PR and protects your senior engineers from becoming nonstop escalation points.

Closing line that sticks

AI didn’t kill software engineering fundamentals; it just made the fundamentals bill you daily.

Teams that win this year won’t be the ones with the flashiest model, they’ll be the ones with the cleanest trust pipeline from prompt to production.

Stop Asking “Which Model?” and Start Fixing Your Team’s AI Supply Chain [Image Test A]

article visual 1

This week made one thing obvious: AI coding speed is up, but trust in code is now your real bottleneck.

Hook: the scary part isn’t wrong code anymore

The biggest AI risk in software teams right now is not bad output, it’s bad provenance.

Most senior devs I know can smell shaky code in a PR. We’ve trained that instinct for years.

What’s newer (and nastier) is code that looks fine, ships fast, and quietly breaks ownership, traceability, or security assumptions.

If your workflow still treats AI as “just a faster autocomplete,” you’re defending the wrong perimeter.

What changed this week (and why it’s not random noise)

A few trend signals lined up this week in a way that matters:

article visual 1

  • Anthropic acquired Stainless, which is a strong hint that “model + API lifecycle” is becoming one product surface, not two separate tools.
  • A repo maintainer story on HN showed AI bot spam getting blocked using Git’s --author flag strategy. Not glamorous, but very real: identity and attribution are now active attack surfaces.
  • Local-first and self-controlled tooling kept getting attention (for example, Files.md traction, Haiku-on-M1 discussions). That’s not nostalgia; it’s developers reclaiming control planes.

Different stories, same direction: we’re shifting from “Can AI write code?” to “Can our system verify who/what changed code, why, and under what guardrails?”

Why this matters in real teams

In a solo project, you can vibe-code and recover. In a team, ambiguity compounds.

Here’s what I’m seeing across real delivery environments:

  • PR volume grows faster than review depth.
  • Generated code lands without enough architectural context.
  • Ownership gets fuzzy: “the assistant wrote it” is not an accountability model.
  • Security and compliance teams get pulled in late, when the blast radius is already big.

The painful part: velocity looks great on paper right until one messy incident forces a freeze, and then everyone pretends this was unpredictable.

It was predictable.

AI didn’t remove software engineering constraints. It moved them.

You used to spend more time producing code; now you spend more time proving code deserves to exist.

The wrong question most people ask

“Which model should we standardize on?”

That’s not useless, but it’s not first-order anymore.

Model choice matters, yes. But teams are overfocusing on model IQ while underinvesting in workflow integrity. A stronger model in a weak workflow just lets you create ambiguity at higher throughput.

Contrarian angle: for most teams, upgrading process quality will outperform upgrading model quality.

Not forever, but definitely this quarter.

If your branch strategy is chaos, your review contract is vague, and your commit identity rules are loose, model gains are mostly cosmetic.

The better question: where does trust break in our AI workflow?

Ask this instead:

“At which exact handoffs can low-context AI output become high-impact production risk, and what lightweight controls close those gaps?”

Then run this playbook.

A practical playbook you can apply this sprint

1) Add a provenance contract to PRs

Every AI-assisted PR should include:

  • what was generated (files or modules)
  • what was human-authored
  • what checks were run
  • what remains unverified

Keep it short and mandatory. You’re not writing a thesis; you’re creating an audit trail.

2) Enforce commit identity hygiene

If bot noise or unclear authorship is possible in your flow, lock this down now:

  • require verified commit emails/domains where possible
  • define rules for bot/service authors
  • reject unexpected author patterns in CI

The HN bot-spam story is your warning shot: attribution is a security control now.

3) Split AI usage into lanes

Stop using one giant “AI helped” bucket.

Create 3 lanes:

  • drafting: scaffolding, boilerplate, test seed generation
  • transforming: refactors, migrations, repetitive edits
  • deciding: architecture, security-sensitive logic, data contracts

Lane 3 always gets human-first review. No exceptions.

4) Upgrade review prompts, not just generation prompts

Most teams polish generation prompts and ignore review prompts.

Use reviewer checklists tuned for AI-heavy diffs:

  • does this change preserve invariants?
  • did naming drift from domain language?
  • are edge cases tested or only happy path?
  • did hidden coupling increase?

Treat review as an explicit system, not heroics.

5) Track one metric that reveals workflow health

Pick one, start simple:

  • PR reopen rate
  • hotfixes within 7 days of merge
  • median review rounds per AI-assisted PR
  • percent of AI-assisted PRs with complete provenance note

Don’t build a dashboard empire. One honest metric beats ten vanity charts.

6) Keep a “human judgment list”

Document decisions AI should not make alone in your codebase:

  • auth boundaries
  • billing logic
  • destructive migrations
  • incident automation steps

This avoids vague arguments mid-PR and protects your senior engineers from becoming nonstop escalation points.

Closing line that sticks

AI didn’t kill software engineering fundamentals; it just made the fundamentals bill you daily.

Teams that win this year won’t be the ones with the flashiest model, they’ll be the ones with the cleanest trust pipeline from prompt to production.

PayPal and Stripe Are Not the Answer for Global Digital Sales

The Problem We Were Actually Solving

I was tasked with building an e-commerce platform that could sell digital products to customers in over 100 countries. The biggest hurdle was not the product itself, but rather the payment processing. We quickly realized that popular payment gateways like PayPal, Stripe, Gumroad, and Payhip had significant restrictions in many countries, making it impossible for us to use them as our sole payment solution. This was not just a minor inconvenience, but a major roadblock that threatened to derail the entire project. I had to find a way to navigate these platform restrictions and ensure that our customers could purchase our products without any issues.

What We Tried First (And Why It Failed)

Our initial approach was to use a combination of PayPal and Stripe, as they are two of the most widely used payment gateways. However, we quickly ran into issues with country restrictions. For example, PayPal is not available in over 20 countries, including some major markets like Bangladesh and Pakistan. Similarly, Stripe has its own set of country restrictions, including Cuba, Iran, and Syria. We also tried using Gumroad and Payhip, but they too had their own set of restrictions and limitations. It became clear that relying on these platforms would not provide the global reach we needed. We also experimented with using APIs to work around these restrictions, but this approach was cumbersome and prone to errors. For instance, we encountered issues with the PayPal API, which would often return error code 400, indicating a bad request. This was usually due to issues with the customer’s account or payment method, but it was difficult to troubleshoot and resolve these issues.

The Architecture Decision

After much research and experimentation, we decided to adopt a crypto-based payment solution. We chose to use Bitcoin and Ethereum as our primary payment methods, as they are widely accepted and have a global reach. We also integrated with a crypto payment gateway that provided a simple and seamless payment experience for our customers. This decision was not taken lightly, as it required significant changes to our architecture and infrastructure. However, it ultimately provided us with the flexibility and scalability we needed to reach a global customer base. One of the key tradeoffs we had to make was to implement a custom payment processing system, which added complexity to our codebase. However, this allowed us to avoid the restrictions and limitations imposed by traditional payment gateways. We also had to implement additional security measures to protect against crypto-related threats, such as wallet hacking and phishing attacks.

What The Numbers Said After

The results were striking. By adopting a crypto-based payment solution, we were able to increase our sales by over 30% in the first quarter. This was largely due to the fact that we could now reach customers in countries that were previously inaccessible to us. We also saw a significant reduction in payment processing errors, with a decline of over 50% in error rates. This was likely due to the fact that crypto payments are more secure and less prone to errors than traditional payment methods. However, we did encounter some issues with crypto price volatility, which affected our revenue and profitability. For example, during one quarter, the price of Bitcoin dropped by over 20%, which resulted in a significant decline in our revenue. To mitigate this risk, we had to implement a hedging strategy, which involved converting a portion of our crypto holdings to fiat currency.

What I Would Do Differently

In retrospect, I would have adopted a crypto-based payment solution from the outset. While it was a more complex and challenging approach, it ultimately provided us with the flexibility and scalability we needed to reach a global customer base. I would also have invested more time and resources in developing a custom payment processing system, as this would have allowed us to avoid the restrictions and limitations imposed by traditional payment gateways. Additionally, I would have implemented more robust security measures to protect against crypto-related threats, such as wallet hacking and phishing attacks. One specific decision I would make differently is to use a more robust crypto payment gateway, such as Coinbase or BitPay, which would have provided us with more advanced security features and better support for multiple crypto currencies. Overall, our experience with platform restrictions and crypto-based payment solutions has been a valuable learning experience, and one that has allowed us to build a more robust and scalable e-commerce platform.

Signs your WordPress site needs a headless CMS rebuild

If your marketing team is filing support tickets just to change a headline, or your Google Ads campaigns are bleeding budget because the landing pages load in four seconds, your WordPress site may have hit a structural ceiling — not a content problem, not a design problem. A structural one. These are the signs your WordPress site needs a headless CMS rebuild, and knowing them early saves you from throwing more money at band-aids.

What ‘headless’ actually means for your business

WordPress bundles everything together: the place editors write content, the code that decides how it looks, and the server that delivers it to visitors. That bundle made setup fast in 2012. In 2026 it creates friction at every layer.

A headless setup separates the content system from the presentation layer. Your editors still log in to a clean dashboard — usually something like Sanity Studio — and write, publish, and schedule exactly as before. But the front end is a purpose-built website (built on Next.js in my projects) that fetches that content and renders it at speed, with full control over layout, performance, and where the content goes next. The content isn’t trapped in one theme’s templates. It can feed your website, your mobile app, a digital signage screen in your showroom, or an email campaign — all from the same source.

That separation is the point. It removes the ceiling.

The warning signs worth taking seriously

Editors are hacking around the theme. I worked with a sports organisation whose content team had resorted to embedding raw HTML in text fields to get a two-column layout the theme didn’t support. They’d built invisible spacer images to control padding. Every new page took 45 minutes and still looked slightly off. This is a sign that the editorial experience has calcified around a theme that was never designed for what the organisation actually needed to publish.

Performance scores are killing paid-ad ROI. A training academy I rebuilt was running Google Ads to a set of course landing pages. Their average mobile PageSpeed score was 41. Industry data is consistent here: pages loading beyond three seconds lose more than half of mobile visitors before the page even finishes loading. The academy’s cost-per-conversion was two to three times what it should have been. The WordPress install had eleven active page-builder plugins, four of which loaded JavaScript on every page regardless of whether that page used them. No amount of caching plugin configuration was going to fix that. The payload was structural.

Dev time is eaten by plugin conflicts. When a WooCommerce update breaks the slider plugin which breaks the checkout page, and the fix involves downgrading a security patch — that’s not bad luck, that’s the compounding cost of a plugin ecosystem with no central contract. One housing developer I worked with was spending roughly two days per month on this kind of firefighting. That’s 24 days a year of developer time producing zero new value.

Your content can’t go anywhere except the website. The housing developer also wanted to launch a buyer portal — a separate web app where prospective buyers could track a property’s construction milestones. All the project descriptions, floor plans, and images lived in WordPress. Getting that content into the portal meant either duplicating it manually or building a fragile custom REST API around a database schema that was never designed to be queried that way. In a headless setup, content is structured from day one to be delivered anywhere via a clean API. The buyer portal becomes a straightforward project, not a six-month integration nightmare.

You’re adding pages by duplicating and editing old ones. This is quieter than the others but just as telling. When there’s no real component system — just a grab-bag of shortcodes and widget areas — teams default to cloning whatever page last worked. The result is a site with 200 pages that are all slightly different versions of three templates, none of them documented, and any design change has to be made 200 times.

What the rebuild project actually looks like

A typical engagement runs eight to fourteen weeks depending on content volume and integration complexity. The first two weeks are spent on content modelling — mapping what you publish today into structured schemas that will work for years. This is the most important work and the part most agencies skip, which is why rebuilds sometimes recreate the same problems in a new tool.

Editors get a staging Studio environment by week three or four. The goal is for them to feel confident before the old site is retired, not after. By the midpoint, the new site exists alongside the old one. You can compare performance, share it with stakeholders, and catch gaps without any public risk.

Content migration from WordPress is usually scripted — posts, images, and structured data move across without manual copy-paste. The edge cases (custom post types built by a plugin that no longer exists, images stored in twelve different places) are where honest scoping matters. I flag these in discovery so there are no surprises at handoff.

When it’s worth it — and when it isn’t

A headless rebuild earns its cost when performance is directly tied to revenue (paid advertising, e-commerce, lead generation), when content needs to reach more than one channel, or when editorial bottlenecks are slowing down a team that publishes frequently.

It is not the right call for a five-page brochure site with no editorial team and no plans to grow. WordPress with a well-configured theme and no unnecessary plugins is genuinely fine for that use case. The rebuild conversation is for organisations that have outgrown the bundle — where the original setup is now fighting against what the business needs to do.

If you recognise two or more of the situations above, the question isn’t really whether to rebuild. It’s whether to do it now or after the next plugin conflict takes down a campaign you can’t afford to lose.