
Four failures that raise no error.
Artificial intelligence projects rarely fail on the capability of the model. They fail on defects that produce no message, return a plausible result, and pass every check one makes spontaneously.
What holds up a project is almost never the model
The published work on the failure of artificial intelligence projects converges on one point: the initiatives that never reach production are counted in large majorities, and the causes cited are not the capability of the models. They are the absence of evaluation, integration with the existing system, and reliability under real conditions.
That formulation stays abstract until one has seen what such a failure looks like. Here are four cases we have lived through. They have nothing in common technically, and yet they share the same trait: none of them produces an error. Each passes a superficial check, and each would have been discovered by a user rather than by us.
A preview flag, and the integration produces nothing
An integration with a third-party service was wired up, called in production, and answered correctly on every request. Yet it produced nothing usable: a parameter set to “preview” by default meant that every document came out struck through with the corresponding wording.
The usual check could not see the problem. The call went out, the service answered, the document arrived. Everything one normally verifies was green.
The most instructive part lies elsewhere. Switching that parameter was not a technical detail: it changed who pays for what, and therefore the accounting and tax treatment of the transaction. Three files with no apparent connection had to move on the same day.
An access hardening, and the table returns nothing
We enabled row-level access control on a database table, documenting that no application access was wanted. What the engine understands by that is different from what the sentence suggests: enabling the control without defining a policy does not mean “reserved for the service”, it means nobody.
The result: a row perfectly present in the database was counted as zero by the application, and the user journey replied that a valid identifier was no longer valid. No error, no refusal, no trace. The symptom is strictly identical to that of an unknown identifier.
The root cause is not the database, it is the verification method. An access hardening is instinctively checked in the direction of “it no longer writes”, whereas it is reading that breaks, and it breaks silently. Zero rows is a valid answer, indistinguishable from a legitimate absence.
A variable changed, a process that never read it
A setting is corrected in the configuration file. The value is right, the file is deployed, and the behaviour does not change. So something else gets changed, the search moves elsewhere, and one ends up doubting the setting itself.
Most programs read their configuration at start-up and keep it in memory. Until the process has been restarted, it works with the old value, whatever the file on disk contains.
This case looks trivial written down in black and white. It costs hours all the same because it disguises itself as something else: the symptom observed is “my fix does not work”, which points towards the fix and never towards the life cycle of the process. The check consists in proving that the process answering is indeed the new one, not in re-reading the value in the file.
A deployment made from the working copy
The fourth case is the most commonplace and the hardest to recover from. A deployment built from the working folder rather than from the code repository carries along everything lying about on the machine: a file modified and not committed, a forgotten experiment, a fix made locally and never versioned.
It works, often for a long time. Then production starts containing code that nobody can find in the history, and whoever picks the subject up spends hours looking for a change that never existed anywhere but on one machine.
The remedy fits in one sentence: what is deployed comes from the repository, and a deployment starts by checking that the working tree is clean. A boring rule, whose value only shows on the day somebody else takes the system over.
What these four cases have in common
None of them raises an error. All of them return a plausible result. And all of them pass the check one makes spontaneously, because that check verifies that the mechanism is running instead of verifying that it produces the right effect.
It is the same lesson in four forms. A check that cannot fail checks nothing. Seeing a request go past does not prove it produced an effect. Seeing a container running does not prove it is serving the right version. Seeing zero rows does not prove there is nothing to see.
These checks cost a few minutes each. They are exactly what separates a prototype that impresses from a system that holds, and they appear in no demonstration.