The Verification Complexity Barrier
I am developing a formal theorem I call the Verification Complexity Barrier.
In a nutshell, if a program has some components n that have connectivity factor of k > 0, then verification complexity increases superlinearly for each new component. Therefore the time required to fully verify the system always exceeds time to generate components.
After a while, because itâs superlinear, the verification complexity takes off and becomes impossible to keep up with in some finite amount of time. This was true before AI, but is much starker now as code generation time trends towards zero. You hit the barrier sooner.
An interactive model
Drag the sliders and watch where the barrier lands.
We all have finite capacity - even AI agents - so there will always be a certain number of components n where the wall is hit. You must spend more and more effort on verification for each new component in the system.
The best thing you can do is spend time changing the âtopologyâ of the problem - change the software architecture - so that the exponent of verification complexity is lowered (Lehman1) and the curve is flattened. You can bundle components into modules, you can add automated tests, you can use formal proofs, you can use type systems. These things push the barrier to the right. They buy you more components and a more complex system. But the theorem suggests you can only ever defer the barrier, never completely eliminate it.
AI Agents can burn tokens all day long generating software components and tests for those components. They can find bugs and fix them, but they cannot prove the absence of bugs (per Dijkstra2, Rice3, Smith4, and others). And âBugâ needs to be defined against someoneâs spec for what âworkingâ and ânot buggyâ looks like. The more you build with Agents, the heavier the verification burden becomes.
This may sound like the same trite observation others have made on X: âOur bottleneck is no longer writing code, but reviewing codeâ5 and âI am the bottleneck now.â6 True, but I donât think anyone has captured the magnitude of the problem. The math is: at a certain component count n, it is literally impossible for you, your team, and your agents to completely verify a system. The bottleneck goes to zero, and nothing gets through.
So what software companies do in the real world is release incompletely verified software and massively scale up. This shifts the burden of verification onto their customers, because âgiven enough eyeballs, all bugs are shallowâ (Raymond7). If you can get enough eyeballs, this is a very cost-effective way to shift the barrier to the right by massively increasing your teamâs capacity. You walk the tightrope of doing enough internal verification before release so you donât lose customers, while tolerating a certain amount of escaped bugs, which - if those bugs matter at all - your customers will find for you.
Meanwhile, massively scaling up just accepts the growing cost of complexity. You can push n* from 15 to 30 by quadrupling your capacity. To get to 60 you need to quadruple again, and then again to get to 120. Your cost curve is superlinear to get linear gains in system size. At a big enough scale, you amortize the cost across your customer base and the economics work.
Contrast that with a sufficiently complex vibecoded app built for a small audience - high complexity costs canât be amortized at small scale. I expect to see many people and companies try and fail at vibecloning complex SaaS in the near term. Complexity cost economics only scale with audience size (I will share another model for this).
I do think SaaS prices will be corrected downwards to account for savings in code generation, but I predict that once the irrational exuberance for vibing fades, weâll see that it still makes sense to buy rather than self-build complex SaaS.
A broader implication is that AI Agents will never be able to self-verify. Humans, too, will never be able to fully verify their behavior, because LLMs are by design of maximal complexity. Did you see the size of those error bars in the latest METR results?8 The longer the horizon on a task, the more spread in AI agent outcomes. This is the Barrier in action. Spread is a feature of GenAI, but in practice it means heaps more output to review and verify.
The Complexity Barrier shows you literally wonât have time to review it all. At the inflection point of verification complexity, you have to fall back on vibes. The implication for fast-takeoff AGI is even scarier: if AI does reach a point of recursive self-improvement, this theorem suggests it will be structurally impossible to know that behavior is aligned, because you wonât be able to completely verify. Drift is bad enough in vibe coding. Runaway AI will drift massively and thereâs no way of knowing where it will end up.
All thatâs to say, verification should be the focal point of AI Engineering for the foreseeable future and maybe forever. That is: how do you capture what you want to do, refine that into specifics, and then follow up with automated tests, assertions, evals, and customer feedback to progressively harden your software?
The verification problem is acute now because of how cheap software generation is. Because of the superlinear nature of software verification complexity, companies that push hard on the barrier and successfully shift it right will have a built-in moat versus those who fail to put in the verification work.
What the sliders reveal
Set coverage \(c\) to 0% and test setup cost \(S\) to 0. This is a team with no tests, and the wall is close. Raise \(c\) and the dashed line moves right: automated testing scales \(n^*\) by \((1/(1 - c))^{1/(L+1)}\). Raise \(S\) and the green baseline flattens, because each component costs more to produce. \(S\) is the price you pay for \(c\), and that trade is almost always worth it.
The âmove fast, no testsâ configuration is high \(g\), zero \(S\), zero \(c\). Early progress looks great. But \(V(n)\) is convex: it barely registers for the first few components, then explodes, with no gradual slowdown to warn you. The team that writes tests from day one appears to lose the race, until the no-test team stalls. Adding tests later is worse: the setup cost lands on every existing component at once, when velocity is already near zero.
Connectivity \(k\) and trace depth \(L\) set the structure of the system. \(k\) is the fraction of component pairs that interact. \(L\) is how far a change travels before verification can stop following it. Lower \(k\) moves the wall out. Raise \(L\) from 1 to 2 and the wall moves in sharply: verification now checks chains of three components, and the number of chains grows as \(n^3\).
Capacity does not buy a way out. At \(L = 1\), doubling the team moves the barrier out by about 41%. At \(L = 3\), it buys about 19%. The constraint is structural, not a staffing problem: every team and process has a finite ceiling.
Appendix: The formal model
For readers who want the precise mechanics behind the visualization.
Correction (October 3, 2026): This appendix replaces the first version of the model, which did not define the connectivity factor \(k\); this version defines \(k\) as the density of the component graph and derives the interaction exponent from trace depth.
The setup
A system has \(n\) components. Draw an arrow from component \(a\) to component \(b\) when a change in \(a\) can change the behavior of \(b\): a function call, a shared database table, an event, a shared config value.
Definition 1 (Connectivity factor). The connectivity factor \(k \in (0, 1]\) is the density of this graph: the probability that a given ordered pair of components has an arrow. We assume that arrows occur independently. A component then interacts directly with about \(k(n - 1)\) others, a number that grows as the system grows.
Definition 2 (Trace). A trace of depth \(L\) is a chain of \(L\) arrows through \(L + 1\) different components, \(c_0 \to c_1 \to \cdots \to c_L\). The trace depth \(L \geq 1\) is how far verification must follow an effect before it can stop. A data point that one service collects and broadcasts, a second service transforms and saves, and a web client displays, is one trace of depth 3.
Lemma 1 (Trace count). A system of \(n\) components with connectivity factor \(k\) has, in expectation,
\[N_L(n) = k^L \, n(n-1)\cdots(n-L) \;\approx\; k^L \, n^{L+1}\]traces of depth \(L\).
Proof. There are \(n(n-1)\cdots(n-L)\) sequences of \(L + 1\) different components. A sequence is a trace when all \(L\) of its arrows exist, which happens with probability \(k^L\). â
The model
Axiom 1 (Finite capacity). A team has capacity \(W > 0\) per sprint. Building and verification both draw from \(W\).
Axiom 2 (Verification follows traces). To release a system of \(n\) components, the team must check each trace of depth \(L\) that automated tests do not cover. With coverage \(c \in [0, 1)\) and one unit of capacity per trace, the cost of each release is
\[V(n) = (1 - c)\, k^L \, n^{L+1}\]The cost recurs each sprint, because a system in use must keep changing (Lehman1), and the release that was verified last sprint is not the release that ships this sprint.
Axiom 3 (Building uses what is left). With no verification, the team builds \(g\) components per sprint. Tests add a setup cost \(S \geq 0\) per component, so the effective generation rate is \(g_{\text{eff}} = g / (1 + S)\). Verification takes its share of capacity first, and the team builds at
\[v(n) = g_{\text{eff}} \cdot \max\!\left(0,\; 1 - \frac{V(n)}{W}\right)\]The interaction exponent is now \(\alpha = L + 1\). It is a result of the model, not an axiom.
Theorem 1 (Existence of the barrier). There is exactly one system size
\[n^* = \left(\frac{W}{(1 - c)\, k^L}\right)^{1/(L+1)}\]at which verification uses all of \(W\). At \(n^*\) and above, the team cannot build anything new.
Proof. \(V(0) = 0\), and because \(k > 0\) and \(c < 1\), \(V\) is continuous, strictly increasing, and unbounded. So \(V(n) = W\) has exactly one positive solution. Solve for \(n\). â
This is where \(k > 0\) enters: any connectivity at all gives \(\alpha \geq 2\) and a finite barrier.
Theorem 2 (The barrier cannot be reached). The time to grow a system to \(n\) components, \(T(n) = \int_0^n dn' / v(n')\), goes to infinity as \(n \to n^*\).
Proof. \(V\) is convex, so it lies above its tangent at \(n^*\): for \(n < n^*\), \(V(n^*) - V(n) \leq V'(n^*)\,(n^* - n)\). Divide by \(W = V(n^*)\), and use \(V'(n^*) / V(n^*) = (L + 1)/n^*\):
\[1 - \frac{V(n)}{W} \;\leq\; \frac{(L + 1)(n^* - n)}{n^*}\]So \(1/v(n) \geq n^* / \left(g_{\text{eff}}\,(L + 1)\,(n^* - n)\right)\), and \(\int^{n^*} dn / (n^* - n)\) diverges. â
Corollaries
Corollary 1 (Capacity has diminishing returns). \(n^* \propto W^{1/(L+1)}\). Doubling \(W\) multiplies \(n^*\) by \(2^{1/(L+1)}\): about 1.41 at \(L = 1\) and about 1.19 at \(L = 3\).
Corollary 2 (Speed does not move the barrier). \(n^*\) does not depend on \(g\) or \(S\). A higher generation rate divides the time \(T(n)\) to reach any size below \(n^*\) by the same factor. AI gets you to the barrier sooner. It does not move the barrier.
Corollary 3 (Tests shift the barrier, less for deep traces). Coverage \(c\) multiplies \(n^*\) by \((1/(1 - c))^{1/(L+1)}\). At 70% coverage this is about 1.83 for \(L = 1\) and about 1.35 for \(L = 3\). Only \(c = 1\) removes the barrier, but Axiom 2 requires \(c < 1\), reflecting the irreducible residual of emergent, unautomatable interactions.
Corollary 4 (Traces without a bound). Suppose traces can cross the system, so that \(L \geq \beta n\) for some \(\beta > 0\), and each component has more than one neighbor, \(k n \geq a > 1\). Then \(N_L \approx n (kn)^L \geq a^{\beta n}\), and
\[n^* \;\leq\; \frac{\ln\!\big(W / (1 - c)\big)}{\beta \ln a}\]The barrier now grows with the logarithm of capacity. Doubling \(W\) adds at most \(\ln 2 / (\beta \ln a)\) components: a fixed number, not a factor.
Corollary 5 (Architecture bounds the traces). Group components into modules, and give each module an interface with a contract that is itself verified. Then verification can stop at an interface, because the contract stands in for everything behind it. \(L\) is bounded by the depth of a trace inside one module plus one step across an interface, no matter how long the end-to-end chain is. This turns the exponential of Corollary 4 back into a polynomial and lowers \(\alpha\). It is the work that Lehmanâs law of increasing complexity asks for1, and it is the lever that changes the curve itself rather than a constant in front of it.
Corollary 6 (Recursive barrier). Capacity \(W\) is itself produced by a team of \(m\) people. They have \(m(m - 1)/2\) communication channels (Brooks9), which is Lemma 1 with \(k = 1\) and \(L = 1\). By the same argument, there exists \(m^*\) beyond which adding people decreases effective \(W\). The barrier is self-similar across levels of organization.
This essay was first published as a post on X on February 28, 2026. This page is the canonical version.
-
Lehmanâs laws of software evolution, Wikipedia. ↩ ↩2 ↩3
-
Edsger W. Dijkstra, The Humble Programmer (EWD340), 1972. ↩
-
Riceâs theorem, Wikipedia. ↩
-
Brian Cantwell Smith, The Limits of Correctness, 1985. ↩
-
Linusâs law, Wikipedia. ↩
-
METR, Measuring AI Ability to Complete Long Tasks, March 2025. ↩
-
Frederick P. Brooks Jr., The Mythical Man-Month, 1975. ↩