274 stories
·
0 followers

Maybe We Shouldn't Be Reviewing All This Code

2 Shares

TL;DR
Or, perhaps the problem isn't that AI has broken code review, maybe it’s that we've been using code review to solve the wrong problems

I was on a panel recently with Brian Houck from DX at Code Remix, hosted by Moderne. It was one of the more interesting panels I’ve done, largely because we disagreed. As my colleague Martin Fowler says, panels are much more interesting when people disagree and both sides have a good argument. Brian and I definitely did.

Brian has since written a thoughtful piece called What are code reviews even for? He is clearly passionate about his position, and I am passionate enough about mine that I’m writing this response. To be clear, I think we mostly want the same things. I just don’t think code review is the best way to get them. Brian is lovely, by the way, and encouraged me to write this. But I’d be lying if I said I didn’t want you to think I’m right by the end :)

So what were we disagreeing about?

AI is producing more code than humans can realistically review. Brian cites some pretty striking numbers: at Meta, significant lines of code per human-landed diff reportedly increased 106% in a year, while DX’s own data shows median pull request size increasing 64%.

His concern, which I share, is that simply automating code review away risks losing all the other things we use it for. Code review isn’t just about finding bugs. It’s how teams share knowledge, teach junior engineers, build collective ownership and spread architectural understanding.

My question is: why are we waiting until code review to do all of those things?

I’ve never particularly liked pull requests as the centre of the software development process. Not because engineers shouldn’t look at each other’s code, but because I’ve always struggled with the idea that we should build something, finish it, package it up, throw it over to somebody else and then have the important conversation about whether we built the right thing in the right way.

And don’t even get me started on merge conflicts. I’ve lost too many hours of my life.

Shift the judgment left

One of the principles I learned very early at Thoughtworks was to shorten feedback loops. If feedback is valuable, don’t remove it. Move it closer to the decision it is informing.

Take the things we say code review gives us.

If we want to explore alternative solutions, I’d rather do that before implementing one of them.

If we want knowledge transfer, pair. Sitting next to someone, physically or virtually, while they reason through a problem teaches you far more than reading their completed solution afterwards.

If we want junior engineers to learn how experienced engineers think, let them work with experienced engineers while they’re thinking. Pairing comes to mind again here, but teams could also do design sessions collectively with a whiteboard before they write (or instruct the agent to write) anything.

If we want collective ownership, organise teams so people actually build and operate software collectively rather than relying on a pull request to tell everyone what somebody else has already built. For this again use pairing, mob programming, or team design sessions around whiteboard.

If we want architectural alignment, design together (I won’t repeat myself about pairing and team design sessions, oh wait…) and then encode the important constraints as fitness functions.

And if we’re reviewing code for formatting, linting, known security problems or things that can be deterministically tested, automate them. We really shouldn’t still be arguing about whitespace in 2026.

Pair programming, trunk-based development, automated testing, static analysis, fitness functions and security scanning all move feedback earlier. Increasingly, agents can participate in those loops too, challenging designs, testing assumptions and continuously verifying what is being built, but the real thinking is coming from experienced humans and if we want that experience to benefit the whole team then we have to act like one much earlier than code review.

Review by exception

None of this means nobody ever reviews code. There are absolutely changes where I want another experienced human looking. An example would be a fundamental architectural change. Assuming we did a design session as a wider team, we might want to review the code as a team or agree it was implemented right, or discuss if we want to change anything. Other examples could be something crossing a sensitive security boundary, a change with a huge blast radius, an unfamiliar part of a critical system or simply something where the team says, “I’m not confident about this.”

Those are exactly the places where human judgment is valuable, but that’s very different from requiring a human to inspect every change because that’s the ceremony we’ve historically used to create confidence.

And we know now it’s not viable to continue down this path, hence why code review keeps coming up as an issue or a blocker. If an agent can produce ten times the code but every line eventually queues up waiting for a senior engineer to inspect it, we haven’t created a ten-times engineering organisation, we’ve created a big backlog and a new bottleneck.

