Adversarial Review

A plausible name is not a real name

If you ask an AI to pull numbers out of your database, it has to write a query, and a query has to name tables and columns. When the AI does not know the exact name of a column, it often does not stop and say so. It writes the name a column like that would usually have.

Usually is not always. On one software product, that gap caused a lot of small, repeated delays.

The names that sounded right

There, AI sessions wrote database queries for a person to run against the live data. Over a short stretch, a run of those queries failed for the same kind of reason.

One query looked for the date of a payment in a column named the obvious way, with the word “date” tacked onto the thing being dated. The real column was just called date. Another looked for a last-login column on the users table. There was no such column; login history lived in a separate table. Another asked a form table for an email column it did not have. In one batch of queries, two failed because a column the AI expected was not there, and three more failed because the AI had written a real column name in the wrong capitalization style.

None of these were wild guesses, which is exactly why they kept getting written.

Loud failures cost time. Quiet ones cost trust.

A query that names a missing column at least fails loudly. The database refuses to run it and says which name it did not recognize. On that product, each refusal still meant a full round trip: someone ran the query, copied the error back to the AI, waited for a corrected version, and ran it again.

The worse case is the one that does not fail. On the same product, one step in a batch compared stored text against a value held in a temporary variable. The two used different rules for comparing text. Databases handle that kind of mismatch differently, and in some setups it raises an error, but on that database, in that query, it quietly matched nothing. The step ran, changed nothing, and raised no error. The only sign was a count of zero at the end. Zero looks like an answer. “Nothing matched” is a perfectly believable result, and a believable wrong answer is much harder to catch than a red error message.

An AI writing a query from memory is making a claim about your data. Some of those claims fail loudly and some fail quietly, and you do not get to choose which.

Why this happens

In practice, when an AI coding tool has to fill in a name it has not been shown, it tends to reach for one that looks typical, and a typical-looking column name is usually the conventional one. Your database may follow conventions most of the time, but it was built by whoever made each decision, sometimes years apart, and it carries their exceptions, abbreviations and old mistakes.

The AI cannot see any of that unless you show it. In practice, if nothing in front of it lists the real names, it tends to fill the gap with the plausible ones.

The habit: one verified list of real names

The fix on that product was a single reference file, kept alongside the project notes the AI reads at the start of every session. It has three parts.

The real names, read from the database itself. Each table that matters, with its columns, pulled from the database and marked with the date it was checked.

A short list of traps. The handful of mistakes that had already happened, written plainly at the top: the date column is called date; capitalization differs between older and newer tables; this kind of comparison returns nothing instead of an error. The trap list is the most valuable part, because it is made of real failures seen on that database, not guesses about future ones. Only list traps you have actually hit.

A lookup to run instead of guessing. Most databases can describe themselves. In MySQL and PostgreSQL there is a built-in catalog called information_schema (a set of read-only tables the database keeps about its own structure), and its columns view lists the columns of the tables your database login is allowed to see (MySQL docs, PostgreSQL docs). SQLite uses a different command, PRAGMA table_info, or PRAGMA table_xinfo if you also need generated or hidden columns (SQLite docs). If a table you expect is missing from the results, check that the login you are using has access to it.

Then one standing instruction, written where the AI will read it every session: read this file before writing any query. If a column you need is not in it, run the lookup first, then add what you learned to the file. Never guess a column name.

Each lookup makes the file a little more complete. When the structure of the database changes, refresh the affected tables in the file.

How to set this up this week

  1. Have your AI write the lookup query for your database, run it, and save the results for your most-queried tables into one file in your project notes.
  2. Add a “traps” section. Seed it with every column-name error you remember, and add to it each time a query fails.
  3. Add the standing instruction to whatever file your AI reads at the start of a session. If you use Claude Code, that is usually a CLAUDE.md file in the project (Claude Code docs).

How to check it is working

Next time the AI hands you a query, ask one question before you run it: “Where did each column name in this query come from?” A good answer points to the reference file or to a lookup it just ran. An answer like “that is the standard name for it” is a guess, and a guess is worth checking before you trust the result.

And when a query comes back with zero rows, do not take the zero at face value. Ask the AI to prove the query can find something: pick one record you know, from somewhere other than this query, should match every condition the query is asking about, and run the query again narrowed to that record. If it still finds nothing, the query is not doing what you meant, and that is worth fixing before you trust the zero.