← All insights

Prompt debt: Why strong AI models fail on old instructions

In short

Prompt debt consists of persistent instructions written for earlier models that now oversteer newer ones. Remove behavior the model has already learned, turn rules that are right 90 percent of the time into conditions, and test the shorter version against typical tasks. Keep safety boundaries and personal context.

Discuss with AIClaude ↗ChatGPT ↗

Software development has a concept called “technical debt.” It means compromises made in code for the sake of speed that have to be corrected later. Technical debt accrues interest when new work is built on compromised code. In large companies, developers spend a substantial share of their working time paying down this debt.

In 2026, an equivalent emerged in work with artificial intelligence. Not just in programming, but in every kind of work. In June, data engineer and data strategist Drew Breunig popularized the term prompt debt. The phrase had appeared occasionally before, but only gained traction after his essay. Breunig examined production applications in which a prompt is supposed to control the behavior of an entire system. The problem goes further, though. Prompt debt sits in prompts, skills, files such as CLAUDE.md, and even loops that AI generates automatically. Anyone who does not pay it down will soon feel the consequences clearly.

On 21 July, Simon Willison published a conversation with Anthropic engineers Thariq Shihipar and Cat Wu. They described a remarkable case: the Claude Code team had cut the system prompt for Fable 5 and Opus 4.8 by 80%. OpenAI likewise recommends leaner prompts with fewer repetitions and examples for GPT-5.6. Shortly before that, Every published “The Case Against Skills”. The article centers on Mike Taylor, co-author of the textbook Prompt Engineering for Generative AI. His argument: many prebuilt instruction packages fight what the model already knows how to do.

In short, what is prompt debt? For a long time, the rule was this: if the AI fails to do something, or does something it should not do, add an instruction to the prompt that corrects the error. The longer you work with the same prompt, the more of these instructions accumulate. Sooner or later, they contradict one another. And the prompt grows so extensive that even its author can no longer follow it.

My assessment: by now, everyone has prompt debt. Even someone who has written short, understandable prompts for more than a year and teaches others to do the same can accumulate it elsewhere. I found my prompt debt in skills I had created together with Claude and in loops I use in AI agents. There are less obvious hiding places too. More on those later.

−80%Claude Code system prompt for Fable 5 and Opus 4.8
36.2%Best strict success rate in the HANDBOOK.md benchmark
39 of 49Software skills with no higher success rate in SWE-Skills-Bench

Down 80%: What it looked like inside

The starting point is the conversation Simon Willison published on 21 July. Shihipar explained that the Claude Code team had cut the system prompt for Fable 5 and Opus 4.8 by 80%. The detailed version remained in place for older models. These are model-specific instructions, not a universal recipe.

Claude Code is Anthropic’s coding agent. Its system prompt controls tool use, working methods, and output, among other things. Every line had been added for a specific reason at some point. Even so, the revision showed that many of those reasons no longer held for the new model generation.

Willison’s transcript names four changes that transfer to ordinary knowledge work:

  1. Examples were removed when they constrained the model more tightly than necessary. Shihipar said the models were more creative without those examples.
  2. Prohibition lists gave way to more context. Less “Do not do X,” more information about the result required and the boundaries that genuinely apply.
  3. Absolute rules were checked for exceptions. The team found instructions that were right in 90% of cases but disruptive in the remaining 10%. Those rules were rewritten as conditions.
  4. The system prompt was maintained per model. The shorter version applied to Fable 5 and Opus 4.8, not automatically to smaller or older models.

The underlying pattern is clear: a criterion leaves room for the model to exercise judgment. An absolute prohibition works only in the situations its authors anticipated.

In June, I analyzed the leaked system prompt of the Claude Fable 5 chatbot: 17,000 words, dozens of sections, repetitions, and outdated passages. For context, that is a different document. The Claude Code team shortened the prompt for its coding agent because every Anthropic product has its own instructions. At the time, I described just how extensive and contradictory such an internal document can become.

You should take one rule from this section. You can apply it immediately without waiting for the rest of the article. Open your instructions, perhaps your user prompt, project description, or preferred work prompt, and look for prohibitions:

  • “Never use lists.”
  • “Do not write at length.”
  • “Do not ask unnecessary questions.”

Rewrite every prohibition as a criterion.

Bad: “Never write at length.”

Better: “Match the length of the answer to the question: a few paragraphs are enough for an everyday question; use as much space as necessary for a professional analysis.”

Why is this better? The criterion works in different situations without requiring you to regulate each one in advance. The Claude Code team described a similar change to its system prompt. Next comes the question of why this became necessary in the first place and where the excess in your prompts comes from.

Where prompt debt comes from: five causes

The 80% reduction raises an uncomfortable question. If four fifths of the prompt were unnecessary even for the team at one of the world’s strongest AI companies, despite metrics, evaluations, and reviews, what do your prompts, skills, and CLAUDE.md files look like if nobody has ever measured them? The answer begins with how the debt accumulates.

The originator of the term provides the starting point. Breunig’s definition rests on a distinction worth remembering. Natural-language prompts are excellent for one-off requests and conversational work. A person sees every answer and can correct it immediately. But they are a poor control mechanism for a system expected to do the same job unattended day after day. Breunig observed this in production applications where the prompt effectively replaces the technical specification. Katie Parrott of Every applied his logic to ordinary users and formulated the definition I use throughout this article: prompt debt consists of instructions that have outlived the model they were written for.

