ROB-360 · Lesson 3 of 12 · 31 min
Software quality: coding standards, static analysis and code review
Robot software is unusually unforgiving. A null pointer in a web app produces an error page; in a robot it may leave a motor running. Defects also cost more the later they are found: a misread unit caught in code review costs minutes, the same defect found during field trials costs a day of travel and a frustrated customer, and found after an incident it can cost far more. This lesson covers the everyday practices that catch defects early and cheaply: coding standards, automatic formatting and linting, static analysis, and code review. Testing, the other half of quality, is the subject of lesson 4.
The cost of defects over time
Studies of software projects have long reported that the cost of fixing a defect grows the later it is found, with figures such as tenfold between design and testing often quoted. The exact multipliers vary widely and some classic numbers are disputed, but the direction is not in doubt for robots, where late defects require hardware time, test-site access and sometimes field visits. The practical lesson is to invest in the cheap early filters: clear requirements, readable code, automated checks on every commit and thoughtful review.
Worked problem
Layered filters
Suppose 100 defects are introduced in a release. A linter catches 15 % of those reaching it, static analysis 25 %, review 40 %, automated tests 50 % and field trials catch the rest that cause visible problems. How many defects reach the automated tests and how many reach field trials?
Show solution
- After the linter: \( 100 \times 0.85 = 85 \).
- After static analysis: \( 85 \times 0.75 \approx 63.8 \).
- After review: \( 63.8 \times 0.60 \approx 38.3 \) reach the automated tests.
- After tests: \( 38.3 \times 0.50 \approx 19 \) reach field trials.
Answer: About 38 defects reach the automated tests and about 19 reach field trials. Improving review from 40 % to 60 % catch would cut field defects to about 13, which is why review quality matters so much. (The catch rates here are illustrative.)
Coding standards
A coding standard is a written set of rules for how code is written in a project. Some rules are about style (naming, layout, comment format); these matter for readability but are best enforced by tools rather than argued about. Others are about safety and correctness: avoid raw owning pointers, initialize every variable, check every return value, never use floating-point equality for control decisions, state units in names. For C++ the C++ Core Guidelines, edited by Bjarne Stroustrup and Herb Sutter, are the most widely used foundation, and ROS 2 publishes its own code style guide built on them. For Python, PEP 8 covers style and PEP 484 type hints add checkable types. For safety-critical embedded C and C++, the MISRA guidelines and AUTOSAR C++14 rules, met in ROB-320, are much stricter and restrict the language to a subset that tools can analyze.
| Rule | Why it matters in robot code |
|---|---|
| Put units in names: speed_mps, angle_rad, timeout_s | Unit mix-ups are a classic integration failure |
| Prefer RAII and smart pointers over manual new and delete | Resources are released even when exceptions occur |
| Never compare floats with == | Rounding makes exact equality unreliable; use tolerances |
| Check and handle every error return | Silent failures leave actuators in unknown states |
| Avoid dynamic allocation in real-time loops | Allocation can block and break timing guarantees |
| Keep functions short with one purpose | Short functions are easier to review and test |
Formatters and linters
A formatter rewrites code into a standard layout automatically: clang-format for C++, black or ruff format for Python. Once a formatter runs on every commit, style debates disappear from review. A linter goes further, flagging suspicious constructs: unused variables, shadowed names, missing type hints, functions that are too long. ROS 2 packages commonly use the ament_lint family (ament_flake8, ament_cpplint, ament_uncrustify, ament_xmllint and others), which can run as part of the package's tests. Python projects increasingly use ruff, which combines linting and formatting very quickly, and mypy or pyright for type checking.
import ast, textwrap
# A tiny "linter": flag float equality comparisons and names without unit suffixes.
src = textwrap.dedent("""
def at_goal(dist, tol_m):
return dist == 0.0
speed = 1.2
timeout_s = 0.5
""")
tree = ast.parse(src)
units = ("_m", "_s", "_mps", "_rad", "_hz", "_a", "_v")
for node in ast.walk(tree):
if isinstance(node, ast.Compare) and any(isinstance(op, ast.Eq) for op in node.ops):
if any(isinstance(c, ast.Constant) and isinstance(c.value, float) for c in node.comparators):
print(f"line {node.lineno}: float compared with ==")
if isinstance(node, ast.Assign):
for t in node.targets:
if isinstance(t, ast.Name) and not t.id.endswith(units):
print(f"line {node.lineno}: '{t.id}' has no unit suffix")line 4: 'speed' has no unit suffix line 3: float compared with ==
Static analysis
Static analysis tools examine code without running it to find deeper problems: possible null dereferences, buffer overruns, use after free, data races, integer overflow, uninitialized reads, dead code and violations of coding rules. For C++, clang-tidy offers hundreds of checks, including the cppcoreguidelines and bugprone families; cppcheck is a lighter alternative; commercial tools such as Coverity, Polyspace and Parasoft add deeper analysis and certified rule sets for safety standards. Compiler warnings are the cheapest static analysis of all: building with -Wall -Wextra and treating warnings as errors in continuous integration catches many problems for free.
Dynamic analysis complements static analysis by instrumenting the running program: AddressSanitizer finds memory errors, ThreadSanitizer finds data races, UndefinedBehaviorSanitizer finds undefined behavior, and Valgrind finds leaks. These sanitizers are best run in continuous integration on the test suite, because they slow programs down too much for normal use.
Every static analyzer produces false positives, warnings about code that is actually fine. A tool that cries wolf is soon ignored, so teams tune the rule set, suppress specific warnings with a written justification next to the code, and require that new code introduce no new warnings. The ratio of real defects to warnings, the precision, is worth tracking.
Worked problem
Is the analyzer worth running?
An analyzer flags 120 warnings on a new module. Triage takes 4 minutes per warning, and 18 warnings turn out to be real defects. If each real defect would otherwise cost an average of 3 engineer-hours to find and fix later, is the triage worth it?
Show solution
- Triage time: \( 120 \times 4 = 480 \) minutes = 8 hours.
- Precision: \( 18 / 120 = 15 \) %.
- Later cost avoided: \( 18 \times 3 = 54 \) hours.
- Net saving: \( 54 - 8 = 46 \) hours, before counting fix time, which is needed either way.
Answer: Yes: about 46 engineer-hours saved even at 15 % precision. Tuning out noisy rules would raise precision and cut triage time further.
Code review
Code review means that every change is read by at least one other engineer before it is merged. Most teams do it through pull requests (or merge requests) on a hosting service, with automated checks running first so that reviewers spend their attention on what tools cannot judge: whether the change does what it claims, whether the design fits the architecture, whether edge cases and failure modes are handled, whether tests are adequate and whether the code will be understandable in a year.
Research and industry experience suggest that review is most effective when changes are small and reviewers are not rushed. Reviewing a few hundred lines at a time, for no more than about an hour, finds more defects per line than reviewing thousands of lines at once. Authors help by writing a clear description of what changed and why, linking the requirement or issue, and pointing out the parts they are least sure about. Reviewers help by commenting on the code rather than the person, distinguishing blocking issues from suggestions, and approving promptly when concerns are met.
| Reviewer question | Robot-specific angle |
|---|---|
| Are units, frames and timestamps correct? | Check REP 103 and REP 105 conventions and message stamps |
| What happens if this input is missing, late or wrong? | Stale sensor data, NaNs, dropped messages, empty point clouds |
| Can this block a real-time path? | Locks, allocation, logging or I/O inside control callbacks |
| Does this change any safety-relevant behavior? | Speed limits, stop distances, timeouts, e-stop handling |
| Is it tested at the right level? | Unit tests for logic, integration or simulation tests for behavior |
| Will this be understandable in a year? | Names, comments explaining why, links to requirements or ADRs |
Worked problem
Sizing review workload
A team of six merges about 2400 changed lines per week. Reviewers are effective at about 300 lines per hour. How many reviewer-hours per week are needed if every line gets one reviewer, and what if safety-relevant code (15 % of lines) gets two?
Show solution
- One review of everything: \( 2400 / 300 = 8 \) hours per week.
- Safety-relevant lines: \( 0.15 \times 2400 = 360 \), needing a second review of \( 360 / 300 = 1.2 \) hours.
- Total: about 9.2 hours per week, or about 1.5 hours per engineer.
Answer: About 9 reviewer-hours a week, roughly 1.5 hours per person. That is a small price compared with the cost of field defects, and keeping pull requests small keeps each session within the effective range.
Ownership, branching and change control
Quality also depends on how changes flow. Most robot teams use trunk-based development or short-lived feature branches merged frequently into a main branch that is always buildable, with protected branch rules requiring passing checks and approval before merge. CODEOWNERS files assign default reviewers for each directory, so that a change to the safety monitor automatically requests review from the safety lead. Release branches, tags and changelogs (lesson 7) give a controlled path from main to robots in the field. For safety-relevant code, change control may also require a documented impact analysis: which requirements and tests are affected, which hazards in the risk assessment might change.
Measuring quality without gaming it
Teams track metrics such as test coverage, static-analysis warnings, open defects, review turnaround, escaped defects (found after release) and mean time to restore service after a failure. Metrics are useful as trends and alarms but harmful as targets: a team pushed to raise coverage will write tests that execute code without checking it. The most telling metric for a robot team is usually escaped defects per release and their severity, because it measures what the customer actually experiences.
Worked problem
Escaped-defect rate
Release A had 4200 changed lines and 7 defects found in the field; release B had 6100 changed lines and 6 field defects. Compare escaped defects per thousand changed lines.
Show solution
- A: \( 7 / 4.2 \approx 1.67 \) per thousand lines.
- B: \( 6 / 6.1 \approx 0.98 \) per thousand lines.
- B improved by about 41 %, though both counts are small and the next release will show whether the trend is real.
Answer: About 1.7 against 1.0 escaped defects per thousand lines. Small counts are noisy, so watch the trend over several releases and weigh defects by severity.
Common mistakes
MistakeArguing about formatting in code review
InsteadRun a formatter automatically and spend review time on behavior and design.
MistakeIgnoring a static analyzer because it reports many false positives
InsteadTune the rule set, suppress with justification and block only new warnings.
MistakeSubmitting a 3000-line pull request for review
InsteadSplit changes into small, focused pull requests with clear descriptions.
MistakeApproving changes that touch stop distances without a safety reviewer
InsteadUse CODEOWNERS and change control so safety-relevant code gets the right reviewers.
MistakeSetting a coverage target and rewarding the team for hitting it
InsteadUse metrics as trends; track escaped defects and their severity.
Summary and key terms
- Defects cost more the later they are found, especially in robots, so cheap early filters pay off.
- Coding standards such as the C++ Core Guidelines, PEP 8 and MISRA set rules for safe, readable code.
- Formatters remove style debates; linters and type checkers flag suspicious constructs automatically.
- Static analysis finds deep defects without running code; sanitizers find memory errors and races at run time.
- Tune analyzers to keep precision high, suppress with justification and block new warnings.
- Code review of small changes, with a robot-specific checklist, catches design and edge-case problems tools miss.
- Branch protection, code owners and change control route changes properly, and metrics should guide rather than become targets.
- Coding standard
- A project's written rules for how code is written.
- Formatter
- A tool that rewrites code into a standard layout.
- Linter
- A tool that flags suspicious or non-conforming code.
- Static analysis
- Examining code without running it to find defects.
- Sanitizer
- A compiler-inserted run-time check for errors such as memory misuse or data races.
- False positive
- A warning about code that is actually correct.
- Pull request
- A proposed change submitted for automated checks and review before merging.
- CODEOWNERS
- A file assigning default reviewers to parts of a repository.
- Trunk-based development
- A workflow in which changes merge frequently into one always-buildable main branch.
- Escaped defect
- A defect found after release, by customers or in the field.
Practice problems
Work each problem on paper first, then reveal the answer to check.
Name a C++ guideline set widely used as a coding standard foundation.
Show answer
The C++ Core Guidelines.
Which tool family lints ROS 2 packages?
Show answer
ament_lint (ament_flake8, ament_cpplint and others).
What does ThreadSanitizer find?
Show answer
Data races.
Why should floats not be compared with ==?
Show answer
Rounding makes exact equality unreliable.
What is analyzer precision?
Show answer
The fraction of warnings that are real defects.
About how many lines per hour can a reviewer cover effectively?
Show answer
A few hundred.
What does a CODEOWNERS file do?
Show answer
Assigns default reviewers to directories or files.
Name one robot-specific review question.
Show answer
For example: what happens if this sensor input is late or missing?
Why is coverage a poor target?
Show answer
Teams can raise it with tests that execute code without checking it.
What metric best reflects customer experience?
Show answer
Escaped defects and their severity.
Lesson quiz · 5 questions
Score 60% or more to complete this lesson.
Create a free account to save your progress, take quizzes and earn a certificate.