Henki ROS 2 Best Practices repository logo

ROS 2 Best Practices

Good architecture and clean coding practices are fundamental for robotics projects to scale and evolve quickly in response to rapidly changing requirements. A messy codebase and short-term shortcuts may deliver fast results at the beginning, but as the project progresses, implementing new changes becomes increasingly time-consuming and carries a higher risk of breaking existing functionality.

ROS 2 best practices are architectural and development guidelines that improve scalability, maintainability, testing, performance, and long-term sustainability of robotics software built with ROS 2.

Currently, information about best practices in ROS 2 is scattered. As a result, many companies often end up compiling their own internal guidelines.

ROS projects usually face similar questions:

  • What are the best ROS 2 practices?
  • How can these best practices be enforced in projects?
  • How can AI coding agents be guided to comply with these best practices?

Announcing the Henki ROS 2 Best Practices

To address the questions above, we decided to write down our own ROS 2 best practices at Henki Robotics – the guidelines we have been gathering and following over the many years we have been working with ROS 2. As an innovative step, these practices can be also very easily integrated with AI coding agents, enabling consistent code quality. 

Check out the full list of the best practices in our GitHub repository: Henki ROS 2 Best Practices.

ROS 2 Best Practices

Here is a summary of our top picks from the best practices described in the repository, which can quickly improve your project architecture and design.

  • Nodes: Follow the single-responsibility principle when writing ROS 2 nodes, and separate ROS 2 communication logic from core application logic.
  • Launch files: There has been recent discussion about the preferred launch format. Prefer XML, as the current Python launch files were not intended to be used as a launching system front-end.
  • Parameters and Configuration files: Define node parameters in parameter files instead of hard-coding them in nodes or launch files.
  • Logging: Start using clean logging practices from the very beginning. Use the correct log levels and avoid log spam.
  • Message Interfaces: Use existing ROS 2 message interfaces when they fit your data. Otherwise, create custom message interfaces with a low threshold. Do not use the deprecated primitive type interfaces such as Bool, String and Float32 from std_msgs.
  • Actions and Services: Use services only for fast-executing tasks (less than a second) and actions for tasks that take time, may fail for different reasons, or should be cancellable.
  • Code style: ROS 2 already defines excellent code style guidelines. Summary: Google style for C++ (with modifications) and PEP8 for Python.
  • Performance: Use C++ for high-bandwidth processing instead of Python. Use composable nodes to avoid passing large data over DDS.
  • Executors and Callback Groups: Use MultiThreadedExecutors only when absolutely necessary, as they can add performance overhead. Single-threaded applications are often cleaner and easier to test.
  • Dependencies: Use the default ROS style with rosdep to handle all package dependencies. rosdep isn’t perfect: it is not possible to choose the dependency versions, and not all libraries are available through it. Follow our tips for using requirements.txt and .repos files.
  • Documentation: At a minimum, provide package-level documentation describing the purpose of the package and all exposed topics, services, actions and parameters.
  • Testing: Aim for high test coverage. Test core application logic with unit tests at a minimum. Test ROS 2 communication logic as part of integration tests, not unit tests.

More best practices and detailed explanations for each point are available in the main Henki ROS 2 Best Practices repository.

Enforcing the Best Practices

Tests and code style are usually straightforward to enforce in a project by integrating automatic code coverage, linting, and formatting tools into the Continuous Integration (CI) pipelines.

However, enforcing best practices is less direct – it’s largely a matter of design, architecture, and writing guidelines. In practice, best practices are often enforced during code reviews. Knowing the best practices is one thing; ensuring that a project actually follows them is another.

For example, if a new developer joins an open-source ROS 2 project, they might hit roadblocks during the review process when a maintainer requests changes to ensure message interfaces are used correctly or that the executor choice is appropriate. Without clearly documented guidelines, new developers have no reference for writing good ROS 2 code. This responsibility typically falls on more experienced developers.

But how can an independent developer ensure best practices in their own project? Or how can a robotics startup write efficient, maintainable code from the very beginning?

