Coding with AI in 2026: what we’ve learned, what we’re preparing for

In our development teams, the arrival of AI coding assistants has prompted mixed reactions. Some have embraced them as part of their daily workflow, using them as partners to push their thinking further and deliver greater value. Others have remained more sceptical about relying on them in production codebases, citing the risk of reduced code quality and losing control over what the agent generates.

Both perspectives are valid, but the debate has recently shifted. It used to focus on the quality of code generated line by line; it now centres on what can be delegated entirely to the agent, and the quality assurance practices needed to support that.

This shift is not just our own observation. Some of the most demanding open-source teams when it comes to maintainability, such as the core team behind TanStack, have made AI-generated code a normal part of their workflow to multiply their productivity on libraries that are widely used in production. AI-generated code is no longer a hypothesis; it is a reality.

What concerns us this year is therefore no longer whether to adopt AI, but how to build the quality assurance processes that allow us to delegate work to AI agents with confidence.

Three ways of using AI: manual, assistant and delegation

In our recent experience, we have identified three ways of using AI.

  1. In manual mode, the developer writes the code and only calls on the agent occasionally.
  2. In assistant mode, the agent suggests code as the developer writes, and the developer accepts or rejects each suggestion.
  3. In delegation mode, the developer defines the task and the agent carries it out from start to finish: planning, implementation, testing and review.

One common misconception, and one we made ourselves, is believing we had moved to delegation while actually remaining in assistant mode. We wait for the agent’s responses, explain the context again in every session, and review every line as though all AI-generated code carried the same level of risk.

Between a skilled developer working without an agent and the same developer using a chat window, we have not observed any significant productivity gains. The real productivity leap comes with delegation, and it happens before a single line of code is written.


Pillar 1: using AI agents requires stronger software engineering fundamentals

Code now has two readers

Humans understand through habit, retain context from one project to another, and tolerate implicit assumptions. An agent has limited memory from one session to the next, and a session itself has a limited context window. Outside what is explicitly written in these files, the agent does not tolerate implicit information. To be provocative, working with an agent is a bit like onboarding a new developer for every task: it knows how to code, but it has to rediscover the project, and everything that is not written down has to be reconstructed.

This constraint pushes us to make explicit what was already considered good practice in large teams, but was not always documented in smaller ones: contracts between modules, typing at boundaries, shared schemas, instruction files, cross-cutting rules. None of these patterns are new. What changes is that they now provide immediate value by offering a clear structure that allows an agent to work without ambiguity.

By standardising not only the way we write our code but also the agentic approach itself, a developer can move from one project to another without having to relearn everything, redefine our development workflow conventions, or rethink how we manage agents.

Agent configuration, a new area of expertise

When properly configured, an agent faithfully reproduces the project’s patterns. When poorly configured, it invents a different structure every time, one that may be plausible but does not follow any established conventions. Configuring an agent means deciding, for each task, what it can see and what it is allowed to do.

This is the difference with a ChatGPT-style chat experience, where the user has to explain their context again for every exchange. The developer’s role is the opposite: to set up a system that consistently provides the right context to the agent.

 

In practice, we do not load all the project conventions into every session; we use scoping and the right triggers to provide the agent with the conventions relevant to the part of the code it is working on.

This framing also applies to security: we restrict the agent’s permissions, and automated gates prevent it from making changes outside its scope.

Sub-agents take this logic even further. For a sub-task, we launch a separate agent with its own context and its own permissions. It carries out its work and only returns the result, without cluttering the main session. And because each one operates independently, we can run several of them in parallel.

A concrete example

On a recent project, around a dozen specifications shared the same pattern: a side panel with a chart and a table, each connected to a different data schema that could be validated on the backend. In the initial estimation, assuming development without an agent, we planned half a day per side panel, which came to around six days in total. The first one was written manually. We then extracted from it an instruction file, a “Skill”: a version-controlled guide that the agent loads on demand, which captures the pattern and recreates it for each context. The remaining eleven were delivered in half a day, including tests.

This is where configuration unlocks the gains on repetitive work: without it, each variation gradually diverges and technical debt accumulates; with it, the agent faithfully reproduces the pattern and the codebase remains consistent.

Drift and codebase consistency

On a simple and well-defined task, an agent remains consistent. As soon as complexity increases, it starts to drift: consistency between modules weakens, the scope expands beyond what was intended, unnecessary edge cases are added, complexity appears without justification, existing code is not sufficiently reused, or the agent leaves behind dead or unused code. This drift is a characteristic of generative models when they operate on extended contexts, and it should be anticipated rather than corrected on a case-by-case basis.

