by Sándor Dargó, Senior Engineer at Spotify. He writes about C++, software design, and code review practice at sandordargo.com.
Two words that make your brain do three jobs
Fix this.
Have you ever received a code review comment that said only that? I have, probably more times than I would like to admit. The thing about a comment like fix this is that it is not really about the code. By the time you have finished reading those two words, your brain has already done at least three different things.
You try to figure out what is wrong. You try to figure out why they did not tell you what is wrong. And if we are honest, there is a third one, because you have probably already started wondering whether you are an idiot, since someone had to leave a comment like that in the first place. That is a lot of work for two words.
So this is the subject I care about. Not formatting, tooling, or the technical mechanics of pull requests. The conversation between people. Even with AI writing more of our code and reviewing more of our pull requests, those conversations still matter. I think they matter more than ever, not less.
This practical deep dive is adapted from Sándor's Deep Engineering session, AI and the Future of Code Reviews. Here are the session slides.
Code reviews are about people, and I say that as someone who barely writes code by hand
I have to admit something. I do not think I have written a single line of code by hand since November last year. I might be exaggerating a little, because sometimes the AI really does not get it right and you go in and change it yourself. But even then, you are more likely to say, this is what I actually meant, use this. It is rare that I start writing code manually now, unless I am doing it for my own enjoyment.
Even in this environment, or maybe especially in this environment, code reviews are often a cause of stress and conflict between people. Done well, they do the opposite. They amplify learning, build trust, and improve the quality of what you ship.
That last one is more important than ever. Quality has always mattered, but I think we can already see it slipping with AI-assisted development. Use almost any software today and you may find yourself cursing more than before and saying, there is a new bug. Of course there were bugs before. But when one person can raise several pull requests in a day, quality does not automatically go up with the volume, at least not for the time being.
There are things I am deliberately not going to cover. Not formatting or style, not the technical parts. Even though I am a C++ developer, there is no C++ code anywhere in this article. I am not going to cover the business process either. What I want to cover is why we review at all, the emotional part of it, the language-agnostic parts, and what good and bad reviews actually do to a team.
A bad pattern that gets merged will be copied by your agents
Code reviews are three things at once. Quality assurance, knowledge sharing, and collaboration.
As quality assurance, a review is a safety net. Not the ultimate safety net, just one of them. It catches inconsistencies, maintains standards, and helps enforce architectural patterns. I said I would not talk about style, and I do not mean formatting here. I mean architectural style, which AI agents still find difficult to get right and difficult to review. They are getting better. They are not there yet.
The part I want to emphasize is catching issues before they get merged and spread through the code. If you accept something that goes against your architectural patterns, the next time an agent may do the same thing because it has already found an occurrence in the codebase. It sees a pattern, so it follows it.
That is why reviewing code manually matters now, probably more than ever. If anything bad goes in, it spreads. Even when you have to accept some technical debt, it is worth paying it off quickly.
As knowledge sharing, a review spreads understanding of the codebase, internal tools and APIs that not everyone knows about, and design decisions that people can discuss further. This matters most with new hires and in larger enterprises where people move between divisions. Good reviews help you avoid the situation where only one person understands this.
A review can also become a form of mentoring. You can even comment on your own code. You can help less experienced developers by explaining the why and the how instead of only the what, and a review is a good place to give feedback with empathy.
Pull requests are one way to review, and they are not the only way
When we think about code reviews, most of us think about having a good look at a pull request on GitHub. That is not the only way.
There are synchronous and asynchronous ways to review code. On the synchronous side, you have pair or mob programming and dedicated review meetings, which yes, people still hold. On the asynchronous side, you have pull requests, which is what almost everyone does.
Pair programming is real-time collaboration. Two people work behind the same screen, or behind different screens while sharing an editor, and they talk. The feedback loop is constant and immediate, which makes it useful for onboarding and complex problems. These days you will probably talk to an agent more often than another human being, but that does not replace onboarding, so I still think pair programming is a useful tool.
The review becomes a byproduct of the coding process. You ask questions, discuss decisions, correct course, and may end up with better code than you would have produced alone.
Mob programming is the extended version. One person types while any number of others guide, comment, ask questions, and raise concerns, and the roles rotate. It builds shared understanding, and it is a strong tool for exploratory work, large architectural decisions, and bringing a team to the same level.
At one of my previous workplaces, we had a six-month project that brought together people from different parts of the company. Their levels of expertise were very different. They had worked on different parts of the system, had different levels of seniority, and used different languages. It was a genuinely diverse team.
We did mob programming for two or three weeks at the start, and it helped close the gap between people. I think that was one of the most important factors in the success of that project.
A dedicated code review meeting is typically pre-scheduled and can include stakeholders from different teams. If you have critical code or an architectural decision, you might want to call one. The risk is anchoring bias. People are together and can convince themselves that one solution is right without giving themselves the mental freedom to explore other ideas.
Then there are pull requests, the most common style today. Some of you probably use none of the previous approaches and review only through pull requests, and that is fine. It is exactly why I wanted to show the other options.
Pull requests are written, which can lead to misunderstandings. They are not real-time at all. Sometimes you get a review in minutes, sometimes in hours, and sometimes it takes days or weeks. That asynchronicity gives you flexibility, and it can also slow you down.
Arguments against dedicated reviews, and why I do not buy them
When I gave an earlier version of this talk, a friend assumed I had used AI to make up the arguments against code reviews. I told him no, people actually claim these things. I would not have been able to make them up.
The first argument is that pair programming should replace code reviews. It is true that pairing catches issues earlier than a dedicated review and improves shared understanding. What it lacks is reflection time and broader input.
Think about it. You are sitting with someone else, the other person is writing code, and you think, I need more time to understand what they are doing. You do not have much time to reflect, and maybe you are afraid to ask for it. You do not want to slow the process down, and you do not want them to think you are slow. With an asynchronous review, you have the time to think.
There is another difference. When you pair, you are in a state of mind where you want to solve the problem. When you review, you deliberately look for flaws in the change and for issues the author may have missed. You are also less biased by the shared context you would have built while pairing.
Someone in the session pointed out that pairing on every task means double the cost per feature. I do not fully agree, because pairing catches problems early that you would otherwise fix later, and more people end up knowing more about the codebase. But I agree with the narrower version of the claim. It is not worth pairing on every simple task, because there it may simply double the cost.
The second argument is that code reviews slow us down. It is true. Reviews increase raise-to-merge latency, and that latency can lead to more merge conflicts. Both are real. But this is engineering, so it is a compromise. You can be fast now, skip the review, and merge quickly, and then have more bugs to fix later or even a rollback in production.
I have seen the extreme version of this. Senior engineers pressured less experienced people into approving their pull request, including someone who was not a coder but had rights on the repository, purely so it could be merged quickly. And it was merged quickly. It was shipped quickly. Most of the senior engineers on the team did not even know that pull request existed.
Then we had to roll back in production, and there were some unpleasant discussions with managers and QA. The time you invest in code reviews is worth investing. Asynchronous reviews still scale better than the alternatives, as long as you prioritize reviewing over writing, which I will come back to.
The third argument is that reviews do not catch bugs. That is also partly true. Reviews are not a substitute for testing, and AI reviewers are worse at this than you might expect, with high false-positive rates. They will get better. But reviews, including AI-assisted reviews, are often more effective at catching design flaws, naming problems, unnecessary complexity, and logic that is unclear to everyone except the person who just authored it.
Review feedback is more about understandability than correctness. You might catch a bug too. It is just not the main role.
I would also be careful about rejecting pull requests below a fixed coverage threshold. If there is a number people have to hit, they will find a way to hit it. If they do not want to write meaningful tests, the number will not make the tests meaningful. The reviewer still has an important role in looking at coverage and asking whether the tests actually prove anything.
All of these trade-offs are real. Yes, reviews slow you down. Yes, they are imperfect. They also prevent expensive mistakes, spread knowledge, and build shared ownership. You pay now or you pay later, and the later you pay, the more you pay.
AI has already changed how we review
A review is the last line of defense. It is essential for the long-term health of your codebase, and after the automation and a long CI pipeline, it is often the final human check before code gets merged, assuming your organization still has one.
AI helps, and I think it will help more. It is not enough yet. Human insight still catches what machines cannot, especially the context of a large project, architectural constraints, and historical knowledge that is not documented anywhere in the codebase.
AI has already changed how we review. As pull request volume grows, you cannot keep pace by reviewing every change manually, and that itself becomes a source of stress. A growing share of those pull requests are partly or fully AI-generated, and you can only hope that the author reviewed what was generated and understands what the code does.
Many teams, perhaps most, now have an AI reviewer involved in pull requests. It is not a replacement yet. The question is not whether AI changes code reviews. It already has.
Reviews also give you a fresh perspective, which matters even more once agents write the first draft. Author bias is real. When you write code, you miss your own mistakes, just as you can miss errors in a letter you wrote and then read back. Hand it to someone else and they may spot the mistake faster.
What is obvious to you, already inside the context of the change, may not be obvious to anyone else. Reviewers have to build their own mental model, and they may reach a different conclusion from that model. That difference is exactly the thing worth sharing in a review. They can question assumptions and point out edge cases you forgot.
You also get diverse insights because different specialties notice different things. One person cares about design patterns, another about readable modern code or API design, and they will each see something different. Different levels of experience focus on different angles too. A staff engineer may look at how your change interacts with the rest of the system, while a less experienced developer may read every line and care about the details. I have had genuinely good experiences with reviewers like that.
I am experimenting with a workflow I picked up from a conference talk. Different agents review the same change from different roles. One focuses only on new and changed APIs. One takes safety and security. Another takes readability, another design, and perhaps a fifth gathers the feedback and makes sure it holds together for whoever has to implement it.
The persona part is something I had only just started exploring when I gave the session, so I would treat it as an experiment rather than a settled workflow. Instead of only telling the agent to check whether the code follows modern C++ practice, you describe the kind of reviewer it should act as. You might tell it that it is a modern C++ engineer who cares deeply about current practice, then ask it to review from that perspective.
What I have already found is that if you do not give the agent proper repository-specific context, it produces garbage comments. It may recommend tools or libraries that are not available to you. The relevant instructions and context have to be documented in the workflow.
Which agents and skills you can use will often be decided by your organization. At work, we have an internal dashboard that states which agents and models are allowed, along with skills specific to our infrastructure that can look up internal repositories or crash analytics. That part will be specific to your organization.
What I would encourage is the experiment itself. Ask whatever agent you are allowed to use to focus on one specific part of the review at a time.
Reviewing AI-authored code is a different job
The author probably did not write every line. They accepted every line, hopefully after a thorough review of their own. Confident-looking code can hide a shallow understanding of what it does.
So why did you do it this way? is now a first-class review question rather than a nitpick. It can lead to an important discussion, and it can reveal that not much was considered because the code was generated quickly and the person wanted to move fast, often for perfectly valid reasons. As a reviewer, you increasingly verify intent, not just implementation.
That is also the answer to what we are actually reviewing. Syntax validity is mostly the compiler’s job. As a reviewer, you make sure the change matches the intention of the team and should be shipped at all, because every piece of code is a liability that someone has to maintain. Even when that someone is an agent, the agent has costs.
Then you make sure the architecture is right. Once something is in your codebase, an agent will recognize it as a pattern to follow, so you want as few bad examples there as possible. If you use bots, you can give them different tasks. Verify the syntax, verify that the code is modern, verify that edge cases are covered, and verify the architecture as long as your architecture is documented. The actual intent stays completely human.
📅 Upcoming workshop
C++20 Modules: A Gentle Hands-On Workshop
On September 30, join Lieven for a hands-on C++20 Modules workshop using CMake.
Most review failures come down to timing, tone, and scope
Feedback arrives too late. Reviews should happen while the details are still fresh, and that matters more when the author did not write everything alone but used an agent. You may have understood the generated code at that moment, but after three or four days, or a week, you will have forgotten some of it.
Reviews should also not block merging for too long, because late reviews lead to frustration and resistance. They produce a specific failure too. If you waited five days, or even three, and the reviewer says this looks fine, there is just one small thing you might want to fix, it is only a nit, there is a fair chance you will not fix it. You do not want to wait another day. You want to move on.
People focus on the nits because the details are easier. Do not get me wrong, the details matter. The problem is that when we focus on them, we often miss the bigger picture. Getting the details right is important, and the architectural decisions are more important still because they are harder to change once merged. Details can usually be updated later with less effort.
Written feedback has no tone of voice or body language, so the reader has to infer both. A message can sound aggressive or passive-aggressive even when that was not the intent, and sometimes it is aggressive. That does not only affect clarity. It affects trust and discourages open discussion.
We all know people, not necessarily in our own organization but in the developer community, who are simply jerks in code reviews. I do not think that is a good strategy in the long term or even the short term.
Another pitfall is reviewing the author instead of the code. Avoid language that feels personal or judgmental. It is not about the who, it is about the what. Do not blame. Help. We are all learning, and kindness scales better than harsh criticism.
Avoid bossy or commanding language too. Do not phrase feedback as an order. Invite collaboration rather than compliance. Even senior developers should stay humble and use softening words. Instead of saying change this, ask whether the author considered another approach. Not only because it is kinder, but because they may have considered it and concluded that their approach was better in this case.
Comments also fail when they have no priority. You receive a comment and do not know what to do with it. As a reviewer, make the intent understandable. Use a word or an emoji at the beginning. I usually mark whether something is a blocker that must be fixed, a have you considered where another approach may be better but I am not sure, a nitpick that the author can take or leave, or a question that is genuinely just a question about the assumptions behind the code.
Or leave a kudos. Have you ever received a comment like that? We send them in the team sometimes, and it feels good. If you get one on every pull request, it stops helping. But when you came up with something elegant, it feels good to have someone notice it.
Comments with no explanation miss the chance to teach or share reasoning. Some people will chase the reviewer and ask why. Not everyone will, because some people are too shy to ask. Without context, authors either comply blindly or push back blindly.
Commenting on everything is another failure. Feedback on every line is overwhelming and discouraging, and it creates the impression of rigid control, as though there is only one right way. Developers need some autonomy. Not every decision has to be perfectly optimal. There are parts of a codebase where it does, but most of the time there are several reasonable ways to achieve the same thing.
Poorly prepared pull requests are on the author. Do not share something that is not green yet, because that takes precious time from reviewers. Do a self-review first, whether or not you used an agent. Do not share a pull request that is too large, because you will wait longer for meaningful feedback than you would for two smaller ones. Do not mix unrelated changes, such as a refactoring, a bug fix, and a feature.
And share a description. If the pull request has to be large because you changed an API and had to update many files, give the reviewer an entry point. Tell them which file to open first and where the change begins. That helps a great deal.