And I don’t think the answer is an AI agent pretending to be the human reviewer so we can preserve exactly the same process at higher speed. That’s automating the ceremony rather than questioning why the ceremony exists.

There is one thing I do worry about in Brian’s argument, though. He talks about teams accumulating cognitive and intent debt: software grows while the humans responsible for it understand less and less about why it works the way it does. I think that’s a very real problem. I just don’t think mandatory pull requests are a particularly strong defence against it.

If agents are going to produce substantially more of the implementation, we need to be much more deliberate about maintaining human understanding through collaborative design, pairing, good boundaries, executable architecture, shared operational responsibility and probably some practices we haven’t invented yet.

We need engineers to understand systems, not diffs.

Perhaps that’s what AI is exposing. We’ve spent years loading an extraordinary number of responsibilities onto the humble code review: quality gate, security check, architecture review, mentoring mechanism, knowledge-sharing system, ownership model.

It worked, sort of, while humans could only produce code so quickly. That constraint is disappearing. So perhaps the question isn’t how we get the code reviewed faster. Perhaps it’s why we’re waiting until code review to have all the important conversations in the first place.

Read the whole story
StephaneDenis
17 hours ago
reply
Saint-Hyacinthe, Quebec
Share this story
Delete

Microservices and GenAI in 2026: my Dear Architects conversation

1 Share

Earlier this year I was a guest on Luca Mezzalira’s Dear Architects podcast. We talked about where microservices are in 2026 and what GenAI changes. My take is that the best software development practices we had before GenAI - you could even say extreme programming - are even more necessary now, because of the nature of GenAI.

Here are the main points. The full conversation is on the episode:

We are still building big balls of mud

Painting with a very broad brush, I think far too many enterprises just muddle along and mostly build big balls of mud.

Software serves two constituencies

Software is an unusual kind of artifact. With most things, how you build one today does not affect how you build the next one. With software, the design decisions you make today affect how easy it is to make changes tomorrow.

Software also serves two constituencies. The end users need features. The organization that builds the software needs it to be easy to change, so that it can keep delivering those features. The second need is the one that gets neglected. The emphasis is on feature development to the exclusion of proper architecture and improving the -ilities. So the software gradually becomes harder to change, and shipping features slows down. As the requirements grow, an application’s architecture has to be improved so that it stays easy to change.

Good practices get devoured

The other part of the problem is what organizations do to good practices. Agile practices came along and got devoured by the organization’s change-resistant antibodies, and morphed into that abomination called SAFe. Then microservices came along and got devoured as well.

The red flags of a distributed monolith

When I start working with an organization, there are a few red flags I look for:

  • The ratio of services to developers - For a long time my rule of thumb was one service per team. These days I would say you should only have a service if it solves a problem. A really common anti-pattern is to have almost as many services as developers, or more. I repeatedly see a team of five people with five services, asking how to solve some distributed problem across them. Usually the answer is to merge them together, and then you do not have a distributed problem anymore.
  • A go-faster architecture that is not delivering any faster - Look at your deployment frequency and your lead time.
  • Testing and releasing services in lockstep - 99% of the reason you have services is so that you can deploy them independently, but then the old QA practice of a system release takes over: we have to test them all together and release them in lockstep.

Either way, you are incurring the cost and complexity of microservices without any of the benefit. They are all symptoms of a distributed monolith.

Dark energy and dark matter

Your default architecture should be a monolith unless you have a clear, tangible problem that introducing one or more services would actually solve. So the question is what problems a service would solve, and conversely, what problems it would create.

