Skip to content
Felipe Reducino

A working method · Six chapters

AI Powered

Agents write code quickly. Most of the engineering lives around them: what they read first, what they agree to before they start, and what checks their work once they say it is done.

Told through one real feature from a project I worked on, start to finish.

01The brief

One sentence, a dozen unknowns

The request arrived as one line: let many organizations share one blog platform without ever seeing each other's data. It reads like a feature. It is closer to a property — something that has to stay true of every query, every link and every email the platform will ever produce.

Hand that sentence straight to an agent and you get a confident diff that answers the half of the question it understood. So the first hour was not code. It was pulling the sentence apart until every branch ended in something a person could decide, and writing those decisions down where the agent would find them.

How the brief branched

  1. Many organizations, one platform, no shared data: the sentence as it arrived.
  2. Where does a request learn which workspace it belongs to? From the address it was sent to, never from anything the caller can type.
  3. What enforces the boundary? Two layers that fail independently — a row policy in the database and a tenant filter in every query.
  4. Can the platform's own staff see inside a workspace? Only by stepping into it explicitly, never through a wider rule.
  5. What about everything that leaves the system — links, emails, images? Each one has to be built for the workspace it came from.
  6. What proves it holds? An isolation test that tries to read across the boundary and fails the build if it can.

02Context

The agent reads before it writes

An agent starts every session knowing nothing about your codebase except what you put in front of it. That makes context the actual interface — and the obvious move, one enormous instructions file, is the wrong one. Everything in it competes for the same attention, and the rule that matters today is buried under the ones that mattered last month.

What worked was an index at the centre and a scoped rule file for each area of the system. The index is short and points outward. A rule loads only when the agent touches the area it governs, so the data-layer rule is in the room exactly when somebody is about to write a query — and nowhere else.

An index, not an encyclopedia
The top-level file says where things are and which lines must never be crossed. The reasoning lives one hop away.
Rules carry the why
“Always filter by workspace” gets optimised away the first time it looks redundant. “Because the row policy alone is one mistake from a leak” does not.
Invariants as tripwires
A short list of things that break silently, each pointing at the rule that argues for it and the test that catches it.
Signal over noise
Every line in context costs attention. If a rule has not changed a decision in months, it moves out of the index.
Agent session — scoping a lookup
  1. 0:00

    › Scope article lookups to the current workspace.

  2. 0:01

    Read index

  3. 0:02

    The index sends anything touching data access to the data-layer rule.

  4. 0:03

    Read data-layer rule

  5. 0:04

    Every lookup carries the tenant filter, even behind a row policy — the policy alone is one mistake from a leak.

  6. 0:06

    Edit article lookup

  7. 0:07
      findArticle(slug)-   where slug = :slug+   where slug = :slug+     and workspace = :current
  8. 0:09

    Run the isolation test

  9. 0:11

    Pass — a read across workspaces returns nothing

  10. 0:11

    Lookup scoped. The test tried to cross the boundary and found nothing there.

What the session shows

  1. The task arrives: scope article lookups to the current workspace.
  2. The agent opens the index first, and the index points it at the data-layer rule.
  3. The data-layer rule loads, and with it the reason the tenant filter is required even behind a row policy.
  4. Only then does the agent write the change — the lookup now carries the workspace it belongs to.
  5. It runs the isolation test, which tries to read an article across the boundary and finds nothing.

03Plan

Plan before code

The cheapest place to be wrong is a plan. Before any change of size, the agent writes one — what it will touch, in what order, and what would tell us it worked — and I read it the way I would read a colleague's design note. Most corrections happen here, in a sentence, instead of later in a diff.

Skip it and the agent plunges: it picks the first plausible path and builds on it, and you discover the wrong assumption three files in, when undoing it costs more than the feature did.

Staff access: ask or assume?
Step order: explicit or implicit?

With a plan: done at tick 18, fixed in the plan. Plunge: done at tick 24, found much later.

With a plan

  1. 0
    Read

    Reads the brief and the context file

  2. 1
    Assume

    Assumes staff see every workspace through a wider rule — and writes that down

  3. 2
    Read

    Explores the codebase: where a request learns its workspace

  4. 4
    Plan

    Writes the plan: the layers it will touch, in no stated order

  5. 6
    Gate

    I read the plan

  6. 7
    Fix on paper

    I catch the wider rule: staff step into a workspace instead. One sentence.

  7. 8
    Fix on paper

    I ask for the order: the policy waits for the column it reads

  8. 9
    Gate

    Approved — no code exists yet

  9. 10
    Build

    Schema: the column, then the row policy that reads it

  10. 12
    Build

    Data access: every query carries the tenant filter

  11. 14
    Build

    Request handling: staff step in explicitly

  12. 16
    Build

    Host resolution: the address decides the workspace

  13. 17
    Green

    Isolation test passes

  14. 18
    Done

    Done, in the order the plan said

Fixed in the plandone at 18 · 2 spent fixing

Every wrong decision was corrected on paper, before a line of code existed.

Plunge

  1. 0
    Build

    Starts writing immediately

  2. 1
    Assume

    Widens the rule so staff see every workspace

  3. 2
    Build

    Schema: the policy first, reading a column a later step adds

  4. 4
    Build

    Data access — on top of the wider rule

  5. 6
    Build

    Request handling — on top of the wider rule

  6. 8
    Build

    Host resolution — on top of the wider rule

  7. 9
    Green

    Every check green — a fresh database applies all the steps at once

  8. 10
    Fail

    Review finds the wider rule under three layers

  9. 11
    Rework

    Rewrite: data access, request handling and host resolution unpicked

  10. 17
    Green

    Ships. Nothing looks wrong.

  11. 21
    Fail

    Much later, a database that applies steps one at a time fails — quietly

  12. 22
    Rework

    Traced back to the order, and fixed by hand

  13. 24
    Done

    Done

