← Technical notes

The bug is at the boundary

Two compilers, both alike in dignity, in fair codespace, where we lay our scene.

You are working in a type-safe language. You have all of the compiler warnings turned on. You have unit tests that exercise every branch.

Another team is also working in a type-safe language. They have all of the compiler warnings turned on. They have unit tests that exercise every branch.

Now, your code needs to interact. It all fits together seamlessly.

Everything links. The end-to-end tests all pass.

Alas, there is a subtle difference in how one compiler aligns long words. There is a subtle difference in what one compiler thinks of as a character. There is a subtle difference in how the compilers represent enumerated types in memory.

I have seen bugs like these lie dormant for years before the wrong value sneaks its way to a variable that the C++ compiler thinks can be any byte and the Ada compiler thinks it has already proven that no one could have assigned it a value greater than 10.

Code reviewers and compilers that know C++ see no issues. Code reviewers and compilers that know Ada see no issues.

I've seen the same thing in C++ and Java code interacting. I've seen the same thing in Common Lisp and C code interacting.

The fault exists only in the relationship. The first question when facing a stubborn bug is not “Where is the mistake?”, but “Where do two assumptions meet?”

Make boundaries explicit: layout, encoding, ownership, ordering — checked on both sides, at build time where you can and at start-up where you can’t. A boundary, or really any bit of code, that fails loudly on day one is worth magnitudes more than one that works until it doesn’t.