I am a science-fiction nerd who is also into software patterns, and the intersection of the two prompted me to come up with the dark energy and dark matter forces. They are the criteria for deciding whether two subdomains should go together in the same service or be separated into different services. I made it up because it sounded really cool, but it turned out to be useful. Both terms come from astrophysics:

  • Dark energy - the concept astrophysicists use to explain the accelerating expansion of the universe, an anti-gravity that pushes matter apart. It is the metaphor for the forces that push your subdomains apart into separate services. One is the need for a fast deployment pipeline: the more components you have, the smaller each one is, and the faster its deployment pipeline runs. Another is team autonomy: more services means teams can be more autonomous.
  • Dark matter - the invisible matter that makes up most of the universe. You cannot see it, only its gravitational effect. It is the metaphor for the forces that resist decomposition and pull your subdomains together into a monolith. One is minimize runtime coupling: inter-service communication involves latency and can impact availability. Another is prefer ACID over BASE: operations that span multiple services are eventually consistent, not ACID.

There are five forces pushing your subdomains apart and five pulling them together. I described a couple of each on the episode; you can read about all ten on microservices.io. They are in conflict, and the job of the architect - whether choosing between a monolith and microservices, or defining service boundaries - is to find the set of trade-offs that resolves them.

Context matters

The strength of each force depends on your context: the complexity of your application and the number of teams you have. Team autonomy is an excellent thing, but if you only have one team it is not a reason to have multiple services. Similarly, a small application with a few developers committing regularly has a lightly used deployment pipeline that gives fast feedback. As the application grows and the number of teams grows, each build takes longer and more commits flow into the pipeline, and eventually it becomes a bottleneck. That is when you want to go from a monolith with a single deployment pipeline to multiple services, each with its own.

Low latency example

Luca raised the case of a latency-sensitive system where services are ruled out because of the overhead. Suppose an application has many REST endpoints and only a handful of them are latency sensitive. You put those operations, and all the code they touch, in the same service so that they are not distributed, and the operations that are not latency sensitive can be distributed. Low latency might constrain your service boundaries in certain ways, but it does not necessarily force you to use a monolith.

The microservice architecture is unforgiving

Where you put things matters more in a microservice architecture. In a monolith you can write a class and put it anywhere; it does not matter immediately, though modularity and coupling catch up with you later. In a microservice architecture, where you put things has an immediate impact on latency and coupling, and that is how you can very easily end up with a distributed monolith.

There is more to microservices than services

Delivering software with fast flow takes more than the right architecture. You also need the right organization and the right process. Specifically, you need an organization structured according to Team Topologies, DevOps as defined by the DevOps Handbook, and an architecture that enables both. That is what I call the success triangle. And that is the role of microservices when you are building at scale: a large organization and a large application.

Luca observed that companies often adopt microservices without changing anything about the socio-technical system around them. I described this in 2018 as the red flag law anti-pattern. In some jurisdictions, a pedestrian waving a red flag and blowing a whistle was required to walk in front of an automobile. You have a car that could go faster than a person, but rules and regulations slow it down. That is what exists in a lot of organizations: a go-faster architecture that the organization’s policies, procedures, and structure slow down to pedestrian pace.

The fundamentals matter even more with GenAI

The practices that make software easy to change matter more now than they did before GenAI. A coding agent depends on fast feedback and on guardrails. Both of those come from your architecture and your tests.

Coding agents need fast feedback

A coding agent essentially has to use test-driven development. It needs to be able to quickly run the tests and get feedback, so the inner developer loop has to run exceedingly quickly. The outer loop has to be fast too: a push triggers the deployment pipeline, and that needs to run quickly and get the change into production, where it gets more feedback.

The whole motivation for a fast-flow architecture is rapid feedback, so you want to structure your code, your tests, and your deployment pipelines so that your coding agents get prompt feedback. In that sense, microservices are even more likely to be used today than they were before GenAI.

Fast feedback is a property of the architecture