Found much laterdone at 24 · 8 spent fixing

Shipped, then failed much later — the wrong order surfaced long after anyone remembered choosing it.

Plan or plunge

  1. With a plan: the agent lists the layers — schema, data access, request handling, host resolution — and the order they depend on each other.
  2. I catch one wrong assumption in review: staff access was going to widen a rule, and it needed to be an explicit step into a workspace instead.
  3. The plan is corrected in one sentence, before a line of code exists.
  4. Without one: the agent widens the rule, three layers build on top of it, and the fix is a rewrite.

04Act

Several agents, one reviewer that wasn't there

Once the plan holds, the work fans out. Schema, data layer, request handling, host resolution and DNS form a graph, and anything without an unmet dependency can run at the same time. More agents is not automatically faster: two of them editing the same file is a merge conflict with extra steps, so the graph decides how wide to go.

Then a reviewer that took no part in writing the code reads all of it. Independence is the whole point — the agents that wrote a query are the worst placed to notice it quietly lost its tenant filter. A human gate sits after that, and nothing merges past it.

3

3 agents: 22 ticks, 4 conflicts.

Agent 1
  1. Schema (shared), ticks 0 to 2
  2. Waits on Schema, ticks 2 to 5
  3. Request handling, ticks 5 to 9
  4. Host resolution (shared), ticks 9 to 12
  5. Isolation test, ticks 12 to 14
  6. DNS, ticks 14 to 16
  7. Idle — nothing ready, ticks 16 to 17
Agent 2
  1. Schema (shared), ticks 0 to 2
  2. Same file as the other half — sequenced after it, not merged, ticks 2 to 5
  3. Data layer · members, ticks 5 to 8
  4. Row policy, ticks 8 to 10
  5. Host resolution (shared), ticks 10 to 11
  6. Same file as the other half — sequenced after it, not merged, ticks 11 to 14
  7. Idle — nothing ready, ticks 14 to 17
Agent 3
  1. Waits on Schema, ticks 0 to 5
  2. Data layer · articles, ticks 5 to 8
  3. Same file as Data layer · members — sequenced after it, not merged, ticks 8 to 11
  4. Links and email, ticks 11 to 14
  5. Same file as Host resolution — sequenced after it, not merged, ticks 14 to 17
  6. Restores the tenant filter, ticks 19 to 20
Reviewer
  1. Reads the whole change; flags a query that lost its tenant filter while resolving the conflict in Data layer · articles, ticks 17 to 19
  2. Re-reads the fix, ticks 20 to 21
You
  1. Approves the merge, ticks 21 to 22
  • Work
  • Waiting
  • Conflict
  • Coordination
  • Review
  • Fix
  • Human gate

3 agents: 22 ticks, 4 conflicts.

The reviewer reads the whole change after every branch lands, and the human gate comes last — whatever the agent count.

Finish time by agent count, this run

  1. 1 agent32 ticks
  2. 2 agents22 ticks
  3. 3 agents22 ticks, selected
  4. 4 agents21 ticks, the fastest
  5. 5 agents22 ticks

How the work fanned out

  1. The schema change runs first; everything else depends on it.
  2. The data layer and request handling run in parallel once the schema lands.
  3. Two agents touch the same file, and the conflict is resolved by sequencing them rather than merging.
  4. Host resolution and DNS follow, each depending on the layer before it.
  5. An independent reviewer reads the full change and flags one query that lost its tenant filter.
  6. The fix goes in, and a person approves the merge.

05Verify

Break the invariant on purpose

“Done” from an agent is a claim, not a result. The loop only closes when something other than the agent checks the work — a type check, a build, and for this feature above all, the isolation test that tries to read one workspace from inside another.

The useful exercise is to switch the defences off one at a time and watch what happens. Each layer covers for the other, and the test is what turns a quiet failure into a loud one before it ships.

Defences

  • Row policy

    The database refuses rows from another workspace.

  • Tenant filter

    Every query names the workspace it belongs to.

  • Isolation test

    A test tries to read across the boundary, layer by layer.

  1. Workspace Areads
  2. Filterrefuses
  3. Policyrefuses
  4. Workspace Buntouched

Blocked twice. Isolation test: passes.

Blockedisolation test passes

Blocked twice.

The tenant filter never asks for the other workspace, and the row policy would refuse it anyway. A leak needs both layers to fail, and the isolation test passes.

All eight combinations
Row policyTenant filterIsolation testOutcome
onononBlocked twice. (current)
ononoffBlocked twice.
onoffonBlocked by the row policy alone.
onoffoffBlocked by the row policy alone.
offononBlocked by the tenant filter alone.
offonoffBlocked by the tenant filter alone.
offoffonCaught by the isolation test.
offoffoffShips silently.

Three ways it can go

  1. Row policy and tenant filter both on: a cross-workspace read is blocked twice, and the isolation test passes.
  2. One layer off: the other still blocks the read — and the isolation test, written to check each layer, flags the missing one.
  3. Both layers off with no isolation test: the read succeeds, every check is green, and the leak ships silently.

06The loop

It is a loop, not a pipeline

None of these steps happens once. A review turns up something the rules should have said, so the rules change; the next plan starts from better context; the next check is sharper. That loop is the actual product of working this way, and the code is what it leaves behind.