Cause 1: Model progress. From the first versions of ChatGPT until mid-2025, models worked very inconsistently. Prompts therefore needed numerous instructions and tricks. Taylor recalls that he used “hacks and magic words” to coax usable code out of GPT-3. These methods were no longer necessary with GPT-4. Yet even o3, OpenAI’s leading reasoning model in the first half of 2025, hallucinated more often than its predecessor o1 according to the company’s measurements. More capability did not automatically mean greater reliability. Errors still had to be corrected through instructions, constraints, and examples of “what it should and should not look like.”

There was another problem: older models ignored even explicit instructions. It was therefore perfectly normal to repeat central requirements two, three, or four times in a prompt. The habit survives to this day. In the snapshot of the Fable chatbot system prompt that Breunig examined in June, he counted repetitions of the copyright rules in as many as six places, spread across six different sections of the document.

Cause 2: The recommendation to correct the prompt after every failure. The usual response is to add instructions intended to prevent the same error in the future. The idea is logical. The problem is that users readily add new instructions but forget to check after each model release whether those instructions are still needed.

Cause 3: Psychology. Many people still do not fully trust AI. The AI does the work, but you bear the responsibility. The impulse to protect yourself psychologically is therefore understandable: you explain in minute detail how the model should work, provide examples of good work, and add prohibitions and constraints. In some prompts, THE AUTHORS NATURALLY LAPSE INTO ALL CAPS because they hope the AI will work better that way.

It does not.

Incidentally, the third cause is the only one that no technique or trick can fix. You have to change your attitude toward AI. The decisive checks of the result remain, of course. But if you instruct and monitor every single step, it is honestly easier to do the work yourself.

Cause 4: Prompt debt is contagious. You see a prompt with many likes online or download a skill with thousands of stars. But have you checked how the whole thing works? Which model generation was it written for? Is the author trustworthy?

Cause 5: Instructions outlive the model. This is the most insidious form of debt and, according to Breunig, its most important symptom. A prompt is always created for a specific model, tailored to its strengths and weaknesses. The model changes, but the instructions remain. Some of them fail in unexpected ways on the new model. Breunig points to data from monitoring company Datadog as of spring 2026: GPT-4o remained the most widely used model in the traffic Datadog observed. That is a model from 2024. Not because it is the best, but because prompts adjusted for GPT-4o over several years fall apart on newer models. For teams, it is easier to touch nothing. At that point, users no longer hold the prompt. The prompt holds its users and ties them to an aging model.

The turning point in 2026: The rules work, but not as intended

Why is the debt exploding now? Prompts have been growing for years, and until recently, hardly anyone was bothered. The answer initially seems contradictory. It was not the weaknesses of new models that turned prompt debt into a problem, but their strength. In 2026, models are trained primarily for agentic scenarios, meaning long, independent work without a person correcting the AI after every step. To do that, a model must follow instructions precisely. It is now being taught to do exactly that in earnest. Suddenly the problem becomes visible: new models fully execute the multi-story prompts that older ones merely skimmed. Everything written into prompts over the years now takes effect at once.

This shift is clearest in the current guide to GPT-5.6. OpenAI recommends outcome-oriented prompts: the goal, relevant context, constraints, required evidence, success criteria, and output format. Requests such as “Think harder” or “Generate multiple candidates” are unnecessary when the selected model and reasoning mode already govern that work.

The guide also advises using leaner system prompts. Repeated instructions and examples should be removed, tool descriptions simplified, and each instruction stated only once. Examples and style rules stay where they express a product requirement or close a measured gap. Brevity is not an end in itself. What matters is the effect in testing.

The obvious conclusion is that everything will work if you phrase rules more carefully, without contradictions or unnecessary words. Unfortunately not. What matters is the scale on which models have become obedient. They now execute individual instructions precisely. That is exactly what makes old prompts dangerous. But there is a gulf between “execute one line” and “keep a hundred-page rulebook in mind across fifty work steps.” The new generation has not crossed it either.

The next blow to the old approach is the HANDBOOK.md benchmark from Surge AI’s evaluation team. The agents received 65 work tasks in virtual companies from five industries. They also received company policies ranging from 20 to 124 pages. The result: the best model passed 36.2% of the tests strictly; most leading models passed fewer than a quarter. The researchers also documented cases in which agents reported complying with the policy even though their actions violated it. A large rulebook does not create control. It can create the illusion of control.

This gives us the following picture. In 2024, an inflated prompt was harmless ballast. The model ignored what it did not need. In 2026, the same prompt causes harm in three ways at once:

  1. Outdated rules are executed literally and eagerly.
  2. Unnecessary instructions consume context, tokens, and time.
  3. Genuinely large rulebooks remain unreliable nonetheless, including false reports of compliance.

This is precisely why cleanup produces measurable gains. In OpenAI’s internal runs, converting prompts to the outcome-first formula improved quality by 10 to 15%. At the same time, token use fell by 41 to 66% and costs by 33 to 67%. OpenAI explicitly adds a caveat to these figures: they are indicative. Test them on your own tasks.

You can put the practical consequence of this section into effect in five minutes too. Open your preferred work prompt and look for intensifying phrases:

  • “as thoroughly as possible”
  • “check every variant”
  • “verify several times”
  • “achieve complete certainty”

Delete them. Then use typical tasks to check whether answer quality remains stable. If you work through an API or with an agent, you may also save money or subscription limits. This optimization consists of pressing the Delete key.

One inconsistency remains. If rules, examples, and details should disappear from prompts, what belongs in them? After all, a prompt cannot consist solely of the line “Do it well.”

It can.

But it should not.

What remains after the cuts is therefore what matters.

How to prompt models today: Do not write rules, fill the “gaps”

One formula is enough for this section: do not burden the model with what it already knows. Give it what only you know. Roughly speaking, modern models have read almost every book in the world. Teaching them the craft is pointless. What a model does not know, and will never discover on its own, is your specific situation.

