WeAreDevelopers 2026 · Development in an Agentic World
The Architect of Validation & Verification — building the harness around the model.
A true story that created perpetual scars
It reported green. Everything looked shipped. Nothing was proven.
Agents collapse the consequence. So you finally get to do the engineering. That's a promotion, and everybody in this room can take it.
Why this talk
Building web applications since 1998: agencies, in-house teams, and Upsun for the last ten years.
Optimizing my own work, the projects, and how teams ship.
Deep in the weeds of AI-driven engineering practices.
3 products, 110 people across engineering and product.
Watch and test new solutions, then figure out how engineering can benefit.
01
An honest pin on the map before we move.
8 stages of AI engineering maturity
1 · Vacuum — no AI strategy
2 · Drift — one dev quietly 10×s
3 · Islands ← you are here
fast devs, unaligned team
4 · Standardize — fund the capability
5 · Workflow redesign — specs before code
6 · Operating system — agents are teammates
7 · Bright factory — agents ship, you supervise
8 · Autonomous — runs on triggers
upsun.com/blog/8-stages-ai-engineering-maturity
The full breakdown. Most teams here are at Stage 3, a few at 4 or 5.
Individual output scales. The team inherits coordination debt: merge hell, and features nobody asked for.
Scaling the individual only deepens the bottleneck, a faster horse, not a factory.
02
Stop trying to go faster. Change what you do.
The role inverts
Write the module. Review the diff line by line. Be the execution unit.
Define what's true, set the constraints, demand the proof. Architect of Intent & Governor.
Verification first isn't the finish line, it's the forcing function. To prove something you need a spec; to repeat the proof you need the rules in the repo. It drags the rest into place.
The loop, not the pipeline
↩ every result feeds back into Context, we'll close that loop shortly
03
Validation & Verification: the heart of the talk.
Two different questions
Did we build it right? Unit tests, types, linting, security, clarity.
Did we build the right thing? Intent, end-to-end behavior, alignment.
Verification can't see this · every dev has shipped a timezone bug
def local_now(utc_now: datetime, offset_minutes: int) -> datetime:
# the user's UTC offset — an int captured at signup ("Berlin = +60")
return utc_now + timedelta(minutes=offset_minutes)
def is_send_minute(local: datetime, pref: str) -> bool:
return local.strftime("%H:%M") == pref # 09:00 happens once a day, right?
# mypy: green · pytest: all passed · lint: green · spec: "digest at 9am local" met
# Nov → Mar 28: 100.0% on time · Mar 29: 5,586 late, 108 never arrived
↩ shipped in November · green for 127 days · an offset is a measurement, not a timezone
Three layers · hand the first two to the machine
The harness proves it. Fully automatable.
e2e / Gherkin, checkable because intent is written down.
The one thing that never delegates. Your judgment.
The instrument: the harness
Tests run read-only & isolated in CI; any edit to a test file is flagged in review. You trust the result because the agent couldn't touch the scorer.
UI work ships a video of the fix. No video → no review.
No commit that fails lint, types or tests. Ever. The agent's only path to ship is a PR.
Structure enforced: no business logic in the view layer; presentation may only call a service, never the database. (Pick your language's equivalent.)
The class of bug you prevent, not re-find · the ratchet made literal
# pyproject.toml — after March 29, naive datetimes are a build failure
[tool.ruff.lint]
select = ["E", "F", "DTZ"] # DTZ: every rule cites the ADR that created it
$ ruff check nineam/naive.py
naive.py:32:12 DTZ003 `datetime.utcnow()` used
help: use `datetime.now(tz=...)` instead
Found 1 error. # red, before a human ever opens the PR
Node: the same ban via ESLint no-restricted-syntax — "new Date() reads the wall clock (ADR-0004; born 2026-03-29)"
Success is silent. Failures are verbose.
# .pre-commit-config.yaml — runs on every commit (husky + lint-staged in Node)
repos:
- repo: local
hooks:
- id: ruff # lint — including the DTZ scar rules
- id: mypy # types
- id: pytest-unit # the fast suite
A rule in AGENTS.md is a suggestion the model can forget. A commit hook is not. Silent on pass; verbose, and blocking, on fail.
What "proven" looks like
### Verification
- tests: 142 passed, 0 failed · tests read-only in CI
- edits to test files: none flagged
### Validation
- year simulation: 3.63M user-days · exactly-once ✓ · on-time ✓
- e2e (Playwright): digest-settings.spec 7/7 green
- proof: ./artifacts/digest-timezone-flow.webm
Verification is only easy when the input was good. Context is a supply chain.
Context is more than code
The Bet, the constraints, the invariants, what "done" means and what must never break.
The live codebase, dependencies, tests, runtime, the system as it actually is.
PRs, commits, and the why behind past decisions, ADRs, not just the diff.
Contributors and ownership, who understands which system, and the scars they carry.
Context isn't free
10,000 generated lines of "skills" made it worse. Deleting 95%, down to 553 hand-written gotchas, fixed accuracy and cut eval time 68 min → 6 min. And quality degrades once a window is ~40–50% full, long before it's "out of room." More isn't better. You curate.
The risk isn't too little context, it's poisoned context
AGENTS.md + decision records: keep Intent & History outside the chat window.
Pull only what this task needs. Query, don't dump. Disable noisy tools.
At the tipping point, summarize and start fresh.
Parallel agents so one bad decision can't poison the next.
The one file you hand-write
## Judgment Boundaries
NEVER: weaken a test or gate to get green · edit an ADR (append a new one) · guess on ambiguous specs
ASK: before a schema migration · before adding a dependency · before deleting files
ALWAYS: explain the plan first · say what you didn't verify
# Before adding any line, ask:
linter can enforce it? → tool config CI can enforce it? → the pipeline
hook can enforce it? → the hook none of the above? → AGENTS.md
keep it < 60 lines · every line earned by a specific failure · an open standard since Dec 2025
Standardized parts
Typed interfaces, schemas and ADRs are machine-checkable contracts. "Did it guess right?" becomes "does it conform?"
# ADR 0002 · Store IANA timezone identifiers,
# never UTC offsets
Status: Accepted · 2026-03-30 (the postmortem)
## Context
An offset is a *measurement* of a timezone at one
instant. It goes stale twice a year by design.
## Decision
`Europe/Berlin` + a local preference (`09:00`).
Convert at the edge: zoneinfo / Temporal.
## Consequences
Nonexistent & ambiguous times need a policy → ADR 0003
"No naive datetimes" becomes enforceable → ADR 0004
ADRs · numbered · one screen · append-only, superseded, never edited
agents read the ledger before proposing changes — the why survives, and nobody re-litigates a settled decision
A requirement prescribes a solution. A Bet states what you believe, how you'll know, and what you won't risk.
# Bet · The 9am-local daily digest
Appetite: 2 weeks · One-way door: no
## Hypothesis
A daily digest at 9am *user-local* lifts
return visits.
## Signal (validates)
Return-visit rate — not digests sent.
Arrives 09:00 ±5 min local · exactly once
per (user, local calendar date).
## Guardrail
Zero double-sends · unsubscribe rate stays
flat · late beats never, up to 3h; then skip.
The Grilling Session · Ask Mode
Steal this prompt
Interview me relentlessly about every aspect of this plan until we
reach a shared understanding. Walk down each branch of the decision
tree, resolving dependencies one at a time. For each question, give
your recommended answer. Ask the questions one at a time.
The conversation is the asset. You don't even review the spec it writes afterward, you already aligned.
Two modes
Explore and brainstorm. Cannot write files. Ends in the Grilling Session and an agreed plan.
Autonomous execution against the agreed plan, proven by the harness when you're back.
Validation needs a running system
Every change gets a real, ephemeral environment, not a mock. The whole app actually runs.
e2e (Playwright) against the Bet's signals · an evaluator agent judges, separate from the one that built it.
Generator ≠ evaluator
# .agents/critic.md
You are a rigorous Critic Agent. Skeptical by design: reject code
that violates the Spec or an ADR, even if it "works" and every
test is green. Favour false positives. Judge in a fresh session.
→ ## Verdict: NOT READY TO MERGE
Scheduler persists a UTC offset; matches wall-clock by string equality
Violated: docs/adr/0002 · Remediation: IANA zone, exactly-once per
(user, local date) — and prove it over a simulated year
build with the cheap model · review with the strong one, in a fresh session
Beat 1 · ship it? · every date in this suite is in January
$ make unit
tests/unit/test_january.py::test_digest_goes_out_at_nine_local PASSED
tests/unit/test_january.py::test_no_send_on_any_other_minute PASSED
tests/unit/test_january.py::test_exactly_one_send_minute_per_day PASSED
tests/unit/test_january.py::test_fixed_scheduler_agrees_in_january PASSED
========================== 4 passed in 0.64s ==========================
✓ test/unit/january.test.ts (4 tests) 5ms # the Node twin agrees
each function is correct, in isolation, on a quiet winter day — that's the camouflage
Beat 2 · the year 2026, as it actually happened · 10,000 users · 12 zones
$ make simulate
digests on-time late early never double
Jan 300,000 100.0% 0 0 0 0
Feb 280,000 100.0% 0 0 0 0
Mar 310,000 94.5% 16,625 0 274 0
2026-03-29 10,000 42.1% 5,586 0 108 0 EU spring-forward
2026-03-30 10,000 62.4% 3,715 0 48 0
2026-03-31 10,000 75.3% 2,439 0 34 0 …and it self-heals
VERDICT ✗ 23,013 late · 23,197 early · 393 never arrived · 411 delivered twice
the unit suite never tested a DST transition. It stayed green.
Beat 3 · the Bet's Signal, turned into a test suite
$ SCHEDULER=naive make validate-naive
FAILED test_no_user_misses_a_day
AssertionError: 19 user-days never received their digest
FAILED test_no_user_gets_a_duplicate
AssertionError: 15 user-days received the digest twice
FAILED test_every_digest_is_on_time
AssertionError: 911 digests arrived late (true local time)
========================== 3 failed in 0.25s ==========================
same schedulers, same assertions — graded in the user's true local time, over the whole year
Beat 4 · same Signal, same year, the ADR-0002 fix
$ make validate
test_resolution_contract PASSED # 500 random zone × date × preference
test_no_user_misses_a_day PASSED
test_no_user_gets_a_duplicate PASSED
test_every_digest_is_on_time PASSED
========================== 4 passed ==========================
$ make simulate-fixed
VERDICT ✓ 3,630,000 user-days · every digest delivered exactly once,
on time (154 shifted by documented gap-day policy)
the property sweep covers the two days a year everyone forgets — nonexistent and ambiguous wall times included
Run it yourself · Python + Node
The suite the agent shipped. Every date in it is in January — that's the camouflage.
The year 2026: Mar 29 — 5,586 late, 108 never arrived. Then it silently self-heals.
The Bet's Signal as a test suite — red on the shipped code, green on the fix — and the ADR-0004 lint gate that makes the scar a build failure.
github.com/gmoigneu/nine-am
scan to clone · make setup && make simulate · same seed, same verdict in both languages
The factory learns
↩ every result, pass or fail, becomes context for the next iteration · the ratchet
Where this goes once the loop runs itself · most teams are 2–3 years out
"Three engineer slots, five agent slots." Context becomes shared infrastructure that bottles the team's battle scars.
You manage the Risk Envelope, not the sprint. "Roll back if the canary shows +2% latency."
The loop goes recursive: agents read production signals and write their own Bets.
The infrastructure reality
The autonomous factory needs shared, ephemeral, sandboxed, observable infrastructure, billed per workflow, not per seat. That's the problem we work on at Upsun.
The factory, productized
Governed collaboration between agents and software teams. The workflow is the unit. The team approves. The audit trail is a first-class artifact.
Runs on a trigger; the plan is approved on the issue first.
Acts as upsun-dispatch — co-authored, identity-separated.
The context bundle it saw, the diff, the critic's findings.
Per-step cost — this run, $0.71.
Please don't ruin our launch ;)
Relocate your judgment · the mandate
Hash it, video it, gate it. Read the evidence, not the diff.
Does it match the intent? Set the signals and the envelope.
Is it the right thing to build? The one thing that never delegates.
The Industrial Age of software is here
Multiple engineers, a fleet of agents, an impenetrable system of verification.
Thank you
Questions, war stories, hot takes. Find me on LinkedIn.
And yes: like and subscribe. The algorithm is also a harness.
linkedin.com/in/guillaumemoigneu
Guillaume Moigneu · Upsun