<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Prasanna Gautam</title>
    <description>The latest articles on DEV Community by Prasanna Gautam (@prasincs).</description>
    <link>https://dev.to/prasincs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F126224%2F203c37dd-bc93-488e-9eb8-f5e02a0b4fa3.jpeg</url>
      <title>DEV Community: Prasanna Gautam</title>
      <link>https://dev.to/prasincs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prasincs"/>
    <language>en</language>
    <item>
      <title>MiniLisp C++: A Compile-Time Lisp Interpreter in C++20</title>
      <dc:creator>Prasanna Gautam</dc:creator>
      <pubDate>Fri, 26 Dec 2025 14:10:00 +0000</pubDate>
      <link>https://dev.to/prasincs/minilisp-c-a-compile-time-lisp-interpreter-in-c20-43o6</link>
      <guid>https://dev.to/prasincs/minilisp-c-a-compile-time-lisp-interpreter-in-c20-43o6</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; : I built a Lisp interpreter that evaluates expressions at &lt;strong&gt;compile-time&lt;/strong&gt; using C++20 &lt;code&gt;constexpr&lt;/code&gt;. The same code works at runtime too—no duplication needed. Along the way, I discovered that macOS adds ~28KB of constant overhead to all C++ binaries, and that Mach-O is surprisingly more efficient than Linux ELF for small programs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it right now&lt;/strong&gt; — this runs the same interpreter compiled to WebAssembly (27KB):&lt;/p&gt;

&lt;p&gt;Lisp Expression:&lt;br&gt;
Eval&lt;/p&gt;

&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Loading WASM...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try: &lt;code&gt;(* 6 7)&lt;/code&gt; ·&lt;code&gt;(car '(10 20 30))&lt;/code&gt; ·&lt;code&gt;(cdr '(1 2 3))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(function() {&lt;br&gt;
const output = document.getElementById('lisp-output');&lt;br&gt;
window.addEventListener('wasm-ready', () =&amp;gt; {&lt;br&gt;
output.textContent = 'Ready! Enter an expression and click Eval.';&lt;br&gt;
});&lt;br&gt;
window.addEventListener('wasm-error', (e) =&amp;gt; {&lt;br&gt;
output.textContent = 'Failed to load WASM: ' + e.detail.message;&lt;br&gt;
});&lt;br&gt;
if (window.WasmLoader &amp;amp;&amp;amp; window.WasmLoader.ready) {&lt;br&gt;
output.textContent = 'Ready! Enter an expression and click Eval.';&lt;br&gt;
}&lt;br&gt;
})();&lt;/p&gt;

&lt;p&gt;Some weeks back I saw that &lt;a href="https://x.com/lemire" rel="noopener noreferrer"&gt;Dan Lemire&lt;/a&gt; had a PR open on &lt;a href="https://github.com/simdjson/simdjson" rel="noopener noreferrer"&gt;simdjson&lt;/a&gt; that added an expression to parse whole JSON. That intrigued me and took the challenge to see if I could write a LISP Interpreter. Here’s a minimal &lt;a href="https://godbolt.org/z/jqb3jK4ad" rel="noopener noreferrer"&gt;godbolt&lt;/a&gt; playground if you’re interested to play around. I don’t have this problem as much anymore but I used to need a little DSLs in programs all the time. Maybe long term Lua is right choice but I can see something like this to be useful in a very small form factor like some kind of verified binary that you want to minimize your dependencies and adding a whole new lib will add more complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Magic: Compile-Time Lisp
&lt;/h2&gt;

&lt;p&gt;Here’s what this looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// This is evaluated by the compiler, not at runtime!
constexpr auto val = "(+ 10 (* 2 5))"_lisp;
static_assert(val == 20);
constexpr auto head = "(car '(10 20 30))"_lisp;
static_assert(head == 10);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the Lisp expression is invalid, &lt;strong&gt;your code won’t compile&lt;/strong&gt;. The compiler becomes your Lisp interpreter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Catch errors at compile time&lt;/strong&gt; - Invalid Lisp expressions fail during build, not at 3 AM in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero runtime cost&lt;/strong&gt; - The result is baked directly into the binary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type safety&lt;/strong&gt; - The compiler verifies your Lisp code before you ship&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The C++20 Features That Make This Possible
&lt;/h3&gt;

&lt;p&gt;C++20 made compile-time programming dramatically more powerful:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;constexpr everything&lt;/strong&gt; - Vectors, algorithms, and even memory allocation now work at compile-time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-defined literals&lt;/strong&gt; - The &lt;code&gt;_lisp&lt;/code&gt; suffix creates elegant syntax&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;std::variant&lt;/strong&gt; - Type-safe unions for representing S-expressions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;std::span&lt;/strong&gt; - Zero-copy parameter passing for operand lists&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;consteval&lt;/strong&gt; - Forces compile-time-only evaluation&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementation Highlights
&lt;/h3&gt;