In my analysis of Fable 5’s system prompt, I called this information the “gaps”: What do you need the result for? Who will read it? Which decision should it support? What does “good” mean to you? The system prompt was not written for you, and the model weights certainly were not. Nobody else will fill these gaps for you. This gives you a simple test for every line in your prompt. Explaining to the model how to write articles makes no sense. It has seen millions of them. Length, structure, and editorial requirements are useful, by contrast. They are not in any book because they come from you.

You need to supply knowledge yourself in only two cases. The first is a very narrow specialist field with few texts worldwide: your company’s internal processes, regional particulars, a rare method, or a tightly defined profession. The second is anything that happened after the model’s knowledge cutoff. I start such a session with this sentence: “We are working on X. First collect up-to-date information on the subject. It is now August 2026.” The model then searches instead of answering from the previous year’s knowledge.

I can now condense this into the framework of a new prompt. The outcome-first formula from OpenAI’s guide can be translated as follows: outcome, acceptance criteria, constraints, specific context. In my first article about prompts, I showed a detailed prompt for choosing a laptop in five blocks: task, output format, constraints, self-check points, and context. Under the 2026 formula, the same task looks like this:

”Help me choose a laptop. I carry it with me every day, so it must weigh no more than 1.8 kg. I work in the browser with 20+ tabs and in Figma, and play Civilization in the evenings. My budget is 800 to 1,200 euros. The task is complete when you have found 3 to 5 current models available at that price, each with verified links to stores. If you are missing information needed for the choice, ask before you begin searching.”

Compared with the old version, two blocks have disappeared. There is no output format because the model can choose the table itself. If you have a fixed requirement, it belongs in a single line as a constraint. The self-check has gone too because it is already built into the acceptance criterion: “current models,” “available,” and “with verified links.” The prompt is only half as long. But the context was not shortened; it was expanded. My rule is: cut rules, not information.

Another interesting point. I briefly tested the prompt in regular ChatGPT and got this result:

ChatGPT asks for the country of purchase and preferred Civilization version before recommending a laptop
ChatGPT clarifies two missing details before researching models and store links. My own test from 14 August 2026.

Current models increasingly ask follow-up questions themselves when crucial information is missing. This is another reason to prompt models as simply as possible. They now try to obtain any additional knowledge they need on their own. My data analysis “The prompt as a Lego figure” shows how widely a request can branch into sub-questions in the process.

For now, however, this only helps with smaller examples. On a substantial task, especially in an AI agent, the work begins before the prompt: review blind spots and rank questions by importance. I have already discussed this preparation using the image of “map and territory,” based on Thariq Shihipar’s guide. Preparation does not mean inflating everything. Its result is a short brief with good references, not a rulebook.

When accepting the work, the artifact counts. If the result is program code, a presentation, a text, or an outline, inspect that artifact rather than the report about it. The documented failures in the HANDBOOK.md benchmark show how convincingly an agent can report policy compliance even when its actions violate that policy. The same applies to work reports: a polished summary is not evidence that the work succeeded.

If you cannot evaluate the artifact directly, upload it to a new chat or a new AI agent and have it analyzed. Even with the same model, the new chat is set up to inspect someone else’s result without preconceptions. An independent review agent works well for this: it does not know the artifact’s history and looks specifically for errors rather than reasons to defend its own work.

The one-line checklist for this section is: expected outcome, acceptance criteria, constraints, your context. Accept the artifact, not the story told about it.

All the advice in this section concerns prompts you are writing now. But that is not where most of the debt sits. It hides in the archive: in Google Docs with collections of “favorite prompts,” in skills you have not opened for a long time, in the model’s memory of you, and in loops where the AI instructs itself.

Where the debt hides: three traps

So far, this article has dealt with debt in prompts. At least that debt is visible because the prompt is in front of you. Debt that accumulates where nobody looks is more difficult. I know three such places. One of them became a problem for me personally.

Trap 1: Models teach one another bad habits. When models write prompts of their own, they often reproduce patterns from 2024 and 2025. In my test with Fable, this produced a draft based on an already outdated pattern.

While working on this article, I wanted to develop a prompt that could clean outdated prompts and skills of unnecessary elements. I gave the task to Fable. This was the result:

”Here is my [skill / CLAUDE.md / user prompt]. First inspect it without rewriting anything. Assign every instruction to one of these categories: (1) invariant: a fact or constraint you cannot discover on your own; (2) duplicate of trained behavior: you do this even without an instruction; (3) relic: written for an old model or an error that can no longer be reproduced; (4) 90% rule: usually true but phrased as ‘always/never’; (5) conflict: contradicts or overlaps another line. For (2) and (3), propose deletion; for (4), propose conditional wording; and for (5), say which line should remain. Leave safety and access rules untouched. At the end, show a shorter version and a list of deleted items, each with a one-line reason. I will decide on every line.”

Look closely. Several errors stand out immediately.

  • Contradictory instructions: nothing is to be rewritten at the beginning, but a shorter version is requested at the end.
  • Analysis and action are mixed: the user is supposed to decide on every line, but Claude already supplies a finished short version in the same answer.
  • The detailed inspection instructions in points 1 to 5 also contradict the new rules for prompts. With some generosity, they are nevertheless justified because even new models do not currently know these rules.

My conclusion is simple: be careful when you ask AI to write a prompt from scratch. It will provide a usable draft. Remove everything unnecessary from the final version. Later in this article, I explain how to refactor prompts manually and also show a very simple prompt I use myself.