As I wrote in GenAI-based software delivery needs a fast flow architecture, fast feedback loops are a property of the architecture. Loose design-time coupling keeps the scope of a change as narrow as possible. Loose build-time coupling minimizes how much unchanged code has to be rebuilt and retested as a consequence of a change. And the decision to divide an application into services, each with its own deployment pipeline, is an architectural decision too. So architecture is absolutely essential if you are going to benefit from coding agents.

Agents need guardrails

I have had success with GenAI when there are strong guardrails in place:

  • Automated tests - one of the first guardrails.
  • The more boring, deterministic guardrails - pre-commit hooks that run code-quality checks (I use CodeScene) and vulnerability checks, to keep the AI from doing something stupid.

Otherwise it writes code that it never uses. On one project, 30% of the generated code was dead code - which admittedly happens with humans too, but this is meant to be world-changing technology.

Michael Feathers defined legacy code as code without tests. A tremendous amount of the code being written today does not have proper automated tests, so you let agents loose on it and who knows what bugs they are going to introduce.

Software development is still a human activity

There is also a troubling trend: the idea that AI generates code so fast that we can’t review it, so we must find other ways to validate it. Call me old-fashioned, but software development is still a human activity. It is your code, with your name on it, that you are committing and are accountable for. And I do not think coding agents are capable of the good design that human beings are.

Brownfield is not greenfield

Luca asked whether AI can really take a 20-year-old ERP and quickly move it to whatever architecture you want.

GenAI helps you understand legacy code

GenAI is a useful tool for understanding a legacy codebase, though you have to take steps to ensure that it does not hallucinate. I have had it produce architecture documentation that was quite imaginary: half of it was real, and half was how it thought the system should work, even though it does not.

Modernization is more than generating code

GenAI can also generate code, but it needs the guardrails, the tests, and the specification in order to generate the right code. And modernizing an application involves a lot more than generating code. Quite often you are having to reverse-engineer the requirements. You are also having to design how the new application should work, applying modern techniques such as domain-driven design, good boundaries, and modularity. And as you build it, you are having to learn the new technology you are building it with. There is all this learning going on, and you do it incrementally. That is why I much prefer the Strangler Fig pattern, where you migrate iteratively, over a big bang approach where you get no validation until you are done.

Next.js is not your legacy system

There are some well-known examples of rewrites. Someone rewrote Next.js in a week. But Next.js had a very exhaustive test suite, was well documented, and the web is full of content about it. That is not your legacy system.

I see the same thing on one of my side projects, a language interpreter. I can tell Claude Code “implement language feature X” and it goes off and implements it perfectly, because that language feature is incredibly well documented. Whereas when I am doing normal enterprise-style development, it muddles around and it is a whole lot harder to figure out what needs to be done. A legacy application is a million times worse: a 30-year-old application that might even predate the web.

Convince me

I am willing to be convinced that there is finally some magic technology where you press a button and, hey presto, you get a modern, well-architected application within a week. But people have to demonstrate that it works and that it is not just a big-tech smoke-and-mirrors demo, like a lot of stuff is. Convince me.

Get the fundamentals right

Luca asked me what advice I would give architects going into 2026. My answer has not changed much, because the problems have not changed much either.

A 50-year-old idea that still has not caught on

Most enterprises are struggling with the fundamentals. Modularity and loose coupling date back to Parnas in 1972. That is a 50-year-old concept, and it still has not caught on. A change should ideally happen within one team, but a lot of enterprises have built software that requires a massive number of meetings to coordinate and align in order to roll out a really simple change. There is all this new shiny stuff, but software development would be way better if people got the hang of the fundamentals. And maybe automated testing will catch on too.

The job of an architect is to solve problems

My advice is the same as always. The job of an architect is to solve problems, and you need a disciplined way of doing that:

  • define the problem,
  • come up with some success criteria,
  • brainstorm candidate solutions,
  • evaluate them against the criteria, and
  • pick the best approach.

Above all, actually think things through.

Demand proof

