Skip to content
root@hisham:~/blog/posts$ glow dev-setup-2026.md
dev-setup-2026.md
· 5 min read · ai

# my dev setup, 2026

Two agents in the terminal with an orchestrator that reviews adversarially, the language servers those agents actually use, DBngin instead of a compose file per project, and a deploy path I no longer think about.

Here is what I run right now, and what each thing replaced. Windows, one editor, two agents in the terminal, and a deploy path I no longer think about.

Most of it is chosen to be dull. I have moved editor twice in three years and each move cost me a week I did not get back, so the bar for replacing anything on this list is that the current thing is actively in the way.

## Two agents, and neither one reviews itself

Codex and Claude, both in the terminal. The arrangement matters more than which model sits in which slot.

One instance is the orchestrator. It plans, splits the work, and writes no implementation code. Sub-agents implement, each holding one bounded piece — a migration, an endpoint, one module's tests — and running in parallel where the pieces do not touch. When they come back, the orchestrator reads what they produced as an opponent rather than as an author.

The rule the whole arrangement rests on is that nothing grades its own work. The sub-agent that wrote the migration never reviews the migration. An author re-reading their own diff re-reads their intention and finds it faithfully implemented, which is the one thing a review is not for.

The review pass is a file rather than a habit, and the same three rules exist twice so both tools get them:

# ~/.claude/agents/adversarial-reviewer.md
# ~/.codex/skills/adversarial-review/SKILL.md
You did not write this code and you do not trust it.

1. Distrust the tests. They share an author with the code,
   so they share its misreading of the requirement.
2. Resolve symbols with the language server. Do not read
   the diff as text.
3. Name the state this change assumes, and where each
   assumption is established.

Rule one exists because a green suite reports agreement between two artefacts that came out of a single reading of the ticket. Rule two exists because review at the level of text invents a plausible signature and then reviews the invention. Rule three exists because the defects that survive a careful read are the ones whose only evidence sits outside the diff.

What the pass reliably catches is the narrow band of defect that comes from having read only the files in front of you: a test that asserts the shape of a response instead of the rule behind it, a call to a method whose signature moved two commits ago, a migration that assumes a column added on a branch that never merged. What it does not catch is anything whose evidence lives in a running system. A change can be correct in every file it touches and still be wrong because of one default set in one bootstrap line, and no amount of adversarial reading gets you there. Those I still have to go and look at myself.

All of this costs real money and real wall-clock time, so it does not run on everything. The boundary is blast radius. Single-file changes, and anything I could write myself inside twenty minutes, go to one agent with no ceremony. The orchestrator comes out when a change crosses a boundary: a migration plus the code that reads it, an auth path, anything touching money or tenancy. Below that line the loop costs more than the defects it finds are worth.

## The local machine

Zed is the editor. XY-Zed dark, the VSCode keymap because I am not relearning chords, autosave after a second, vim mode off, and the integrated terminal pointed at PowerShell 7.

The part of it that earns its place is the language servers, because the agents use them too. Reading a diff as text and reading it through a resolver are different activities, and only one of them can tell you that the field you were about to comment on does not exist. What is installed: basedpyright and ruff for Python, vtsls and eslint for TypeScript, intelephense for PHP, tailwindcss-language-server, plus the JSON, YAML, CSS and bash servers and package-version-server.

Herd is the terminal. It ships the PHP versions, the CLI, and a shell that already knows about both, which removes the part of PHP setup on Windows that used to cost an afternoon per version bump.

DBngin runs Postgres and Redis. It replaced a docker-compose.yml per project. Docker earns its keep when the topology is the thing under test; for the two services that every project on this machine needs anyway, a compose file was a lot of YAML to describe a database on a port.

None of this is a Windows story, but Windows sets the constraints. Herd and DBngin are both here because the alternative was a Linux VM to keep in sync with a machine I already had. The tax I do pay is line endings and path separators, and the fix is the same as everywhere else on this list: say it once in a file the agents read, rather than once per session.

## Deploy

Cloudflare Workers for anything static. This blog is Workers Static Assets: the repo root is the upload, _headers sets the CSP and the cache lifetimes and is read natively, and npx wrangler deploy is the entire ceremony. No build step except Tailwind.

GitHub Actions for everything that is not a static upload. Tests, then a zero-downtime Octane reload, then a health check fired at the deployed service — that last step because a deploy that succeeded and a service that works are two different claims.

Hetzner for the Laravel work: FrankenPHP with Laravel Octane, Postgres, Redis, and Supervisor. A box I can reason about, without the managed-platform pricing that shows up in month four.

Tailscale SSH for reaching it. Port 22 is not open to the internet on any machine I run. Deploys and debugging both go over the tailnet, and the CI runner joins it for the length of the job. Of everything on this list it is the piece I would keep if I had to throw the rest away.

Questions or corrections: hishammedhat0@gmail.com github
root@hisham:~/blog/posts$