This reliance on experienced developers to enforce best practices is beginning to shift as AI gradually starts to take over code writing.

Integration Of The Best Practices With AI Agents

AI tools are becoming increasingly capable of writing code, which raises an important question: what is the role of software developers in the AI era?

It will shift more toward ideas, design, and architecture. Code quality remains critical – we still need to understand and review the code AI produces. Developers provide the instructions for how the code should be structured and what requirements it must meet.

This is exactly the goal of Henki ROS 2 Best Practices. It’s not only a set of guidelines for humans, but it can also be directly integrated with AI agents, enabling them to write code according to these best practices while also helping us review that existing code follows these practices.

Example Package Generation Using Best Practices

In our best practices repository, we included an example demonstrating how AI-generated code quality improves when these best practices are applied. While the example is intentionally simple, the same principles scale to larger packages and projects.

We used Claude Code (claude-opus-4-6) to complete the following task, both before and after applying our best practices:

Create me a new ROS 2 package with a Python node that subscribes to /odom topic and publishes to a single topic the robot speed in km/h and mph.

Before Applying the Best Practices

By default, before integrating any of our best practices in Claude’s workflow, the generated node and ROS 2 project structure are fairly standard.

Project structure before applying ROS 2 best practices
Project structure generated by Claude without ROS 2 best practices

The project includes a basic speed_monitor_node.py, with the necessary ROS 2 package files, such as package.xml and setup.py.

Example ROS 2 node code before applying ROS 2 best practices
ROS 2 node generated by Claude without ROS 2 best practices

The generated Node is also pretty straightforward: It creates a subscription to the /odom topic, and publishes the robot speed to the /speed topic using Float64MultiArray as the message type. The core application logic, the speed calculation, happens directly inside the odometry callback.

After Applying the Best Practices

After cloning the henki_ros2_best_practices repository, we added the following text to the CLAUDE.md file to provide instructions for our coding agent:

Always read and follow the best practices defined in:
- henki_ros2_best_practices/henki_ros2_best_practices.md

When asking Claude to generate the package again with the exact same prompt, the improvements in the generated code were immediately noticeable.

Project structure after applying ROS 2 best practices
Claude generated project structure with ROS 2 best practices applied

The package structure is now significantly different from what we saw previously. Instead of a single speed_monitor package, there are two packages: speed_reporter and speed_reporter_msgs. Claude generated a new custom message interface, RobotSpeed.msg, which describes the robot speed data much more clearly than the deprecated Float64MultiArray format.

In addition, the speed_reporter package now includes:

  • Configuration and launch files.
  • Documentation of the Node API in the README.md file.
  • Core application logic separated into speed_converter.py, decoupled from the ROS 2 node communication layer.
  • Unit tests that target only the core application logic.
Example ROS 2 node code after applying ROS 2 best practices
Claude generated node with ROS 2 best practices applied
Example application code after applying ROS 2 best practices
When best practices were applied, Claude split the core application logic into its own class

Looking at the code, the differences compared to the previously generated version are clear:

  • Topics and QoS settings are defined in parameter files, allowing end users to modify them through configuration changes rather than code edits or remaps in the launch file.
  • The core application logic – the speed conversion – has been extracted into its own class. This makes it straightforward to test the logic in unit tests without involving the ROS 2 communication layer. The benefits become even more apparent as complexity grows. Imagine, for example, having core autonomous planning or SLAM logic fully separated from ROS. This whole topic would deserve a blog post of its own.
  • Logger was previously printing an info message at the end of every odometry callback. If odometry is published at 30hz, that results in 30 log messages per second. With the best practices applied, Claude now used throttled log, to print the info message only once per second to avoid log spam.

The benefits of applying these best practices are already visible in a small example like this. As project size and complexity increase, their importance only grows.

All the example code is available in our Agentic Examples repository.

Summary

Scalable and maintainable ROS 2 systems require clear architecture and consistent best practices. Henki ROS 2 Best Practices provide a structured foundation for both developers and AI coding agents, improving code quality and enforceability.

As software developers, our role is shifting from manually writing code to defining how code should be written, ensuring it follows established best practices.