Have a healthy skepticism. You do not want to be super negative, but you should demand proof: extraordinary claims require extraordinary evidence. Just because some random person on social media says “this worked for me” does not mean it is going to work for you. My mindset is heavily patterns oriented: proven solutions to problems in a given context. Your context is not the same as the random person on X’s context, and you have to carefully think it through.

What else we got into

On the episode, Luca and I also discuss:

  • why code is not free - human attention is still a limited resource - and why “three commits before breakfast” leads to burnout: a relentless pair programming partner who won’t stop, and you can’t stop either
  • agents for everything: the same overuse we saw with microservices, and how to decide which sub-problems actually need a non-deterministic agent
  • “SaaS is dead”, and why big tech is so desperate to make GenAI look revolutionary
  • why Luca thinks we are all juniors again, and what it feels like to use GenAI without a conceptual framework
  • whether GenAI is undermining the business model of open source, and whether it is hard to justify hiring junior developers
  • whether this can even be a profitable industry once you get past Nvidia, the cloud providers, and the consulting companies
  • whether spec-driven development is a return to waterfall

You can watch the whole conversation on the Dear Architects podcast.

Need help with modernizing your architecture?

I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.

Learn more about my modernization and architecture advisory work →

Read the whole story
StephaneDenis
17 hours ago
reply
Saint-Hyacinthe, Quebec
Share this story
Delete

Presentation: AI Works, Pull Requests Don’t: How AI Is Breaking the SDLC and What To Do About It

1 Share

Michael Webster discusses the rise of headless AI agents and their impact on software delivery pipelines. He shares how massive, AI-generated pull requests create a severe bottleneck for human reviewers and introduce persistent technical debt. Learn how engineering leaders can leverage test impact analysis and automated validation pipelines to verify agentic output without sacrificing stability.

By Michael Webster
Read the whole story
StephaneDenis
68 days ago
reply
Saint-Hyacinthe, Quebec
Share this story
Delete

Open source 3D avatars that can speak and lip-sync in real-time

1 Share

Talking Head (3D) is a browser JavaScript class featuring a 3D avatar that can speak and lip-sync in real-time. It also knows a set of emojis and can convert them into facial expressions.

The class supports full-body 3D avatars (GLB) and Mixamo animations (FBX).

By default, the class uses Google Cloud TTS for text-to-speech and has a built-in lip-sync support for English, German, French, Finnish, and Lithuanian. New lip-sync languages can be added by creating new lip-sync language modules.

It is also possible to integrate the TalkingHead class with any external TTS service that can provide word-level timestamps, such as the ElevenLabs WebSocket API. Note that a lip-sync language module is not required if your TTS engine can output viseme IDs or blend shape data directly. For example, by using the Microsoft Azure Speech SDK, you can extend TalkingHead’s lip-sync support to 100+ languages.

See this MIT licensed software on GitHub.

Read the whole story
StephaneDenis
114 days ago
reply
Saint-Hyacinthe, Quebec
Share this story
Delete

Meet the A2Family

1 Share

At Google, we know that building on open source gives teams the freedom and flexibility to use meaningful technologies faster. Openness drives innovation and security, and it is core to our mission. As we look toward the future of computing, we want to ensure that developers across all open source communities have the foundational tools they need to build secure and collaborative AI systems.

That is why we are excited for you to get to know the "A2Family"—a suite of open source protocols and tools designed to help you build, connect, and scale your AI agents.

A2A: The cornerstone of agent interoperability

The Agent2Agent (A2A) Protocol is an open standard designed to enable seamless communication and collaboration between AI agents. It provides the definitive common language for agent interoperability in a world where agents are built using diverse frameworks and by different vendors.

Originally developed by Google, A2A has now been donated to the Linux Foundation. As a famous open source aphorism reminds us: "If you want to go fast, go alone. If you want to go far, go together." A2A brings this collaborative philosophy to AI, allowing agents to delegate sub-tasks, exchange information, and coordinate actions to solve complex problems that a single agent cannot.

