On software carpentry

 

tl;dr: “Beware of bugs in the above code; I have only proved it correct, not tried it.” –Donald Knuth

$ $

We call ourselves software “engineers”, but any sincere “engineer” who wrote more than 10,000 lines of code will tell you engineering has very little to do with what we do. We’re mostly duct-taping things.

Before building a bridge, a bridge engineer1 computes stress, strain and deflection in their beams2. A software “engineer” just starts writing code. A bridge engineer operates with a safety margin: the bridge should resist 2x to 4x the maximum expected load. A software “engineer” continues writing more code. Some of this code may be tested, for some of its input values, on some of its execution branches. But that’s about it. A bridge engineer signs off on their design3 and is subject to professional negligence lawsuits if they seriously mess up4$^,$5. A software “engineer” just ships the code.

What is the net result of our “engineering” practice? Years later, when our code leaks your phone number, home address and the names of all your family members, our “engineering” company is “deeply sorry.” This happens several times per year now6. If your cryptocurrency is stolen due to a hack, this is only because “we are seeing an increasing wave of sophisticated ‘cyber attacks’“7. We are sorry. Don’t ask us about our historical disregard for security. Shipping is more important. “Done is better than perfect”, says our PM, TPM, EM and even the CEO.

A more honest name for our practice should be software carpentry. It recognizes that, while we do have a skilled craft, safety calculations hardly enter into it. My thesis is simple and, I gather, hardly controversial by now: to graduate from “software carpentry” to “software engineering”, it would be sufficient for us to embrace formal verification8. Not because it’s bulletproof. Not because it will patch all holes in our software. But because it will force us to think carefully about the properties of the software we put out.

Luckily, in the LLM age, formal verification is becoming easier to apply at larger scales9. Our first step should be to apply formal verification to our less complex software, software with clean interfaces that admits small specifications. This would allow us to make reasonable assumptions about our software building blocks. “This library (only) sorts.”10 “This binary (only) zips.”11 “This is a EUF-CMA signature scheme with no timing side-channels.” Kind of how a bridge engineer can reasonably assume that he’s been pouring concrete and not mud, you know?

Then, as our collective formal verification expertise expands, we can move on to more complex systems. “This query optimizer produces a plan semantically equivalent to the unoptimized query.” “This consensus protocol satisfies safety and liveness under \(f < n/3\) Byzantine faults.”12$^,$13 “This C compiler preserves program semantics from source to assembly”.14$^,$15

Indeed, there is plenty of formal verification work that predates the LLM age. In 2009, work on seL4 started: an OS microkernel proven functionally correct16. Sure, a few years later, we found out seL4’s specification did not account for timing channels17. Nonetheless, this is great progress: so timing channels are the only thing we have to worry about now? Fantastic! In 2015, AWS wrote a short paper explaining how they used TLA+ to model some of their system designs (e.g., DynamoDB and S3)18. Their paper insightfully points out how “in order to find subtle bugs in a system design, it is necessary to have a precise description of that design.” Absolutely! In 2017, we saw progress towards a formally verified TLS implementation that covered the full protocol state machine.19. I could keep going with more examples20.

Embracing formal verification would bring many advantages. First, we could safely unleash AI to generate and optimize our code. Given that “engineers” are barely reviewing their AI-generated code anyway, why not actually enable them to safely (not) do so? It will take a bit more work. For example, we would need compiled (say) Lean programs to execute fast. But, if that doesn’t work out, we could give the “final form of software development”21 a try.

Second, insted of testing our code, which never covers all executions with all values22, we could prove all executions perform what we expect them to. Furthermore, instead of doing repeated, error-prone (LLM/manual) audits on our ever-changing, humongous codebases, we could reduce our audit surface to just the specs. Assuming a sound theorem proving framework23, auditors would mostly try to poke holes through bad assumptions or incomplete modelling in the specs.

Third, and something that gets me very excited, we could obviate software dependency attacks! Instead of trusting that an imported dependency does only what we expect it to do, we would only need to update our proof after integrating the dependency. If our proof passes, then the program only does what the spec says. The “only” part seems tricky, but doable?24

