This article unintentionally offers another solution, which has two parts, the first being to not make the system so incredibly complex. Don't put all of a cars smart functions on one chip, don't even put them in one module. It is much more likely to be able to understand what code is doing if it is only doing one thing. So coming up with an adequately detailed description of what the system will do is a very good start.
The second part of the solution is after a function is selected for a given system, carefully define all of what the function must do, and then make that description be the specification, and, most importantly, avoid feature creep. Both in initial concept creation and as the project is in process, constantly changing and adding is a certified way to add errors.
Interestingly this article contains 6 or more arguments ended by a 'counsel of despair' typified by 'its so complex you'll never find all the bugs, so why bother?' then describes a brute-force method of testing to try to find said bugs, and _prove_their_absence_(I'll not go there!). Reliable design used to start by designing a core so simple it could be proven to be correct using tools that themselves were simple enough to be proven likewise. Thereafter, this virtuous circle was widened step by step to encompass the entire application, with each proven building block introducing no additional uncertainty save that due to the current layer being developed. But before the impetus had built, or critical mass achieved, commercial forces would often kill the project or force it to market early, and the work done to date would be lost, instead of forming the core of future systems. It would be fascinating to be a code archeologist and unearth some ultra-safe, elegant and incredibly simple systems that have been scrapped without regard to their potential value. Real 'lost knowledge', worthy of a Dan Brown novel (and then some).