MCP & Skills: Agents need tools and skills

Since day one A2A has loved MCP, and we love skills too ♥️. Agents discover, negotiate, converse, make plans, adapt when those plans don't work out – that's a different interaction pattern than a tool and that's what A2A was built for. But for your agents to function, they need access to tools, and instructions on how to use those tools safely and securely. While MCP and A2A might not be from the same origin story, they are a family that works better together.

When you're not sure – if it's a quick deterministic resource or action, it's a tool, but if you may end up with a conversation, it's an agent. Another good mental model is "are you the expert agent which uses tools" (MCP) or "is there some other expert agent you are collaborating with" (A2A).

A2UI: A protocol for agent-driven interfaces

When agents need to communicate with humans, how can they safely send rich interfaces across trust boundaries? Instead of relying on text-only responses or risky code execution, we use A2UI.

A2UI enables AI agents to generate rich, interactive user interfaces that render across web, mobile, and desktop platforms—without executing arbitrary code. It is secure by design, allowing agents to use only pre-approved components from your catalog through declarative component descriptions.

You may also have heard of MCP Apps (formerly MCP UI). It is a complementary alternative to A2UI which ships your agent driven widget inside of an iframe orchestrated with MCP events and tool calls. There are some interesting ways of configuring A2UI and MCP Apps together, for generative UI inside of an iframe or generative UI driving the iframe.

The AG UI protocol, developed by CopilotKit, is a standard for connecting agents to front ends with low latency. It makes developer lives much easier, with integrations to most agent frameworks and front ends. If you are using AG UI, you already have both A2UI and A2A support!

AP2: Securing the agent economy

When an autonomous agent initiates a payment, current systems struggle with questions of authorization, authenticity, and accountability. To solve this, we introduced the Agent Payments Protocol (AP2), an open protocol for the emerging Agent Economy.

Available as an open extension for the A2A protocol, AP2 is designed to enable secure, reliable, and interoperable agent commerce for developers, merchants, and the payments industry. The protocol engineers trust into the system using verifiable digital credentials (VDCs), which are tamper-evident, cryptographically signed digital objects that serve as the building blocks of a transaction.

UCP: The common language for agentic commerce

While AP2 secures the transaction, the Universal Commerce Protocol (UCP) defines the building blocks for the entire shopping journey, from discovering and buying to post-purchase experiences. UCP provides a common language for platforms, agents, and businesses, allowing the diverse commerce ecosystem to interoperate through a single standard without the need for custom builds.

UCP seamlessly connects different systems using open industry standards, featuring built-in support for both the A2A and AP2 protocols. It empowers retailers to meet customers wherever they are, ensuring that businesses retain control of their own rules and remain the Merchant of Record with full ownership of the customer relationship.

Bringing it all together with ADK

Protocols need a solid foundation to run on. Enter the Agent Development Kit (ADK).

Technically not part of the A2Family, ADK is an open-source agent development framework that lets you build, debug, and deploy reliable AI agents at enterprise scale. Available in Python, TypeScript, Go, and Java, ADK helps you build production agents, not just prototypes. It connects everything together, allowing you to easily equip your agents with tools, integrate them with the A2A protocol, and scale them globally on your infrastructure of choice.

Google champions collaboration, transparency, and shared progress to build a better future for everyone through open technologies. We are thrilled to share these tools with you and cannot wait to see what we can build together.

What kind of multi-agent workflows are you planning to build with the A2Family? Let us know in the comments below or tag us on social media!

Read the whole story
StephaneDenis
129 days ago
reply
Saint-Hyacinthe, Quebec
Share this story
Delete

UML 2.5

1 Share
Debrauwer, Laurent [Lieu de publication non identifié] : Editions ENI, [2024] 1 ressource en ligne Texte intégral en ligne

Read the whole story
StephaneDenis
156 days ago
reply
Saint-Hyacinthe, Quebec
Share this story
Delete
Next Page of Stories