Trap 2: Skills. A skill is also a prompt, only stored: an instruction that the model loads for a specific type of task. Debt can therefore hide there too.

First, consider a skill downloaded from the internet. In “The Case Against Skills,” Taylor’s position is supported by a measurement: SWE-Skills-Bench. The researchers took 49 public programming skills and ran the same tasks with and without them. The result: 39 of the 49 skills produced no improvement at all. The average gain was plus 1.2%, while the additional token cost of individual skills reached as much as plus 451% for the same result. The measurement is from March 2026 and tested software engineering tasks with Claude Code and Haiku 4.5. The authors themselves name the benchmark’s narrow context as a limitation. The figures therefore cannot simply be transferred to writing, research, or other knowledge work.

The benchmark examined third-party public skills in software engineering tasks. That is exactly where another problem lies. Many people download popular skills that promise better results in programming or web design or claim to stop the model producing AI slop. But the author wrote the skill for themselves, not for you. Their idea of “improvement” is not automatically yours. Taylor’s principle therefore convinces me: every skill has to earn its place in the library through measurement, not through a feeling of usefulness. Take several tasks, run them with and without the skill, and compare the results. If you do not like the result, the skill is not right for you.

In winter, I began using skills for Claude, first in a chatbot for web search and text work, later in Claude Code as well. My process was this: first I discussed the task with Claude and developed a draft workflow. Then I tested it in several experiments, gave feedback, and had Claude turn the rules that held up into a skill.

When the model made mistakes during the work or I noticed opportunities for improvement, I described them to Claude and had it revise the skill.

At first, this worked well. Working with the skill became faster, and the model delivered results I liked. Later, the quality fell somewhat. I thought this was the typical effect where you are initially satisfied with a piece of work and only recognize its flaws later. So I continued revising the skill in the same way without noticing the actual problem.

The slump came with the release of Opus 4.7. Using the same skill, the model suddenly wrote terrible texts: broken structure, strange language, completely unreadable headings. In small writing experiments, by contrast, the model did not work badly at all.

After another failed text, I explained directly in the chat how Opus should revise it. The model unexpectedly refused with the words: “The skill forbids that!” Only then did I open the skill myself, and I was horrified. Over the course of months, the model had filled it with instructions and examples of what to do and what to avoid. It was a textbook case of prompt debt, ready for framing.

Since then, I have followed a simple rule: do not just keep developing a skill. Refactor it regularly and remove everything outdated or disruptive. The same applies to the next point, incidentally.

Trap 3: Loops in which AI instructs itself. In my assessment, these are the most important potential breeding ground for prompt debt in the coming months. I described how such loops work in my article on loop engineering: an AI prompts itself according to the rules of 2024 and 2025, repeatedly evaluates the result, and tries to improve it through revised prompts. Naturally, those prompts also follow the rules of 2024 and 2025.

How quickly this pattern will change with new model generations cannot be predicted seriously. Until then, the solution is the same as for skills: switch to manual mode occasionally, intervene in the loop, and analyze the instructions governing its work together with the model. Everything unnecessary goes.

Refactoring prompt debt

In large IT teams, paying down technical debt is part of ongoing operations. There are different methods, but they all follow the same idea: you must regularly reserve a fixed amount of time to reduce technical debt.

The psychology is easy to understand. Suppose you have set up your AI cleanly and save 2 hours a day as a result. If you do nothing, those savings gradually shrink, as my example with skills shows. Eventually, you have to set up your processes again. If instead you invest 10 minutes of the time saved each day in refactoring, the savings will at least remain stable and will most likely grow. You do not have to spend 10 minutes on prompts and skills every day. I prefer to do it every 2 weeks and set aside 2 hours at once.

This is how I would structure the process.

Prompt refactoring
From an old rule to a tested short version
01 · InventoryCollect persistent instructionsCombine the user prompt, memory, skills, and project files in one list.
02 · Decision for each lineInvariant or judgment?Can the model know this fact or boundary on its own?
Judgment → delete“Write correctly” or “Pay attention to detail” merely requests vague behavior.
or
Invariant → keep checkingPersonal context, product requirements, and safety and access boundaries remain candidates.
03A · AbsoluteGenuine safety rule?Then keep it unchanged. Rewrite a 90% rule as a condition instead.
plus
03B · ContextCan it be inferred from the material?Then delete it. Keep only information the model cannot otherwise know.
04 · MeasurementTest old against shortenedRun two or three typical tasks with identical acceptance criteria.
WorseRestore the effective rule.
Equal or betterKeep the shortened version.
The decision tree separates deletion candidates from genuine invariants. Measurement decides, not the ideal of the shortest possible prompt.

Step 1: Take inventory. Collect all persistent instructions in one document: user prompt, project instructions, memory, meaning what the model has stored about you, skills, and, if applicable, CLAUDE.md and its equivalents. I explained where these elements live in detail in my article about the six areas of personalization. The most important reminder is enough here: the instructions are scattered across different corners of the user interface. That is half the problem. You only have to take this inventory once. Afterward, everything is readily available in one place.

Do not try to refactor everything immediately. Start with tasks that are current and useful but not critical. There is no guarantee that everything will work perfectly on the first attempt. Damaging a critical process would not be a particularly good idea. Nor would it make much sense to refactor the prompt for planning a vacation if you do not intend to travel for another year.

Step 2: Invariant or judgment? Go through the list and assign each line to one of two types. An invariant is a fact the model cannot discover on its own: your name, what you do for a living, that you write without the letter “ё,” or that you only need tables for comparisons. A judgment names something the model already knows how to do without being told: “Write correctly,” “Pay attention to detail,” “Answer professionally,” “Check the logic.” The rule is simple: invariants stay, judgments go. You do not make a model more attentive by commanding attention. You merely force it to guess what you meant.