Keeping reviews fast is mostly a team decision, not a personal one
A review-first policy helps. Review the pull requests in the queue before you create new ones, because the cost of waiting is usually higher on the other side than the cost of you reviewing a piece of code.
This only works if the team agrees together. If they do not, a few people will do most of the reviews and burn out. If the team agrees, lead developers go first and teach by example, reviewing before they move on to their own next pull request.
You can also reduce your own burden by asking an agent to do a first pass and surface likely problems, then reviewing those areas yourself. That brings up the new failure mode, which is noise. Too many pull requests and too many comments mean that many comments get ignored. Too many false positives from an AI reviewer can lead to fatigue and rubber-stamping.
I think we are still discovering how to cut through that. If you have good ideas, I am all ears. What I believe, and it slightly contradicts something I say later, is that concise comments are more likely to be acted upon. That is another reason not to let an agent comment directly on the pull request by itself. Run it for yourself first, then write the comments that matter.
Keep the review flowing. Give a first response quickly, even if it is only an emoji telling the author that you are looking at it, ideally within an hour of a green pull request being shared. If there is a lot of back and forth, do not play ping-pong. Call each other, then summarize the discussion in the pull request so everyone else knows the outcome.
Escalate to the team if the real problem is scope or size, and ask for help rather than doing it alone. If you have done all of that and the change still does not merge in a reasonable time, treat it as a process issue and bring it back to the team.
After three round trips, I would make the discussion synchronous. Too many comments on one pull request usually means there is no shared mental model of the problem, the codebase, or the language, and a pairing session will do more than another round.
If the same comments keep coming up with the same person, that is a coaching opportunity rather than a review. The other person may feel it too and be too intimidated to ask. So say it. I have noticed that I keep making the same comment every few pull requests. Let us talk about why I think this matters. Be proactive and be nice. We are all in this together.

