BlogAI
From the Trenches to the Watchtower: How to Work as a Developer When an AI Agent Writes the Code
I came back from JDD 2026 in Krakow with one main observation: the conversations in the hallways and on the stages had shifted from “how to write prompts” to “how not to drown in generated code.” Below I collect takeaways from talks by practitioners who showed how to shift the craft from hand-made work to supervising a system.
From Jira to Spec-Driven Development, before the agent writes the first line
Victor Rentea, in his talk “Code Review in Agentic Engineering,” said he stopped writing code by hand in March, after 19 years in the profession. This year he helped more than 30 companies with their agentic transformation and sees one recurring mistake in them: engineers take a vague task from Jira and immediately throw it at the agent. If we do not understand the business problem and cannot say “no” to the business, we will only generate a mountain of useless code. Rentea recalled the Three Amigos technique, a meeting of the business, a developer and a tester where they settle what we are building and why.
Once the problem is understood, we are left alone with a model of the Opus class. Rentea warned against fighting alone with such a powerful tool and against the thoughtless “looks good to me.” Instead of generating code right away, it is worth staying at the level of Markdown files: use planning mode, the “grill me” skill (by Matt Pocock) or Spec-Driven Development frameworks such as Superpowers, Get Shit Done (GSD) or OpenSpec. If we skip this stage, during code review we will have to painstakingly reconstruct architectural decisions hidden in thousands of lines of code.
Artur Sobczyk developed this thread in his talk “Where Vibe Meets Spec.” In a recorded demo, in the Kiro tool, he compared loose vibe coding with a specification-based approach. There was one task: add rate limiting (a maximum of 5 requests per minute) to an application in Spring Boot 3.5 and Java 17. Vibe coding produced working code in a few dozen seconds. The problem showed up when Sobczyk asked the agent about the assumptions it had made silently. The model used the local machine's memory (which will fall apart with multiple instances), mishandled the X-Forwarded-For header, applied the limit only per IP (so a whole instance can still be taken down) and used a fixed time window based on the system clock.
In the spec-driven approach, the process started with a requirements.md file with a glossary and acceptance criteria, went through design.md with the architecture and diagrams, and ended with breaking the work into eight tasks in tasks.md, carried out by subagents. The whole thing took over 20 minutes instead of a few dozen seconds, but the architectural assumptions surfaced while reading a document, not after deployment to production. Sobczyk did not turn this into a rule: for a PoC or a throwaway script the structure can only get in the way, while for production code and bug fixes it pays off. Arkadiusz Migała (“Master or Disaster: AI Engineering”) added that moving review to the specification and requirements stage is many times cheaper. He also warned about an organizational trap: a single developer fluent in AI who produces huge amounts of cheap code without proper processes in the team blocks everyone else, because they cannot keep up with verification.
Agent context and slop that multiplies by budding
Why do agents make such strange architectural decisions? Krystian Mikła (“Managing the Context of AI Agents in Large Projects”) and Renato Cavalcanti (“Depth Over Fluency”) looked for answers. Mikła started with what an agent is under the hood: a combination of a language model (token-in, token-out reasoning) and a control layer, that is, a harness (e.g. Claude Code, Cursor, Copilot, Codex) that manages context and tools.
Cavalcanti reminded us that models learn from the entire available internet. Next to great open source projects and books on architecture, the training data contains a dumpster of Reddit. Without guidance, a model gravitates toward mediocrity and picks the first path along which the tests turn green (which, by the way, is in the interest of AI providers, who want to limit compute usage at a fixed subscription price). On top of that, the code we accept and merge today becomes the context of the agent's next session tomorrow, and local context weighs more for the model than training data.
Cavalcanti showed this with an example from his work on the Akka platform (he gave the class name only as an illustration). In a Java record, the team let through a redundant safeguard in a compact constructor that turned null into Optional.empty(). After a short time, agents took it for a project standard and started generating code everywhere that, when data was missing, initialized objects with random identifiers, the name “unknown” and empty lists, destroying domain invariants. A single piece of slop let into the repository starts reproducing itself.
How do you give an agent the right context in a large project? Mikła explained the phenomena of context pollution and lost in the middle. The more noise (ls and grep results, whole files read from scratch) lands in the context window, the more the model's attention drops and the higher the risk that key rules get ignored. His team tested several approaches on a sample repository, on the GPT6 Luna model with the high effort setting:
The default /init, which generates one large AGENTS.md (or CLAUDE.md) file in the root directory, turned out to be ineffective. The file is loaded in full for every task and mixes frontend, backend and CI/CD domains.
A separate docs/ai folder, from which the agent picked files itself, did not work, because the model, protecting its context, avoided reading the instructions.
Forcing a subagent to read the documentation at the start of the session worked well from the main agent's perspective. However, it cost time and tokens, did not keep up with a task that changes during the conversation, and did not work in orchestration with many subagents.
The best result came from the approach recommended by Anthropic: hierarchical, nested AGENTS.md / CLAUDE.md files in the appropriate subdirectories and packages. The harness loads them dynamically only when the agent actually explores a given area of the code. That is how it turned out in the internal benchmark and at a workshop with nine participants. There the median mattered, because the spread was large (some people without documentation reached nearly 90 points), which is why Mikła noted that the answer is “it depends.” He also cited a study according to which context files usually did not improve agent effectiveness and raised the cost, and those written by humans did better than auto-generated ones. From his own practice he drew the conclusion that no documentation is better than poor-quality documentation. In context files it is worth recording only the minimum written or edited by a human: unusual architectural decisions, hidden dependencies and real pitfalls the agent has already failed on. Line numbers and obvious generalities are out.
Deterministic guardrails and blind trust
The letter “D” in AI does not stand for determinism, as Arkadiusz Migała joked, so the agent has to be surrounded by hard constraints. Rentea puts it bluntly: we need scripted quality checks that run locally (through the harness's stop hook or git hooks, to take advantage of the prompt cache and get quick feedback) and, without exception, on CI/CD, out of the agent's reach.
His list includes things known for years but rarely deployed as a complete set: linters, formatters, Checkstyle, Sonar, Snyk, ArchUnit tests guarding the architecture, and mutation tests that show whether the tests generated by AI verify anything at all. Rentea also described “zero drift architecture.” A Maven plugin generates an OpenAPI schema from Java DTO classes, and an NPM package generates TypeScript interfaces on the frontend from that YAML file. The agent is not allowed to edit the generated files by hand. Changing a field in the backend immediately breaks the frontend build, without any AI involved.
And what if the agent modifies the workflow files on GitHub or disables rules in Sonar to push through a green build? Rentea points to the CODEOWNERS mechanism: every change to CI/CD configuration files, build scripts and the guardrails themselves must automatically call for review by the tech lead or the project's “elders.”
Caution about what the agent does and reads also has a security dimension. Brian Vermeer of Snyk spoke about it in the keynote “Understanding Prompt Injection.” He showed attacks on GPT-3.5 Turbo, GPT-4o, GPT-4.1 and GPT-5.4 mini, combining a structured output attack (forcing a response in a strict JSON schema for a supposed audit), role-playing, virtualization and delimiter confusion. He stressed that this is not a benchmark but experiments in which the attack does not work every time, but it only needs to work once. His tests also showed that a newer model is not always safer than an older one. The multimodal GPT-4o turned out to be significantly more resistant to some simple injections than the newer GPT-4.1.
For developers using coding agents, the most important part was about indirect prompt injection and privilege escalation. When we give an agent access to the terminal, email, the repository or external sources through MCP, we ourselves become the target. Vermeer showed how an instruction hidden in the code of an HTML page prompted a local assistant to create a bash script on the user's Mac and grant it permissions. In another demo, hidden commands in files with conference talk proposals inflated the scores the model gave and leaked data through an image. Even more dangerous is link smuggling: the model appends a Markdown image tag (Show Image) to the end of every response, and the content of the conversation is encoded in the URL parameters. It is enough for the client to render the Markdown, and a GET request flies to the attacker's server, even if the model itself is safe.
Vermeer asked the room an uncomfortable question: how many of us read every line of code and scripts in skills files downloaded from the internet before plugging them into our agent? Agents run on our machines with access to AWS keys and databases, so an unverified skill is a Trojan horse. Rentea added one more remark for code review: it is worth looking at new logger calls, because logging strings risks prompt injection (he did not elaborate). I would add on my own that the risk grows when the logs are later read by a maintenance agent, of the kind Migała talked about.
Adversarial code review and the observation tower
Suppose the specification was good and the tests on CI passed. A pull request of 700 or 2,000 lines lands in front of us. How do you review it in half an hour without going mad? This is where Rentea's talk gave the most practical solutions.
First, his iron rule: no human reviews the code until a second agent has reviewed it. This is adversarial review. The same agent that wrote the code is not suitable, because after a long session its context window is clogged and it sits in the “dumb zone.” A new reviewing agent starts with a fresh context, that is, in the “smart zone.” You can give it a persona (e.g. “You are Uncle Bob, you check the SOLID principles and Clean Code”) or, as Migała suggests, run specialized agents for performance or architecture, depending on the automatic risk assessment of the changes in the PR. The reviewer mostly reads tokens and produces few, so it is relatively cheap. It pays to use a stronger model. Rentea also joked that anyone who wants to see ruthless criticism and “blood on the screen” should have another model review Opus's work, because that is a war within the family.
Second, once the author agent finishes its work, it should list all the places where it had to make its own decisions or depart from the specification, and assign each a confidence level in percent (e.g. “60% confidence level”). Then human attention goes only where the author hesitated, and where sparks flew between the author and the reviewer.
Third, you need to get out of the trenches of reading code line by line and build yourself an observation tower. Rentea showed his open tool Human Review. Instead of raw Markdown files, it generates a rich HTML report, tied to IntelliJ IDEA through a PR plugin, so that the agents' comments land in the editor as comments. On such a tower you will find:
Video and runtime environment: a short generated video of the new feature working in the UI, and a button that launches the application in Docker and loads previously prepared database fixtures with one click (e.g. a minimal set of 9 rows for repeatable manual tests).
Deterministic diffs of the API and the data model: the tool checks OpenAPI backward compatibility and highlights breaking changes in red. Python scripts and reflection in Java (or SQL schema dumps from a Docker container) generate PlantUML diagrams showing in green and red which classes, fields or tables changed in the PR. This picture is created from the code, without AI involved, so there are no hallucinations in it.
Tested Draw.io diagrams: with huge database schemas, PlantUML's automatic layout keeps rearranging the boxes and ruins the team's visual memory. So Rentea exploits the fact that PNG files exported from Draw.io contain the diagram's semantics (boxes, lines, labels) in hidden headers. This makes it possible to write a unit test comparing the image against the domain model in Java. When the agent adds a new relationship in the code, the test forces it to draw a red line on the diagram, asking a human to lay it out by hand.
Requirements mapping and runtime behavior analysis: this is one of the few places where AI works in Rentea's tool. The model maps specific sentences from the GitHub task to acceptance test scenarios (feature files, Playwright) that covered the changed code. With 100% sampling from OpenTelemetry, traces from test execution are automatically converted into sequence diagrams. By clicking an arrow, we see the exact JSON payload, SQL query or call between modules of the monolith (detected with a simple JVM argument), and we immediately see that the AI designed an absurd cycle of calls between services.
Complexity visualization: from mapping package dependencies in ArchUnit, through a three-dimensional “Code City” (a building grows with the number of lines of a class and its visible cognitive complexity, and the blue lines are coupling), to a simple syntax walker pointing out which controller or mapper gained conditional branches. Rentea himself admitted that Code City looks great in a presentation but is the least useful of the whole set in practice.
Rentea's advice: when, despite these safeguards, you find garbage in the agent's code, do not just fix it by hand. Update the deterministic guardrails, add one line to the instructions file, or build a new tool on the observation tower, so that tomorrow you notice the same problem in two minutes from a picture, not in twenty, shoveling code in the trenches.
Depth over fluency and the paradox of automation
Renato Cavalcanti raised the question of our professional identity. For decades we defined ourselves by fluency in a particular language, whether Java or Scala. Translating thoughts into code gave us flow, built an emotional relationship with the project and let us learn through friction and problem solving. Today, fluency in quickly writing syntax is going into the past.
Cavalcanti recalled the paradox of automation (Ironies of Automation) described over 40 years ago by Lisanne Bainbridge. The more we automate a process, the less often the operator practices their skills day to day. Automation takes over 90% of routine tasks and leaves the human the hardest 10%, and when a critical situation arises, an operator without regular practice does not react correctly. The example was an Air France flight from Rio to Paris: during a storm the plane was flown by the autopilot, and the less experienced co-pilot woke the more experienced colleagues too late, and by then the situation could no longer be reversed.
In software engineering this means we cannot become “hollow engineers” who only press the accept button. Value shifts from syntactic fluency to engineering depth. We are the ones who must know that the agent should not write its own GZIP compression algorithm from scratch (even though it wrote green tests for it), but use the battle-tested Apache Commons Compress, after checking the library's license and maintenance. We are the ones who must know that wrapping a blocking call in CompletableFuture without your own thread pool will block the shared system pool. Cavalcanti added that in his framework this was not needed at all, because user code runs there on virtual threads.
Migała put it tongue in cheek: congratulations, you have been promoted to architects, unfortunately without a raise and without a new entry on LinkedIn. To avoid losing touch with your own codebase, Migała proposes “Socratic code review”: after generating changes, the agent quizzes you (or the PR author) on what the code does and what consequences it carries. Cavalcanti, in turn, recommended the book “Thinking with AI” and the approach to training juniors described in it. New programmers write code entirely without AI for the first three months. Then they get a license and are randomly quizzed on their pull requests, and if they cannot explain the generated code, their agent license is temporarily taken away. He himself treats AI as a pair programming partner, but deliberately writes some tasks by hand to keep his mind sharp.
What to roll out in your team starting tomorrow
Do not feed raw Jira tasks directly to the agent. Introduce a mandatory planning and specification stage (a Three Amigos session, then a plan and requirements in Markdown files before the first line of production code).
Instead of one gigantic file in the root directory, create small, human-written AGENTS.md / CLAUDE.md files in key modules. Describe in them only the architecture, unusual solutions and pitfalls the model has already failed on. Delegate the initial code exploration to cheap subagents.
Require the agent, after every longer task, to list the places where it made its own assumptions, along with a percentage confidence rating.
Enable linters, static analysis (Sonar, Snyk), architecture tests (ArchUnit) and mutation tests in CI/CD and local hooks. Protect pipeline configuration files, quality rules and instruction files with CODEOWNERS.
Introduce adversarial code review: a human looks at the code only after a separate agent with a fresh context has reviewed it, preferably a stronger model, with a defined reviewer persona.
Have the agent write deterministic scripts (in Python or Java) that, for every PR, generate visual diffs of database schemas, check OpenAPI backward compatibility and draw sequence diagrams from the OpenTelemetry traces of the tests.
Treat skills files and MCP servers like external dependencies and read their code before running them. In applications with LLMs, enforce structured output (JSON Schema on the server side) and block rendering of external images in Markdown, to prevent leaks through link smuggling.
Before approving a large PR, have the agent quiz itself or the change author on the key design decisions in the generated code.
Summary
The change we are seeing resembles the move from low-level languages to the Java virtual machine, with the difference that the new layer of abstraction is non-deterministic. Migała invoked Amdahl's law: merely speeding up code generation will not speed up software delivery if the bottleneck remains human attention on review. My takeaway from the conference is that an engineer's work is shifting from typing syntax to designing protective barriers, precisely defining problems and building tools that let us answer for a system whose code we did not write with our own hands.
Read next
Code review when nobody wrote the code
Not long ago, code review had one condition. The person who clicked Approve could explain the change to someone who had not seen the code.…
ReadHow we built a receptionist that answers the phone for a salon
In a small salon, the phone is answered by the same person who is in the middle of a treatment. When their hands are busy, the phone rings…
ReadPrefer to see this in a shipped project rather than in theory? Have a look at our case studies