Instruction files align the intent at the start of a session, but they are not enough to prevent execution drift. The real safety net lies elsewhere: in strict contracts between modules. Rigorous typing at boundaries, shared schemas between front end and back end, business invariants protected by tests that fail when deviations occur. The agent can write whatever it wants inside a module; it cannot cross the boundary without breaking a contract. It is this contract, maintained by the developer, that preserves consistency.

On the API of one of our projects, the module pattern is documented: each module follows the same structure. An audit script greps this expected structure and reports deviations. It highlights specific cases: one module whose DTOs use a different validation library than the convention, another where part of the business logic has been placed outside the service layer.

Taken individually, these deviations are minor. Accumulated over time, they are what breaks the consistency of the codebase, and an agent extending one of these modules will reproduce the deviation instead of the convention.

 

A conventional project slows down as it grows because only the code accumulates; when verifiers, documented patterns and instruction files accumulate as well, we observe the opposite across entire areas of development.

At this stage, this is an objective for us, not an achievement: it only holds if we maintain strong consistency and invest in verifiers as much as we produce code. Otherwise, the agent amplifies disorder, and the codebase quickly becomes difficult to maintain.

The verifiability criterion

An agent is only reliable if it can verify what it is doing, and it is the tooling and the codebase that provide it with the means to do so. A failing test, a type that does not compile: each error is concrete feedback that the agent can use to correct itself and try again, without human intervention. With slow tools, or a codebase without tests, typing or conventions, this verification loop breaks down, and a better model does not fix it: it only accelerates the production of code that will need to be reworked.

This leads us to our criterion for delegation. The question is not “is the agent good enough to do this?”, but rather “can the result be verified?”. Some tasks have a correctness criterion that the agent can assess on its own: types compile, tests pass, the contract holds. Structural and repetitive code, data cleaning, generating tests from a precise specification: these are tasks we delegate because the codebase itself proves that the result is sound. The loop closes without us.

 

This automatic verification covers one specific aspect: contract correctness. It does not say whether the architecture is the right one, nor whether a business rule has been correctly interpreted. These judgements still require human review. Delegation refocuses the review process: CI validates the form, while humans validate the intent. Keeping this volume of review manageable at scale remains an open question; we will come back to it later.

This delegation boundary is not fixed. Each test that locks in a behaviour, each typed contract at a boundary, transforms a portion of unverifiable code into verifiable code, and therefore into code that can be delegated.

It is the developer’s responsibility to expand the surface area on which the agent can verify its own work autonomously.

The areas where the developer remains in control

The other side of the criterion: several areas require the developer to remain in control, in order to provide expertise or context that the agent cannot discover on its own.

Choosing technologies and libraries
We use AI to accelerate research and learning, but we test libraries ourselves and integrate these choices into our internal technical training. Without this regular contact with the code, we would lose the development of our expertise and our ability to frame these decisions.

Designing a complex architecture
An architecture results from trade-offs negotiated with the client, business constraints, external systems to integrate, existing history, and a level of maturity. None of this can be inferred from the code alone; without this context, an AI suggestion remains too generic to be actionable.

Technical debt analysis
AI can identify apparent bad practices, but it does not know why they exist or what they imply. On a legacy project, it struggles to distinguish what really matters and generates a lot of noise that a developer has to filter.

Legacy refactoring
A legacy project sometimes has no tests, no typing, no conventions, none of the safeguards that would allow an agent to modify the code without breaking anything. The first step is to put this safety net in place. On a legacy project with an ageing stack, we introduced Playwright and Vitest to capture frontend behaviour before refactoring it. The agent can write part of these tests by running the code and recording its results. But it is up to the developer to decide which behaviours are worth freezing: everything that works today is not necessarily intended, and a test that captures a bug protects it instead of allowing it to be fixed.

Once this safety net is in place, the refactoring progresses incrementally. At each stage, we retain the ability to fall back to the legacy code as long as the new version has not proven itself: we never replace everything at once, we substitute piece by piece under the control of tests.

At the Web Engineering Summit, the teams at monday.com presented a refactor they had initially planned over eight years, but delivered in six months using this approach.

Exact logic and business calculations
When the result needs to be correct down to the smallest unit, such as financial calculations, regulatory conditions or authorisation logic, an error can easily go unnoticed. We write these calculations manually: they are inherently tied to business rules, and writing them ourselves remains the most reliable way to understand and validate them. Automated verification is not sufficient here: a test can pass even when the result is wrong, if the expected result written in the test is itself incorrect. What protects the calculation is the developer’s ownership of the cases and their expected results, along with a sufficiently solid understanding of the implementation to validate it. It is the same rule as everywhere else: we delegate what we can verify, and here verification relies on that understanding.