Step 3: Turn “always” and “never” into conditions. Apply a method the Claude Code team calls “90/10” to the lines that survived step 2: if a rule is right in 90% of cases, do not phrase it as a prohibition. Describe when it applies. “Always answer briefly” becomes: “Answer everyday questions briefly and professional analyses in detail.” OpenAI’s recommendation points in the same direction: reserve absolute wording for genuine prohibitions, meaning things that must never happen under any circumstances.

For a normal person, this produces two or three lines in a long prompt, not twenty.

Step 4: Remove everything that can be inferred from context. Instructions that merely repeat what the model already recognizes from context form a category of prompt debt in their own right. If you ask the model to work with code, you do not also need to write “Answer as if speaking to a developer” in the prompt. The model chooses that style as its default on its own. If you attach editorial guidelines to a prompt, you do not need to repeat each point from them.

There is also the opposite case, where such an instruction is useful. Suppose you come across the preprint of an important scientific paper on mathematics, download it, give it to the AI, and ask what happened in it. By default, the model speaks to you as if you were a mathematician. If you are not, add this to the prompt: “Explain it to a layperson.”

Step 5: Measure. Remember OpenAI’s note: “The figures are indicative; test them against your own tasks”? It applies against us too. Do not simply take my word or Anthropic’s. Take two or three typical tasks, run each once with the old collection and once with the shorter version, and compare the results. Does the shorter version perform worse anywhere? Good. You have found an instruction that actually works. Restore it with a clear conscience. A revision is not an ideology of total minimalism. It helps you distinguish effective lines from ballast.

When you should not shorten prompts radically

Genuine invariants, personal facts, and all safety and access restrictions stay. These include rules such as “Do not send emails without confirmation,” “Do not touch the production environment,” and “Do not use payment data.” They are not debt but seat belts. Their value does not depend on how often they are needed.

The same applies to internal specialist knowledge, regional particulars, and binding product requirements. If the model cannot know this information on its own, its length is no reason to delete it.

Examples and formatting rules can also stay when a comparison test shows a measurable advantage. Refactoring follows the results of your tasks, not an ideal of the shortest possible prompt.

Checking prompt debt automatically: Script and audit prompt

I built a small detector for the initial inventory. The Python script has no external dependencies. It flags model self-descriptions, absolute prohibitions, repeated objects of prohibitions, and a few clearly defined contradictions. A meta-prompt for semantic review with a language model is included as well.

There is an important limitation: the script understands neither your work context nor the consequences of deleting a rule. It currently checks German-language prompts. A match is therefore a review candidate, not an instruction to delete. Safety rules, access boundaries, and binding product requirements remain in place until a person approves a change.

Download both files as a ZIP. The script and prompt are also available separately as prompt_schulden_detektor.py and prompt-schulden-audit-prompt.txt.

How to run the detector

Save the script as prompt_schulden_detektor.py. It runs on Python 3.10 or newer:

python3 prompt_schulden_detektor.py "Your prompt"
python3 prompt_schulden_detektor.py path/to/prompt.md
cat prompt.txt | python3 prompt_schulden_detektor.py --stdin
python3 prompt_schulden_detektor.py --stdin --json < prompt.txt

The text output is suitable for manual review. The --json option returns structured findings for other tools. The --no-outdated, --no-forbidden, and --no-repetitions options disable individual checks.

The complete Python detector

#!/usr/bin/env python3
"""Heuristischer Prompt-Schulden-Detektor ohne externe Abhängigkeiten.

Der Detektor markiert Kandidaten. Er kann nicht entscheiden, ob eine Sicherheits-
oder Produktregel fachlich notwendig ist. Prüfen Sie jeden Treffer im Kontext und
vergleichen Sie Änderungen an realen Aufgaben.

Verwendung:
    python prompt_schulden_detektor.py "Ihr Prompt"
    python prompt_schulden_detektor.py pfad/zum/prompt.md
    cat prompt.txt | python prompt_schulden_detektor.py --stdin
    python prompt_schulden_detektor.py --stdin --json
"""

from __future__ import annotations

import argparse
import json
import re
import sys
from collections import Counter, defaultdict
from pathlib import Path
from typing import Iterable