Where the human job matters most
AI can flag style issues, duplication, and small refactors, but it does not understand team context. It can catch obvious bug patterns, but it is not good at weighing trade-offs and explaining them. It can generate alternative implementations and something like a learning plan, but it is not good at mentoring. It can comment quickly and at scale.
What it cannot do, and I do not see this changing in any reasonable time frame, is take responsibility. That is something we can and should do. Some people say AI can build trust. I think AI can build trust in a solution. It cannot build trust between people.

There is a related point from the discussion that I want to keep. Staff engineers have told me they do not want to ask a question in a code review only to receive an agent’s answer pasted back. They want a human discussion.
There is still value in writing your comments yourself and making the mental effort to understand what the other person was trying to do.
Action, Information, Reference
Let us diagnose a few comments.
Rename this.
Do not use magic numbers.
This is wrong.
Three things can be missing when a comment goes wrong. Action, is it clear what to do? Information, is it clear why it matters? Reference, is there something to learn from? AIR, if you want a mnemonic.
For action, phrase your feedback as a suggestion rather than a command. Use softening language such as consider this, perhaps you could, or could we do that? It encourages discussion instead of compliance.
For information, explain your reasoning clearly. It helps the author understand your intent and builds shared knowledge.
For reference, link to a style guide, internal documentation, an external document, or a relevant discussion. It justifies your feedback without starting a debate inside the pull request and encourages self-directed learning.
Take the first bad comment, rename this. It has no context, no reasoning, no learning opportunity, and it sounds too direct. An improved version would read:
Consider renaming this to something like
config. I first read it as the results, but it is the configuration object. Our style guide suggests clarity over brevity. See the choosing names section.
The first sentence gives the action, the second gives the information, and the third gives the reference.
That last part matters more than you might think. I have been in a situation where people did not even know the company had a style guide. That is not surprising. If you never share it in a code review, how would they find out?
The second example is do not use magic numbers. It is not that bad, but it could be better:
Consider replacing
42with a named constant. Unclear values are risky to change later. We recommend symbolic constants for readability. See C++ Core Guidelines ES.45.
After one of these talks, someone came up to me and said they did not know the C++ Core Guidelines existed. Thanks for sharing. The same thing happens in code reviews. What is obvious to you is not obvious to everyone.
The third example is this is wrong. That is clearly bad, and it could be attached to almost any line of code. A useful version would say:
Capture the return value of
eraseand use that. The current code dereferences an iterator aftererase, which invalidates it. cppreference documents the iterator invalidation rules for each container.
You do not need all three every time. You might think you do not want to write a short novel on every review, and you are right. Sometimes there is just a typo, and typo here is a perfectly good comment. Use the formula when a comment would otherwise teach nothing and there is a teaching opportunity. The goal is not longer comments. It is fewer useless ones.
If you receive a comment that lacks information or a reference, or where the action is unclear, say so. Ask back. Sorry, it is not clear what I should do. Can you phrase it differently? Or, why should I do that? I want to learn more. Can you point me to something?
You might think that takes a long time to write. It does not, because over time you build templates that you reuse and refine. And if nobody ever highlights a bad comment, bad comments will keep coming.
Five changes worth making this week
Code reviews are a tool and an investment in quality, clarity, and shared understanding. They are conversations between people, even now, in the age of AI-assisted development. How we communicate defines both the code we write and the teams we build.
So, five things. Encourage self-reviews in your team to catch the obvious before the code reaches anyone else. Pick one thing you personally want to improve about how you give feedback. Try the AIR formula in your next review where it makes sense. Talk with your team about your review culture if you think there is something to improve. And start using reviews as opportunities to teach and learn.
Code reviews do not just improve code. They improve coders.





