Sep 5, 2026 · robotics-middleware · lock-in · ros-2 · team-decisions
What Middleware Choice Actually Locks You Into
A middleware choice locks in message contracts, build tooling and hiring far more than the transport. Here is what ROS 2 and the alternatives each bind you to.
Choosing ROS 2 or HORUS locks in your message shapes, your build tooling and your hiring pool harder than it locks in the transport underneath. The transport is the part you could swap in a month; the message contracts, the launch layer and everyone's habits are what take a year. That flips when a part you have already bought has a driver in exactly one ecosystem, because then the ecosystem is the lock. The rest of this is for teams making the call once, early, who will be living inside it long after the reasons are forgotten.
Nobody sets out to be locked in. What happens is quieter. Someone picks a framework in the first week because every tutorial used it, and eighteen months later a sentence starts appearing in planning meetings: we cannot, because. We cannot put that cheaper depth camera on the robot, because the only driver anyone ships targets one stack. We cannot hire the candidate everyone liked, because they would need a full quarter before they were useful. We cannot move to the newer operating system on the units already in the field, because the version we build against stops being supported and the replacement renames half of what we depend on. None of these are bugs. Nothing is broken. The robot still works, the tests still pass, and the team is still shipping — and yet the list of things that are technically possible but practically off the table gets one line longer every month. The frustrating part is that nobody can point at the decision responsible, because the decision was made by someone who has since left, in a week when the goal was to get a wheel to turn.
What does a middleware choice actually lock you into?
A middleware choice locks in four things, and how fast messages move is not one of them. The first is the shape of your messages: field names, units, timestamps, and the assumptions two components quietly share about what a value means. The second is how programs get built, started and configured, which is where a framework spreads widest and where a migration hurts most. The third is your view of the running robot — the recorder, the plotting tool, the thing that shows you what happened in the second before the arm stopped short of the table. The fourth is who can join the team without a long ramp, which is a hiring decision you make years before you notice making it. All four outlive the reason you chose. Teams that regret a middleware pick almost never regret how the messages moved; they regret discovering how much of their own code turned out to be written in the framework's dialect rather than their own. That is the thing to weigh in week one, and it appears on no comparison page anywhere.
What is robotics middleware, and which parts of a robot does it own?
Robotics middleware is the layer that carries messages between the separate programs on a robot and decides what happens when one of them is slow, missing or dead. The products sold under that name bundle four separable things: the message layer itself, a way to start and stop many programs together, a way to watch and record what flows while the robot moves, and a catalogue of components other people already wrote. Only the first is what the word means. The other three are what you actually adopt. That distinction is the whole subject here, because the four have wildly different exit costs. Swapping the message layer underneath a stable set of message definitions is a contained job that one person can do. Swapping the launch and configuration layer touches every deployment, every test and every page of instructions you have written. Swapping the catalogue means writing by hand what you used to download. If you want the runtime mechanics in plain language, what middleware does inside a robot covers them; this post is about the bill that arrives afterwards.
What does middleware lock-in look like when it goes wrong?
Lock-in goes wrong as a meeting where three sensible ideas get ruled out in a row and nobody can name the line of code responsible. A hardware engineer proposes a different lidar that costs less and sees further, and the software lead says no, because the driver exists for one stack and writing another is a month nobody has. A manager asks why onboarding takes so long, and the honest answer is that the new person is not learning the robot, they are learning the framework the robot is written in. Someone raises the operating system upgrade, and the room goes quiet, because the distribution the fleet runs is heading for the end of its support window and the newer one moves things the codebase assumes. None of that reads as a technical failure. It reads as a team that has become cautious. The tell is the vocabulary: a locked-in team stops saying "how would we do that" and starts saying "we would have to change too much".
What are the actual options, and what does each one bind you to?
There are five families, and each one binds you somewhere different. A full framework, meaning ROS 2, binds you to a message vocabulary, a build tool, a packaging model and a catalogue — the widest commitment on the list, and the one that gives the most back. A single-machine message layer such as HORUS binds you to one computer and to living outside the ROS package set, in exchange for Rust, Python and C++ sharing the same shared-memory ring buffers so messages are not serialised between processes on that machine; it is open source under Apache-2.0. A general message library of the ZeroMQ or LCM sort binds you to nothing much and gives you nothing much: you define every message shape, supervision rule and debugging tool yourself, and you maintain them. A broker such as MQTT binds you to a hub-and-spoke shape that suits telemetry and suits nothing the robot waits on. Writing your own binds you to yourself, which is the most complete lock-in available and the one people underestimate most consistently.
How do the options compare on what they commit you to?
The commitment worth comparing is not the feature list but the assumption each option makes about the robot you are building.
| Option | Who it is for | What it assumes you know | When to pick it | When not to |
|---|---|---|---|---|
| ROS 2, full distribution | Teams whose value comes from existing drivers and stacks | Linux packaging, its build tool, its message model | Mapping, navigation or arm planning are on the roadmap | Nobody has time for the upkeep the stack expects |
| ROS 2, trimmed to a few packages | Teams wanting one or two ecosystem pieces | Exactly which packages you actually use | You need a planner or a driver, not the catalogue | You quietly rebuild the rest by hand anyway |
| HORUS | One-machine robots mixing Rust, Python and C++ | One of those languages, and life outside the ROS package set | Sensing, control and actuation share a computer | You need ROS drivers, or a graph spanning machines |
| A general message library | Teams happy defining their own contracts | Sockets, message shapes, process supervision | The components are few and the shapes are stable | You would rebuild tools that already exist |
| A broker for telemetry | Robots reporting to a dashboard or a fleet service | Topics, retained messages, running a broker | The traffic is status and human commands | Something on the robot waits on the answer |
| A vendor platform SDK | Teams who bought a robot rather than built one | Whatever model the vendor picked for you | The platform is the product | You expect to change hardware within two years |
| No middleware, one program | Early prototypes in a single language | Threads and queues in that language | The whole robot fits in one process | A second language or a crash boundary appears |
Two rows at once is normal and healthy: a message layer on the robot, a broker carrying status off it.
What gets locked in for a two-person team that does not for a twenty-person one?
For two people the binding lock is upkeep, and for twenty it is convention. A pair maintains everything they adopt out of the same evenings the robot needs, so every layer of configuration is a standing tax, and the framework that repays itself at twenty people can quietly consume a small team's entire capacity. The lock is that a stack too large to reinstall from a blank machine in one sitting becomes a stack you cannot afford to leave, because leaving requires the time you never had. At twenty people the lock is different and stronger: the choice is now embedded in the review habits, the internal tooling, the interview questions and four people's mental models. Changing it is a coordination problem rather than a coding one, and coordination problems in a shipping team lose to the roadmap every single quarter. The practical consequence is that a small team should optimise for the cost of leaving, and a large team should accept that it has already stopped optimising for that and choose for the next five years instead.
What does the hardware you buy lock in before any middleware does?
Hardware locks in more than middleware, and it locks in first. A sensor whose vendor ships one driver for one stack has made your middleware decision for you before anyone in the software team joined the conversation, and no architecture diagram undoes that. The same is true of a robot bought whole: a platform SDK is a middleware, whatever the vendor calls it, and it comes with the vendor's message shapes and the vendor's assumptions about who runs what where. The way to keep options open is unglamorous and takes an afternoon: take the parts list, search each item by name, and write down what the vendor actually ships — a maintained driver for one ecosystem, a plain library any language can call, or a protocol document and good luck. Parts that speak a plain protocol leave your choices open. Parts that ship one framework's driver and nothing else have chosen for you. This is worth doing before the architecture discussion, because it usually settles half of it.
What does a six-month deadline lock in that a three-year plan does not?
A six-month deadline locks in whatever is already running on someone's laptop, and the sooner a team admits that the better the six months go. Foundations cannot be changed and a demo delivered in the same window; the version of this decision that ends badly is always the one made under time pressure, halfway through, because someone read a comparison. If nothing is running yet, pick by what you refuse to write: a demo needing mapping and navigation takes the ecosystem that has them, rough edges included, while a demo needing one arm to pick one object takes the smallest thing that moves messages. Then stop evaluating. A three-year plan inverts every part of that. Over three years the upkeep cost of a heavy stack compounds, the catalogue you adopted gets a version bump you must follow, and the people who made the original choice leave. Choose for six months by what exists today; choose for three years by what you can still maintain when the person who set it up is gone.
What does this choice lock in if Python is the strongest language on the team?
Python locks you into a ceiling that arrives later than people expect and always in the same place. Sensor reading, coordination, planning at human speeds, dashboards, logging, the whole shape of a prototype: Python does all of it, and a team fluent in Python will move faster in Python than in a language they are still learning. The ceiling appears when a loop that holds a wheel speed or keeps an arm from hitting the table misses its moment because the language paused to tidy memory. That is not a reason to rewrite a robot. It is a reason to move one component. Which means the property worth protecting in a middleware choice is whether a compiled component can be dropped in beside the Python without a translation layer between them, because the alternative is a bridge somebody maintains forever. A stack where the fast part and the Python part read the same buffers keeps that door open; one where crossing languages means copying and converting closes it slowly, in a way nobody notices until the rewrite.
What do you give up by choosing the smaller option?
You give up the catalogue, and the catalogue is the actual product. No lidar driver waiting for you, no navigation stack, no arm planner, no simulator that already speaks your message types, no visualiser that finds your topics on its own, no recording tool with a decade of use behind it. You also give up the strangers who already had your problem: searching a ROS 2 error lands you on a thread from someone who solved it years ago, while searching an error from a smaller project lands you on the source. And you give up handover. A robot built on the standard stack can be picked up by a contractor, a new hire or a university team; a robot built on your own arrangement of parts can be maintained by the people who arranged it. What you get back is a smaller surface that one person can hold in their head, and no upkeep bill for a catalogue you never open. Whether that trade is good depends entirely on whether your hard problems are framework problems or robot problems.
When is ROS 2 the better choice?
ROS 2 is the better choice whenever the value of your robot comes from parts other people already built. A mobile base with a lidar that has to map a building and navigate it: take ROS 2 and do not think about it again, because that stack exists today and writing it is a multi-year project. An arm using an established motion planner. Anything you intend to test in a simulator that already speaks the same message types. Anything a student, a contractor or a new hire will touch, because the standard stack is the one they already know and hiring for it is a solved problem. Anything spanning two computers by design, where HORUS is not the answer at all, because shared memory stops at the edge of a machine. And anything whose loops run at a rate that makes the messaging cost irrelevant, which covers a great many working robots. In each of those cases the ecosystem is the deciding factor and nothing about the transport underneath comes close to outweighing it.
Can you always migrate later if the choice turns out wrong?
No, and here is why: the part that is easy to migrate is not the part you built. Swapping how messages travel is a contained job. What resists is everything written in the framework's vocabulary — the message definitions every component assumes, the launch and configuration layer that starts the robot, the recorded runs your tests replay, the plots, the deployment scripts, the internal documentation, and the shared understanding four engineers carry around. Teams who have done it describe the same shape: the compute survives and the scaffolding does not. There is also a timing trap. Migration is cheapest before the robot works, when there is nothing to preserve, and that is exactly when no team can justify the spend. By the time the pain is undeniable there is a fleet, a customer and a roadmap, and the honest calculation is that leaving costs a quarter you cannot take. So "we can migrate later" is true in principle and, in practice, is the sentence that decides you will not.
Is the lock-in really in the transport underneath?
Partly, but not the way you think. The transport does lock something in, and it is not what benchmarks discuss: it is the assumption about where your components live. A stack built for one machine assumes the sensor, the control loop and the actuator share a computer, and adding a second computer means adding a network layer that did not exist. A stack built for a distributed graph assumes discovery, network transports and peers that vanish, and carries that machinery whether or not your robot is spread out. Changing that assumption late is a genuine rewrite, because it changes what failure means in your system. What does not lock you in is the mechanism itself. Whether messages are copied, serialised or shared is an implementation detail you could change under a stable set of message definitions, and teams routinely do. So worry about the shape of the deployment the transport assumes, and stop worrying about the transport. One is an architectural commitment; the other is a component.
How do I decide this without guessing?
Write down three lists and let them decide, because the lists are less flattering than an argument. First: what you refuse to write yourself — drivers, mapping, navigation, planning, simulation. Second: how many computers will run your code when the robot is finished, not today. Third: how many languages the robot will contain, and which one holds the loop that must not miss. Then read them together. A long first list means take the ecosystem regardless of the other two, because rebuilding that list is a project of its own. A short first list plus one computer means take the smallest thing that moves messages and keep the time. Several computers means you want a networked message layer whether or not you want the catalogue attached to it. More than one language on one machine is the case where sharing memory across languages earns its place. When the lists disagree, weight the first one hardest — the catalogue is the only item here that takes years to replace, and reversing this decision later costs more than any of the alternatives cost up front.
A short version, by situation:
- If you are building a mobile base that must map and navigate a building -> ROS 2, because the stack you need already exists and rewriting it is a year you do not have.
- If your robot is one computer running sensing, control and actuation together -> a single-machine message layer, because none of the network machinery applies and all of its upkeep would.
- If a compiled control loop sits beside Python glue on the same board -> a stack where both read the same buffers, because a translation layer is a component you maintain forever.
- If your parts list contains a sensor with exactly one vendor driver -> whichever ecosystem that driver targets, because hardware decided before you did.
- If you are two people with evenings only -> whatever you can reinstall from a blank machine in one sitting, because upkeep is the constraint that actually binds you.
- If contractors, students or new hires will touch the code -> ROS 2, because the stack they already know is worth more than any property of a smaller one.
To compare options rather than symptoms, the HORUS Fit Framework lines them up on five things that are not numbers: ecosystem size, setup effort, team size fit, deployment target, and licence. For lock-in specifically, deployment target and ecosystem size do most of the work, and licence quietly removes several candidates before the discussion starts.
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.