Contents
Why C for Critical Infrastructure?
When I joined InMage Systems' disaster recovery team in 2006, the choice of C for core platform components wasn't arbitrary. It was deliberate. For software that needed to operate reliably at the kernel level, with predictable memory behaviour and minimal runtime dependencies, C was the right choice.
This post is about what I learned building in that environment, and why those lessons translate to any language or platform.
The Memory Discipline
C forces you to think about memory explicitly. Every allocation has a corresponding free. Every pointer is an address you're responsible for. This discipline, what I'd now call ownership thinking, is something I carry into every technology I work with.
In C++, RAII and smart pointers help manage it. In Java, the garbage collector takes care of it. But understanding why ownership matters (preventing use-after-free, double-free, and memory leaks in long-running processes) is foundational.
For a disaster recovery platform that might run for months without a restart, even small memory leaks are fatal. We profiled everything with Valgrind. We treated any memory warning as a P0 bug.
Error Handling Without Exceptions
C has no exceptions. Every function that can fail must communicate that failure through its return value or an output parameter. This leads to verbose but honest code:
int result = replicate_block(src, dst, block_size);
if (result != DR_SUCCESS) {
log_error("Block replication failed: %d", result);
return result;
}Verbose, yes. But also explicit. The caller knows the operation can fail and is forced to handle it. Compare this to languages where exceptions can silently propagate up the call stack, crashing in unexpected places.
This explicitness is something I advocate for in all languages: make your error paths obvious, handle them close to the source, and never let failures silently disappear.
Lessons That Will Outlast the Language
Whatever language I write next, these patterns from C will come with me:
- Assume failure at every system boundary
- Own your resources: connections, file handles, memory: clean them up
- Make the error case as visible as the success case
- Understand what's happening below your abstraction layer
Languages change. These principles don't.