class PromptSchuldenDetektor:
    """Findet auffällige Regeln mit bewusst konservativen Heuristiken."""

    def __init__(
        self,
        enable_outdated: bool = True,
        enable_forbidden: bool = True,
        enable_repetitions: bool = True,
    ) -> None:
        self.enable_outdated = enable_outdated
        self.enable_forbidden = enable_forbidden
        self.enable_repetitions = enable_repetitions

        self.severity = {
            "outdated": "info",
            "forbidden": "warning",
            "repetition": "warning",
            "contradiction": "critical",
        }

        self.outdated_patterns = [
            (
                re.compile(
                    r"\b(?:du|Sie)\s+(?:bist|sind)\s+(?:ein(?:e)?\s+)?"
                    r"(?:KI-Assistent|Sprachmodell|künstliche\s+Intelligenz|Assistent|KI)\b",
                    re.IGNORECASE,
                ),
                "Die Selbstidentifikation beschreibt das Modell statt der Aufgabe.",
                "Entfernen, sofern die Rolle nicht für die konkrete Ausgabe nötig ist.",
            ),
            (
                re.compile(
                    r"\b(?:denke\s+daran|erinnere\s+dich|vergiss\s+nicht)\s*,?\s*"
                    r"(?:dass\s+)?(?:du|Sie)\s+(?:ein(?:e)?\s+)?"
                    r"(?:Sprachmodell|KI|Assistent|kein\s+Mensch)\s+(?:bist|sind)?\b",
                    re.IGNORECASE,
                ),
                "Die Erinnerung an die KI-Natur steuert kein fachliches Ergebnis.",
                "Entfernen oder durch ein konkretes Ausgabeziel ersetzen.",
            ),
            (
                re.compile(
                    r"\b(?:erwähne|sage)\s+nicht\s*,?\s*(?:dass\s+)?du\s+"
                    r"(?:eine?\s+)?(?:KI|Modell|Assistent)\s+bist\b|"
                    r"\b(?:erwähnen|sagen)\s+Sie\s+nicht\s*,?\s*(?:dass\s+)?Sie\s+"
                    r"(?:eine?\s+)?(?:KI|Modell|Assistent)\s+sind\b",
                    re.IGNORECASE,
                ),
                "Das Verbot betrifft die Selbstdarstellung des Modells, nicht die Aufgabe.",
                "Nur behalten, wenn die Formulierung eine verbindliche Produktanforderung ist.",
            ),
            (
                re.compile(
                    r"\b(?:du|Sie)\s+(?:hast|haben|besitzt|besitzen)\s+"
                    r"(?:kein|keine|keinen)\s+(?:Bewusstsein|Emotionen|Gefühle)\b",
                    re.IGNORECASE,
                ),
                "Die Aussage über Bewusstsein oder Gefühle hilft selten bei der Aufgabe.",
                "Entfernen, sofern sie nicht Gegenstand der gewünschten Antwort ist.",
            ),
        ]

        object_text = r"(?P<object>[^.!?\n]{2,120})"
        self.forbidden_patterns = [
            re.compile(
                r"\b(?:verwende|benutze|nutze|verwenden\s+Sie|benutzen\s+Sie|nutzen\s+Sie)\s+"
                r"(?:bitte\s+)?(?:nicht|kein(?:e|en|er|es)?)\s+" + object_text,
                re.IGNORECASE,
            ),
            re.compile(
                r"\b(?:sage|erwähne|antworte|sagen\s+Sie|erwähnen\s+Sie|antworten\s+Sie)\s+"
                r"(?:bitte\s+)?nicht\s+" + object_text,
                re.IGNORECASE,
            ),
            re.compile(
                r"\b(?:vermeide|vermeiden\s+Sie)\s+" + object_text,
                re.IGNORECASE,
            ),
            re.compile(
                r"\b(?:es\s+ist\s+verboten|verboten\s+ist)\s+" + object_text,
                re.IGNORECASE,
            ),
            re.compile(
                r"\b(?:niemals|auf\s+keinen\s+Fall)\s+" + object_text,
                re.IGNORECASE,
            ),
        ]

        self.contradiction_pairs = [
            (
                re.compile(r"\b(?:sei|seien\s+Sie|antworte|antworten\s+Sie)\s+kurz\b", re.IGNORECASE),
                re.compile(
                    r"\b(?:gib|geben\s+Sie)\s+(?:eine\s+)?ausführliche\s+Antwort\b|"
                    r"\bantworte\s+ausführlich\b",
                    re.IGNORECASE,
                ),
                "Kürze und Ausführlichkeit werden gleichzeitig verlangt.",
                "Bedingungen ergänzen: wann kurz, wann ausführlich.",
            ),
            (
                re.compile(
                    r"\b(?:verwende|benutze|verwenden\s+Sie|benutzen\s+Sie)\s+"
                    r"(?:keinen|nicht)\s+Slang\b|\b(?:vermeide|vermeiden\s+Sie)\s+Slang\b",
                    re.IGNORECASE,
                ),
                re.compile(
                    r"\b(?:verwende|benutze|verwenden\s+Sie|benutzen\s+Sie)\s+Slang\b",
                    re.IGNORECASE,
                ),
                "Slang wird zugleich verboten und verlangt.",
                "Eine Regel entfernen oder die Zielgruppen als Bedingungen nennen.",
            ),
            (
                re.compile(
                    r"\b(?:verwende|verwenden\s+Sie)\s+(?:keine|nicht)\s+Listen\b|"
                    r"\b(?:vermeide|vermeiden\s+Sie)\s+Listen\b",
                    re.IGNORECASE,
                ),
                re.compile(
                    r"\b(?:verwende|verwenden\s+Sie)\s+(?:immer\s+)?Listen\b",
                    re.IGNORECASE,
                ),
                "Listen werden zugleich verboten und verlangt.",
                "Den passenden Ausgabetyp je Aufgabe als Bedingung formulieren.",
            ),
        ]

    @staticmethod
    def _normalisiere_objekt(text: str) -> str:
        text = text.casefold().strip(" \t.,!?;:()[]{}\"'„“‚‘")
        text = re.sub(r"\b(?:der|die|das|den|dem|des|ein|eine|einen|einem|einer)\b", " ", text)
        text = re.sub(r"\s+", " ", text)
        return text.strip()

    @staticmethod
    def _overlaps(a: dict, b: dict) -> bool:
        start = max(a["start"], b["start"])
        end = min(a["end"], b["end"])
        if end <= start:
            return False
        shorter = min(a["end"] - a["start"], b["end"] - b["start"])
        return (end - start) / max(shorter, 1) >= 0.8

    def _finde_veraltete_muster(self, text: str) -> list[dict]:
        findings = []
        for pattern, explanation, recommendation in self.outdated_patterns:
            for match in pattern.finditer(text):
                findings.append(
                    {
                        "category": "outdated",
                        "severity": self.severity["outdated"],
                        "snippet": match.group(0).strip(),
                        "explanation": explanation,
                        "recommendation": recommendation,
                        "start": match.start(),
                        "end": match.end(),
                    }
                )
        return findings

    def _finde_verbote(self, text: str) -> list[dict]:
        findings = []
        for pattern in self.forbidden_patterns:
            for match in pattern.finditer(text):
                findings.append(
                    {
                        "category": "forbidden",
                        "severity": self.severity["forbidden"],
                        "snippet": match.group(0).strip(),
                        "object": self._normalisiere_objekt(match.group("object")),
                        "explanation": (
                            "Die absolute Formulierung kann eine sinnvolle Ausnahme blockieren. "
                            "Die Heuristik kann ihre fachliche Notwendigkeit nicht beurteilen."
                        ),
                        "recommendation": (
                            "Prüfen, ob eine echte Sicherheitsgrenze vorliegt; andernfalls als "
                            "Bedingung formulieren und im Vergleichstest prüfen."
                        ),
                        "start": match.start(),
                        "end": match.end(),
                    }
                )
        return findings

    def _finde_wiederholungen(self, prohibitions: Iterable[dict]) -> list[dict]:
        groups: defaultdict[str, list[dict]] = defaultdict(list)
        for finding in prohibitions:
            obj = finding.get("object", "")
            if obj:
                groups[obj].append(finding)

        findings = []
        for obj, items in groups.items():
            if len(items) < 2:
                continue
            findings.append(
                {
                    "category": "repetition",
                    "severity": self.severity["repetition"],
                    "snippet": " | ".join(item["snippet"] for item in items),
                    "phrase": obj,
                    "count": len(items),
                    "explanation": f'Dasselbe Verbotsobjekt „{obj}“ erscheint {len(items)} Mal.',
                    "recommendation": "Zu einer einzigen, möglichst bedingten Regel zusammenführen.",
                    "start": min(item["start"] for item in items),
                    "end": max(item["end"] for item in items),
                }
            )
        return findings

    def _finde_widersprueche(self, text: str) -> list[dict]:
        findings = []
        for left_pattern, right_pattern, explanation, recommendation in self.contradiction_pairs:
            left = left_pattern.search(text)
            right = right_pattern.search(text)
            if not left or not right:
                continue
            findings.append(
                {
                    "category": "contradiction",
                    "severity": self.severity["contradiction"],
                    "snippet": f'„{left.group(0)}“ vs. „{right.group(0)}“',
                    "explanation": explanation,
                    "recommendation": recommendation,
                    "start": min(left.start(), right.start()),
                    "end": max(left.end(), right.end()),
                }
            )
        return findings

    def analysiere(self, prompt_text: str) -> dict:
        outdated = self._finde_veraltete_muster(prompt_text) if self.enable_outdated else []
        prohibitions = self._finde_verbote(prompt_text) if self.enable_forbidden else []

        # Eine modellbezogene Altlast wird nicht zusätzlich als allgemeines Verbot gezählt.
        prohibitions = [
            finding
            for finding in prohibitions
            if not any(self._overlaps(finding, old) for old in outdated)
        ]

        findings = [*outdated, *prohibitions]
        if self.enable_repetitions:
            findings.extend(self._finde_wiederholungen(prohibitions))
        findings.extend(self._finde_widersprueche(prompt_text))
        findings.sort(key=lambda item: (item["start"], item["end"], item["category"]))

        counts = Counter(finding["category"] for finding in findings)
        return {
            "total_findings": len(findings),
            "counts": dict(sorted(counts.items())),
            "findings": findings,
            "limitations": (
                "Heuristische Prüfung: Treffer sind Kandidaten, keine automatische "
                "Löschentscheidung. Sicherheits- und Produktregeln müssen fachlich geprüft werden."
            ),
        }

    def formatiere_bericht_text(self, report: dict) -> str:
        if report["total_findings"] == 0:
            return "Keine Prompt-Schulden gefunden."

        category_names = {
            "outdated": "Veraltete Muster",
            "forbidden": "Potenziell überflüssige Verbote",
            "repetition": "Wiederholte Anforderungen",
            "contradiction": "Widersprüchliche Anweisungen",
        }
        order = ("outdated", "forbidden", "repetition", "contradiction")
        grouped: defaultdict[str, list[dict]] = defaultdict(list)
        for finding in report["findings"]:
            grouped[finding["category"]].append(finding)

        lines = [f"Potenzielle Probleme gefunden: {report['total_findings']}", ""]
        for category in order:
            if not grouped[category]:
                continue
            lines.append(category_names[category] + ":")
            for finding in grouped[category]:
                lines.extend(
                    [
                        f"- Fragment: {finding['snippet']}",
                        f"  Warum: {finding['explanation']}",
                        f"  Empfehlung: {finding['recommendation']}",
                        f"  Schweregrad: {finding['severity']}",
                    ]
                )
            lines.append("")
        lines.append("Hinweis: " + report["limitations"])
        return "\n".join(lines)

    @staticmethod
    def formatiere_bericht_json(report: dict) -> str:
        return json.dumps(report, ensure_ascii=False, indent=2)