Lastly, I am not arguing that formal verification will be a panacea. There are many challenges. To start, some software can be as complex to specify as it is to implement (e.g., EC2 cloud architectures come to mind). Plus, all software evolves, not just in its implementation, but also in its specification. Moreover, formal verification rarely covers the full system. So, naturally, bugs will creep in the uncovered parts. For example, the compiler may still be unverified. Or, even if your software is bulletproof, the execution environment may not offer formal guarantees and let you down: e.g., your operating system may kill your process, your file system may lose your writes, your CPU may not execute instructions correctly25$^,$26$^,$27. Even worse, the formal verification language may itself have soundness or completness issues28.

But, assuming you too are a carpenter who’s tired of your chairs always breaking, what other options do you have?

Postscript

After writing this post, I keep running into evidence that the future is bleak.

On vibe spec’ing

As fate would have it, one day after drafting this post, Boris Cherny, the creator of Claude Code, tweeted that he “used Opus 5.5 to formally verify the Claude Agent SDK using Lean”.

He “sometimes combine[s] Lean and TLA+” but admits he “do[es]n’t know either language well, but [that] Claude is excellent at both.” He clearly does not understand the specs that Claude generated. (Forget about auditing them.) Is what he did useless?

From a carpentry perspective, not at all. He’ll probably find some bugs – business as usual. Good for him. Good for Anthropic.

The price paid though: confusing formal verification for abysmal verification29.

How insane would it sound if a nuclear reactor engineer adopted this philosophy?

You might object: our software infrastructure should not be equated to our energy infrastructure. But this ignores how pervasive and critical (sloppy) software has become: remember CrowdStrike in July 2024?

Doubling down on software carpentry

It’s disheartening to see David Heinemeier Hansson, the creator of Ruby on Rails, doubling down on software carpentry.

Reality has a way of fighting back though.

Acknowledgements

Thanks to Vineeth Kashyap, Victor Gao, Ittai Abraham and Kobi Gurkan for their feedback on a draft version of this post.

References

