Sep 5, 2026 · build-vs-buy · robot-software · team-decisions · ros2
Build vs Buy for Robot Software: A Decision Guide
Adopt a stack and build only the layer that is your product, because the pieces you would write first are the ones no customer will ever notice.
Adopt an existing robot stack and build only the layer that is your product, because writing your own middleware costs a year nobody funds. The parts you would write first, meaning message passing, launching, recording and replay, are exactly the parts customers never see, and ROS 2 or a focused middleware such as HORUS already ship them. That flips when the timing of the motion is itself the product. The rest of this post is for a team that has to make this call this month and then defend it to whoever pays for it.
The meeting keeps ending the same way. Somebody says the sensible thing, which is that you are not in the business of writing middleware and should use the standard framework everybody knows. Somebody else says the other sensible thing, which is that most of last month went into fighting the framework rather than improving the robot. Both of you are right, which is why the meeting keeps happening.
Meanwhile the symptoms pile up. A new engineer took four days to get the robot running from a clean checkout and nobody can say exactly why. A demo failed on a Thursday afternoon and the recording of it does not exist. There is a script in the repository that began life as a workaround and is now the thing every launch depends on, and precisely one person understands it. Somebody has quietly started writing a small library that does message passing properly this time, on evenings, without telling anyone.
Underneath all of it sits a question nobody wants to say out loud, because saying it commits you to answering it: are we using this thing, or are we slowly replacing it?
Should you build your own robot software or adopt an existing stack?
Adopt an existing stack for everything except the one layer that makes your robot different from every other robot. The build-versus-buy question sounds like an engineering question and is almost always a calendar question, because whatever you write you also maintain, document, debug at midnight and explain to the next person you hire.
Teams that end up with a homegrown stack rarely set out to build one. They adopt something, hit a wall in week three, write a small shim to get past the wall, and eighteen months later the shim has a configuration format, a logging layer and a maintainer. Nobody ever decided to build a middleware. It accreted, one reasonable Tuesday at a time.
The honest test is whether a customer would notice the difference. Nobody buys a warehouse robot because the message transport is elegant. They buy it because the machine finds the pallet, does not clip the shelving, and still works on a bad-network Tuesday. Spend your build budget exactly there, and adopt everything upstream of it. The layer worth writing yourself is the layer you would put in the pitch.
What does buying actually mean when most robot software is free?
Buying, in robotics, almost never means paying money. It means adopting somebody else's decisions and paying the bill in learning time, constraints and dependency rather than in invoices. A free framework still charges you: a week of setup, a mental model the whole team has to carry, a release cadence you do not control, and a set of assumptions baked into how processes talk to each other.
That is why "it is open source, so it is free" is a misleading sentence in a planning meeting. The cost is entirely real. It is simply denominated in engineer-weeks instead of currency. The comparison worth making is not price against price. It is engineer-weeks spent learning something that already works against engineer-weeks spent writing, then owning, the same capability forever.
There is a third bucket teams forget: things you genuinely pay cash for. A supported commercial platform, a certified real-time kernel, the SDK that came bundled with the arm. Those arrive with invoices and also with a phone number you can call at two in the morning, which turns out to be worth more than most teams expect the first time a shipped machine stops moving and a customer is watching.
What are the real options between writing everything and adopting everything?
There are about six, and the interesting ones sit in the middle rather than at the ends. At one extreme, a single program with no middleware at all: one process, one language, function calls instead of messages. At the other extreme, writing your own transport, discovery, tooling and recording from nothing. In between sit the ecosystem option, ROS 2, which brings drivers, visualisation, recording and a hiring pool; the focused option, a middleware such as HORUS that moves messages between Rust, Python and C++ processes through shared memory on one machine and leaves the rest of the robot to you; the vendor option, whatever SDK arrived with the arm or the base; and the commercial option, a supported platform with a contract behind it.
Most teams treat this list as a binary and then feel trapped by the binary they invented. It is not one. The common shipping arrangement is two of these at once: an ecosystem carrying perception, drivers and tooling, plus something small and fast carrying the handful of processes that share a control loop. Choosing is not marrying.
How do the main options compare against each other?
The comparison comes down to what each option assumes you already know and how much of the robot it expects you to write yourself. Read the middle two columns first, because that is where teams misjudge the cost.
| Option | Who it is for | What it assumes you know | When to pick it | When not to |
|---|---|---|---|---|
| One program, no middleware | One or two people, one machine, one language | Ordinary application programming | The whole robot fits in a design you can hold in your head | Once several processes or several languages appear |
| ROS 2 | Teams that need perception, navigation and drivers they did not write | Publish and subscribe, launch files, a Linux workstation | The hard part is the task, the sensors or the integration | The whole product is a tightly timed loop on one machine |
| HORUS | Small teams whose processes share one machine and one control loop | Rust, Python or C++, and what your loop must not miss | Timing between processes is part of what you sell | You need mapping, planning, drivers and a large ecosystem |
| Vendor SDK for your robot | Teams building on a bought arm, base or humanoid | The vendor's own API and its assumptions | The SDK already does what the product needs | The SDK cannot express your behaviour and cannot be extended |
| Supported commercial platform | Companies shipping into regulated or unreachable settings | Procurement, and how to run a support relationship | Certification paperwork and a phone number matter more than source | Budget is people rather than licences |
| Your own middleware | Companies whose product is the stack itself | Transport, discovery, tooling and years of edge cases | You are selling infrastructure to other robot builders | You are selling a robot that does a job |
No row is the winner. The right row is the one whose "when not to" column does not describe your project.
Which option fits a two-person team compared with a funded engineering group?
A two-person team should adopt more than feels comfortable, and a funded group should still own only one layer. The constraint for the smallest teams is not talent and not money. It is that every piece of infrastructure you write is a piece only one person understands, and that person eventually takes a holiday, gets sick, or leaves.
At two people, the maintenance burden of anything homegrown falls on the same hands that are supposed to be making the machine work. A framework you did not write comes with documentation you did not write, answers on forums you did not staff and updates that arrive whether or not you had time. That asymmetry is the whole argument, and it is covered in more depth in what suits solo builders and pairs.
A funded group of fifteen can genuinely afford to own a layer, and should pick which single layer that is before anybody starts. Usually it should be the layer nearest the customer, not the layer nearest the wire. Groups that choose to own the wire end up with two products and staff for one.
Does the hardware you already chose decide this for you?
Often, yes, and teams discover it after the decision rather than before. The arm, the mobile base or the humanoid you bought arrived with an SDK, and that SDK was written against some assumption about how the rest of your software is organised. If the vendor ships drivers for one ecosystem, choosing anything else means porting drivers, which is unpaid work that a customer will never see.
Hardware decides the question in a second way too. If your compute is a microcontroller with no operating system worth the name, most of this article is irrelevant, because none of these options run there and your real choice is firmware structure. If your compute is one reasonably capable board with everything running on it, what actually runs on a robot matters more than transport.
And if your robot is really several machines on a network, the link becomes the constraint that overrides taste. Wireless behaviour, discovery across subnets and reconnection after a dropout will shape the architecture whether or not anybody planned for that.
How much does your deadline change the answer?
A deadline inside one quarter settles the argument entirely: adopt. Building your own layer is a bet that you have more calendar than uncertainty, and a team with a demo date has neither. Every week spent on infrastructure is a week the machine is not getting better at the thing you will be judged on.
Deadlines also distort judgement in a specific direction worth naming. Under time pressure, writing something small feels faster than learning something large, because the first hour of writing produces visible progress and the first hour of learning produces confusion. The comparison is dishonest. You are weighing your first hour against their tenth year.
The reverse case is real but rarer. If your runway is long, your requirements are unusual and you have already proven that the standard answer does not fit on the actual hardware, then a quiet six months building the layer you need can be the right investment. That describes very few teams, and the ones it describes can normally point at a specific failure on a specific machine rather than a preference.
What if nobody on the team has shipped a robot before?
Adopt, and adopt the popular option, because the value of a large ecosystem is highest exactly when you do not yet know what you do not know. A first robot project fails on things nobody anticipated: coordinate frames that disagree, a sensor that lies at startup, timestamps from two clocks, a motor controller that needs a specific initialisation order.
Every one of those has been hit, written up and answered somewhere in a mature ecosystem. On a homegrown stack, each one is a fresh mystery that costs a weekend. That is the difference between a project that reaches a working robot and one that quietly stalls, which is the pattern described in why projects stall after the prototype.
There is a second reason that matters more than it sounds. A first-time team cannot yet tell the difference between a hard problem and a self-inflicted one. Working inside something established gives you a baseline: when the standard thing behaves strangely, the strangeness is usually yours. Without that baseline, every bug looks equally plausible and debugging becomes guesswork.
What does a wrong choice look like six months in?
It looks like the stack becoming the project. The clearest sign is where hours go. Standups fill with configuration, environment differences, why the machine behaves differently after a reboot, and why the recording from last week will not replay. The robot's actual job, the thing on the pitch deck, gets whatever time is left over.
On the build-too-much side, the symptom is a private vocabulary. Your team has words for concepts nobody outside the company shares, onboarding takes weeks instead of days, and every new hire's first month is spent learning software that will never appear on their CV. Documentation is a wiki page last edited eight months ago by somebody who has left.
On the adopt-too-much side, the symptom is waiting. Waiting for an upstream fix, waiting for a release, working around a design decision you cannot change, shipping a patched fork nobody wants to maintain. Both failures feel like bad luck from the inside and look entirely predictable from the outside. The difference between them decides what you do next, so name which one you have before choosing a remedy.
What do you give up by adopting somebody else's stack?
You give up control over the parts you did not write, and that shows up as waiting rather than as pain. When a bug lives in a dependency, your options shrink to reporting it, working around it, or maintaining a fork. Each of those is survivable, and none of them are in your plan.
You also inherit assumptions. Every framework encodes a view of how a robot should be organised, and that view will occasionally disagree with your machine. Fighting the disagreement is expensive, accepting it means changing your design, and neither feels good when a customer is waiting.
The third thing you give up is a kind of understanding. A team that adopts everything eventually cannot explain why the robot behaved as it did, because the interesting behaviour happens inside code nobody read. That becomes acute during certification, during a serious field failure, or during due diligence when somebody asks how the safety stop is guaranteed. The mitigation is not building your own. It is reading the source of what you adopted, at least once, before you depend on it.
When is ROS 2 the better choice?
ROS 2 is the better choice for most robots, and specifically whenever the hard part of your machine is perception, navigation, integration or being maintainable by a team that changes over time. If your robot has to map a building it has never seen, plan a path around a person, recognise objects and coordinate with a fleet manager, ROS 2 is not a compromise. It is years of work you get to skip.
ROS 2 also wins on the boring axes that decide projects. You can hire people who already know it. Vendors ship drivers for it. You can record a failed run in the field and replay it at a desk. Students, contractors and collaborators arrive already fluent, and that alone is worth more than most technical arguments.
HORUS is not the answer in those cases. A shared-memory middleware carries messages between processes on one machine and does not bring you a mapper, a planner, a fleet interface or a driver for the lidar you ordered last week. If those are your hard problems, choosing anything smaller means writing all of it yourself, which is the build decision this post is warning you about.
Is writing your own middleware really cheaper than learning one?
No, and here is why. The comparison people make is between reading documentation for a week and writing a working prototype in three days. That comparison is accurate and irrelevant, because the prototype is not the deliverable. Two processes exchanging messages is a weekend. Everything after that is the actual job.
What follows the weekend is discovery when processes start in a different order, backpressure when a consumer falls behind, clean shutdown when one process dies, recording so failures can be studied later, replay so fixes can be verified, introspection so somebody can see what is being published, versioning so old recordings still open, and documentation so the next hire is not dependent on you. None of that is glamorous, all of it is required, and every item is discovered painfully rather than planned.
The cost is also permanent. A framework you adopted improves while you sleep. A framework you wrote decays unless somebody funds its upkeep forever, and that somebody is always the person you can least afford to lose.
Does adopting a stack lock you in for good?
Partly, but not the way you think. The code is the portable part. Your controller, your state machine, your perception pipeline and your calibration maths do not care how the numbers reached them, and porting a node body between frameworks is normally a smaller job than the team fears.
What genuinely locks you in is everything around the code. Launch configuration, recorded datasets in a specific format, dashboards, test rigs, the continuous integration setup, the deployment scripts, and above all the habits of everyone you hired. A team fluent in one ecosystem carries that fluency into every design discussion, and changing the substrate means changing how people think, which takes longer than any rewrite.
The practical consequence is not "avoid lock-in". It is to keep your own logic in plain libraries that a framework calls, rather than scattered through framework-specific classes. Do that from the first month and a future migration becomes a rewiring job rather than a rewrite. Do it in year three and the entanglement has already happened. This is the cheapest insurance available and almost nobody buys it early.
How should a team make this call in a week rather than a quarter?
Write one sentence describing what you sell, and let that sentence choose. If the sentence is "our machine works out where it is and picks things up", the answer is the ecosystem, because every noun in that sentence already exists as a package somebody maintains. If the sentence is "our machine moves in a way nobody else's does", timing is part of the product and the calculation changes.
Then spend three days, not three months, building the ugliest part of your robot on the hardware you actually intend to ship, in the language your team actually writes. Not a benchmark. The ugly part. Watch the machine rather than a chart, and ask three questions: could a new hire reproduce this from a clean checkout, can you replay yesterday's failure at your desk, and what does the robot do when one process is killed.
Whatever you decide, write the decision down with its reason, in the repository, where the person who inherits it can find it.
- If you are two people with a demo next month -> adopt the ecosystem, because drivers you do not have to write are the cheapest engineering available.
- If the motion itself is what customers are paying for -> a shared-memory middleware alongside the ecosystem, because the loop is the product.
- If your robot is one machine, one language and one loop -> one program and no middleware, because the design you can hold in your head ships first.
- If you sell into a regulated or unreachable setting -> a supported commercial platform, because somebody else's certification paperwork is cheaper than your own.
- If the vendor SDK already does what the product needs -> stay on the vendor SDK, because a port buys nothing a customer can see.
- If the arguments are really about your own control maths -> change nothing, because no migration ever tuned a controller.
The HORUS Fit Framework reduces the whole comparison to five axes: ecosystem size, setup effort, team size fit, deployment target, and licence. Score every option on all five, and choose the one that is not weak on the axis your company cannot afford to be weak on.
If timing keeps turning out to be that axis, put HORUS on your reading list rather than on this quarter's roadmap: star it so it is in your list when you start building.