Sep 5, 2026 · code-review · ai-generated-code · robot-safety · vibe-coding
How to Review Robot Code You Didn't Write
Review robot code you did not write by running it under supervision in a simulator first; reading line by line finds style, not what breaks hardware.
Review robot code you did not write by running it under supervision in a simulator first, not by reading every line and hoping. Reading finds structure and naming; running finds the behaviour that breaks hardware. The order flips only when the machine cannot hurt anything; what you can see while code runs depends on whether it sits on a ROS 2 graph, on HORUS, or in one script. The rest of this post is for anyone holding a repository they did not write — generated, inherited, or handed over — that is about to drive something physical.
An assistant wrote most of it over a weekend, or a graduating student left it behind, or a contractor delivered it and moved on. It runs. That is the confusing part. You start it and something moves, so the code is clearly not nonsense, but you could not say which parts matter, which parts are dead, or what happens if the camera stops sending. There are functions that look important and are never called. There is a number written in three different places, and you do not know which one is the real one. There are comments that describe a plan rather than the code beneath them. Nobody who wrote it is available to ask, and the parts that worry you most are exactly the parts you understand least: the loop that keeps an arm from hitting the table, the branch that runs when a reading is missing, the thing that is supposed to happen when the connection drops. Meanwhile somebody wants a demonstration on Thursday, and the machine costs more than your car.
How should you review robot code you did not write?
Run it under conditions where it cannot do damage, and review what you observe rather than what you read. Software review habits from web work assume the worst outcome is a bad response to a user; here the worst outcome is a machine moving in a direction nobody predicted while a person stands next to it. So the order changes. First, find how the machine stops and prove that path works. Second, run the whole thing in a simulator or with the motors unpowered, and watch what actually happens rather than what the code claims. Third, read only the parts that surprised you, because surprise is a far better guide to danger than any reading order. Fourth, put limits around the code before trusting a single line of it — a boundary the machine cannot cross regardless of what the logic decides. Only then does line-by-line reading pay for itself, and by that point you know which lines deserve the attention. A review that begins with the first file and works downwards nearly always runs out of time before it reaches the part that matters.
What is a robot code review actually checking for?
A robot code review checks four things, and only one of them resembles ordinary software review. The first is whether the machine stops: what happens on a crash, on a lost connection, on a command that never arrives, and whether the motors go quiet in all three cases. The second is whether the machine's beliefs match the world: which way an axis points, what units a sensor reports, whether a reading arriving late is treated as a fresh reading. The third is timing behaviour: whether the part that must run on schedule can be delayed by something slower sharing the same process, which shows up not as an error but as a machine that stutters. The fourth is the ordinary one — is the logic correct, is the structure clear, is anything unused. Most reviewers spend their whole review on the fourth because it is the one they know how to do. The first three are where the expensive failures live, and none of them are visible in a code diff.
What do people try first with unfamiliar robot code, and why does it fail?
Most people start by reading the repository from the top, and it fails for a simple reason: robot behaviour lives in the interaction between parts, not inside them. You can read every function in a file and understand each one perfectly while remaining unable to answer the only question that matters, which is what the machine does when the camera stalls for a moment while the wheels are turning. The second thing people try is asking an assistant to explain the code, which produces a fluent summary of what the code appears to intend and quietly skips the places where intention and behaviour differ — and those places are the bugs. The third attempt is running it on the real machine, carefully, on the theory that careful will be enough. It sometimes is. When it is not, the review ends with a bent part and a conversation with a supervisor. Each of these fails the same way: they treat unfamiliar code as a text to be understood rather than a system to be observed, so the surprises arrive on hardware instead of on a screen.
What are your real options for getting unfamiliar robot code under control?
There are seven, and the honest list mixes approaches with foundations, because what a review can see depends on where the code already lives. Reading line by line, which suits small code and rarely scales. Running everything in a simulator, which is the default answer for anything with mass. Watching a live system through the ecosystem's own tools, which is where ROS 2 is strong: recording a run and replaying it at a desk turns a lab-only bug into a desk-side one, and a middleware such as HORUS gives the same visibility on one board for Rust, Python and C++ sharing shared-memory ring buffers, so the messages between languages are inspectable rather than hidden inside one process. Adding a separate supervisor that limits what the machine can do regardless of the code. Rewriting the smallest risky piece so that at least one part is one you can explain. Handing it to somebody who knows the machine class. And doing nothing but logging heavily, which is a real option when the machine is harmless and your time is not.
Which review approach fits which situation?
The approach that fits depends on how much the machine can hurt, and how much of the code you will still own next month.
| Option | Who it is for | What it assumes you know | When to pick it | When not to |
|---|---|---|---|---|
| Reading it line by line | Reviewers of small code with time to spare | The language, and the machine the code drives | The whole thing is a few files and one loop | Behaviour depends on timing between separate parts |
| Running it in a simulator | Anyone who cannot risk the hardware | How to start the code with a fake machine underneath | The machine is heavy, expensive or shared | The suspect part is a driver the simulator replaces |
| Watching a live ROS 2 system | Reviewers of code already built on ROS 2 | The inspection and recording tools | The code is split into parts that publish messages | The interesting behaviour hides inside one part |
| HORUS | Reviewers of one-board code mixing Rust, Python and C++ | One of those languages, and life outside the ROS package set | You are moving the timing-critical part somewhere observable | You need ROS drivers, or the code spans machines |
| Adding an independent supervisor | Anyone who must run code before understanding it | What too fast, too far and too long mean for this machine | The demonstration is close and the review is not finished | The worrying behaviour is subtle rather than extreme |
| Rewriting the smallest risky part | Reviewers who will own the code afterwards | That part's job well enough to restate it | The piece is small and its purpose is clear | You cannot yet say why the original behaves as it does |
| Logging heavily and watching | Owners of harmless machines and short deadlines | Where to put the log lines | Nothing can be damaged by a bad run | A single bad run costs hardware or a person |
Most real reviews use three of these rows at once, and the supervisor row belongs in almost every one of them.
What if an assistant wrote the code rather than a person?
Treat generated code as confident in tone, sensible in shape, and wrong in exactly the details that matter, because that is the pattern. Assistants produce robot code that looks like robot code: sensible file names, a loop with the right shape, comments that read well. The errors cluster in places where the code had to know something about your machine that no assistant could know. Which direction is positive on that joint. What the actual travel limit is. Whether the sensor reports in the units the code assumes. What should happen when a reading does not arrive — generated code frequently carries on with the last value, silently, which is the behaviour that drives an arm into a table. Read the boundaries rather than the middle: every place the code meets hardware, every place it handles a missing input, every constant that encodes a physical fact. The middle, where the logic sits, is usually the part generated code gets right. What vibe coders should know before their code controls hardware covers the same ground from the writing side rather than the reviewing side.
What if the machine is already built and you cannot take it apart?
Review from the outside in, and start by taking away the machine's ability to hurt you before you understand a single line. Remove the load: lift the wheels off the ground, unbolt the tool from the arm, put the machine on blocks, run with reduced power. Then use whatever the machine already offers as a limit — a current cap, a travel limit, a speed setting in firmware — and set every one of them tighter than the task needs. These limits sit below the code and hold even when the logic is wrong, which is exactly the property you want while reviewing logic you do not trust. Only after that, run the code and watch. If the machine is genuinely irreplaceable and cannot be de-risked physically, build the smallest possible simulator instead: something that reads the same commands and reports a plausible position is enough to expose an unbounded loop or a command that grows without limit. A crude simulator that exists beats an accurate one that stays on a to-do list.
What if you have an afternoon rather than a week?
Spend the afternoon on the stopping path and the limits, and leave everything else unreviewed on purpose. In a few hours you can find how a stop command reaches the motors, trigger it deliberately, confirm the machine goes quiet, and then set every limit the hardware offers below what the task needs. That is not a complete review and does not pretend to be. It is the difference between a bad run that wastes an afternoon and a bad run that ends a project. Write down what you did not check, in the repository, as a list rather than a memory — the next person will read it, and so will you in three weeks when you have forgotten. Then run the code once, slowly, with somebody's hand on the switch, and note every moment that surprised you. Those notes become the actual review when time appears. A time-boxed review is only dangerous when nobody records how shallow it was, because a shallow review that is described honestly still protects the machine.
What if you cannot read the language it is written in?
Review the behaviour rather than the text, which is harder but far from impossible. If the repository is C++ and you write Python, you can still find where the program starts, what it reads, what it writes, and where it talks to hardware — those are structural questions that survive not knowing the idioms. You can still run it, watch what it does, and record what flowed between the parts. A stack that carries declared message types helps here more than anywhere else, because the messages describe the system in a form you can read even when the implementation is opaque. Ask an assistant for a map rather than an explanation: which files talk to hardware, where the loop is, what runs on startup. Maps generated this way are reliable in a way that summaries of intent are not. And be honest about the boundary of your review afterwards. Saying you checked how the machine stops but cannot vouch for the control mathematics is useful information. Implying a full review you could not perform is how a machine ends up trusted for the wrong reasons.
What do you give up by reviewing this way?
You give up the comfort of having read everything, and that loss is real rather than rhetorical. A behaviour-first review leaves whole regions of the repository unexamined, including some that will bite later: a rarely taken branch, an error path that has never run, a configuration file nobody opens until deployment day. You give up the ability to say the code is correct, and can only say it behaved acceptably in the situations you produced. You give up finding the elegant refactor, since the review never builds a complete mental model of the design. And you spend time on setup — a simulator, a test rig, a way to record a run — that produces nothing a manager can see on the day you do it. What you get in exchange is that the failures you did not predict happen where nothing breaks. For an inherited machine that is the correct trade, but it is a trade, and pretending otherwise leads teams to over-trust reviews that were never designed to prove correctness.
When is ROS 2 the better choice?
ROS 2 is the better choice whenever the code you inherited is already built on it, and that is not a small case — it covers most robot code you are handed. Reviewing a ROS 2 system means you can list what is running, watch messages while the machine moves, record a session and replay it at a desk, and see the machine's own picture of the room without adding a line of code. Rebuilding that visibility elsewhere costs weeks that a review does not have. ROS 2 is also the better choice when the code spans several computers, since HORUS is a single-machine middleware and its shared memory stops at the edge of one computer, so a review of a distributed system needs tooling that crosses the network. And it is the right answer when the code leans on catalogue parts — mapping, navigation, a planner — because reviewing your own use of a well-known component is a much smaller job than reviewing a private implementation of the same idea.
Does a code review catch the bugs that actually hurt a robot?
No, and here is why: the failures that damage machines are usually not wrong lines, they are missing ones. Nothing in a diff shows the case nobody handled — the sensor that stops reporting rather than reporting badly, the command that arrives twice, the operator who starts the program while the arm is already moving. Reading looks for wrongness, and absence is invisible to it. This is why the useful review is a list of situations rather than a pass over files. Write down what the machine should do when a reading is missing, when the connection drops, when a command contradicts the current state, when a limit is reached, when power returns after a cut. Then find each answer in the code, and note every question the code does not answer at all. That list is worth more than any amount of reading, and it produces a review a reviewer can hand to somebody else. It also survives the code changing, which a line-by-line review does not.
Can an assistant review the robot code it wrote?
Partly, but not the way you think. An assistant is useful for the mechanical half of this work, and genuinely good at it: mapping which file talks to hardware, listing every place a value is written, finding the branches that handle a missing reading, restating a function in plain terms so you can check it against what you expected. What it cannot do is know your machine. It does not know the arm's real travel, which way the encoder counts, that the third motor was replaced with a different model, or that the table is closer than the drawing says. Those are the facts that decide whether code is safe, and they exist only in the room. There is also a subtler problem: an assistant reviewing its own output tends to defend the reasoning it produced, so it explains why the code is correct rather than looking for the case that breaks it. Ask for lists and maps, not verdicts. Whether to trust generated robot code goes further into where that trust is safe.
How do you decide what to review and what to rewrite?
Sort every part of the code by what it can damage and by whether you can explain what it does, then act on the four groups that produces. Something dangerous that you can explain gets reviewed carefully and kept. Something dangerous that you cannot explain gets rewritten, however good it looks, because code you cannot explain cannot be maintained and will be edited under pressure by somebody who understands it even less. Something harmless that you can explain gets left alone. Something harmless that you cannot explain gets a note and a date, and no more time than that. Most people invert this and start rewriting the parts they find ugly, which are usually harmless and understood. Beware the third group in disguise: a part that looks harmless because it does not touch a motor, but decides something a motor obeys. Trace outwards from the actuators, not inwards from the file list. If you are choosing what to build on next rather than what to keep, whether ROS 2 fits a fixed-deadline project is the companion question.
A short version, by situation:
- If you cannot say how the machine stops -> stop reviewing and find that path, because nothing else you learn will protect the hardware.
- If the machine is heavy or expensive -> run everything in a simulator first, because a wrong guess on hardware costs more than the simulator ever will.
- If an assistant wrote the code -> read the boundaries and the constants, because the middle is usually right and the edges usually are not.
- If you have one afternoon -> limits and the stopping path only, because a shallow review that is honest still protects the machine.
- If you will own this code next year -> rewrite what you cannot explain, because unexplainable code gets edited under pressure eventually.
To compare the ground the code sits on rather than the code itself, the HORUS Fit Framework lines up options on five things that are not numbers: ecosystem size, setup effort, team size fit, deployment target, and licence. For a review, deployment target and ecosystem size matter most, since they decide what you can watch while the machine runs.
HORUS is open source under Apache-2.0 and the repository is linked below. Star it so it is in your list when you start building.