Make invalid states unrepresentable (for your agents) | ⌨️🤷🏻♂️📷<br>Make invalid states unrepresentable (for your agents)<br>July 6, 2026This blog post was written by me, a human (as evidenced by how long it takes to explain a simple idea). If you want to skip straight to the main idea, scroll to The Point.<br>There is an old saying: make invalid states unrepresentable .<br>Like all maxims in computer science, one could debate its usefulness. Maybe you read it and think: Yes! Exactly! Or maybe you read it and recall a time as a junior dev, when a senior dev ruined your Friday by commenting on your PR with these four words.<br>Regardless, as a guiding principle, it is certainly useful. Entire classes of bugs can be eliminated from programs, just by designing your solution in a way that certain failure states are not merely edge cases but logically impossible.<br>Lately I have noticed that this approach is very effective when applied to agents , massively increasing their reliability for many tasks. I will detail the pattern below.<br>First, some old-school code examples. Feel free to skip them if you already get the gist.<br>Some (contrived) examples<br>Ways you might “make invalid states unrepresentable”:<br>In some programming languages, null reference exceptions are made impossible by treating nullable references as entirely different types from non-nullable references.
If a purchase order can only be “accepted”, “rejected”, or “pending”, you can represent this with a closed enum instead of a string (so it is not “stringly typed”), and it becomes impossible for callers to provide an invalid value.
Say you have a USERS table, and all users are required to have email addresses. But, some user accounts are pre-created by their employer, before the email address is known. In such cases, users provide the email address upon first sign-in. Instead of allowing the email field to be nullable, you can have an entirely separate table PENDING_USERS which allows null emails, and only create the real USERS entry once the email is known upon first sign-in. You just eliminated the possibility that a USER may be missing an email address; such a state is unrepresentable, since email is NOT NULL on USERS.
Maybe those examples are obvious, boring, even common sense. So let’s move on to agents.<br>“Make no mistakes”<br>I’d say there are two general approaches when designing LLM-powered solutions:<br>The code drives the LLM.<br>The LLM drives the code.<br>The former was common in prehistoric times (~2023). Think: langchain, semantic kernel, old-school OpenAI SDK. In this approach, you write “traditional” code, which calls into LLMs when it’s time to make a decision. You control the loop, you control the… well, the control flow. Your code drives the LLM.<br>The latter is becoming the norm, now that everything is “agentic”. Think: Claude Code, OpenClaw, the latest harness-du-jour, etc. In this approach, the LLM operates in a harness (as an “agent”) with lots of tools at its disposal. You may provide it with code it can run; whether it actually invokes the code is up to the agent. The agent has free rein, and you let it loose on a task. The agent owns the loop, decides what to do, decides when to stop. There isn’t strictly a “program” being executed; at each step, the next step is decided on the fly by the agent. The LLM drives your code, invokes your tools… if it chooses.<br>Apologies for the Agents 101. My audience surely is familiar with these concepts. But I wanted to outline it explicitly, to set up this thought experiment:<br>Say you wanted to auto-generate a technical wiki for any given codebase. Basically, implement DeepWiki. How would you do this?<br>Well, first let’s define what a “wiki” is. For our purposes, an example “wiki” created by our system might be a folder that looks like this:<br>wiki/<br>OVERVIEW.md<br>control-plane/<br>OVERVIEW.md<br>running-locally.md<br>system-architecture.md<br>plugin-system.md<br>user-management-service/<br>OVERVIEW.md<br>account-creation-flow.md<br>...<br>service-lifecycle/<br>...<br>There’s always a top-level OVERVIEW.md page which is a landing page for the entire wiki.<br>There are “sections” which are folders that group relevant wiki pages by logical systems or concepts (e.g. user-management-service/)<br>Each “section” also has an OVERVIEW.md which is the landing page for that section.<br>Each “section” has “pages” which are the markdown files that contain the wiki content.<br>On top of that, let’s say wiki generation is configurable . A config file for our wiki-generator system might look like this:<br># wiki-config.yaml<br>max_sections: 12<br>max_pages_per_section: 3
# These sections are always generated, forcefully:<br>default_sections:<br>- name: Application Lifecycle<br>description: Explains the startup, shutdown, and deployment process
Basically, we may...