A note on tests, because the term covers two different things. Deciding what the correct result is, for a given input and expected output, requires knowledge of the business domain; this is the developer’s responsibility. Writing the test code that verifies this result: once the expected result has been defined, this is formatting work, and the agent can handle it. When we talk about delegating test generation, we are referring to this second part. The first part does not disappear once it has been written: it remains in the codebase as a control that the agent can run afterwards.


Pillar 2: the developer’s effort shifts upstream, towards design before development

A vague architecture cannot be fixed with a better model

AI agents work better on a clear architecture. In a composable front end with few dependencies between components, the agent understands what it is manipulating, and its code integrates cleanly. In a coupled architecture, with implicit logic and cross-dependencies, it reproduces the existing patterns, including those we would like to remove, and amplifies the disorder.

An example
On a codebase undergoing modernisation, two data-fetching approaches coexist: the old one, based on Redux Saga, and the target approach, based on TanStack Query. This coexistence is normal during a modernisation refactor. The problem is that, without explicit guidance, the agent extends the pattern it encounters most often, which is the old one. The migration moves backwards instead of forwards. The safety net is a folder-scoped rule that defines the target: new code uses TanStack Query, while Redux Saga is frozen in the parts of the codebase that we do not want to modernise. An architectural decision, written as a rule, becomes a constraint that guides the agent towards the right direction.

This is why the effort shifts towards design: writing code is often no longer the step that consumes the most time. An incomplete design upfront is expensive to fix later, especially when an agent multiplies code production in the wrong direction.

The agent as a sparring partner

Before a long task, a developer on our team goes through an alignment phase with the agent. They answer scoping questions that are not about writing code, but about translating a specification into a design: module boundaries, API formats, edge cases, expected error behaviour. The agent does not passively receive instructions; it asks its own questions and requests clarifications. It forces the developer to make explicit what they would otherwise have left implicit.

On a recent project started with an agentic architecture, it was these questions that helped us better frame the work before writing any code.

This framing produces a written specification that the agent then uses to execute: the scope is defined, edge cases and error behaviours are decided. If a point remains open, the agent does not guess; it either stops or makes the decision in our place. It is the same rule as for code: what is not written down, it has to reconstruct.

Two real examples:

  • The first was a contradiction in the specification: a severity scale described in two places in the document, with six colour levels in the text of a user story, and five in the specification table further down. Which one takes precedence, and should the sixth level be removed or moved somewhere else? Until this is resolved, the component cannot be coded.
  • The second was a gap: each card displays “last updated X minutes ago”; if the data is two days old, do we write “2,880 minutes ago”, switch to a date, grey out the card, and from what threshold should the data be considered stale? The specification defined the format, but never the limit.
  • The direct consequence is that the quality of this dialogue depends on the developer’s judgement. With limited expertise, we follow the agent and accept the choices that work. With expertise, we guide it, reject some suggestions, and impose others.

To ensure this expertise is in place, we have strengthened our training and internal R&D efforts. We encourage knowledge sharing through architecture documentation maintained at company level, which aligns teams and provides agents with configurations from a shared registry that is continuously improved. This registry is an internal dependency, and we choose it deliberately: it is a team asset, versioned and documented, rather than a third-party product that a provider evolves without us.


The remaining open challenges of agentic development

If we had to go through the journey again from 2025, we would have spent less time forming our opinion by testing tools, and more time thinking about what this paradigm meant for the engineering of our systems and for team dynamics.

AI agents bring their own set of questions and challenges to workflows that had been working well until now. Here are a few examples we have observed in our teams.

The “it works” trap

Before, producing working code required understanding the system being modified: understanding came through doing the work. The agent breaks this link. Code can work whether or not the developer understands it. The risk is real: expertise becomes superficial, and anyone can drift into a passive role, simply validating an agent’s output.

We observe this in recruitment and within teams. During recruitment, candidates who claim “AI codes better than us”: the warning sign is not the use of AI, but the abandonment of judgement over its output. Within teams, developers who had strong fundamentals when they joined have, after several months of intensive AI use, allowed those fundamentals to erode. The “it works” moment comes quickly; as soon as complexity increases, cross-module refactoring, business edge cases, performance, code delivered without the expertise to support it does not hold up, and the burden falls back on those who retain a complete understanding of the codebase.

This is why we evaluate developers on two fronts: their fundamentals and their ability to master an agentic architecture. One does not replace the other. We support this by moving developers between projects rather than isolating them on a single one, so that everyone remains exposed to varied contexts and challenges their delegation boundaries against those of others.

