Skip to content
ALI HAITHAM·TECH·responds in <1h
  • Homestart here
  • Workcase studies, projects
  • Serviceswhat I will build for you
  • Writingessays, series
  • Traincourses, labs, cohorts
  • Aboutthe engineer behind this site
Sign inStart a project
ALI HAITHAM · TECH
  • Home↗
  • Work↗
  • Services↗
  • Writing↗
  • Train↗
  • About↗
Sign inStart a project
Online
ALI HAITHAM·TECH

Engineering studio. Damascus, GMT+3.

aliyosef.online

Studio

  • Work
  • Writing
  • Training
  • About
  • Contact me

Portal

  • Sign in
  • Open a ticket
  • Track project
  • My account

Resources

  • Docs
  • Status
  • Changelog
  • Brand kit
  • Privacy
  • Terms

Newsletter

Field notes and tech news. Weekly. No fluff.

Free. Unsubscribe anytime.

Find me elsewhere
© 2026 Ali Haitham Yosef. All rights reserved.Hand-built in React 19. No frameworks of frameworks.Last deployed · 2026-05-08All systems operational
  1. Home/
  2. Writing/
  3. AI engineering
AI engineering

AI-paired engineering, part one: the workflow

Series opener. Where the model helps, where it lies, and how we built a daily rhythm around both.

February 2, 2026·11 min·by Ali Yosef
Part ofai-paired→

Loading essay…

ShareXLinkedInRSS
Get the next essay in your inbox.New posts every Sunday morning. Engineering, in plain language. Free, unsubscribe anytime.Subscribe→
AY
Ali Haitham Yosef

Engineer and writer. Builder of multi-tenant platforms, sync engines, and quietly reliable systems. 600+ shipped projects since 2015.

SubscribeStart a project→

Read next

  • SD
    Jan 26, 2026·AI engineering

    Spec-driven development with LLMs

    A spec the model can act on without rework. The structure, the anti-patterns, the actual diff.

    9 min
  • ES
    Jan 18, 2026·AI engineering

    Eval suites for non-deterministic tools

    How to test something that gives a different answer twice. The harness, the tolerances, the budget.

    8 min
  • CG
    Jan 10, 2026·AI engineering

    Cost guardrails for AI workflows

    A small accounting library that pays for itself in three weeks. Tokens, retries, and the budget alarm.

    6 min
←Back to all essays

A year of pair-programming with an LLM in production has taught us something the demos never did: the model is excellent at one part of the work, average at another, and dangerous at a third. The trick is the workflow that puts each in its proper place. This is part one of three on AI-paired engineering after the demo is over.

What "paired with an LLM" actually looks like#

Strip the marketing. Day-to-day, the model does three things on our team:

  1. Drafts code from a structured spec. Given a clear description of what to build, including types and edge cases, it produces a first pass that is usually 70–90 % correct. We edit, we test, we ship.
  2. Reviews code we wrote. Given a diff and the surrounding context, it spots edge cases, suggests test names, and occasionally catches a real bug. False-positive rate is high but the cost of a false positive is low.
  3. Walks unfamiliar code. When somebody is reading a file they did not write, the model is good at producing a "what does this do, in one paragraph" summary that orients without lying too much.

It is bad at three things:

  1. Open-ended design. Given "design a sync engine," it produces reasonable surface text and almost always misses the constraint that matters. Real design happens in conversation with someone who has run the system in production.
  2. Estimating effort. It is wildly optimistic. We do not ask.
  3. Producing novel decisions where the right answer is "no." Asked for help with a feature, it almost never says "you should not build this." It will help. That help is sometimes a trap.

The shape of a workflow that works#

We landed on a four-step pattern, in this order:

1. Spec  →  2. First pass  →  3. Edit + test  →  4. Review
   you           model            you (mostly)        model

Notice the bookends. The model's role is in the middle. The human defines the problem and validates the result. Anything else and you are paying for a confident wrong answer.

Step 1: Spec#

The spec is a structured document. We use a four-section template:

## Goal
One sentence.

## Inputs
Types, constraints, edge cases worth naming.

## Outputs
Types, what "success" means, what "failure" returns.

## Examples
Three: one happy path, one edge case, one failure case.

This is not optional. The spec is what makes the difference between "the model produced something useful" and "the model produced plausible-looking nonsense." Most engineers who say "the model doesn't write good code" skipped step 1.

Step 2: First pass#

The model takes the spec and produces a first draft. We do not edit mid-stream. We do not ask follow-up questions. We let it finish, then we read it as a complete proposal.

The rule we landed on: if the first pass is more than 30 % wrong, we do not iterate with the model on it. We rewrite the spec to be tighter and try once more. Iterating with the model on a bad first pass tends to produce three more bad passes that get progressively further from what we want.

Step 3: Edit + test#

We edit the first pass by hand. We write the tests by hand. The model helps with test names but not with the assertions. The reason: the model's tests have a tendency to assert what the code does rather than what the spec said.

The most common AI bug

The model will write code that passes tests it also wrote. Both can be wrong in the same way. We have shipped this and only caught it in production. The fix is structural: the human writes the test assertions; the model writes only setup, mocks, and names.

Step 4: Review#

After we are done, we paste the diff back into the model with a fresh context and ask "what edge cases did we miss?" The model's hit rate here is around 1 in 5: most of what it suggests is already covered or out of scope, but the 1 that lands is sometimes a real bug.

What we measure#

Three numbers, monthly:

  1. Time to first commit on a new feature. Down ~35 % vs. the year before we adopted this workflow.
  2. Tests written per feature. Up ~20 %, mostly because step 4 surfaces edge cases we would have skipped.
  3. Bugs reported per feature in the first 30 days. Roughly flat. Not down. We were initially surprised; the explanation is that the bugs are different (fewer typos, more "I asked for the wrong thing"), and the count is similar.

The last number is the one that taught us the most. The model does not remove bugs from the system. It moves them upstream into the spec. That is fine if you treat the spec as the artifact, not the code.

What does not work#

Three patterns we have abandoned:

"Auto-pilot" mode#

Letting the model write code, run tests, fix failures, run tests again, in an unattended loop. This works for trivial cases and fails silently on anything important. The failure mode is that the model "fixes" tests by weakening them. We have not found a guardrail that catches this reliably.

Conversation-driven coding#

Long back-and-forth with the model on the same chunk of code, 10+ turns. This produces code that is structurally weird because the model is averaging across its own earlier suggestions. Better: write a fresh spec, fresh first pass.

Asking "is this code good?"#

The model answers yes. It is a confident speaker. Worse, it sometimes hallucinates issues that do not exist. Replace the open question with a structured one: "list the edge cases in this code that are not covered by the tests below." The structured question gets useful output; the open one does not.

What's next#

Part two of this series is about spec-driven development specifically: the structure of a spec the model can act on without rework, the anti-patterns, and an actual diff from a feature we shipped this way.

Series page tracks the rest.