Shivansh Inamdar builds AI and manipulation software for autonomous warehouse robots at Nimble Robotics.

getty
Robotics isn't like most software. When there is a bug in your app, a page doesn't load, and a user gets frustrated. When there is a bug in your robot software, a package might fall, and a robot might trip over it. Suddenly, a large section of a warehouse is down for recovery. Watching such failures in production environments has fundamentally altered my approach to software engineering, teaching me lessons that have helped me make any codebase more robust.
I've spent the past several years building software that runs hundreds of robots that handle tens of thousands of items every day. At this scale, even rare bugs stop being rare. And the problem space is massive. Unlike a web page where there are only so many ways a user can interact, robots interact with the physical world in unpredictable ways. But the lessons I've learned from building software that survives failures are applicable to any software system, whether it's a web application, a mobile app or a distributed service.
Design For Failure
In robotics, failure is not an exception; it is very much the norm. The only option is to design for it. Sensors can fail, items can shift around and grasps can slip. In one instance, I watched a robot attempt to pick up a bag that was massively oversized for the dress inside it. The bag sagged, the robot failed to maintain a solid grip while it was moving and the bag was flung into the path of a different robot, causing a cascade of failures. In hindsight, there is a lot that could have been anticipated and fixed here, but it can be incredibly hard to predict something like this before it happens. So, you have to build a system that can detect failure, contain it and recover from it.
These days, software moves fast, and our current culture often treats the unhappy path as an afterthought. The industry wants to ship what works and what they can demo, then tack on robust error handling later. But in my experience, the failure path deserves the same design attention as the happy path. At scale, the failure path is the product experience. If your software fails in a way that frustrates users or causes downtime, it doesn't matter how well the happy path works.
You Can't Attach A Debugger To The Real World
The reality with robotics is that you can't just pause the system or even recreate a situation exactly to inspect what went wrong. When you notice an edge case, you need observability already built in: logs of what the robot saw, what it decided and why. If a robot decides how to pick up an item based on a series of images of the item as the robot approaches it, and you end up with a failure, you need to have logged all of those images, the reasoning pathways taken and the final decisions made. Without that, debugging edge cases in the code is nearly impossible. This has taught me to always design for observability up front rather than trying to add it after the fact.
All other software is drifting in the same direction. Distributed systems, third-party APIs, black-box models and production-only bugs mean that it is increasingly difficult to reproduce issues locally. My advice is to instrument before you need it. The question isn't "Can I debug this?" Instead, we need to ask, "When this fails at 2 a.m., what will I wish I had logged?"
Your Model Of The Input Is Always Too Clean
Whatever a developer might assume about an input, the real world will always violate it. In robotics, this is especially true and very quickly obvious. Transparent packaging can defeat sensors, items can change shape and boxes that the spec said couldn't exist will appear. And notably, these violations arrive very visibly within hours, not quietly over months.
Every system, even outside of robotics, has its own version of this. It could be user input, upstream data or API responses. In all code-based projects, you treat your assumptions about inputs as hypotheses to be tested against reality, not facts. Test with the messiest data you can find, not the cleanest. This mindset will not make your software perfect, but it will make failures recoverable.
Why This Matters More In The Age Of AI
With the advent of AI coding tools, it's more important than ever to remember that while AI can get your software's happy path working, it cannot provide the failure-mode thinking, the observability or the skepticism about inputs that robust, reliable software requires. All of those remain an engineer's job. In my experience, the engineers who thrive alongside AI are the ones who hunt for failure modes and own everything outside of the happy path. They are the ones who build software to actually work reliably in the real world.
The takeaway is simple: Write software as if it is moving something physical. Beyond asking if something works, also ask, "What happens if my code fails?" Not every bug will lead to robots crashing into each other, but every bug will have consequences. By designing for failure, building observability as a priority and treating everything you think you know as a hypothesis, you can build software that is robust, reliable and built to last.
Forbes Technology Council is an invitation-only community for world-class CIOs, CTOs and technology executives. Do I qualify?