For cited works, see below 👇👇

  1. It is not lost on me that we’ve been questioning the “engineering” in “software engineering” for almost 30 years (as of 2026). See this old comp.lang.ada USENET convo from 1997. ↩

  2. Stress, Strain, and Deflection in Beams, a chapter in a structural engineering textbook. ↩

  3. Texas Occupations Code § 1001.401 - Seal Required, Texas Statutes, current ↩

  4. Chapter 150, Texas Civil Practice and Remedies Code — Certificate of Merit requirement, discussed in Texas Court of Appeals case citing Ch. 150 ↩

  5. Carlson, Brigance & Doering, Inc. v. Compton — Texas Appellate Court on Certificate of Merit and vicarious liability, LGWM Law summary of Tex. App. decision, Dec. 8, 2020 ↩

  6. Why does it matter if everyone can find out where you live, you ask? Try doing anything of significance in today’s world and you’ll get a “fan club” in no time. ↩

  7. “Our space is seeing increasingly sophisticated cyber security attacks” is utter and complete misdirection. The truth is every company has several employees who cry out about poor security hygiene. The truth is closer to this: “We’ve been writing sloppy code. As quickly as we can. Our architecture? Perpetually-optimistic about the adversary. We’ve been treating security as a paranoid afterthought, rather than as a mandatory engineering practice. And now we’re reaping what we’ve sown.” ↩

  8. Not saying it is necessary to. Only saying it would be sufficient to. ↩

  9. Formal verification and AI, by Martin Kleppmann, 2025 ↩

  10. “Just Lean: a verified, fast sort”, by Gregor Mitscha-Baude ↩

  11. “Why Lean is faster than Rust”, by Kim Morrison, July 24th, 2026 ↩

  12. IronFleet: proving practical distributed systems correct, by Hawblitzel, Chris and Howell, Jon and Kapritsos, Manos and Lorch, Jacob R. and Parno, Bryan and Roberts, Michael L. and Setty, Srinath and Zill, Brian, in Proceedings of the 25th Symposium on Operating Systems Principles, 2015, [URL] ↩

  13. Etheorem: a Lean 4 implementation of the Ethereum consensus specification ↩

  14. Formal verification of a realistic compiler, by Leroy, Xavier, in Communications of the ACM, 2009, [URL] ↩

  15. CompCert14 had bugs in its unverified parts: parsing bugs that produced a bad abstract syntax tree (AST), since the formal verification only modeled compilation from the AST30, but also bugs in its C elaborator, in its Win64 ABI model and in its assembly printer31. ↩

  16. seL4: formal verification of an OS kernel, by Klein, Gerwin and Elphinstone, Kevin and Heiser, Gernot and Andronick, June and Cock, David and Derrin, Philip and Elkaduwe, Dhammika and Engelhardt, Kai and Kolanski, Rafal and Norrish, Michael and Sewell, Thomas and Tuch, Harvey and Winwood, Simon, in Proceedings of the ACM SIGOPS 22nd symposium on Operating systems principles, 2009, [URL] ↩

  17. The Last Mile: An Empirical Study of Timing Channels on seL4, by Cock, David and Ge, Qian and Murray, Toby and Heiser, Gernot, in Proceedings of the 2014 ACM SIGSAC Conference on Computer and Communications Security, 2014, [URL] ↩

  18. How Amazon Web Services uses formal methods, by Chris Newcombe and Tim Rath and Fan Zhang and Bogdan Munteanu and Marc Brooker and Michael Deardeuff, in Communications of the ACM, 2015, [URL] ↩

  19. Everest: Towards a Verified, Drop-In Replacement of HTTPS, by Bhargavan, Karthikeyan and Bond, Barry and Delignat-Lavaud, Antoine and Fournet, Cédric and Hawblitzel, Chris and Hritcu, Catalin and Ishtiaq, Samin and Kohlweiss, Markulf and Leino, Rustan and Lorch, Jay and Maillard, Kenji and Pang, Jinyang and Parno, Bryan and Protzenko, Jonathan and Ramananandro, Tahina and Rane, Ashay and Rastogi, Aseem and Swamy, Nikhil and Thompson, Laure and Wang, Peng and Zanella-Béguelin, Santiago and Zinzindohoué, Jean-Karim, in SNAPL 2017 - 2nd Summit on Advances in Programming Languages, 2017, [URL] ↩

  20. For example, FSCQ: a file system with a machine-checked proof that it never loses data across crashes, specified using Crash Hoare Logic. See “Using Crash Hoare Logic for Certifying the FSCQ File System”, by Haogang Chen, Daniel Ziegler, Tej Chajed, Adam Chlipala, M. Frans Kaashoek and Nickolai Zeldovich, in SOSP’15. ↩

  21. “The end of coding as we know it”, by zkSecurity ↩

  22. How to misuse code coverage, by Marick, Brian and Smith, John and Jones, Mark, in Proceedings of the 16th Interational Conference on Testing Computer Software, 1999 ↩

  23. Not a trivial assumption: we are finding Lean kernel bugs lately28, for example. ↩

  24. Forward and Backward Simulations, by Lynch, N. and Vaandrager, F., in Information and Computation, 1995, [URL] ↩

  25. “How I found a bug in Intel Skylake processors”, by Xavier Leroy, July 3rd, 2017 ↩

  26. “Some AMD Processors Have a Hardware RNG Bug, Losing Randomness After Suspend Resume”, TechPowerUp, May 2019 ↩

  27. “crypto: arm/aes-ce - work around Cortex-A57/A72 silicon errata”, by Ard Biesheuvel, Linux kernel commit, 2019 ↩

  28. “Postmortem for Kernel Soundness Bug #14576”, by Leonardo de Moura, August 1st, 2026 ↩ ↩2

  29. Kind of reminds me of the “It’s closer to a British carbonara” meme. ↩

  30. Finding and understanding bugs in C compilers, by Yang, Xuejun and Chen, Yang and Eide, Eric and Regehr, John, in ACM SIGPLAN Notices, 2011, [URL] ↩

  31. “How We Found Three Bugs in a Compiler Proven Correct”, by Cantina, August 18th, 2026. None of the three bugs contradicted CompCert’s correctness theorem: they were in the unverified parts around it (the C elaborator, the Win64 ABI/register-allocation model, and the assembly-printing stage, where a newline in a source filename could inject arbitrary ARM instructions). ↩