def _looks_like_path(value: str) -> bool:
    path = Path(value)
    return (
        value.startswith(("/", "./", "../", "~"))
        or path.suffix.casefold() in {".txt", ".md", ".prompt", ".json", ".yaml", ".yml"}
    )


def _read_input(value: str) -> str:
    path = Path(value).expanduser()
    if path.is_file():
        return path.read_text(encoding="utf-8")
    if _looks_like_path(value):
        raise FileNotFoundError(value)
    return value


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Prompt-Schulden heuristisch erkennen")
    source = parser.add_mutually_exclusive_group(required=True)
    source.add_argument("input", nargs="?", help="Prompt-Text oder Pfad zu einer Textdatei")
    source.add_argument("--stdin", action="store_true", help="Prompt aus stdin lesen")
    parser.add_argument("--json", action="store_true", help="Ausgabe im JSON-Format")
    parser.add_argument("--no-outdated", action="store_true", help="Veraltete Muster nicht prüfen")
    parser.add_argument("--no-forbidden", action="store_true", help="Verbote nicht prüfen")
    parser.add_argument("--no-repetitions", action="store_true", help="Wiederholungen nicht prüfen")
    return parser


def main(argv: list[str] | None = None) -> int:
    args = build_parser().parse_args(argv)
    try:
        prompt_text = sys.stdin.read() if args.stdin else _read_input(args.input)
    except (FileNotFoundError, OSError) as error:
        print(f"Datei nicht gefunden: {error}", file=sys.stderr)
        return 2

    detector = PromptSchuldenDetektor(
        enable_outdated=not args.no_outdated,
        enable_forbidden=not args.no_forbidden,
        enable_repetitions=not args.no_repetitions,
    )
    report = detector.analysiere(prompt_text)
    if args.json:
        print(detector.formatiere_bericht_json(report))
    else:
        print(detector.formatiere_bericht_text(report))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Meta-prompt for semantic review

