SOFTWARE CRAFTSMANSHIP // IA GÉNÉRATIVE
La loi de Kidlin à l'ère de l'IA : spécifier est le nouveau coder
« Si vous parvenez à écrire clairement le problème, il est à moitié résolu. » Face à un agent IA qui préfère deviner plutôt que demander, cette maxime devient une discipline d'ingénierie : questions, tests, types.
« Si vous parvenez à écrire clairement le problème, il est à moitié résolu. » — La loi de Kidlin. Avec un agent IA, la seconde moitié ne coûte presque plus rien. Toute la valeur se concentre donc dans la première.
Plus de vingt ans de code m'ont appris une chose : ce n'est presque jamais la syntaxe qui ralentit une équipe, c'est l'ambiguïté du besoin.
Les agents de code ont déplacé le goulot d'étranglement. Produire du code ne coûte presque plus rien. Savoir précisément quoi produire, si.
📜 Une maxime d'origine floue, un principe limpide
L'origine de la loi de Kidlin est incertaine. On la trouve partout sur le web, rarement sourcée. Elle est cousine d'une phrase attribuée à l'ingénieur Charles Kettering : « Un problème bien posé est un problème à moitié résolu. »
Peu importe le parrain. L'idée tient : écrire le problème force à trancher ce que l'on croyait évident, et révèle ce que l'on ignorait.
💥 Le déclencheur : un prompt d'une ligne, une régression invisible
Prenons un scénario que beaucoup d'équipes reconnaîtront. Un moteur de facturation B2B, une fin de mois qui presse, et un prompt lancé à la volée : « Ajoute une remise de 10 % aux clients jusqu'à la fin du mois. »
En quelques secondes, l'agent livre plusieurs centaines de lignes propres, typées, accompagnées de tests verts qu'il a lui-même écrits.
Quelques jours plus tard, la comptabilité et le support tirent la sonnette d'alarme : la remise de 10 % s'est appliquée sur des clients grands comptes bénéficiant déjà de tarifs négociés contractuellement. Pire : le 31 du mois à 23h45, des commandes ont été rejetées en raison d'un fuseau horaire implicite non géré.
Personne n'avait posé la question : « La remise s'applique-t-elle aux prix négociés ? » ou « Quel fuseau horaire fait foi pour la fin du mois ? » L'agent ne l'a pas posée non plus. Il a choisi la réponse la plus plausible, sans le dire.
🌪️ Le coût caché : un agent qui préfère deviner
Un collègue humain s'arrête devant un besoin flou. Il fronce les sourcils et pose des questions jusqu'à dissiper le brouillard.
Un modèle de langage fait l'inverse. Les travaux sur la complaisance des assistants IA (sycophancy) montrent qu'ils tendent à satisfaire l'utilisateur plutôt qu'à le contredire.
Face au doute, il prend un raccourci. Il comble les trous par des suppositions, rarement explicitées, et produit un code plausible qui passe ses propres tests.
Le réflexe du prompt fleuve ne règle rien. Trois pages de prose restent ambiguës, simplement plus longues. Le flou change de taille, pas de nature.
🎯 La première moitié : forcer l'agent à poser des questions
Pour contrer ce biais, on équipe l'agent d'un outil de spécification. Une skill dédiée de cadrage inverse la dynamique : plutôt que de foncer tête baissée et de deviner, l'agent inspecte le besoin, liste ses zones d'ombre et soumet un questionnaire ciblé à l'humain.
Sur notre prompt initial, voici ce qu'un tel outil fait remonter avant toute génération de code :
? La remise de 10 % s'applique-t-elle aux lignes à prix déjà négociés ?
? Se cumule-t-elle avec une promotion catalogue déjà en cours ?
? « Fin du mois » : quel fuseau horaire fait foi (UTC ou celui du client) ?
? Quel événement déclenche l'éligibilité : mise au panier ou paiement finalisé ?
? Arrondi : par ligne ou sur le total TTC, au centime le plus proche ?
L'utilisateur reste le pilote. C'est lui qui arbitre chaque point d'ambiguïté. Rien n'est perdu du monde d'avant l'IA : les pratiques agiles et le craftsmanship, comme l'Example Mapping, trouvent ici tout leur sens. Formaliser trois exemples concrets et des questions ouvertes canalise l'agent infiniment mieux qu'une tartine de prose.
Tout va plus vite, y compris pour faire des erreurs. L'IA accélère la livraison quand le cap est net, mais elle démultiplie la vitesse d'accumulation de bugs silencieux quand l'intention de départ est incomplète.
🛡️ L'antidote craftsmanship : transformer les réponses en contraintes
Une réponse en prose reste fragile. Pour qu'elle contraigne l'agent, elle doit devenir exécutable. Deux outils suffisent : un test et des types.
[ PROMPT FLOU ]
Intention vague
|
v
Agent complaisant ---> comble les trous seul
|
v
Tests verts ---> incident en production
[ KIDLIN-CRAFT ]
Example mapping ---> zones d'ombre listées
|
v
Arbitrage humain ---> réponses écrites en tests rouges
|
v
Types stricts ---> l'agent implémente
|
v
Tests verts ---> intention vérifiée sans ambiguïté
🔍 Avant : ce que produit le prompt d'une ligne
Le code est plausible. Il applique la remise partout sans vérifier la nature des prix, manipule des devises dans des flottants et se base sur une date système sans fuseau.
function applyMonthlyDiscount(cart: any): number {
const now = new Date();
let total = 0;
for (const line of cart.lines) {
total += line.price;
}
if (now.getDate() <= 31) {
return total * 0.90;
}
return total;
}
✅ Après : l'exemple devient un test rouge
On écrit d'abord le test. Il encode l'exemple tranché : les lignes à prix négocié ne sont jamais remisées et la date est évaluée explicitement en UTC.
test("10% monthly discount never applies to negotiated prices during active period", () => {
const promoPeriod = {
startUtc: new Date("2026-10-01T00:00:00Z"),
endUtc: new Date("2026-10-31T23:59:59Z"),
};
const cart = [catalogLine(euros(100)), negotiatedLine(euros(50))];
const evaluationDate = new Date("2026-10-15T12:00:00Z");
assert.deepEqual(
totalWithMonthlyPromotion(cart, promoPeriod, evaluationDate),
euros(140)
);
});
Puis les types et l'implémentation. L'origine du prix devient un type, le taux devient une constante nommée, l'argent se compte en centimes immuables.
const CENTS_PER_EURO = 100;
const TEN_PERCENT_DISCOUNT = 0.10;
type PriceOrigin = "Catalog" | "Negotiated";
type Money = { readonly cents: number };
type CartLine = { readonly price: Money; readonly origin: PriceOrigin };
type PromotionPeriod = { readonly startUtc: Date; readonly endUtc: Date };
const euros = (amount: number): Money => ({ cents: Math.round(amount * CENTS_PER_EURO) });
const catalogLine = (price: Money): CartLine => ({ price, origin: "Catalog" });
const negotiatedLine = (price: Money): CartLine => ({ price, origin: "Negotiated" });
const calculateLinePrice = (line: CartLine, isPromoActive: boolean): Money => {
if (line.origin === "Negotiated" || !isPromoActive) {
return line.price;
}
const discountedCents = Math.round(line.price.cents * (1 - TEN_PERCENT_DISCOUNT));
return { cents: discountedCents };
};
const totalWithMonthlyPromotion = (
lines: readonly CartLine[],
period: PromotionPeriod,
currentDate: Date
): Money => {
const isPromoActive = currentDate >= period.startUtc && currentDate <= period.endUtc;
const totalCents = lines.reduce(
(sum, line) => sum + calculateLinePrice(line, isPromoActive).cents,
0
);
return { cents: totalCents };
};
Le résultat est court et vérifiable. Pas de else, pas
de any, pas de taux magique. Surtout, la règle oubliée du scénario est
désormais impossible à rater : un test rouge la protège.
🤖 Le TDD à l'ère de l'IA : donner une définition exécutable du « terminé »
Une spécification purement textuelle finit toujours par dériver. Face à un modèle probabiliste, la prose est une suggestion, mais un test rouge est un contrat indiscutable.
Le TDD n'est pas mort avec l'IA. Il devient la façon la plus rapide et la plus économique de guider un agent : poser l'intention sous forme de tests et de types stricts, puis le laisser implémenter jusqu'à ce que tout soit vert.
⚖️ Matrice comparative
+-----------------------+---------------------------+---------------------------+
| Dimension | Prompt flou | Kidlin-Craft |
+-----------------------+---------------------------+---------------------------+
| Formulation | Prose ambiguë | Example mapping + types |
| Gestion du doute | Raccourcis silencieux | Questions à l'humain |
| Cas limites | Découverts en prod | Tranchés avant le code |
| Code produit | Volumineux, défensif | Court, vérifiable |
| Maîtrise cognitive | Diluée dans le généré | Ancrée dans la spec |
| Maintenance | Croissante | Prévisible |
+-----------------------+---------------------------+---------------------------+
🚀 Conclusion : votre métier n'est plus de taper, mais de cadrer
L'IA ne rend pas le craftsmanship obsolète. Plus le moteur va vite, plus les rails doivent être précis.
La loi de Kidlin, version agent : si vous savez écrire votre problème sous forme de questions tranchées, de tests et de types, l'agent fait le reste. Sinon, il automatise votre flou.
Et vous : quelle est la dernière question que votre agent aurait dû vous poser, et qu'il a tranchée seul ?
“If you can write the problem down clearly, then the matter is half solved.” — Kidlin’s Law. With an AI agent, the second half costs almost nothing. All the value now sits in the first half.
More than twenty years of code taught me one thing: syntax rarely slows a team down. Ambiguous requirements do.
Coding agents moved the bottleneck. Producing code costs almost nothing. Knowing exactly what to produce still does.
📜 A Maxim of Murky Origin, a Crystal-Clear Principle
Nobody really knows where Kidlin’s Law comes from. It is quoted everywhere online and rarely sourced. It is a cousin of a line attributed to engineer Charles Kettering: “A problem well stated is a problem half solved.”
The parentage hardly matters. The idea holds: writing the problem down forces you to settle what you thought was obvious, and exposes what you did not know.
💥 The Catalyst: a One-Line Prompt, an Invisible Regression
Consider a scenario many teams will recognize. A B2B billing engine, a looming end-of-month deadline, and a prompt thrown together on the fly: “Add a 10% discount for customers until the end of the month.”
Within seconds, the agent delivers several hundred clean, typed lines, complete with green tests it wrote itself.
A few days later, accounting and support raise the alarm: the 10% discount was applied to key enterprise clients who already had contractually negotiated prices. Worse: on the 31st at 11:45 PM, orders were rejected because of an implicit, unhandled time zone offset.
Nobody had asked the question: “Does the discount apply to negotiated prices?” or “Which time zone governs the end of the month?” The agent did not ask either. It picked the most plausible answer, and kept quiet about it.
🌪️ The Hidden Cost: an Agent That Would Rather Guess
A human colleague stops in front of a vague requirement. They frown and ask questions until the fog lifts.
A language model does the opposite. Research on sycophancy in AI assistants shows they tend to please the user rather than push back.
Faced with doubt, it takes a shortcut. It fills the gaps with assumptions it rarely states, and produces plausible code that passes its own tests.
The wall-of-text prompt does not fix this. Three pages of prose are still ambiguous, just longer. The fog changes size, not nature.
🎯 The First Half: Forcing the Agent to Ask Questions
To counter this sycophancy, we equip the agent with a specification tool. A dedicated scoping skill flips the dynamic: rather than rushing ahead and guessing, the agent inspects the requirement, lists its blind spots, and hands a targeted questionnaire back to the human.
On our initial prompt, here is what such a tool surfaces before writing a single line of code:
? Does the 10% discount apply to lines with already negotiated prices?
? Does it stack with an ongoing catalog promotion?
? “End of the month”: which time zone governs (UTC or the customer's)?
? Which event triggers eligibility: cart creation or finalized payment?
? Rounding: per line or on the total with tax, to the nearest cent?
The user stays in the driver's seat. They settle every point of ambiguity. Nothing is lost from the world before AI: agile practices and software craftsmanship, such as Example Mapping, prove invaluable here. Formalizing three concrete examples and open questions channels an agent infinitely better than a wall of prose.
Everything moves faster, including making mistakes. AI accelerates delivery when constraints are crystal clear, but it dramatically multiplies the speed at which silent regressions accumulate when the initial intent is incomplete.
🛡️ The Craftsmanship Antidote: Turning Answers into Constraints
An answer written in prose stays fragile. To constrain the agent, it has to become executable. Two tools are enough: a test and types.
[ VAGUE PROMPT ]
Vague intent
|
v
Sycophantic agent ---> fills the gaps alone
|
v
Green tests ---> production incident
[ KIDLIN-CRAFT ]
Example mapping ---> blind spots listed
|
v
Human decision ---> answers written as red tests
|
v
Strict types ---> the agent implements
|
v
Green tests ---> verified intent without ambiguity
🔍 Before: What the One-Line Prompt Produces
The code is plausible. It applies the discount everywhere without checking price origins, manipulates currencies in raw floats, and relies on an unzoned system date.
function applyMonthlyDiscount(cart: any): number {
const now = new Date();
let total = 0;
for (const line of cart.lines) {
total += line.price;
}
if (now.getDate() <= 31) {
return total * 0.90;
}
return total;
}
✅ After: the Example Becomes a Red Test
The test comes first. It encodes the settled example: negotiated price lines stay untouched and dates are explicitly validated in UTC.
test("10% monthly discount never applies to negotiated prices during active period", () => {
const promoPeriod = {
startUtc: new Date("2026-10-01T00:00:00Z"),
endUtc: new Date("2026-10-31T23:59:59Z"),
};
const cart = [catalogLine(euros(100)), negotiatedLine(euros(50))];
const evaluationDate = new Date("2026-10-15T12:00:00Z");
assert.deepEqual(
totalWithMonthlyPromotion(cart, promoPeriod, evaluationDate),
euros(140)
);
});
Then the types and the implementation. The price origin becomes a type, rates become named constants, and money is counted in immutable cents.
const CENTS_PER_EURO = 100;
const TEN_PERCENT_DISCOUNT = 0.10;
type PriceOrigin = "Catalog" | "Negotiated";
type Money = { readonly cents: number };
type CartLine = { readonly price: Money; readonly origin: PriceOrigin };
type PromotionPeriod = { readonly startUtc: Date; readonly endUtc: Date };
const euros = (amount: number): Money => ({ cents: Math.round(amount * CENTS_PER_EURO) });
const catalogLine = (price: Money): CartLine => ({ price, origin: "Catalog" });
const negotiatedLine = (price: Money): CartLine => ({ price, origin: "Negotiated" });
const calculateLinePrice = (line: CartLine, isPromoActive: boolean): Money => {
if (line.origin === "Negotiated" || !isPromoActive) {
return line.price;
}
const discountedCents = Math.round(line.price.cents * (1 - TEN_PERCENT_DISCOUNT));
return { cents: discountedCents };
};
const totalWithMonthlyPromotion = (
lines: readonly CartLine[],
period: PromotionPeriod,
currentDate: Date
): Money => {
const isPromoActive = currentDate >= period.startUtc && currentDate <= period.endUtc;
const totalCents = lines.reduce(
(sum, line) => sum + calculateLinePrice(line, isPromoActive).cents,
0
);
return { cents: totalCents };
};
The result is short and verifiable. No else, no
any, no magic rate. Above all, the rule the scenario forgot can no
longer be missed: a red test guards it.
🤖 TDD in the Age of AI: Providing an Executable Definition of “Done”
A purely text-based specification will always drift. When working with a probabilistic model, prose remains a suggestion, but a red test is an indisputable contract.
TDD is not dead with AI. It becomes the fastest and most cost-effective way to guide an agent: establishing clear intent through tests and strict types, then letting the agent implement until everything turns green.
⚖️ Comparison Matrix
+-----------------------+---------------------------+---------------------------+
| Dimension | Vague prompt | Kidlin-Craft |
+-----------------------+---------------------------+---------------------------+
| Problem statement | Ambiguous prose | Example mapping + types |
| Handling doubt | Silent shortcuts | Questions to the human |
| Edge cases | Found in production | Settled before coding |
| Generated code | Bulky, defensive | Short, verifiable |
| Cognitive ownership | Diluted in the output | Anchored in the spec |
| Maintenance | Growing | Predictable |
+-----------------------+---------------------------+---------------------------+
🚀 Conclusion: Your Job Is No Longer to Type, but to Frame
AI does not make craftsmanship obsolete. The faster the engine, the more precise the rails must be.
Kidlin’s Law, agent edition: if you can write your problem down as settled questions, tests and types, the agent does the rest. If you cannot, it automates your fog.
Your turn: what is the last question your agent should have asked you, and settled on its own instead?