Working as a team with AI

Each developer places the boundary of what they delegate in a different place, depending on their knowledge and judgement. As long as it is defined individually, it creates friction. A task completed by one developer may be perceived by a peer as “done by AI”; the cost then shifts to the reviewer, who has to make a manual effort after automated work.

At the same time, noise accumulates in the backlog: the agent makes it trivial to suggest architectural evolutions, refactors, and three edge cases where one would have been enough. Each proposal taken individually is defensible; what is lost is the team’s alignment on what is worth coding. Ticket triage regains the importance it had lost: it is the moment when the team collectively decides what enters the scope and what is removed from it. We are experimenting with delegating part of this to the agent, identifying duplicate tickets, grouping bugs with the same root cause, but the decision remains collective.

We have noticed that explicitly discussing this boundary helps unlock these situations. As long as the question remains “for or against AI”, everyone stays entrenched in their position and the debate goes in circles. When brought back to specific cases, what we delegate here, what we keep there, the discussion moves away from ideology and helps practices evolve.

Adopting AI without rethinking the workflow

The risk is adopting agentic development without rethinking the way we work. Three pitfalls can be observed when the process does not evolve alongside the tools: extending the working day to wait for the agent’s responses, running several sessions in parallel “to save time”, and allowing the scope to expand because the agent can absorb more.

We have observed a common point in feedback from teams: a higher perceived workload, without anyone asking teams to do more. They do it because AI makes “doing more” accessible, and often immediately rewarding. The mechanism is self-reinforcing: AI speeds things up, which increases expectations around speed, which expands the scope, which further increases the workload. This is a limit we take seriously: a productivity gain that comes at the cost of overwork is not a net gain, and we do not count it as such.

We have not solved this entirely; we are acting on the workflow. AI makes it technically possible to code in the evening, but for humans, we do not encourage it. Instead, we let automated sessions run overnight on scoped and verifiable tasks, which developers pick up again in the morning. When they return, contracts and tests have already settled the correctness of the implementation; the developer checks the intent and the scope, without reviewing every line. The night shift is the agent.


What we are preparing

The technical challenge for the coming months is focused on three areas.

Scaling review
At this volume of generated code, line-by-line review no longer scales, and we have not yet finalised the target system. The directions we are exploring: concentrating human attention where it makes the difference, such as interface typing, module boundaries and the intent behind a change. CI already validates the form; the remaining challenge is to provide better tooling for reviewing intent.

This volume also puts pressure on the integration pipeline: more code reviews mean more CI runs, and a slow pipeline cancels out part of the gain. We are acting on both sides. Upstream, we limit noise so that we do not open three code reviews where one would be enough, which connects back to ticket triage. Downstream, we rely on caching to only rerun what has changed and keep CI fast.

Agent orchestration
Night sessions on scoped and verifiable tasks are a first step. The next stage is more autonomous and routine agents operating on the codebase, iterating on a task until it is genuinely complete, moving on to the next one, and only surfacing what requires human judgement.

Extending this to other roles
This change will not stop with developers: it is already reaching the roles we build with, including BAs, designers and QA, whose work is directly connected to ours and who face the same challenges around quality and volume.


What agentic architecture shifts in our projects

Concretely, this approach shifts three things in the way we work.

The start of a project is faster: initial structure, routes, services, first screens based on the design system, test harnesses, what used to take around ten days now takes a few days, provided we have the fundamentals and technical opinions ready to deploy.

Simple changes can be automated: anything that does not affect the architecture, a translation, error handling, adding a field to a form, in short, a pattern being replicated, can be handled with a simple developer review.

The time saved on repetitive execution is redirected towards design, structural trade-offs, architecture reviews, technical debt management and documentation: things that did not fit within the budget become possible.

Software has always aged in the same direction: the larger a codebase becomes, the more expensive each change is. Agentic development provides a way to reverse this curve. When a project accumulates, alongside its code, its verifiers, documented patterns and instruction files, each month expands the surface area that can be delegated and makes the next month faster. This curve does not reverse automatically: the loop holds because a developer frames each task, maintains the contracts and decides what deserves to be frozen. This is the objective we have set ourselves.

Models will continue to improve, and each improvement will be available to everyone on the same day. What cannot be downloaded is what we accumulate internally: a verifiable codebase, maintained contracts, tooled conventions, and developers who know where to place their delegation boundary and how to move it forward. It is on these two fronts, engineering and judgement, that we are investing.

We deliver not only an application that holds up in production, but also one where agents can continue accelerating feature development without degrading quality.