Regular expressions recognize explicit wording but not repetitions that express the same idea across several paragraphs. That is what the second part is for: a language model proposes review candidates and comparison tests. Here too, the decision remains with the person doing the review.

You are reviewing existing prompts for possible prompt debt. Analyze the text between <PROMPT> and </PROMPT>. Do not change it yet.

Check five categories:

1. Unnecessary or overly absolute prohibition: wording with "not," "never," "avoid," or similar terms that may block useful exceptions.
2. Outdated pattern: an instruction describes the nature of the model or visibly repairs the behavior of an earlier model generation.
3. Repetition: the same intent appears several times in the prompt, including in different wording.
4. Contradiction: two instructions cannot be satisfied at the same time or in the same context.
5. Unclear purpose: the text does not show which outcome or risk the instruction is intended to control.

For every finding, return:

- type
- exact fragment from the prompt
- possible reason for the original rule
- current risk created by the rule
- recommendation: keep, test, merge, or phrase as a condition
- severity: info, warning, or critical
- confidence in your assessment: low, medium, or high

Rules for the analysis:

- Treat every finding as a review candidate, not an automatic deletion decision.
- Keep safety rules, access boundaries, legal requirements, and binding product requirements unless their purpose has been disproved.
- Do not claim that a rule is part of the model's default behavior when you have only a hunch. Recommend a comparison test in that case.
- If the original purpose is not evident from the text, write "unclear."
- Quote fragments exactly. Do not invent additional instructions.
- Do not produce a revised complete version yet.

End with two lists:

1. "Decision by a person": every passage where context or risk knowledge is missing.
2. "Suggested comparison tests": small tasks that can test the effect of the suspect rules with and without the instruction.

If you find no candidates, write: "No prompt debt found."

<PROMPT>
[INSERT THE PROMPT TO ANALYZE HERE]
</PROMPT>

Run the analysis first. Then decide which candidates genuinely need testing. A shorter version is created only after this selection and must compete against the original on typical tasks.

Three simple methods help with ongoing maintenance.

The first is the “clean run,” which I already recommended in the article about Fable 5. When a new model appears, disable the user prompt, cross-chat memory, and all skills. Then run a few of your simple standard tasks with the model from scratch. This reveals the model’s “raw” abilities. Afterward, switch everything back on and quickly compare whether the quality changed on the same tasks. If it fell, refactoring is due.

The second method is a prompt I have used for more than a year to improve my prompts, skills, and other elements:

Read [prompt / skill / CLAUDE.md]. What is done well? What is missing for the work? What restricts you while working? Are there instructions that contradict one another? List every suggestion. I will decide what to improve.

This prompt does not replace refactoring. But it lets you view the task through the model’s “eyes”: what it likes, what it does not, and what it would like to improve. Do not adopt every change automatically. Select only the best suggestions from the list.

The third method is for Claude Code users: since July, the /doctor command has been able to find unused skills and plugins, show their context costs, and suggest what you should remove from CLAUDE.md. A powerful tool that many people do not know about.

Conclusion: Prompt debt is measured, not assumed

A modern prompt does not have to be a short document. It has to contain effective information and shed rules that have outlived their purpose. Keep context, invariants, and safety boundaries. Remove behavior the model has already learned, repetitions, and unmeasured prohibitions. Whether the shorter version is better is decided by comparison on your typical tasks.

Frequently asked questions about prompt debt

What is prompt debt?

Prompt debt consists of persistent instructions written for an earlier model, an earlier error, or a different work context. On a new model, they can become unnecessary, create conflicts, or request additional work.

How can I recognize prompt debt?

Typical signs include repeated rules, long prohibition lists, absolute words such as “always” and “never,” examples with no measurable benefit, and instructions that a current model version already follows reliably.

Which rules should I not delete?

Keep safety and access boundaries, personal facts, binding product requirements, and context the model cannot know on its own. This information consists of invariants, not ballast.

Are long prompts fundamentally bad?

No. Length alone is not a sufficient criterion. A long prompt may be necessary if it contains genuine context or binding requirements. Contradictions, repetitions, and instructions without measured benefit are the problem.

How do I test a shortened version?

Run two or three typical tasks once with the old version and once with the shorter one. Compare output quality, rule violations, tokens, runtime, and costs. If a relevant result deteriorates, restore the effective instruction.