&lt;p&gt;The interpreter is built on a few key components:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FixedString&lt;/strong&gt; - A template struct that captures string literals at compile-time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;template &amp;lt;size_t N&amp;gt;
struct FixedString {
char data[N];
consteval FixedString(const char (&amp;amp;str)[N]) {
std::copy(str, str + N, data);
}
constexpr std::string_view get() const {
return std::string_view(data, N - 1);
}
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;S-Expression Types&lt;/strong&gt; - The classic Lisp data structures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// An "Atom" is either a number or a symbol
using Atom = std::variant&amp;lt;long, std::string_view&amp;gt;;
// A "List" is a vector of S-Expressions
using List = std::vector&amp;lt;SExpr&amp;gt;;
// An S-Expression is either an Atom or a List
struct SExpr {
std::optional&amp;lt;Atom&amp;gt; atom;
std::optional&amp;lt;List&amp;gt; list;
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The User-Defined Literal&lt;/strong&gt; - The magic entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;template &amp;lt;FixedString S&amp;gt;
consteval auto operator""_lisp() {
std::string_view s = S.get();
auto ast = MiniLisp::parse(s);
auto result_sexpr = MiniLisp::eval(ast);
// Extract and return the final long value
return std::get&amp;lt;long&amp;gt;(*result_sexpr.atom);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Functional Arithmetic&lt;/strong&gt; - Using &lt;code&gt;std::transform_reduce&lt;/code&gt; for clean, constexpr-compatible operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (op == "+") {
long result = std::transform_reduce(
operands.begin(), operands.end(),
0L, // Initial value
std::plus&amp;lt;long&amp;gt;(), // Reduce operation
[](const SExpr&amp;amp; e) { return get_long(e); } // Transform
);
return SExpr{Atom{result}};
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Comparison with Other Approaches
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime OOP&lt;/td&gt;
&lt;td&gt;&lt;a href="https://gist.github.com/ofan/721464" rel="noopener noreferrer"&gt;ofan’s Lisp&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Simple, ~200 lines&lt;/td&gt;
&lt;td&gt;Runtime only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template metaprogramming&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/petrizhang/crisp" rel="noopener noreferrer"&gt;Crisp&lt;/a&gt;, &lt;a href="https://github.com/olsner/templisp" rel="noopener noreferrer"&gt;Templisp&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Compile-time&lt;/td&gt;
&lt;td&gt;Ugly syntax, hard to debug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;constexpr (this project)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;minilisp-cpp&lt;/td&gt;
&lt;td&gt;Clean, dual-mode, debuggable&lt;/td&gt;
&lt;td&gt;Requires C++20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The ofan gist shows a classic runtime interpreter in ~200 lines of clean C++. But with C++20 constexpr, we get the same readable code that &lt;strong&gt;also works at compile time&lt;/strong&gt; —that’s the key insight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending the Interpreter
&lt;/h2&gt;

&lt;p&gt;Adding new functions is straightforward. Here’s how to add a &lt;code&gt;max&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;else if (op == "max") {
p_assert(!operands.empty(), "'max' requires at least one argument");
long result = get_long(operands[0]);
for (size_t i = 1; i &amp;lt; operands.size(); ++i) {
long val = get_long(operands[i]);
if (val &amp;gt; result) result = val;
}
return SExpr{Atom{result}};
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically works at both compile-time and runtime—no extra effort needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Binary Size Deep Dive
&lt;/h2&gt;

&lt;p&gt;While optimizing the interpreter for size, I learned two lessons that would have saved me hours if I’d known them upfront.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 1: Know When to Stop
&lt;/h3&gt;

&lt;p&gt;After applying every optimization I could find, the macOS binary sat stubbornly at 34KB. I spent time trying to squeeze out more bytes before realizing: &lt;strong&gt;34KB is the floor&lt;/strong&gt;. On macOS, the Mach-O binary format has ~28KB of unavoidable overhead. Once you hit that limit, further code optimization is wasted effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 2: Measure the Right Thing
&lt;/h3&gt;

&lt;p&gt;File size (&lt;code&gt;ls -l&lt;/code&gt;) is misleading—it’s dominated by format overhead you can’t control. What you &lt;em&gt;can&lt;/em&gt; control is actual code size, measured with the &lt;code&gt;size&lt;/code&gt; command. My real win was a &lt;strong&gt;32% reduction in executable code&lt;/strong&gt; (10.7KB → 7.3KB), even though the file size barely budged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Configurations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Build&lt;/th&gt;
&lt;th&gt;macOS&lt;/th&gt;
&lt;th&gt;Linux&lt;/th&gt;
&lt;th&gt;WASM&lt;/th&gt;
&lt;th&gt;Techniques&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;39KB&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-O2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small&lt;/td&gt;
&lt;td&gt;36KB&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-Os&lt;/code&gt;, LTO, strip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ultra-small&lt;/td&gt;
&lt;td&gt;34KB&lt;/td&gt;
&lt;td&gt;66KB (10KB UPX)&lt;/td&gt;
&lt;td&gt;27KB&lt;/td&gt;
&lt;td&gt;POSIX I/O, no iostream, wasm-opt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What We Actually Removed
&lt;/h3&gt;

&lt;p&gt;Here’s the real code reduction (measured with &lt;code&gt;size&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFAULT BUILD (with iostream):
Code section: 8,484 bytes
Exception tables: 844 bytes
Total code: 10,753 bytes
ULTRA-SMALL BUILD (POSIX I/O):
Code section: 5,752 bytes (32% reduction!)
Exception tables: 288 bytes (66% reduction!)
Total code: 7,273 bytes (32% reduction!)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The techniques:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;iostream&amp;gt;&lt;/code&gt; with POSIX &lt;code&gt;write()&lt;/code&gt;/&lt;code&gt;read()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;std::string&lt;/code&gt; with fixed buffers&lt;/li&gt;
&lt;li&gt;Simplify exception handling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Build flags: &lt;code&gt;-Os -flto -fno-rtti -ffunction-sections -fdata-sections -Wl,-dead_strip&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why macOS Has a 34KB Floor
&lt;/h2&gt;

&lt;p&gt;Let’s dig into why you can’t go below 34KB on macOS—understanding this saves you from chasing impossible optimizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mach-O Segment Layout
&lt;/h3&gt;

&lt;p&gt;Running &lt;code&gt;size -m lisp_repl&lt;/code&gt; reveals the structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Segment __PAGEZERO: 4294967296 (4GB virtual, catches NULL pointers)
Segment __TEXT: 16384 (contains ~7KB code + padding)
Segment __DATA_CONST: 16384 (contains 328 bytes + padding)
Segment __LINKEDIT: varies (symbols, code signature)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Full output of &lt;code&gt;size -m lisp_repl&lt;/code&gt;
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Segment __PAGEZERO: 4294967296 (zero fill)
Segment __TEXT: 16384
Section __text: 8484
Section __stubs: 336
Section __gcc_except_tab: 844
Section __cstring: 737
Section __unwind_info: 352
total 10753
Segment __DATA_CONST: 16384
Section __got: 328
total 328
Segment __LINKEDIT: 16384
total 4295016448

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The key insight&lt;/strong&gt; : Mach-O uses &lt;strong&gt;16KB segment alignment&lt;/strong&gt;. Each segment must start on a 16KB boundary, so even tiny segments consume 16KB of disk space.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 on-disk segments × 16KB = ~48KB baseline&lt;/li&gt;
&lt;li&gt;After strip: ~34KB (removes some &lt;code&gt;__LINKEDIT&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This means 34KB is essentially the floor for any C++ program on macOS&lt;/strong&gt; —even “hello world” is ~33KB.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Counter-Intuitive Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;macOS Mach-O&lt;/th&gt;
&lt;th&gt;Linux ELF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stripped size&lt;/td&gt;
&lt;td&gt;35,016 bytes&lt;/td&gt;
&lt;td&gt;67,952 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actual code (text)&lt;/td&gt;
&lt;td&gt;~7KB&lt;/td&gt;
&lt;td&gt;~11KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format overhead&lt;/td&gt;
&lt;td&gt;~28KB&lt;/td&gt;
&lt;td&gt;~56KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page alignment&lt;/td&gt;
&lt;td&gt;16KB&lt;/td&gt;
&lt;td&gt;4KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Despite 16KB pages, Mach-O is MORE efficient than ELF!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why ELF is larger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More section headers and metadata&lt;/li&gt;
&lt;li&gt;Debug info remnants even after strip&lt;/li&gt;
&lt;li&gt;Symbol table overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Inspecting Your Own Binaries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS - see segment sizes
size -m your_binary
# macOS - detailed Mach-O structure
otool -l your_binary | grep -A5 "segname"
# Linux - section sizes
size your_binary
# Linux - detailed sections
readelf -S your_binary

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The UPX Factor (Linux Only)
&lt;/h2&gt;

&lt;p&gt;UPX (Ultimate Packer for eXecutables) compresses binaries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Compression&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Uncompressed&lt;/td&gt;
&lt;td&gt;67,952 bytes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UPX NRV (default)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10,288 bytes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;85% reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UPX LZMA&lt;/td&gt;
&lt;td&gt;11,528 bytes&lt;/td&gt;
&lt;td&gt;83% reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NRV beats LZMA for small binaries by 11%!&lt;/strong&gt; This surprised me—I expected LZMA to always win.&lt;/p&gt;

&lt;p&gt;Why not macOS?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UPX is officially unsupported for Mach-O&lt;/li&gt;
&lt;li&gt;Code signing conflicts with compressed binaries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--force-macos&lt;/code&gt; often causes segfaults&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  WebAssembly Build
&lt;/h2&gt;

&lt;p&gt;The interpreter also compiles to WebAssembly, producing a &lt;strong&gt;27KB&lt;/strong&gt; binary after optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  wasi-sdk vs Emscripten
&lt;/h3&gt;

&lt;p&gt;I chose wasi-sdk over Emscripten for one reason: &lt;strong&gt;no JavaScript bloat&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Toolchain&lt;/th&gt;
&lt;th&gt;Output Size&lt;/th&gt;
&lt;th&gt;What You Get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;wasi-sdk + wasm-opt&lt;/td&gt;
&lt;td&gt;27KB&lt;/td&gt;
&lt;td&gt;Single &lt;code&gt;.wasm&lt;/code&gt; file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emscripten&lt;/td&gt;
&lt;td&gt;100KB+&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.wasm&lt;/code&gt; + JavaScript runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Emscripten provides a full POSIX-like environment with filesystem emulation. For a simple eval function, that’s overkill. wasi-sdk produces a minimal WASI-compliant binary that only needs stub implementations for a handful of syscalls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Flags
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Compile with wasi-sdk
clang++ -std=c++20 -Os -fno-exceptions -Wl,--no-entry -Wl,--export-dynamic
# Optimize with wasm-opt (from Binaryen)
wasm-opt -Oz --strip-debug --strip-producers lisp.wasm -o lisp.wasm

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-fno-exceptions&lt;/code&gt; - Errors via &lt;code&gt;__builtin_trap()&lt;/code&gt;, reduces binary size&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-Wl,--export-dynamic&lt;/code&gt; - Export the &lt;code&gt;eval&lt;/code&gt; function for JS access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-Wl,--no-entry&lt;/code&gt; - Library mode, no &lt;code&gt;main()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  wasm-opt Optimization
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;After wasi-sdk compile&lt;/td&gt;
&lt;td&gt;33KB&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After wasm-opt -Oz&lt;/td&gt;
&lt;td&gt;33KB&lt;/td&gt;
&lt;td&gt;~0% (already optimized)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After –strip-debug&lt;/td&gt;
&lt;td&gt;28KB&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After –strip-producers&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;27KB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18% total&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-Oz&lt;/code&gt; flag alone doesn’t help much since wasi-sdk already optimizes well, but stripping debug info and producer metadata saves ~6KB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Clone and Build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/prasincs/minilisp-cpp
cd minilisp-cpp
# Default build
make
# Size-optimized
make small
# Ultra-small (POSIX I/O)
make ultra-small

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Compile-Time Evaluation
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;static_assert&lt;/code&gt; statements in &lt;code&gt;main.cpp&lt;/code&gt; prove compile-time evaluation works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constexpr auto val = "(+ 10 (* 2 5))"_lisp;
static_assert(val == 20); // Fails to compile if wrong!
constexpr auto val3 = "(car '(10 20 30))"_lisp;
static_assert(val3 == 10);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try introducing an error—the compiler will catch it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// This fails at compile time with a parse error
constexpr auto bad = "(+ 1"_lisp;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cross-Compile for Linux (from macOS)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./build-linux.sh
# Uses Docker to build Linux ARM64 binary
# Shows size comparison automatically

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Measure Binary Sections
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS
size -m lisp_repl
otool -l lisp_repl | grep -A5 "segname"
# Linux
size lisp_repl
readelf -S lisp_repl

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On binary size optimization:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Know when to stop&lt;/strong&gt; - macOS has a ~34KB floor due to Mach-O format overhead. Once you hit it, further code optimization is wasted effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure actual code size&lt;/strong&gt; - Use &lt;code&gt;size&lt;/code&gt;, not &lt;code&gt;ls -l&lt;/code&gt;. File size is dominated by format overhead; code size is what you can control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iostream is expensive&lt;/strong&gt; - Removing it saved 32% of actual executable code. If size matters, use POSIX I/O.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;On C++20:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;constexpr is powerful&lt;/strong&gt; - A full Lisp interpreter at compile time in readable code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same code, dual modes&lt;/strong&gt; - No template metaprogramming gymnastics required&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a compile-time Lisp interpreter turned out to be a journey through modern C++ and binary format archaeology. The compile-time evaluation is genuinely useful for catching errors early, but the binary size investigation taught me more about platform-specific behavior than I expected.&lt;/p&gt;

&lt;p&gt;The source code is available at &lt;a href="https://github.com/prasincs/minilisp-cpp" rel="noopener noreferrer"&gt;github.com/prasincs/minilisp-cpp&lt;/a&gt;, as well as a standalone &lt;a href="https://nextdoorhacker.com/minilisp-cpp/" rel="noopener noreferrer"&gt;playground&lt;/a&gt;. Try adding new operations—they’ll automatically work at both compile-time and runtime.&lt;/p&gt;

&lt;p&gt;Sometimes the journey of optimization teaches more than the destination.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>cpp</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Neovim for Blog Writing: Plugins, Keymaps, and a Cheatsheet</title>
      <dc:creator>Prasanna Gautam</dc:creator>
      <pubDate>Fri, 26 Dec 2025 13:25:00 +0000</pubDate>
      <link>https://dev.to/prasincs/neovim-for-blog-writing-plugins-keymaps-and-a-cheatsheet-38fn</link>
      <guid>https://dev.to/prasincs/neovim-for-blog-writing-plugins-keymaps-and-a-cheatsheet-38fn</guid>
      <description>&lt;p&gt;My Neovim config has always been coding-focused—LSP, treesitter, language-specific keymaps for Zig, Rust, and Go. But I recently wanted to use the same setup for writing blog posts. Here’s what I added to make Neovim a solid prose environment.&lt;/p&gt;

&lt;p&gt;The full config is at &lt;a href="https://github.com/prasincs/vim-config" rel="noopener noreferrer"&gt;github.com/prasincs/vim-config&lt;/a&gt;. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7m7r336yx42toi1yf0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7m7r336yx42toi1yf0w.png" alt="screenshot-20251226-041442" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Writing-Focused Plugins
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Zen Mode
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/folke/zen-mode.nvim" rel="noopener noreferrer"&gt;zen-mode.nvim&lt;/a&gt; removes distractions by centering your buffer and hiding UI elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"folke/zen-mode.nvim",
opts = {},
keys = {
{ "&amp;lt;leader&amp;gt;z", "&amp;lt;cmd&amp;gt;ZenMode&amp;lt;cr&amp;gt;", desc = "Zen Mode" },
},
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;&amp;lt;Space&amp;gt;z&lt;/code&gt; to toggle. The buffer centers itself, line numbers fade, and you’re left with just your text.&lt;/p&gt;

&lt;p&gt;The screenshot below shows what zen-mode looks like &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5p03r540kleithah5nm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5p03r540kleithah5nm2.png" alt="screenshot-20251226-043407" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Twilight
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/folke/twilight.nvim" rel="noopener noreferrer"&gt;twilight.nvim&lt;/a&gt; dims inactive portions of your file. When combined with Zen Mode, it auto-activates to keep your focus on the current paragraph.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"folke/twilight.nvim",
opts = {},
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The screenshot above also shows the dimming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Render Markdown
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/MeanderingProgrammer/render-markdown.nvim" rel="noopener noreferrer"&gt;render-markdown.nvim&lt;/a&gt; renders markdown decorations directly in the buffer—headings get background colors, code blocks are highlighted, lists show proper bullets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"MeanderingProgrammer/render-markdown.nvim",
dependencies = { "nvim-treesitter/nvim-treesitter", "nvim-tree/nvim-web-devicons" },
ft = { "markdown" },
opts = {},
keys = {
{ "&amp;lt;leader&amp;gt;mr", "&amp;lt;cmd&amp;gt;RenderMarkdown toggle&amp;lt;cr&amp;gt;", desc = "Toggle markdown rendering" },
},
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;&amp;lt;Space&amp;gt;mr&lt;/code&gt; to toggle rendering on/off. This is optional if you’re using Hugo’s live server for previews—sometimes the raw markdown is easier to edit.&lt;/p&gt;

&lt;p&gt;This was a big new and confusing to me, so here’s a helper &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymdmfdqrp4gxepiwo75k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymdmfdqrp4gxepiwo75k.gif" alt="screen-recording-markdown-toggle" width="800" height="236"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Markdown Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Treesitter Parsers
&lt;/h3&gt;

&lt;p&gt;Add &lt;code&gt;markdown&lt;/code&gt; and &lt;code&gt;markdown_inline&lt;/code&gt; to your treesitter config for proper syntax highlighting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"nvim-treesitter/nvim-treesitter",
opts = {
ensure_installed = { "markdown", "markdown_inline", --[[other languages]] },
},
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prose-Friendly Settings
&lt;/h3&gt;

&lt;p&gt;Markdown files need different settings than code. This autocmd enables word wrap, spell checking, and hides markdown syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim.api.nvim_create_autocmd("FileType", {
pattern = "markdown",
callback = function()
vim.opt_local.wrap = true
vim.opt_local.linebreak = true
vim.opt_local.spell = true
vim.opt_local.conceallevel = 2
end,
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wrap&lt;/code&gt; + &lt;code&gt;linebreak&lt;/code&gt;: Text wraps at word boundaries instead of mid-word&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spell&lt;/code&gt;: Highlights misspellings (use &lt;code&gt;z=&lt;/code&gt; to see suggestions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;conceallevel = 2&lt;/code&gt;: Hides markdown syntax like &lt;code&gt;**bold**&lt;/code&gt;, showing just &lt;strong&gt;bold&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Adding Images and Screenshots
&lt;/h2&gt;

&lt;p&gt;One friction point in markdown blogging is adding images. You either need to manually copy files and write the markdown tag, or leave Neovim entirely. I added helpers to streamline this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup: pngpaste
&lt;/h3&gt;

&lt;p&gt;For clipboard paste support on macOS, you need &lt;code&gt;pngpaste&lt;/code&gt;. I use &lt;a href="https://github.com/prasincs/pngpaste/tree/error-handling" rel="noopener noreferrer"&gt;my fork with error handling&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b error-handling https://github.com/prasincs/pngpaste.git
cd pngpaste
make &amp;amp;&amp;amp; sudo make install

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the original via Homebrew: &lt;code&gt;brew install pngpaste&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Image Helper Functions
&lt;/h3&gt;

&lt;p&gt;The config auto-detects your images directory (&lt;code&gt;static/images&lt;/code&gt;, &lt;code&gt;assets/images&lt;/code&gt;, etc.) and provides three ways to add images:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap/Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Paste from clipboard&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mi&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paste with custom name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert from file path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mI&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert via command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:MarkdownInsertImage ~/path/to/image.png&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you paste, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Saves the clipboard image to &lt;code&gt;static/images/screenshot-YYYYMMDD-HHMMSS.png&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Inserts &lt;code&gt;![screenshot-YYYYMMDD-HHMMSS](/static/images/screenshot-YYYYMMDD-HHMMSS.png)&lt;/code&gt; at cursor&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;:MarkdownInsertImage&lt;/code&gt; command copies an external file into your images directory and inserts the tag—useful for images from Downloads or other projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Screen Recordings
&lt;/h3&gt;

&lt;p&gt;For screen recordings, there’s a dedicated workflow that converts videos to GIF automatically:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fuzzy find recording from Desktop&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert via command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:MarkdownInsertRecording &amp;lt;path&amp;gt; &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The search directories are configurable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;local markdown_media_config = {
image_search_dir = "~/Downloads", -- Where to search for images with &amp;lt;Space&amp;gt;mI
recording_search_dir = "~/Desktop", -- Where to search for recordings with &amp;lt;Space&amp;gt;mR
image_search_depth = 2, -- How deep to search for images
recording_search_depth = 1, -- How deep to search for recordings
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;Space&amp;gt;mR&lt;/code&gt; keymap opens Telescope filtered to video files (&lt;code&gt;.mov&lt;/code&gt;, &lt;code&gt;.mp4&lt;/code&gt;, &lt;code&gt;.webm&lt;/code&gt;) in your configured directory—&lt;code&gt;~/Desktop&lt;/code&gt; by default, where macOS saves screen recordings.&lt;/p&gt;

&lt;p&gt;The command handles spaces in filenames naturally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:MarkdownInsertRecording ~/Desktop/Screen Recording 2025-12-26 at 4.52.47 AM.mov my-demo

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last word without &lt;code&gt;/&lt;/code&gt; is treated as the output name, everything before it is the path. This converts the video to GIF using &lt;code&gt;ffmpeg&lt;/code&gt; and inserts the markdown tag.&lt;/p&gt;

&lt;p&gt;For static images, the keymaps use Telescope for fuzzy finding:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fuzzy find image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mI&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paste from clipboard&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mi&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paste with custom name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2vq3aomlwblr74e5clf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2vq3aomlwblr74e5clf.gif" alt="neovim-screen-recording-to-gif" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  AI-Assisted Writing with Claude Code
&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="https://github.com/coder/claudecode.nvim" rel="noopener noreferrer"&gt;claudecode.nvim&lt;/a&gt; for AI assistance while writing. It integrates Claude directly into Neovim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"coder/claudecode.nvim",
dependencies = { "folke/snacks.nvim" },
opts = {
terminal_cmd = vim.fn.expand("~/.claude/local/claude"),
},
keys = {
{ "&amp;lt;leader&amp;gt;ac", "&amp;lt;cmd&amp;gt;ClaudeCode&amp;lt;cr&amp;gt;", desc = "Toggle Claude" },
{ "&amp;lt;leader&amp;gt;af", "&amp;lt;cmd&amp;gt;ClaudeCodeFocus&amp;lt;cr&amp;gt;", desc = "Focus Claude" },
{ "&amp;lt;leader&amp;gt;as", "&amp;lt;cmd&amp;gt;ClaudeCodeSend&amp;lt;cr&amp;gt;", mode = "v", desc = "Send to Claude" },
{ "&amp;lt;leader&amp;gt;ab", "&amp;lt;cmd&amp;gt;ClaudeCodeAdd %&amp;lt;cr&amp;gt;", desc = "Add current buffer" },
},
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select text, hit &lt;code&gt;&amp;lt;Space&amp;gt;as&lt;/code&gt;, and Claude sees your selection. Useful for rephrasing paragraphs or getting feedback on technical explanations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cheatsheet
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Starting a Writing Session
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open file finder&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;ff&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toggle Zen Mode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;z&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toggle file tree&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;e&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toggle markdown rendering&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Navigating Content
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Search in files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;fg&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switch buffers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;fb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Editing
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Move line down&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;J&lt;/code&gt; (visual mode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Move line up&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;K&lt;/code&gt; (visual mode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yank to clipboard&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;y&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toggle comment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  AI Assistance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Toggle Claude&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;ac&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send selection to Claude&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;Space&amp;gt;as&lt;/code&gt; (visual)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add buffer as context&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;ab&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Spell Checking
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Next misspelling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;]s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Previous misspelling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Suggest corrections&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z=&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add word to dictionary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zg&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Images &amp;amp; Recordings
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Keymap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Paste image from clipboard&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mi&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paste with custom name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fuzzy find image from home&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mI&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fuzzy find recording from Desktop&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Space&amp;gt;mR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This setup gives me distraction-free writing with in-buffer markdown rendering, spell checking, and AI assistance—all without leaving Neovim.&lt;/p&gt;

&lt;p&gt;It’s been over a decade since I wrote about &lt;a href="https://dev.to/prasincs/perfect-vim-setup-for-go-3na3-temp-slug-9490140"&gt;Perfect Vim Setup for Go&lt;/a&gt;. The tooling has evolved dramatically, but the core idea remains: configure your editor to fit how you work.&lt;/p&gt;

&lt;p&gt;Full config: &lt;a href="https://github.com/prasincs/vim-config" rel="noopener noreferrer"&gt;github.com/prasincs/vim-config&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>writing</category>
      <category>tooling</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Don't Unwrap in Production: A Formal Verification Guide</title>
      <dc:creator>Prasanna Gautam</dc:creator>
      <pubDate>Thu, 25 Dec 2025 06:13:04 +0000</pubDate>
      <link>https://dev.to/prasincs/dont-unwrap-in-production-a-formal-verification-guide-49ei</link>
      <guid>https://dev.to/prasincs/dont-unwrap-in-production-a-formal-verification-guide-49ei</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; : On November 18, 2025, a single &lt;code&gt;.unwrap()&lt;/code&gt; call caused a 3-hour global Cloudflare outage. Formal verification tools like Verus could have caught this at compile time—before it reached production. This post shows how Verus’s built-in specifications for &lt;code&gt;Option::unwrap()&lt;/code&gt; and &lt;code&gt;Result::unwrap()&lt;/code&gt; can mathematically prove panic-freedom, with working examples you can run today.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bug
&lt;/h2&gt;

&lt;p&gt;On November 18, 2025, Cloudflare experienced a &lt;a href="https://blog.cloudflare.com/18-november-2025-outage/" rel="noopener noreferrer"&gt;3-hour global outage&lt;/a&gt;. The root cause? A panic in Rust code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MAX_FEATURES: usize = 200;
fn load_bot_features(file_path: &amp;amp;Path) -&amp;gt; Result&amp;lt;BotFeatures, Error&amp;gt; {
// 💥 PANIC: parse_feature_file can return Err, but we unwrap anyway
let features = parse_feature_file(file_path).unwrap();
if features.len() &amp;gt; MAX_FEATURES {
return Err(Error::TooManyFeatures); // Never reached!
}
BotFeatures { features }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What went wrong:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The developer knew the file size mattered and wrote a check: &lt;code&gt;if features.len() &amp;gt; MAX_FEATURES&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;However, they placed this check &lt;strong&gt;after&lt;/strong&gt; the &lt;code&gt;unwrap()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When the config file accidentally doubled in size during deployment, &lt;code&gt;parse_feature_file&lt;/code&gt; failed internally (returning &lt;code&gt;Err&lt;/code&gt;) due to buffer limits.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.unwrap()&lt;/code&gt; triggered immediately.&lt;/li&gt;
&lt;li&gt;The manual safety check was dead code—it was never reached.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The frustrating part:&lt;/strong&gt; This code &lt;em&gt;looked&lt;/em&gt; safe. It had error handling below. It passed code review. It had tests. But the &lt;code&gt;.unwrap()&lt;/code&gt; was in the wrong place.&lt;/p&gt;

&lt;p&gt;Here’s how Verus would have caught this at compile time.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Verus Catches This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Unsafe Version (Cloudflare’s Bug)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn load_bot_features(file_path: &amp;amp;Path) -&amp;gt; Result&amp;lt;BotFeatures, Error&amp;gt; {
let features = parse_feature_file(file_path).unwrap(); // 💥
if features.len() &amp;gt; MAX_FEATURES {
return Err(Error::TooManyFeatures);
}
Ok(BotFeatures { features })
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verus output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: precondition not satisfied
--&amp;gt; src/bot_manager.rs:42:52
|
42 | let features = parse_feature_file(file_path).unwrap();
| ^^^^^^^^
|
= note: cannot prove `parse_feature_file(file_path).is_Ok()`
= help: unwrap() requires proof that Result is Ok

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Translation:&lt;/strong&gt; “I cannot prove this won’t panic. Fix it or I won’t compile.”&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Safe Version (Verus-Approved)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Proper error propagation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn load_bot_features(file_path: &amp;amp;Path) -&amp;gt; Result&amp;lt;BotFeatures, Error&amp;gt; {
let features = parse_feature_file(file_path)?; // ✅ Propagate error
if features.len() &amp;gt; MAX_FEATURES {
return Err(Error::TooManyFeatures);
}
Ok(BotFeatures { features })
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Explicit proof&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn load_bot_features(file_path: &amp;amp;Path) -&amp;gt; Result&amp;lt;BotFeatures, Error&amp;gt; {
let result = parse_feature_file(file_path);
if result.is_ok() {
let features = result.unwrap(); // ✅ Verus proves: is_ok() → unwrap() safe
if features.len() &amp;gt; MAX_FEATURES {
return Err(Error::TooManyFeatures);
}
Ok(BotFeatures { features })
} else {
Err(Error::ParseFailed)
}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verus output: ✅ &lt;strong&gt;Verification successful&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But how does Verus know &lt;code&gt;.unwrap()&lt;/code&gt; is safe after an &lt;code&gt;is_ok()&lt;/code&gt; check? The answer lies in Verus’s built-in specifications.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Verus Works: Built-In Proofs for Unwrap Safety
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/verus-lang/verus" rel="noopener noreferrer"&gt;Verus&lt;/a&gt; is a formal verification tool for Rust developed at CMU and VMware Research. It leverages Rust’s ownership system and uses SMT solvers to prove panic-freedom.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Secret Sauce: Unwrap Is Conditionally Safe
&lt;/h3&gt;

&lt;p&gt;Here’s the key insight—Verus &lt;strong&gt;already knows&lt;/strong&gt; that &lt;code&gt;.unwrap()&lt;/code&gt; is safe &lt;strong&gt;if and only if&lt;/strong&gt; you have &lt;code&gt;Some&lt;/code&gt; or &lt;code&gt;Ok&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option::unwrap() specification&lt;/strong&gt; (&lt;a href="https://github.com/verus-lang/verus/blob/ab8296c1b81381dd035cc4568951666b5c1d6750/source/vstd/std_specs/option.rs#L142-L147" rel="noopener noreferrer"&gt;source&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;impl&amp;lt;T&amp;gt; Option&amp;lt;T&amp;gt; {
#[verifier::external_body]
pub fn unwrap(self) -&amp;gt; T
requires self.is_Some(), // ← MUST prove this!
{
// ...
}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result::unwrap() specification&lt;/strong&gt; (&lt;a href="https://github.com/verus-lang/verus/blob/main/source/vstd/std_specs/result.rs#L165-L171" rel="noopener noreferrer"&gt;source&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;impl&amp;lt;T, E&amp;gt; Result&amp;lt;T, E&amp;gt; {
#[verifier::external_body]
pub fn unwrap(self) -&amp;gt; T
requires self.is_Ok(), // ← MUST prove this!
{
// ...
}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this means:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ If Verus can prove &lt;code&gt;option.is_Some()&lt;/code&gt; → &lt;code&gt;.unwrap()&lt;/code&gt; is safe&lt;/li&gt;
&lt;li&gt;✅ If Verus can prove &lt;code&gt;result.is_Ok()&lt;/code&gt; → &lt;code&gt;.unwrap()&lt;/code&gt; is safe&lt;/li&gt;
&lt;li&gt;❌ If Verus &lt;strong&gt;cannot&lt;/strong&gt; prove it → &lt;strong&gt;Compilation fails&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No runtime overhead. No test coverage gaps. Just mathematical certainty.&lt;/p&gt;

&lt;p&gt;Now that you’ve seen how Verus works, let’s try it yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Working Examples You Can Try Today
&lt;/h2&gt;

&lt;p&gt;I’ve created a repository with runnable Verus examples showing panic detection in practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 &lt;a href="https://github.com/prasincs/formal-verification-experiments/tree/main/verus" rel="noopener noreferrer"&gt;formal-verification-experiments/verus&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples included:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Array bounds checking&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Division by zero&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integer overflow&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Option/Result unwrapping&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each example shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The unsafe version (Verus rejects)&lt;/li&gt;
&lt;li&gt;The safe version (Verus approves)&lt;/li&gt;
&lt;li&gt;The exact verification error message&lt;/li&gt;
&lt;li&gt;How to fix it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to run:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Install Verus
git clone https://github.com/verus-lang/verus.git
cd verus &amp;amp;&amp;amp; ./tools/get-z3.sh &amp;amp;&amp;amp; source ./tools/activate
# Clone examples
git clone https://github.com/prasincs/formal-verification-experiments.git
cd formal-verification-experiments/verus
# Verify examples
verus examples/panic_detection.rs
# See it catch bugs
verus examples/unsafe_unwrap.rs # ❌ Fails verification
verus examples/safe_unwrap.rs # ✅ Passes verification

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Developer Experience: VSCode Integration
&lt;/h2&gt;

&lt;p&gt;Formal verification used to mean batch runs and cryptic error messages. Not anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://verus-lang.github.io/verus/guide/getting_started_vscode.html" rel="noopener noreferrer"&gt;Verus VSCode extension&lt;/a&gt;&lt;/strong&gt; provides real-time feedback:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Instant verification&lt;/strong&gt; : See errors as you type (like clippy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline diagnostics&lt;/strong&gt; : Red squiggles under unsafe code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helpful messages&lt;/strong&gt; : “Cannot prove &lt;code&gt;i &amp;lt; bytes.len()&lt;/code&gt;”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counterexamples&lt;/strong&gt; : Verus often shows the exact input that would cause panic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn parse_first_byte(bytes: &amp;amp;[u8]) -&amp;gt; u8 {
bytes[0] // ⚠️ Red squiggle appears immediately
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hover over the warning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Verification failed
Cannot prove: 0 &amp;lt; bytes.len()
Counterexample: bytes = []
Suggestion: Add `requires bytes.len() &amp;gt; 0`

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn parse_first_byte(bytes: &amp;amp;[u8]) -&amp;gt; u8
requires bytes.len() &amp;gt; 0
{
bytes[0] // ✅ Green checkmark
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a game-changer. You don’t run verification in CI and wait 20 minutes. You get instant feedback, &lt;strong&gt;right in your editor&lt;/strong&gt; , as you write code.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Verus Doesn’t Prove (And Why That’s OK)
&lt;/h2&gt;

&lt;p&gt;Let’s be honest about limitations:&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Verus does NOT prove:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Business logic correctness&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Liveness&lt;/strong&gt; (yet)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Full functional correctness&lt;/strong&gt; (unless you specify it)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ✅ What Verus DOES prove:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Panic-freedom&lt;/strong&gt; : No unwrap/bounds/overflow/div-by-zero panics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory safety&lt;/strong&gt; : Already guaranteed by Rust, but Verus can verify unsafe code too&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom invariants&lt;/strong&gt; : You can prove application-specific properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; Even proving &lt;em&gt;just&lt;/em&gt; panic-freedom is hugely valuable. Studies show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~40% of production outages&lt;/strong&gt; involve panics, crashes, or overflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare&lt;/strong&gt; : 3-hour outage from single unwrap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ethereum&lt;/strong&gt; : $150M+ lost to integer overflow (The DAO hack, 2016)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knight Capital&lt;/strong&gt; : $440M loss from uncaught exception (2012)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eliminating 40% of outages is &lt;strong&gt;not&lt;/strong&gt; “just” anything.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Should You Use Verus?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Use Verus when:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parsing untrusted input&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Arithmetic on money/tokens&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Safety-critical code&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High-leverage code&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compliance requirements&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;*Important note:&lt;/em&gt;&lt;/strong&gt; seL4’s verified kernel has had zero vulnerabilities, but the broader ecosystem (musllibc, userspace) has had bugs like &lt;a href="https://github.com/seL4/musllibc/issues/7" rel="noopener noreferrer"&gt;CVE-2020-28928&lt;/a&gt;. &lt;strong&gt;Lesson:&lt;/strong&gt; Formal verification only covers what you verify, not dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Skip Verus when:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prototyping&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rapid iteration&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Glue code / scripts&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You already have robust testing&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Decision Tree:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
A[Is this code safety-critical&amp;lt;br/&amp;gt;or high-leverage?] --&amp;gt;|No| B[Skip Verus&amp;lt;br/&amp;gt;use tests]
A --&amp;gt;|Yes| C[Can bugs cause outages&amp;lt;br/&amp;gt;or financial loss?]
C --&amp;gt;|No| D[Skip Verus&amp;lt;br/&amp;gt;use tests]
C --&amp;gt;|Yes| E[Is the codebase stable?]
E --&amp;gt;|No| F[Wait, use tests for now]
E --&amp;gt;|Yes| G[Use Verus]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also matches the approach that Amazon AWS has adopted called &lt;a href="https://youtu.be/ZuPGZ3W-ITA?list=TLGG1onXDorenzUyNTEyMjAyNQ" rel="noopener noreferrer"&gt;Verification Guided Development&lt;/a&gt; in the talk by Mark Hicks where they first require properties to be documented and verified by using &lt;strong&gt;Property-based Testing&lt;/strong&gt; and then once this is verified, a mathematical proof starts to make sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison: Other Rust Verification Tools
&lt;/h2&gt;

&lt;p&gt;Verus isn’t the only game in town:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Maturity&lt;/th&gt;
&lt;th&gt;Backed By&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SMT-based&lt;/td&gt;
&lt;td&gt;Systems code, panic-freedom&lt;/td&gt;
&lt;td&gt;2 OSDI Best Papers 2024&lt;/td&gt;
&lt;td&gt;CMU, VMware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kani&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bounded model checking&lt;/td&gt;
&lt;td&gt;Unsafe code, bit-level&lt;/td&gt;
&lt;td&gt;Production-ready&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prusti&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Viper backend&lt;/td&gt;
&lt;td&gt;Safe Rust, functional correctness&lt;/td&gt;
&lt;td&gt;Research-grade&lt;/td&gt;
&lt;td&gt;ETH Zurich&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Creusot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Why3 backend&lt;/td&gt;
&lt;td&gt;Algorithms, faster verification&lt;/td&gt;
&lt;td&gt;Research-grade&lt;/td&gt;
&lt;td&gt;Inria&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Quick guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verifying unsafe code?&lt;/strong&gt; → &lt;a href="https://model-checking.github.io/kani/" rel="noopener noreferrer"&gt;Kani&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proving algorithms correct?&lt;/strong&gt; → &lt;a href="https://github.com/creusot-rs/creusot" rel="noopener noreferrer"&gt;Creusot&lt;/a&gt; or &lt;a href="https://www.pm.inf.ethz.ch/research/prusti.html" rel="noopener noreferrer"&gt;Prusti&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preventing production panics?&lt;/strong&gt; → &lt;a href="https://verus-lang.github.io/verus/guide/" rel="noopener noreferrer"&gt;Verus&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple tools?&lt;/strong&gt; → They’re complementary, not exclusive&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Success Stories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;OSDI 2024 Best Papers (Both Used Verus)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.usenix.org/conference/osdi24/presentation/sun-xudong" rel="noopener noreferrer"&gt;Anvil&lt;/a&gt;&lt;/strong&gt;: Verified liveness properties in Kubernetes controllers&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Found bugs in real production controllers&lt;/li&gt;
&lt;li&gt;Proved they can’t deadlock or livelock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.usenix.org/conference/osdi24/presentation/zhou" rel="noopener noreferrer"&gt;VeriSMo&lt;/a&gt;&lt;/strong&gt;: Verified security module for confidential VMs&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Found security bug in AMD SVSM&lt;/li&gt;
&lt;li&gt;Proved memory isolation properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both papers used Verus. Both won Best Paper. This isn’t theory—it’s validated research.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;IronFleet (Microsoft Research, 2015)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Verified distributed systems (Paxos, chain replication):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100x fewer bugs&lt;/strong&gt; than unverified implementations&lt;/li&gt;
&lt;li&gt;Proved safety and liveness properties&lt;/li&gt;
&lt;li&gt;Paper: &lt;a href="https://www.microsoft.com/en-us/research/publication/ironfleet-proving-practical-distributed-systems-correct/" rel="noopener noreferrer"&gt;SOSP 2015&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Amazon Web Services (Ongoing)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;From &lt;a href="https://cacm.acm.org/magazines/2015/4/184701-how-amazon-web-services-uses-formal-methods/" rel="noopener noreferrer"&gt;“How Amazon Web Services Uses Formal Methods”&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We have used TLA+ on 10 large complex real-world systems. In every case, it has found bugs.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Different tool (TLA+), same principle: Proofs catch bugs testing misses. Amazon has also built &lt;a href="https://pl.cs.cornell.edu/pldg/stefan-zetzsche.pdf" rel="noopener noreferrer"&gt;Dafny&lt;/a&gt; Programming Language that builds static verification into the development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Asterinas OS (December 2025)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Verified page table implementation in general-purpose OS using Verus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proved memory safety properties&lt;/li&gt;
&lt;li&gt;Showed Verus scales to OS-level code&lt;/li&gt;
&lt;li&gt;Blog: &lt;a href="https://asterinas.github.io/2025/02/13/towards-practical-formal-verification-for-a-general-purpose-os-in-rust.html" rel="noopener noreferrer"&gt;Asterinas Formal Verification&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this growing adoption, the question becomes: what’s driving the timing?&lt;/p&gt;




&lt;h2&gt;
  
  
  The AI Connection: Why Now?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://martin.kleppmann.com/2025/12/08/ai-formal-verification.html" rel="noopener noreferrer"&gt;Martin Kleppmann&lt;/a&gt; (author of &lt;em&gt;Designing Data-Intensive Applications&lt;/em&gt;) made a bold prediction in December 2025:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Prediction: AI will make formal verification go mainstream.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;His argument:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LLMs are good at proof scripts&lt;/strong&gt; (formal reasoning)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hallucinations don’t matter&lt;/strong&gt; (proof checker rejects invalid proofs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI + Verus = powerful combo&lt;/strong&gt; (human intent + machine rigor)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Early evidence:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ChatGPT/Claude can write Verus annotations (with guidance), in fact Claude helped me write and validate lot of examples in this blog post.&lt;/li&gt;
&lt;li&gt;GitHub Copilot learns from verified code&lt;/li&gt;
&lt;li&gt;Proof assistants are a perfect fit for LLMs (discrete, verifiable output)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My take:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Promising, not proven&lt;/strong&gt; (as of Dec 2025)&lt;/li&gt;
&lt;li&gt;Current AI helps with &lt;strong&gt;boilerplate&lt;/strong&gt; annotations&lt;/li&gt;
&lt;li&gt;Still need human expertise for complex proofs&lt;/li&gt;
&lt;li&gt;But the trajectory is clear: AI lowers the barrier to entry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2015:&lt;/strong&gt; Write proofs by hand (PhD required)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2024:&lt;/strong&gt; Verus + VSCode (expert-friendly)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2025+:&lt;/strong&gt; Verus + AI + VSCode (accessible to senior engineers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re not there yet, but we’re getting close.&lt;/p&gt;




&lt;h2&gt;
  
  
  The ROI Question: Is It Worth It?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cost of Verification
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Time investment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning curve: 1-2 weeks to basic proficiency&lt;/li&gt;
&lt;li&gt;Annotation overhead: +10-30% development time (initially)&lt;/li&gt;
&lt;li&gt;Refactoring: Proofs need updates when code changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When it pays off:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code with long lifespan (&amp;gt;1 year in production)&lt;/li&gt;
&lt;li&gt;High-blast-radius bugs (outages affect millions)&lt;/li&gt;
&lt;li&gt;Financial or safety-critical systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost of NOT Verifying
&lt;/h3&gt;

&lt;p&gt;Let’s do the math for Cloudflare:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conservative estimate:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outage duration: 3 hours&lt;/li&gt;
&lt;li&gt;Revenue impact: ~$1.2M (based on $1.5B annual revenue)&lt;/li&gt;
&lt;li&gt;Reputation damage: Priceless (trust erosion)&lt;/li&gt;
&lt;li&gt;Engineer time: 50+ engineers × 3 hours = 150 engineer-hours&lt;/li&gt;
&lt;li&gt;Incident response, postmortem, fixes: Another 200 hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total visible cost:&lt;/strong&gt; $1.2M+ revenue + 350 hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What if they’d used Verus?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time to annotate &lt;code&gt;load_bot_features&lt;/code&gt;: 15 minutes&lt;/li&gt;
&lt;li&gt;Compilation would have failed: “Cannot prove unwrap is safe”&lt;/li&gt;
&lt;li&gt;Fix: Change &lt;code&gt;.unwrap()&lt;/code&gt; to &lt;code&gt;?&lt;/code&gt;: 30 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total cost:&lt;/strong&gt; 15.5 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ROI:&lt;/strong&gt; $1.2M / 15 minutes = &lt;strong&gt;$4,645 per minute&lt;/strong&gt; of verification time.&lt;/p&gt;

&lt;p&gt;Even if verification added &lt;strong&gt;50 hours&lt;/strong&gt; to their development cycle, the ROI is absurd.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Boring Case For Verification
&lt;/h3&gt;

&lt;p&gt;Forget Cloudflare. Here’s the boring truth:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Formal verification is insurance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You pay a small premium (development time) to avoid catastrophic loss (outages). Just like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fire insurance: Hope you never need it, but worth it&lt;/li&gt;
&lt;li&gt;Backups: Boring, essential, pays off when disaster strikes&lt;/li&gt;
&lt;li&gt;Tests: Everyone does them, verification is the next level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who benefits most:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infrastructure companies (Cloudflare, AWS, Datadog)&lt;/li&gt;
&lt;li&gt;Blockchain/crypto (The DAO: $150M lost to overflow)&lt;/li&gt;
&lt;li&gt;Financial services (Knight Capital: $440M lost to exception)&lt;/li&gt;
&lt;li&gt;Medical devices, aerospace (FDA/FAA requirements)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your company’s revenue depends on uptime, verification isn’t optional—it’s actuarial.&lt;/p&gt;




&lt;h2&gt;
  
  
  Criticisms and Rebuttals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  “This is cherry-picking—one outage doesn’t prove anything”
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fair point.&lt;/strong&gt; Let’s look at the data:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Study: &lt;a href="https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf" rel="noopener noreferrer"&gt;“Simple Testing Can Prevent Most Critical Failures”&lt;/a&gt;&lt;/strong&gt; (OSDI 2014)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyzed 198 production failures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;77%&lt;/strong&gt; could be prevented by checking error codes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;40%&lt;/strong&gt; involved uncaught exceptions or panics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Translation:&lt;/strong&gt; Verus-level checks could prevent ~40% of major outages.&lt;/p&gt;

&lt;p&gt;Is 40% worth it? You decide. But it’s not cherry-picking.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Formal verification slows down development”
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;True… initially.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning curve:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Week 1-2: Frustrating (proofs don’t go through)&lt;/li&gt;
&lt;li&gt;Week 3-4: Productive (patterns emerge)&lt;/li&gt;
&lt;li&gt;Month 2+: Faster (proofs guide refactoring)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Long-term benefit:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proofs act as &lt;strong&gt;executable documentation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Refactoring is safer (proofs break if you violate invariants)&lt;/li&gt;
&lt;li&gt;Bugs caught at compile time, not in production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Initially:&lt;/strong&gt; “Why do I need to annotate types? Just use &lt;code&gt;any&lt;/code&gt;!”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Later:&lt;/strong&gt; “Types caught 10 bugs before I even ran the code.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verification is types for &lt;strong&gt;behavior&lt;/strong&gt; , not just data.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Too immature / unproven”
&lt;/h3&gt;

&lt;p&gt;I have believed for over 10 years since I first came across Isabelle proof that the grind of writing provers is what prevents us from adopting these methods for more safety critical systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Counter evidence:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;seL4:&lt;/strong&gt; 15 years in production, zero vulnerabilities in verified code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IronFleet:&lt;/strong&gt; Deployed at Microsoft (2015)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon:&lt;/strong&gt; Uses TLA+ for 10+ years&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verus:&lt;/strong&gt; 2 OSDI Best Papers (2024)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;“Unproven” was true in 2015. Not anymore.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started: A Practical Path
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase 1: Experiment (1-2 weeks)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install Verus&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Try the examples&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wrote the run script to build the container and run the commands so that it only takes time once and not potentially wreck your workstation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Read the tutorial&lt;/strong&gt; : &lt;a href="https://verus-lang.github.io/verus/" rel="noopener noreferrer"&gt;Verus Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install VSCode extension&lt;/strong&gt; : &lt;a href="https://marketplace.visualstudio.com/items?itemName=verus-lang.verus-analyzer" rel="noopener noreferrer"&gt;verus-analyzer&lt;/a&gt; [YMMV but I had to stop rust-analyzer extension to get verus-analyzer to run]&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Phase 2: Low-Hanging Fruit (2-4 weeks)
&lt;/h3&gt;

&lt;p&gt;Pick &lt;strong&gt;one small, high-value module&lt;/strong&gt; in your codebase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arithmetic with financial values&lt;/li&gt;
&lt;li&gt;Parsing untrusted input&lt;/li&gt;
&lt;li&gt;Array/slice indexing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Verify one function. Get the workflow down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Expand (Ongoing)
&lt;/h3&gt;

&lt;p&gt;Once you’ve verified one module:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expand to related functions&lt;/li&gt;
&lt;li&gt;Document patterns that work&lt;/li&gt;
&lt;li&gt;Train team members&lt;/li&gt;
&lt;li&gt;Integrate into CI (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 4: Cultural Shift (6+ months)
&lt;/h3&gt;

&lt;p&gt;Formal verification becomes part of code review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Did you verify this panic path?”&lt;/li&gt;
&lt;li&gt;“Can we prove this array access is safe?”&lt;/li&gt;
&lt;li&gt;Proofs as first-class artifacts&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion: The Future Is Verified
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Here’s what we know:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Panics cause real outages&lt;/strong&gt; (Cloudflare: 3 hours, millions affected)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing alone isn’t enough&lt;/strong&gt; (Cloudflare had tests, code review, staging)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verus can prove panic-freedom&lt;/strong&gt; (mathematical certainty, not probabilistic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in proofs make it practical&lt;/strong&gt; (Option/Result unwrap safety is already specified)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling is maturing&lt;/strong&gt; (VSCode integration, instant feedback)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROI is compelling&lt;/strong&gt; (15 minutes of verification vs. $1.2M outage)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The question isn’t:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Should formal verification replace testing?” (No.)&lt;/li&gt;
&lt;li&gt;“Is verification perfect?” (No—it only proves what you verify.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The question is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“For my critical code paths, is 99.9% confidence (testing) enough?”&lt;/li&gt;
&lt;li&gt;“Or do I need 100% certainty (proof)?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Cloudflare’s bot detection? They needed proof. For seL4’s kernel? They needed proof. For your config parser that crashes production? Maybe you need proof too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Try It Yourself
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Examples repo:&lt;/strong&gt; &lt;a href="https://github.com/prasincs/formal-verification-experiments/tree/main/verus" rel="noopener noreferrer"&gt;formal-verification-experiments/verus&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verus tutorial:&lt;/strong&gt; &lt;a href="https://verus-lang.github.io/verus/" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VSCode extension:&lt;/strong&gt; &lt;a href="https://marketplace.visualstudio.com/items?itemName=verus-lang.verus-analyzer" rel="noopener noreferrer"&gt;verus-analyzer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Papers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/verus-lang/paper-sosp24-artifact" rel="noopener noreferrer"&gt;Verus: Verifying Rust Programs using Linear Ghost Types&lt;/a&gt; (OOPSLA 2023, SOSP 2024)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.usenix.org/conference/osdi24/presentation/sun-xudong" rel="noopener noreferrer"&gt;Anvil: Verifying Liveness of Cluster Management Controllers&lt;/a&gt; (OSDI 2024 Best Paper)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.usenix.org/conference/osdi24/presentation/zhou" rel="noopener noreferrer"&gt;VeriSMo: A Verified Security Module for Confidential VMs&lt;/a&gt; (OSDI 2024 Best Paper)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Incident Reports
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/18-november-2025-outage/" rel="noopener noreferrer"&gt;Cloudflare: 18 November 2025 Outage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Knight_Capital_Group" rel="noopener noreferrer"&gt;Knight Capital: $440M Trading Loss&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other Perspectives
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://martin.kleppmann.com/2025/12/08/ai-formal-verification.html" rel="noopener noreferrer"&gt;Martin Kleppmann: AI will make formal verification mainstream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf" rel="noopener noreferrer"&gt;Simple Testing Can Prevent Most Critical Failures&lt;/a&gt; (OSDI 2014)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CMU and VMware Research&lt;/strong&gt; for developing Verus&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare&lt;/strong&gt; for transparent incident reporting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Martin Kleppmann&lt;/strong&gt; for bringing formal methods’ potential into forefront&lt;/li&gt;
&lt;li&gt;The Rust community for building memory-safe foundations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Corrections?&lt;/strong&gt; Open an issue or PR on the &lt;a href="https://github.com/prasincs/formal-verification-experiments" rel="noopener noreferrer"&gt;examples repo&lt;/a&gt;. I’m still learning about this tool and limitations, so I am happy to get educated here.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published: December 2025&lt;/em&gt;&lt;em&gt;Last Updated: December 2025&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>testing</category>
      <category>computerscience</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
