<?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>Forem: Eresh Gorantla</title>
    <description>The latest articles on Forem by Eresh Gorantla (@eresh_g_721f1d5ca1de0a5b9).</description>
    <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9</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%2F3805226%2Fc8a323ab-afec-4941-91ea-4e49a5743795.png</url>
      <title>Forem: Eresh Gorantla</title>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/eresh_g_721f1d5ca1de0a5b9"/>
    <language>en</language>
    <item>
      <title>Architecture Breaks Silently. Now There's a Tool That Investigates.</title>
      <dc:creator>Eresh Gorantla</dc:creator>
      <pubDate>Sun, 05 Apr 2026 18:10:36 +0000</pubDate>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9/architecture-breaks-silently-now-theres-a-tool-that-investigates-3kgk</link>
      <guid>https://forem.com/eresh_g_721f1d5ca1de0a5b9/architecture-breaks-silently-now-theres-a-tool-that-investigates-3kgk</guid>
      <description>&lt;p&gt;There's a class of bugs that no single-file view will ever reveal.&lt;/p&gt;

&lt;p&gt;Working in IoT, I've seen failures surface in one place while the real cause lived somewhere completely different. A device message handler would fail under load -- but the handler code looked correct. The root cause was spread across multiple files: a config value that disabled a cache, three async paths racing against the same shared resource, and a state recomputation that should never have been happening on every request. Each file was fine in isolation. The bug only existed in how they interacted at runtime.&lt;/p&gt;

&lt;p&gt;This pattern keeps showing up in every complex system I've worked on. The hardest bugs aren't syntax errors. They're architectural -- config that silently changes downstream behavior, interface contracts that drift, concurrent paths that only break under real load.&lt;/p&gt;

&lt;p&gt;Fixing them requires investigation: trace the failure, read the caller chain, inspect config, connect behavior across modules. That's exactly the workflow I wanted to automate.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://marketplace.visualstudio.com/items?itemName=EreshGorantla.archexa" rel="noopener noreferrer"&gt;Archexa&lt;/a&gt;&lt;/p&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%2F000j715jdcrbajfwky03.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%2F000j715jdcrbajfwky03.gif" alt="Demo Of Archexa" width="600" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Archexa is a VS Code extension powered by the &lt;a href="https://github.com/ereshzealous/archexa" rel="noopener noreferrer"&gt;Archexa CLI&lt;/a&gt; -- a self-contained binary that scans your project with tree-sitter, then uses an LLM agent to investigate across files.&lt;/p&gt;

&lt;p&gt;Six commands, all from right-click or keyboard shortcuts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Review&lt;/strong&gt; (&lt;code&gt;Cmd+Shift+R&lt;/code&gt;) -- Architecture-aware code review. Findings appear as inline squiggles, just like ESLint or TypeScript diagnostics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diagnose&lt;/strong&gt; (&lt;code&gt;Cmd+Shift+D&lt;/code&gt;) -- Paste an error or stack trace. It traces the call chain to the root cause across files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impact&lt;/strong&gt; (&lt;code&gt;Cmd+Shift+I&lt;/code&gt;) -- "What breaks if I change this?" Traces callers and interface contracts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query&lt;/strong&gt; (&lt;code&gt;Cmd+Alt+Q&lt;/code&gt;) -- Ask anything about your codebase. Evidence-backed answers with file references.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gist&lt;/strong&gt; -- Quick codebase overview. Great for day-one onboarding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analyze&lt;/strong&gt; -- Full architecture documentation with Mermaid diagrams.&lt;/li&gt;
&lt;/ul&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%2Fxfx26ishizdcfxxn4hrw.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%2Fxfx26ishizdcfxxn4hrw.png" alt="Review findings" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup (60 seconds)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# From VS Code Marketplace — search "Archexa" and click Install&lt;/span&gt;
&lt;span class="c"&gt;# Or from the command line:&lt;/span&gt;
code &lt;span class="nt"&gt;--install-extension&lt;/span&gt; EreshGorantla.archexa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The setup wizard downloads the &lt;a href="https://github.com/ereshzealous/archexa" rel="noopener noreferrer"&gt;CLI binary&lt;/a&gt; automatically (~20 MB). No Python, no pip, no runtime dependencies.&lt;/p&gt;

&lt;p&gt;Then: &lt;strong&gt;Sidebar &amp;gt; Settings &amp;gt; Connection&lt;/strong&gt; &amp;gt; set your API key + endpoint.&lt;/p&gt;

&lt;p&gt;Works with &lt;strong&gt;any OpenAI-compatible provider:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI (&lt;code&gt;gpt-4o&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;OpenRouter (&lt;code&gt;gemini-2.5-flash&lt;/code&gt;, &lt;code&gt;claude-sonnet-4-20250514&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Ollama (&lt;code&gt;llama3.1&lt;/code&gt; -- fully local, zero code leaves your machine)&lt;/li&gt;
&lt;li&gt;vLLM, Azure OpenAI, LiteLLM&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Every command follows the same three-phase approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Scan&lt;/strong&gt; -- The CLI parses your project with tree-sitter AST parsing. Maps imports, function signatures, call sites, and module boundaries. Runs offline in seconds. Results are cached.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Investigate&lt;/strong&gt; -- The LLM becomes an agent with tools: &lt;code&gt;read_file&lt;/code&gt;, &lt;code&gt;grep_pattern&lt;/code&gt;, &lt;code&gt;trace_callers&lt;/code&gt;, &lt;code&gt;list_directory&lt;/code&gt;. It decides what to read and when it has enough evidence. Typical run: 3-6 iterations, 10-20 tool calls, 5-15 files examined.&lt;/p&gt;

&lt;p&gt;Here's a real investigation trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1: Read error handler -&amp;gt; found verify_token()
Step 2: Search verify_token -&amp;gt; 8 references, 5 files. Read cache module -&amp;gt; no synchronization
Step 3: Trace callers of cache.get_token -&amp;gt; 3 async handlers. Read config -&amp;gt; TTL=0
Step 4: Root cause: race condition. Concurrent handlers, unprotected cache, caching disabled by config.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent followed the trail across files, just like an experienced engineer would -- in under a minute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Synthesize&lt;/strong&gt; -- All evidence assembled into one context-optimized prompt. The LLM generates the final output with file references and severity ratings.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Archexa CLI
&lt;/h2&gt;

&lt;p&gt;The extension is the UI layer. The real engine is the &lt;a href="https://github.com/ereshzealous/archexa" rel="noopener noreferrer"&gt;Archexa CLI&lt;/a&gt; -- a standalone binary that handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tree-sitter AST parsing across 13 languages&lt;/li&gt;
&lt;li&gt;Agent orchestration with tool-calling loops&lt;/li&gt;
&lt;li&gt;Progressive context pruning as evidence grows&lt;/li&gt;
&lt;li&gt;Streaming LLM communication via any OpenAI-compatible API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The binary communicates with the extension over stdout (streaming markdown) and stderr (JSON events). One download, &lt;code&gt;chmod +x&lt;/code&gt;, works everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design principles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fresh investigation every time.&lt;/strong&gt; No memory between runs. Every analysis starts from a fresh scan of current code. The answer is always grounded in your codebase as it exists right now, not a cached version from last week. Tree-sitter caching keeps the structural scan fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your data stays yours.&lt;/strong&gt; Zero telemetry -- not usage stats, not crash reports. The binary talks to exactly one service: the LLM endpoint you configure. API keys are stored in VS Code's encrypted credential store and passed via environment variable. Never written to config files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No gatekeeping.&lt;/strong&gt; No accounts, no sign-up, no Archexa API key. Bring your own LLM provider. Open source under Apache 2.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fully local option
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;ollama
ollama pull llama3.1

&lt;span class="c"&gt;# In Archexa settings:&lt;/span&gt;
&lt;span class="c"&gt;# Endpoint: http://localhost:11434/v1/&lt;/span&gt;
&lt;span class="c"&gt;# Model: llama3.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero code leaves your machine. AST parsing is offline. LLM runs locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform support
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;macOS (Apple Silicon + Intel), Linux (x86_64 + ARM), Windows (x64)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Languages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python, TypeScript, JavaScript, Go, Java, Rust, Ruby, C#, Kotlin, Scala, C++, C, PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM Providers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any OpenAI-compatible endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beta notice
&lt;/h2&gt;

&lt;p&gt;Archexa is currently in beta. The core pipeline is stable, but the CLI binary is not yet code-signed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;macOS users:&lt;/strong&gt; Gatekeeper may block the binary on first run since it's unsigned. The extension handles quarantine removal automatically in most cases. If you see a "cannot be opened" dialog, a notification with a "Fix Permissions" button will appear. Full troubleshooting steps are in the &lt;a href="https://github.com/ereshzealous/archexa-vscode/blob/main/USAGE.md#troubleshooting" rel="noopener noreferrer"&gt;Usage Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CI integration (findings as GitHub PR comments)&lt;/li&gt;
&lt;li&gt;Custom agent tools (project-specific scripts during deep mode investigation)&lt;/li&gt;
&lt;li&gt;Auto-fix after review (investigate a finding, generate the fix, apply with your approval)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install:&lt;/strong&gt; &lt;a href="https://marketplace.visualstudio.com/items?itemName=EreshGorantla.archexa" rel="noopener noreferrer"&gt;VS Code Marketplace&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extension source:&lt;/strong&gt; &lt;a href="https://github.com/ereshzealous/archexa-vscode" rel="noopener noreferrer"&gt;github.com/ereshzealous/archexa-vscode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI source:&lt;/strong&gt; &lt;a href="https://github.com/ereshzealous/archexa" rel="noopener noreferrer"&gt;github.com/ereshzealous/archexa&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage Guide:&lt;/strong&gt; &lt;a href="https://github.com/ereshzealous/archexa-vscode/blob/main/USAGE.md" rel="noopener noreferrer"&gt;USAGE.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues/feedback:&lt;/strong&gt; &lt;a href="https://github.com/ereshzealous/archexa-vscode/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This is a beta release -- the core is stable and I'm iterating fast. Feedback, bug reports, and feature requests are all welcome on GitHub. Star the repo if you find it useful.&lt;/p&gt;

&lt;p&gt;Built by &lt;a href="https://github.com/ereshzealous" rel="noopener noreferrer"&gt;Eresh Gorantla&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vscode</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>From Stack Trace to Root Cause - Archexa's New Diagnose Command</title>
      <dc:creator>Eresh Gorantla</dc:creator>
      <pubDate>Sun, 22 Mar 2026 10:38:04 +0000</pubDate>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9/from-stack-trace-to-root-cause-archexas-new-diagnose-command-1532</link>
      <guid>https://forem.com/eresh_g_721f1d5ca1de0a5b9/from-stack-trace-to-root-cause-archexas-new-diagnose-command-1532</guid>
      <description>&lt;p&gt;&lt;em&gt;Archexa's new diagnose command correlates errors with actual source code to find the root cause, not just the symptom.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Archexa v0.2.1-beta. Apache 2.0 license. Single binary — macOS, Linux, Windows.&lt;/p&gt;

&lt;p&gt;GitHub: github.com/ereshzealous/archexa&lt;/p&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%2F6xhn4o3a81d5h6hb8j5r.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%2F6xhn4o3a81d5h6hb8j5r.png" alt="Archexa CLI Tool" width="384" height="122"&gt;&lt;/a&gt;&lt;/p&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%2Fim0q4l3swqq2nm2r6nbc.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%2Fim0q4l3swqq2nm2r6nbc.gif" alt="Diagnose Demo" width="720" height="477"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;A stack trace tells you &lt;em&gt;where&lt;/em&gt; your code failed. It doesn't tell you &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NullPointerException at UserService.java:42&lt;/code&gt; is the symptom. The root cause is three files upstream — a repository method that returns null instead of Optional, called by a service that doesn't check for it, triggered by a controller that passes through an unvalidated ID.&lt;/p&gt;

&lt;p&gt;Finding that chain takes time. You read the failing line. You find the caller. You read the caller. You check what data it passed. You follow that data to where it originated. You do this across 3, 5, sometimes 10 files until you find the actual bug.&lt;/p&gt;

&lt;p&gt;Most developers paste the stack trace into ChatGPT. It gives a plausible-sounding answer — but it doesn't have your code. It guesses based on the error message. It can't read your &lt;code&gt;UserRepository.java&lt;/code&gt; to see that &lt;code&gt;findManagerByDepartment()&lt;/code&gt; always returns null. It can't grep your codebase to find three other methods with the same missing null check.&lt;/p&gt;

&lt;p&gt;That gap is what Archexa's new &lt;code&gt;diagnose&lt;/code&gt; command fills. It has the stack trace &lt;strong&gt;and&lt;/strong&gt; your codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Ways to Feed It an Error
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Stack Trace File (&lt;code&gt;--trace&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Save your stack trace — from a console, crash report, or exception log — to a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--trace&lt;/span&gt; stacktrace.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Archexa parses stack frames from &lt;strong&gt;six languages&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;Language&lt;/th&gt;
&lt;th&gt;Stack Frame Format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;File "path.py", line 42, in func&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java / Kotlin&lt;/td&gt;
&lt;td&gt;&lt;code&gt;at com.package.Class.method(File.java:42)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/path/file.go:42 +0x1a4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript / TypeScript&lt;/td&gt;
&lt;td&gt;&lt;code&gt;at func (file.ts:42:10)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;&lt;code&gt;panicked at src/file.rs:42&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C#&lt;/td&gt;
&lt;td&gt;&lt;code&gt;at Namespace.Class.Method() in File.cs:line 42&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It extracts every file path, line number, and function name from the trace — then maps them to actual files in your repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Log File (&lt;code&gt;--logs&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Point it at your application logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; /var/log/app.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It parses the log line by line, identifies error entries (lines containing ERROR, FATAL, EXCEPTION, panic, etc.), groups each error with its stack trace (subsequent indented lines), and extracts timestamps for filtering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter by time window&lt;/strong&gt; to focus on recent errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; app.log &lt;span class="nt"&gt;--last&lt;/span&gt; 1h      &lt;span class="c"&gt;# last hour&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; app.log &lt;span class="nt"&gt;--last&lt;/span&gt; 30m     &lt;span class="c"&gt;# last 30 minutes&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; app.log &lt;span class="nt"&gt;--last&lt;/span&gt; 2d      &lt;span class="c"&gt;# last 2 days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Specify the log timezone&lt;/strong&gt; if your timestamps aren't UTC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; app.log &lt;span class="nt"&gt;--last&lt;/span&gt; 4h &lt;span class="nt"&gt;--tz&lt;/span&gt; &lt;span class="s2"&gt;"UTC+5:30"&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; app.log &lt;span class="nt"&gt;--last&lt;/span&gt; 1h &lt;span class="nt"&gt;--tz&lt;/span&gt; &lt;span class="s2"&gt;"UTC-5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool converts your log timestamps to UTC before comparing against the &lt;code&gt;--last&lt;/code&gt; window. This way, a log entry at &lt;code&gt;10:15:33&lt;/code&gt; in &lt;code&gt;UTC+5:30&lt;/code&gt; is correctly treated as &lt;code&gt;04:45:33 UTC&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When multiple errors are found, it analyzes the 20 most recent ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Inline Error (&lt;code&gt;--error&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;For a quick diagnosis from a single error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--error&lt;/span&gt; &lt;span class="s2"&gt;"NullPointerException at UserService.java:42"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It extracts any file:line references from the message and treats the whole string as the error to investigate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Config Support
&lt;/h2&gt;

&lt;p&gt;All diagnose options can be set in your &lt;code&gt;archexa.yaml&lt;/code&gt; so you don't have to type them every time. CLI flags override config values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;archexa&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;."&lt;/span&gt;

  &lt;span class="na"&gt;openai&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google/gemini-2.5-flash"&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://openrouter.ai/api/v1/"&lt;/span&gt;

  &lt;span class="na"&gt;diagnose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs/application.log"&lt;/span&gt;
    &lt;span class="na"&gt;trace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4h"&lt;/span&gt;
    &lt;span class="na"&gt;tz&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UTC+5:30"&lt;/span&gt;

  &lt;span class="na"&gt;prompts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;diagnose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Focus on the root cause, not the symptom.&lt;/span&gt;
      &lt;span class="s"&gt;Show the exact code path that failed.&lt;/span&gt;
      &lt;span class="s"&gt;Recommend a fix with before/after code.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this config, you can just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it will read &lt;code&gt;logs/application.log&lt;/code&gt;, filter errors from the last 4 hours in UTC+5:30. Override any field on the fly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Different log file, same config for everything else&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; /tmp/crash.log

&lt;span class="c"&gt;# Different time window&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--last&lt;/span&gt; 1h

&lt;span class="c"&gt;# Use a trace file instead of logs&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--trace&lt;/span&gt; stacktrace.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What Happens Under the Hood
&lt;/h2&gt;

&lt;p&gt;When you run diagnose, this is the sequence:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parse&lt;/strong&gt; — The input is parsed into structured data: error messages, file paths, line numbers, function names, timestamps, severity levels. A single log file might contain multiple errors — each is extracted as a separate entry with its associated stack trace lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Map&lt;/strong&gt; — File paths from the stack trace are matched to actual files in your repository. &lt;code&gt;UserService.java:42&lt;/code&gt; becomes &lt;code&gt;src/main/java/com/example/service/UserService.java&lt;/code&gt;. Files that don't exist in the repo (framework internals, third-party libraries) are noted but the agent focuses on what it can find.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scan&lt;/strong&gt; — Your repository is indexed — directory layout, file list, language distribution. No heavy parsing. Just enough for the agent to orient itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investigate&lt;/strong&gt; — This is where diagnose differs from everything else. The AI agent opens the files referenced in the stack trace at the reported line numbers. It reads the surrounding function. It finds the caller using &lt;code&gt;find_references&lt;/code&gt;. It traces the data that caused the failure upstream using &lt;code&gt;grep_codebase&lt;/code&gt;. It checks for similar patterns elsewhere. It makes 10-30 autonomous decisions about what to read next — exactly like a developer debugging unfamiliar code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Synthesize&lt;/strong&gt; — Everything the agent learned is distilled into a structured root cause analysis document.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Output Looks Like
&lt;/h2&gt;

&lt;p&gt;The generated document follows a consistent structure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Error Summary&lt;/strong&gt; — What happened, where, and severity assessment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Root Cause&lt;/strong&gt; — The underlying reason, distinguished from the error location. "The error manifests at line 34, but the root cause is at line 37 where null originates."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Execution Flow&lt;/strong&gt; — Step-by-step trace from entry point to failure, with file:line references at each step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Affected Code&lt;/strong&gt; — Table of every file involved in the error chain, what each does, and how it's affected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Related Issues&lt;/strong&gt; — Similar patterns elsewhere in the codebase that might have the same vulnerability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Fix Recommendation&lt;/strong&gt; — What to change, where, with before/after code snippets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Prevention&lt;/strong&gt; — Specific tests to write, validation to add, and monitoring suggestions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Example: Spring Boot NullPointerException
&lt;/h2&gt;

&lt;p&gt;I built a Spring Boot application with a deliberate bug to test diagnose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;UserController&lt;/code&gt; with a &lt;code&gt;/api/users/{id}/profile&lt;/code&gt; endpoint&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;UserService&lt;/code&gt; that fetches the user and their department manager&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;UserRepository&lt;/code&gt; where &lt;code&gt;findManagerByDepartment()&lt;/code&gt; always returns null&lt;/li&gt;
&lt;li&gt;The service calls &lt;code&gt;manager.getName()&lt;/code&gt; without checking for null&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hit the endpoint, get a 500. Copy the stack trace, run diagnose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--trace&lt;/span&gt; trace.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent traced the full chain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Controller&lt;/strong&gt; (&lt;code&gt;UserController.java:28&lt;/code&gt;) → calls &lt;code&gt;userService.getUserProfile(id)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service&lt;/strong&gt; (&lt;code&gt;UserService.java:24&lt;/code&gt;) → calls &lt;code&gt;userRepository.findManagerByDepartment(user.getDepartment())&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt; (&lt;code&gt;UserRepository.java:37&lt;/code&gt;) → &lt;code&gt;findManagerByDepartment()&lt;/code&gt; always returns null&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Back to Service&lt;/strong&gt; (&lt;code&gt;UserService.java:34&lt;/code&gt;) → &lt;code&gt;manager.getName()&lt;/code&gt; throws NullPointerException&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It correctly distinguished the &lt;strong&gt;error location&lt;/strong&gt; (line 34 where NPE occurs) from the &lt;strong&gt;root cause&lt;/strong&gt; (line 37 where null originates).&lt;/p&gt;

&lt;p&gt;It also found &lt;strong&gt;two additional bugs&lt;/strong&gt; I hadn't asked about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;findById()&lt;/code&gt; returns null instead of Optional — a similar NPE waiting to happen&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getActiveUser()&lt;/code&gt; doesn't check the active flag before returning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix it proposed: change the repository to return &lt;code&gt;Optional&amp;lt;User&amp;gt;&lt;/code&gt;, add null checks in the service — with complete before/after code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scaling Test: FastAPI (2,600+ Files)
&lt;/h2&gt;

&lt;p&gt;To test on a real-world codebase, I ran diagnose against the FastAPI framework with a simulated dependency injection cycle error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; config-diagnose.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; sample-trace.txt &lt;span class="nt"&gt;--last&lt;/span&gt; 1d &lt;span class="nt"&gt;--tz&lt;/span&gt; &lt;span class="s2"&gt;"UTC+5:30"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent read &lt;code&gt;fastapi/dependencies/utils.py&lt;/code&gt; (600+ lines), traced the recursive &lt;code&gt;solve_dependencies&lt;/code&gt; function, and identified that &lt;code&gt;dependency_cache&lt;/code&gt; caches solved values but doesn't track in-progress resolutions — meaning circular dependencies cause infinite recursion instead of a clear error.&lt;/p&gt;

&lt;p&gt;It proposed the fix: a &lt;code&gt;_solving_dependencies&lt;/code&gt; set parameter threaded through recursive calls, checked before each resolution. The standard cycle detection algorithm, applied to FastAPI's specific dependency resolution architecture.&lt;/p&gt;

&lt;p&gt;18 tool calls across 10 iterations. 2,661 files scanned. 44 seconds total.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Deep Mode Is the Default
&lt;/h2&gt;

&lt;p&gt;Every other Archexa command defaults to pipeline mode — you opt into deep mode with &lt;code&gt;--deep&lt;/code&gt;. Diagnose is the exception. It defaults to deep mode because root cause analysis inherently requires reading actual source files.&lt;/p&gt;

&lt;p&gt;A pipeline mode analysis can tell you "the error is probably in UserService.java." But it can't open the file, read the function, find the caller, and trace the data flow upstream. That requires the agent's tools — &lt;code&gt;read_file&lt;/code&gt;, &lt;code&gt;grep_codebase&lt;/code&gt;, &lt;code&gt;find_references&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;--no-deep&lt;/code&gt; if you want a quick pipeline assessment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--trace&lt;/span&gt; crash.txt &lt;span class="nt"&gt;--no-deep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the real value is in the investigation.&lt;/p&gt;




&lt;h2&gt;
  
  
  No Evidence Extraction
&lt;/h2&gt;

&lt;p&gt;Diagnose skips Archexa's evidence extraction pipeline entirely in deep mode. The other commands (gist, analyze, query) scan every file and build AST blocks, dependency maps, and communication links before the agent starts. That takes 4-5 seconds on a large repo and produces data the agent mostly ignores because it reads the actual files anyway.&lt;/p&gt;

&lt;p&gt;Diagnose is a focused command — the stack trace tells it exactly where to start. The agent gets a lightweight repo summary (file list, directory structure, language distribution) and the matched error files. Then it investigates with tools. No wasted work.&lt;/p&gt;

&lt;p&gt;The same optimization applies to &lt;code&gt;impact&lt;/code&gt; and &lt;code&gt;review&lt;/code&gt; in deep mode as of this release.&lt;/p&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install or update&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ereshzealous/archexa/main/install.sh | bash

&lt;span class="c"&gt;# Create config&lt;/span&gt;
archexa init

&lt;span class="c"&gt;# From a stack trace&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--trace&lt;/span&gt; stacktrace.txt

&lt;span class="c"&gt;# From logs with timezone&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--logs&lt;/span&gt; app.log &lt;span class="nt"&gt;--last&lt;/span&gt; 4h &lt;span class="nt"&gt;--tz&lt;/span&gt; &lt;span class="s2"&gt;"UTC+5:30"&lt;/span&gt;

&lt;span class="c"&gt;# From an error message&lt;/span&gt;
archexa diagnose &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--error&lt;/span&gt; &lt;span class="s2"&gt;"ConnectionError at db_client.py:42"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works with any OpenAI-compatible model. Tested with Gemini Flash via OpenRouter (~$0.03-0.05 per diagnosis). Use a larger model for complex multi-service errors.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>sre</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Building a SQL Tokenizer and Formatter From Scratch — Supporting 6 Dialects</title>
      <dc:creator>Eresh Gorantla</dc:creator>
      <pubDate>Sat, 21 Mar 2026 17:19:02 +0000</pubDate>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9/building-a-sql-tokenizer-and-formatter-from-scratch-supporting-6-dialects-fia</link>
      <guid>https://forem.com/eresh_g_721f1d5ca1de0a5b9/building-a-sql-tokenizer-and-formatter-from-scratch-supporting-6-dialects-fia</guid>
      <description>&lt;p&gt;Try it: &lt;a href="https://devprix.dev/tools/sql-formatter" rel="noopener noreferrer"&gt;devprix.dev/tools/sql-formatter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is part of &lt;a href="https://devprix.dev" rel="noopener noreferrer"&gt;DevPrix&lt;/a&gt; — 56 free developer tools that run entirely in your browser. No sign-up, no tracking, no server calls.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;SQL formatting seems simple until you try to build it. Keyword capitalization? Easy. Proper indentation of subqueries, CASE expressions, and JOINs across PostgreSQL, MySQL, SQL Server, Oracle, SQLite, and BigQuery? That's a compiler problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture: Tokenizer + State Machine
&lt;/h2&gt;

&lt;p&gt;I chose a two-stage approach: tokenize the SQL into a stream of typed tokens, then format by iterating through tokens with a state machine. No AST (Abstract Syntax Tree) needed — SQL formatting doesn't require understanding query semantics, just structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: The Tokenizer
&lt;/h2&gt;

&lt;p&gt;The tokenizer is a single-pass, character-by-character scanner. It produces an array of typed tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TokenType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keyword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;operator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;punctuation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open_paren&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;close_paren&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment_single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment_multi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whitespace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;semicolon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wildcard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TokenType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  String Literals: Four Quoting Styles
&lt;/h3&gt;

&lt;p&gt;Different SQL dialects use different quoting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Single-quoted strings (standard SQL)&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// escaped quote&lt;/span&gt;
      &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tokenizer also handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double-quoted identifiers&lt;/strong&gt;: &lt;code&gt;"column_name"&lt;/code&gt; (PostgreSQL, standard SQL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backtick identifiers&lt;/strong&gt;: &lt;code&gt;`table_name`&lt;/code&gt; (MySQL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Square bracket identifiers&lt;/strong&gt;: &lt;code&gt;[column]&lt;/code&gt; (SQL Server)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each has its own escape rules — SQL uses doubled quotes (&lt;code&gt;''&lt;/code&gt;, &lt;code&gt;""&lt;/code&gt;) rather than backslashes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Wildcard Problem
&lt;/h3&gt;

&lt;p&gt;Is &lt;code&gt;*&lt;/code&gt; a wildcard or a multiplication operator? It depends on context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;          &lt;span class="c1"&gt;-- wildcard&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;      &lt;span class="c1"&gt;-- multiplication&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;   &lt;span class="c1"&gt;-- wildcard inside function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tokenizer disambiguates by looking at the previous non-whitespace token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastNonWs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findLast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whitespace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;lastNonWs&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;lastNonWs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keyword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;lastNonWs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;lastNonWs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open_paren&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wildcard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// SELECT *, COUNT(*)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;operator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// price * qty&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compound Keywords
&lt;/h3&gt;

&lt;p&gt;SQL has multi-word keywords: &lt;code&gt;ORDER BY&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;, &lt;code&gt;INNER JOIN&lt;/code&gt;, &lt;code&gt;INSERT INTO&lt;/code&gt;, &lt;code&gt;UNION ALL&lt;/code&gt;. The tokenizer uses lookahead to detect these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compoundMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;BY|INTO|JOIN|ALL|TABLE|FROM|INDEX|KEY|EXISTS&lt;/span&gt;&lt;span class="se"&gt;)\b&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compoundMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;compoundMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MAJOR_CLAUSE_KEYWORDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compound&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compound&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;compoundMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is preferable to treating them as separate tokens because &lt;code&gt;ORDER&lt;/code&gt; alone might be an identifier in some contexts, but &lt;code&gt;ORDER BY&lt;/code&gt; is always a keyword.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dialect-Specific Keywords
&lt;/h3&gt;

&lt;p&gt;Each dialect extends the base keyword set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialectKeywords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ENGINE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INNODB&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SHOW&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DESCRIBE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ENUM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JSON&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]),&lt;/span&gt;
  &lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RETURNING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ILIKE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JSONB&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LATERAL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LISTEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]),&lt;/span&gt;
  &lt;span class="na"&gt;sqlserver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TOP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NOLOCK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NVARCHAR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PIVOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UNPIVOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]),&lt;/span&gt;
  &lt;span class="na"&gt;oracle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ROWNUM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SYSDATE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DECODE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NVL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CONNECT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]),&lt;/span&gt;
  &lt;span class="na"&gt;sqlite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PRAGMA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ATTACH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GLOB&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When tokenizing, a word is checked against both the base keywords and the dialect-specific set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 2: The Formatter
&lt;/h2&gt;

&lt;p&gt;The formatter is a state machine that tracks context as it iterates through tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatSql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormatOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// indentation level&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lineStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// at beginning of line?&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// inside SELECT clause?&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inWhere&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// inside WHERE clause?&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;afterClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// just saw a clause keyword?&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subqueryStack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// paren depth for subqueries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Core Loop
&lt;/h3&gt;

&lt;p&gt;Each token type has formatting rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keyword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MAJOR_CLAUSE_KEYWORDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// SELECT, FROM, WHERE, JOIN, etc.&lt;/span&gt;
      &lt;span class="nf"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;addIndent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uppercaseKeywords&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;afterClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;inSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FROM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;inSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WHERE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;inWhere&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Subquery Detection
&lt;/h3&gt;

&lt;p&gt;The trickiest part is detecting subqueries — a &lt;code&gt;SELECT&lt;/code&gt; inside parentheses gets extra indentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open_paren&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Peek ahead: is the next non-whitespace token SELECT?&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whitespace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// It's a subquery — indent&lt;/span&gt;
    &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;subqueryStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;addIndent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Regular parenthesis (function call, IN list)&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the matching close paren arrives, we check &lt;code&gt;subqueryStack&lt;/code&gt; to know whether to dedent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comma Formatting
&lt;/h3&gt;

&lt;p&gt;Two styles — trailing commas (traditional) and leading commas (some teams prefer this):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trailingCommas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inSelect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;addIndent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// extra indent for continuation&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Leading comma style&lt;/span&gt;
    &lt;span class="nf"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;addIndent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AND/OR in WHERE Clauses
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;WHERE&lt;/code&gt; conditions can be compact or expanded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AND&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;inWhere&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compactWhere&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;addSpace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;addIndent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// extra indent&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CASE Expression Formatting
&lt;/h3&gt;

&lt;p&gt;CASE/WHEN/THEN/ELSE/END requires careful indentation tracking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CASE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;addSpace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WHEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;addIndent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;END&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;addIndent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Minifier
&lt;/h2&gt;

&lt;p&gt;The reverse operation — collapse SQL to a single line while preserving semantics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;minifySql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whitespace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Only add space when needed between identifiers/keywords&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastChar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_'"@#`&lt;/span&gt;&lt;span class="se"&gt;\[]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment_single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Convert -- comments to /* */ to avoid line-break dependency&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/* &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; */&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;lastChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The single-line comment conversion is a subtle but critical detail. &lt;code&gt;-- comment&lt;/code&gt; depends on a newline to terminate. In minified SQL on one line, a &lt;code&gt;--&lt;/code&gt; would comment out everything after it. Converting to &lt;code&gt;/* */&lt;/code&gt; preserves the comment without the line-break dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax Highlighting
&lt;/h2&gt;

&lt;p&gt;The highlighter re-tokenizes the formatted SQL and wraps each token in a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; with a CSS class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;highlightSql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;escaped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keyword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;SQL_FUNCTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;span class="sql-function"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;escaped&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;span class="sql-keyword"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;escaped&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;span class="sql-string"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;escaped&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;span class="sql-number"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;escaped&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment_single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment_multi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;span class="sql-comment"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;escaped&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;escaped&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Functions (COUNT, SUM, AVG, etc.) get a different color than keywords (SELECT, FROM, WHERE) even though both are tokenized as "keyword" type. A secondary lookup distinguishes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query Complexity Scoring
&lt;/h2&gt;

&lt;p&gt;The tool scores query complexity using a weighted formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;analyzeQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;QueryStats&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;subqueries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;joins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keyword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;subqueries&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JOIN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;joins&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CASE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;cases&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subqueries&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;joins&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
              &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;complexity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Very Complex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Complex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Moderate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Simple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subqueries contribute the most weight (3 points each) because they're the hardest to read. JOINs and CASE expressions contribute 2 points each. Every 10 keywords adds 1 point for general density.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You don't always need an AST.&lt;/strong&gt; For formatting (not optimization or execution), a token stream + state machine is sufficient and much simpler. You lose the ability to validate semantics, but formatting doesn't need that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compound keywords require lookahead.&lt;/strong&gt; Treating &lt;code&gt;ORDER&lt;/code&gt; and &lt;code&gt;BY&lt;/code&gt; as separate tokens makes formatting ambiguous. Combining them during tokenization simplifies everything downstream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single-line comment conversion is essential for minification.&lt;/strong&gt; This is the kind of edge case that seems minor but would break every query that contains a &lt;code&gt;--&lt;/code&gt; comment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dialect differences are mostly about keywords.&lt;/strong&gt; The actual formatting rules are the same across dialects. The main difference is which words are reserved — &lt;code&gt;JSONB&lt;/code&gt; in PostgreSQL, &lt;code&gt;NVARCHAR&lt;/code&gt; in SQL Server, &lt;code&gt;PRAGMA&lt;/code&gt; in SQLite.&lt;/p&gt;




</description>
      <category>sql</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I Built a QR Code Encoder From Scratch in TypeScript — Here's How It Works</title>
      <dc:creator>Eresh Gorantla</dc:creator>
      <pubDate>Sat, 21 Mar 2026 17:09:53 +0000</pubDate>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9/i-built-a-qr-code-encoder-from-scratch-in-typescript-heres-how-it-works-16m8</link>
      <guid>https://forem.com/eresh_g_721f1d5ca1de0a5b9/i-built-a-qr-code-encoder-from-scratch-in-typescript-heres-how-it-works-16m8</guid>
      <description>&lt;p&gt;Try it yourself: &lt;a href="https://devprix.dev/tools/qr-code-generator" rel="noopener noreferrer"&gt;devprix.dev/tools/qr-code-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is part of &lt;a href="https://devprix.dev" rel="noopener noreferrer"&gt;DevPrix&lt;/a&gt; — 56 free developer tools that run entirely in your browser. No sign-up, no tracking, no server calls.&lt;/em&gt;&lt;/p&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%2F9evy9nwzzhuhx075xget.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%2F9evy9nwzzhuhx075xget.gif" alt="QR Generator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most developers reach for a library when they need QR codes. I decided to implement the entire QR encoding pipeline from scratch — Reed-Solomon error correction, Galois Field arithmetic, mask pattern optimization, and all. Here's a deep dive into what it took.&lt;/p&gt;

&lt;p&gt;The full implementation is of TypeScript with zero dependencies, running entirely in the browser at &lt;a href="https://devprix.dev/tools/qr-code-generator" rel="noopener noreferrer"&gt;devprix.dev/tools/qr-code-generator&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a QR Code Actually Is
&lt;/h2&gt;

&lt;p&gt;A QR code is a 2D barcode that encodes data in a grid of black and white squares called "modules." But there's a lot more going on than just turning data into dots. The encoding pipeline has six stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data analysis&lt;/strong&gt; — determine what you're encoding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bit stream encoding&lt;/strong&gt; — convert data to a binary stream&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error correction&lt;/strong&gt; — add redundancy using Reed-Solomon codes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matrix construction&lt;/strong&gt; — place structural patterns and data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Masking&lt;/strong&gt; — apply patterns to improve readability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format information&lt;/strong&gt; — encode metadata about the QR code itself&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's walk through each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: Version Selection
&lt;/h2&gt;

&lt;p&gt;QR codes come in versions 1 through 40. Version 1 is 21x21 modules, and each version adds 4 modules per side (Version 2 = 25x25, Version 10 = 57x57, etc.). The version determines how much data you can store.&lt;/p&gt;

&lt;p&gt;Each version also has four error correction levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt; (Low) — recovers ~7% damage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;M&lt;/strong&gt; (Medium) — recovers ~15% damage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Q&lt;/strong&gt; (Quartile) — recovers ~25% damage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;H&lt;/strong&gt; (High) — recovers ~30% damage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Higher error correction means less data capacity but more resilience. My implementation supports versions 1-10, which handles up to ~420 characters at the lowest error correction — more than enough for URLs, WiFi credentials, and contact cards.&lt;/p&gt;

&lt;p&gt;The encoder automatically picks the smallest version that fits the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;chooseVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataLen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ecLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;VERSION_CAPACITY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataLen&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;VERSION_CAPACITY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;ecLevel&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// data too long&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stage 2: Bit Stream Encoding
&lt;/h2&gt;

&lt;p&gt;Data gets converted to a binary bit stream. The stream starts with a 4-bit mode indicator (I use byte mode = &lt;code&gt;0100&lt;/code&gt;), followed by a character count indicator (8 bits for versions 1-9), then the actual data bytes.&lt;/p&gt;

&lt;p&gt;After the data, the stream gets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;terminator&lt;/strong&gt; — up to 4 zero bits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Byte padding&lt;/strong&gt; — zeros to reach a byte boundary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fill padding&lt;/strong&gt; — alternating &lt;code&gt;0xEC&lt;/code&gt; and &lt;code&gt;0x11&lt;/code&gt; bytes until the stream reaches the exact capacity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That alternating padding pattern isn't random — it's part of the QR spec and ensures the bit stream doesn't accidentally form patterns that confuse scanners.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pad to exact capacity with alternating bytes&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codewords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;codewords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;padToggle&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mh"&gt;0xec&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;padToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;padToggle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stage 3: Reed-Solomon Error Correction
&lt;/h2&gt;

&lt;p&gt;This is where it gets mathematically interesting. Reed-Solomon codes add redundancy so the QR code can be read even if parts are damaged or obscured. The math happens in GF(256) — a Galois Field with 256 elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Galois Field Arithmetic
&lt;/h3&gt;

&lt;p&gt;Normal arithmetic doesn't work for error correction because we need every operation to stay within 0-255 (one byte). Galois Fields solve this by redefining multiplication using logarithm tables.&lt;/p&gt;

&lt;p&gt;First, I precompute exponential and logarithm lookup tables using the primitive polynomial &lt;code&gt;0x11d&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GF_EXP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GF_LOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;GF_EXP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;GF_LOG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="mh"&gt;0x11d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// reduce by primitive polynomial&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Extend table for easy wraparound&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;GF_EXP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;GF_EXP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now multiplication becomes addition of logarithms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gfMul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;GF_EXP&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="nx"&gt;GF_LOG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;GF_LOG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is elegant — we've turned expensive multiplication into a table lookup and an addition. The extended &lt;code&gt;GF_EXP&lt;/code&gt; table (512 entries) avoids modulo operations during polynomial math.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Generator Polynomial
&lt;/h3&gt;

&lt;p&gt;The Reed-Solomon generator polynomial is built iteratively. For &lt;code&gt;n&lt;/code&gt; error correction codewords, we multiply &lt;code&gt;(x - a^0)(x - a^1)...(x - a^(n-1))&lt;/code&gt; where &lt;code&gt;a = 2&lt;/code&gt; in GF(256):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rsGenPoly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;degree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;degree&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="nf"&gt;gfMul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;GF_EXP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Encoding
&lt;/h3&gt;

&lt;p&gt;The actual encoding is polynomial long division in GF(256). We divide the data polynomial by the generator polynomial, and the remainder becomes our error correction codewords:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rsEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ecCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rsGenPoly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ecCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ecCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coeff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyWithin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ecCount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;ecCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="nf"&gt;gfMul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;coeff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Block Interleaving
&lt;/h3&gt;

&lt;p&gt;For larger QR codes, data is split into multiple blocks, each encoded independently with its own error correction. The blocks are then &lt;strong&gt;interleaved&lt;/strong&gt; — codewords are taken round-robin from each block. This distributes errors across blocks, so localized damage doesn't wipe out a single block's worth of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 4: Matrix Construction
&lt;/h2&gt;

&lt;p&gt;The QR matrix is built in layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Finder Patterns
&lt;/h3&gt;

&lt;p&gt;Three 7x7 squares at the top-left, top-right, and bottom-left corners. These are how scanners locate and orient the QR code. Each has a concentric pattern: 7x7 dark border, 5x5 white, 3x3 dark center.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alignment Patterns
&lt;/h3&gt;

&lt;p&gt;Starting from version 2, smaller 5x5 patterns appear at calculated positions. These help scanners correct for perspective distortion (like scanning at an angle).&lt;/p&gt;

&lt;h3&gt;
  
  
  Timing Patterns
&lt;/h3&gt;

&lt;p&gt;Alternating dark/light modules along row 6 and column 6, connecting the finder patterns. These establish the grid spacing so scanners know module positions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Placement
&lt;/h3&gt;

&lt;p&gt;Data bits are placed in a zigzag pattern, starting from the bottom-right corner. The path moves in column pairs (two columns wide), alternating between upward and downward passes. It skips the timing pattern column (column 6) and all reserved areas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Zigzag right-to-left, alternating up/down&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bitIdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// skip timing column&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;vert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;vert&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;vert&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;upward&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;vert&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vert&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;reserved&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;bitIdx&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bitIdx&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;upward&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;upward&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stage 5: Masking — The Optimization Problem
&lt;/h2&gt;

&lt;p&gt;Here's where QR encoding becomes an optimization problem. Raw data placement can create patterns that confuse scanners — large uniform areas, patterns that look like finder squares, or uneven dark/light ratios.&lt;/p&gt;

&lt;p&gt;The spec defines 8 mask patterns, each a mathematical function that determines whether to flip a module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mask 0: (row + col) % 2 === 0         — checkerboard
Mask 1: row % 2 === 0                  — horizontal stripes
Mask 2: col % 3 === 0                  — vertical stripes
Mask 3: (row + col) % 3 === 0         — diagonal
Mask 4: (row/2 + col/3) % 2 === 0     — block grid
Mask 5: (r*c)%2 + (r*c)%3 === 0       — product-based
Mask 6: ((r*c)%2 + (r*c)%3) % 2 === 0 — product variation
Mask 7: ((r+c)%2 + (r*c)%3) % 2 === 0 — mixed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Masks are only applied to data areas — structural patterns are preserved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Penalty Scoring
&lt;/h3&gt;

&lt;p&gt;Each masked result is scored with four penalty rules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 1: Consecutive modules&lt;/strong&gt; — 5+ same-color modules in a row/column scores 3 + (count - 5). This penalizes long runs that blur together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 2: 2x2 blocks&lt;/strong&gt; — Each 2x2 block of the same color scores 3. Large uniform areas are bad for scanning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 3: Finder-like patterns&lt;/strong&gt; — The sequence &lt;code&gt;1011101 0000&lt;/code&gt; (or reversed) scores 40 per occurrence. These look like finder patterns and confuse the position detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 4: Dark balance&lt;/strong&gt; — Deviation from 50% dark modules is penalized. The formula rounds the dark percentage to the nearest 5% boundaries and penalizes based on distance from 50%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Rule 4: Dark module balance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;darkCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;darkCount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prev5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prev5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;penalty&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev5&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next5&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The encoder evaluates all 8 masks and picks the one with the &lt;strong&gt;lowest total penalty&lt;/strong&gt;. This brute-force approach guarantees the optimal mask for readability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 6: Format Information
&lt;/h2&gt;

&lt;p&gt;The last step encodes the error correction level and mask pattern as a 15-bit BCH code, placed at two locations around the top-left finder pattern. This is how scanners know which EC level and mask were used.&lt;/p&gt;

&lt;p&gt;The format information is pre-computed as a lookup table (32 combinations: 4 EC levels x 8 masks), avoiding BCH calculation at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;The complete pipeline — from a string like "&lt;a href="https://devprix.dev" rel="noopener noreferrer"&gt;https://devprix.dev&lt;/a&gt;" to a scannable QR matrix — runs in under 50ms in the browser. The output is rendered as SVG for crisp scaling at any size, with PNG export via Canvas API.&lt;/p&gt;

&lt;p&gt;The tool also generates specialized payloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WiFi&lt;/strong&gt; — &lt;code&gt;WIFI:T:WPA;S:NetworkName;P:password;;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;vCard&lt;/strong&gt; — structured contact card format&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email&lt;/strong&gt; — &lt;code&gt;mailto:&lt;/code&gt; with subject and body&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phone&lt;/strong&gt; — &lt;code&gt;tel:&lt;/code&gt; format&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Galois Field arithmetic is beautiful.&lt;/strong&gt; Converting multiplication to table lookups via logarithms is one of those elegant mathematical tricks that feels like cheating. The entire GF(256) setup is 15 lines of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Penalty scoring is clever engineering.&lt;/strong&gt; The four rules model human readability heuristics — too much uniformity, finder-like patterns, and dark/light imbalance all make codes harder to scan. Evaluating all 8 masks is brute force but guaranteed optimal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The zigzag data placement is weird but purposeful.&lt;/strong&gt; Moving in column pairs, alternating direction, skipping column 6 — it seems arbitrary until you realize it maximizes the distance between adjacent data bits, improving error resilience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero dependencies was worth it.&lt;/strong&gt; The implementation is 1,161 lines. A library would be smaller to import but adds supply chain risk, bundle size, and a black box. Building from scratch means I understand every bit of the output.&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built a Schema Migration Tool for Cassandra Because Nothing Else Worked</title>
      <dc:creator>Eresh Gorantla</dc:creator>
      <pubDate>Fri, 20 Mar 2026 18:22:05 +0000</pubDate>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9/i-built-a-schema-migration-tool-for-cassandra-because-nothing-else-worked-4ikd</link>
      <guid>https://forem.com/eresh_g_721f1d5ca1de0a5b9/i-built-a-schema-migration-tool-for-cassandra-because-nothing-else-worked-4ikd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Flyway and Liquibase treat Cassandra like PostgreSQL. They miss async DDL, distributed locking, and partial failures. So I built cqltrack — open-source CLI + Python library with schema agreement, LWT locking, and a CQL linter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;And then open-sourced it.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you manage Apache Cassandra in production, you've felt this pain. You need to add a column, create an index, or restructure a table. So you SSH into a node, open &lt;code&gt;cqlsh&lt;/code&gt;, and paste your CQL. Then you do it again on staging. Then someone on your team does the same change slightly differently on another cluster.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;I looked at existing tools. Flyway has a Cassandra plugin. Liquibase can talk CQL. But they all treat Cassandra like PostgreSQL with a different query language. They miss the hard parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DDL is asynchronous.&lt;/strong&gt; When you &lt;code&gt;CREATE TABLE&lt;/code&gt;, the coordinator returns success before all nodes know the table exists. Your next statement — &lt;code&gt;CREATE INDEX&lt;/code&gt; on that table — lands on a different node that hasn't seen it yet. It fails. Flyway doesn't handle this. Neither does Liquibase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There are no DDL transactions.&lt;/strong&gt; If statement 3 of 5 fails, statements 1 and 2 already executed. You can't roll back. Your database is in a half-migrated state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple processes can migrate simultaneously.&lt;/strong&gt; Two CI runners deploying to the same cluster. Both see pending migrations. Both try to apply them. Chaos.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://github.com/ereshzealous/cql-track" rel="noopener noreferrer"&gt;cql-track&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;cqltrack is a schema migration tool that actually understands Cassandra's distributed nature. Install it with pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;cql-track
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;14 commands. One CLI. Everything you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cqltrack init         Create keyspace + tracking tables
cqltrack migrate      Apply pending migrations
cqltrack rollback     Undo migrations
cqltrack status       Applied vs pending at a glance
cqltrack history      Full audit trail with timing
cqltrack lint         Static analysis for dangerous CQL
cqltrack diff         Compare schemas across environments
cqltrack snapshot     Export live schema as CQL
cqltrack baseline     Adopt on existing databases
cqltrack pending      CI gate — exit code 1 if unapplied
cqltrack validate     Detect modified migration files
cqltrack repair       Accept intentional file changes
cqltrack new          Scaffold a migration file
cqltrack profiles     List environment profiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;But the real value is in what happens under the hood.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Two entry points — CLI for terminal/CI, Python API for embedding in applications. Same engine underneath.&lt;/p&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%2Fi4cjxkqcntsmgb5zrcla.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%2Fi4cjxkqcntsmgb5zrcla.png" alt="High Level Architecture" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Schema Agreement — The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Cassandra propagates schema changes through gossip. After a DDL statement, nodes converge asynchronously. The common "fix" is &lt;code&gt;sleep(5)&lt;/code&gt;. That's not a fix.&lt;/p&gt;

&lt;p&gt;cqltrack polls &lt;code&gt;system.local&lt;/code&gt; and &lt;code&gt;system.peers&lt;/code&gt; after every DDL statement, waiting until all nodes report the same &lt;code&gt;schema_version&lt;/code&gt; UUID. Only then does it proceed.&lt;/p&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%2Fto6lngrxfw890m7rfema.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%2Fto6lngrxfw890m7rfema.png" alt="Schema Agreement" width="720" height="1599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Configurable timeout (default: 30s). DML statements (&lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;) skip this entirely — they don't need agreement.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Distributed Locking with Cassandra's Own LWT
&lt;/h2&gt;

&lt;p&gt;No external dependencies. No ZooKeeper. No Redis. cqltrack uses Cassandra's &lt;strong&gt;Lightweight Transactions&lt;/strong&gt; to implement a distributed lock.&lt;/p&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%2Fxqgptthhqn5y9t2ys5wy.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%2Fxqgptthhqn5y9t2ys5wy.png" alt="Distributed Locking" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When two CI runners or Kubernetes pods try to migrate simultaneously:&lt;/p&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%2F2ntgbjypm0x9wirxlp6t.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%2F2ntgbjypm0x9wirxlp6t.png" alt="Locking Mechanism" width="573" height="973"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key design decisions:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TTL safety net&lt;/strong&gt; — if a worker crashes without releasing, the lock auto-expires in 10 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownership check&lt;/strong&gt; — &lt;code&gt;DELETE IF owner = me&lt;/code&gt; prevents one process from releasing another's lock&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SERIAL consistency&lt;/strong&gt; — full linearizability regardless of your configured consistency level&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;30 retries, 2s intervals&lt;/strong&gt; — patient waiting before giving up&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Partial Failure Tracking
&lt;/h2&gt;

&lt;p&gt;Statement 3 fails. Statements 1 and 2 already ran. Cassandra has no rollback.&lt;/p&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%2F1mu1nvx0mp4frfonx8r8.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%2F1mu1nvx0mp4frfonx8r8.png" alt="Partial Failures" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a migration fails:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's recorded in the history table with &lt;code&gt;status = failed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cqltrack status&lt;/code&gt; shows it as &lt;strong&gt;pending&lt;/strong&gt; — ready for retry&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cqltrack history&lt;/code&gt; shows it as &lt;strong&gt;FAILED&lt;/strong&gt; — full audit trail&lt;/li&gt;
&lt;li&gt;Fix the CQL file, run &lt;code&gt;migrate&lt;/code&gt; again — clean retry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The linter helps prevent retryability issues. It warns you if you write &lt;code&gt;CREATE TABLE&lt;/code&gt; without &lt;code&gt;IF NOT EXISTS&lt;/code&gt; — because on retry, that statement would fail even though the table already exists from the first attempt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Not Just a CLI — A Python Library
&lt;/h2&gt;

&lt;p&gt;Starting from v1.1.0, cqltrack works as a &lt;strong&gt;programmatic API&lt;/strong&gt;. Embed migrations directly in your application startup:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FastAPI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cqltrack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CqlTrack&lt;/span&gt;

&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;CqlTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cqltrack.yml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Django:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.apps&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AppConfig&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cqltrack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CqlTrack&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyAppConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;CqlTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cqltrack.yml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Plain Python — no YAML needed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cqltrack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CqlTrack&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;CqlTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contact_points&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;keyspace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The distributed lock ensures only one Gunicorn worker or Kubernetes pod runs migrations. Others wait, then find nothing pending.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&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%2Ffi5j3pbg90rmxip0n1e8.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%2Ffi5j3pbg90rmxip0n1e8.png" alt="Kubernetes Deployment" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in Static Analysis
&lt;/h2&gt;

&lt;p&gt;8 rules that catch real problems:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no-rollback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing &lt;code&gt;@down&lt;/code&gt; section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;empty-rollback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Empty rollback — false sense of safety&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;drop-no-if-exists&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DROP TABLE&lt;/code&gt; without &lt;code&gt;IF EXISTS&lt;/code&gt; — not idempotent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create-no-if-not-exists&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CREATE TABLE&lt;/code&gt; without &lt;code&gt;IF NOT EXISTS&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;column-drop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ALTER TABLE DROP&lt;/code&gt; in UP section — permanent data loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;truncate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TRUNCATE&lt;/code&gt; — wipes all data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pk-alter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PRIMARY KEY change — impossible in Cassandra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type-change&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Column type change — very limited support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Context-aware: &lt;code&gt;ALTER TABLE DROP&lt;/code&gt; in &lt;code&gt;@down&lt;/code&gt; (rollback) is expected and not flagged. No running Cassandra needed.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Schema Diff Across Environments
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cqltrack diff --source dev --target prod

Schema diff: myapp_dev &amp;lt;-&amp;gt; myapp_prod

  table       audit_log                     only in dev
  column      users.phone                   only in dev        text
  index       idx_users_email               only in dev

3 difference(s) found.
&lt;/code&gt;&lt;/pre&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%2F8dx6kgew83y61yhvoc6t.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%2F8dx6kgew83y61yhvoc6t.png" alt="CQL Track Diff Tracking" width="746" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Compares tables, columns (with types), partition keys, clustering keys, indexes, and UDTs. Works across clusters via profiles or between keyspaces on the same cluster.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Multi-Environment Profiles
&lt;/h2&gt;

&lt;p&gt;One YAML file. All environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;keyspace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp_dev&lt;/span&gt;
  &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cassandra&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contact_points&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;prod-1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;prod-2&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;prod-3&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;consistency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LOCAL_QUORUM&lt;/span&gt;
    &lt;span class="na"&gt;keyspace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp_prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cqltrack &lt;span class="nt"&gt;--profile&lt;/span&gt; prod migrate
cqltrack &lt;span class="nt"&gt;--profile&lt;/span&gt; dev diff &lt;span class="nt"&gt;--target-profile&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Config resolution (last wins):&lt;/p&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%2F9lx3g40ai9ixkp0azpqu.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%2F9lx3g40ai9ixkp0azpqu.png" alt="Config Resolution" width="800" height="71"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Migration Lifecycle
&lt;/h2&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%2Fektr5d8ejd4e6icargxr.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%2Fektr5d8ejd4e6icargxr.png" alt="Migration Life Cycle" width="664" height="2215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;cassandra-migrate&lt;/th&gt;
&lt;th&gt;cassandra-migration&lt;/th&gt;
&lt;th&gt;cqltrack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Schema agreement&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed lock (LWT)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Yes + TTL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial failure tracking&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CQL linter&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;8 rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema diff&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-env profiles&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON output for CI&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Astra DB&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baseline adoption&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Programmatic API&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Compatibility
&lt;/h2&gt;

&lt;p&gt;Verified with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apache Cassandra&lt;/strong&gt; 3.x, 4.x, 5.x&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DataStax Enterprise (DSE)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DataStax Astra DB&lt;/strong&gt; (cloud)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS Keyspaces is not supported yet — it doesn't support Lightweight Transactions the way open-source Cassandra does. It's on the roadmap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;cql-track
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ereshzealous/cql-track" rel="noopener noreferrer"&gt;github.com/ereshzealous/cql-track&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;PyPI:&lt;/strong&gt; &lt;a href="https://pypi.org/project/cql-track/" rel="noopener noreferrer"&gt;pypi.org/project/cql-track&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://github.com/ereshzealous/cql-track/blob/main/USAGE.md" rel="noopener noreferrer"&gt;USAGE.md&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Examples:&lt;/strong&gt; &lt;a href="https://github.com/ereshzealous/cql-track/tree/main/examples" rel="noopener noreferrer"&gt;FastAPI, Django, plain Python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MIT licensed. Contributions welcome.&lt;/p&gt;

&lt;p&gt;If you've struggled with Cassandra schema management, give it a try. Star the repo if it saves you time.&lt;/p&gt;

&lt;p&gt;If you get a chance to try it out, I'd appreciate any feedback. Feel free to open an issue at &lt;a href="https://github.com/ereshzealous/cql-track/issues" rel="noopener noreferrer"&gt;github.com/ereshzealous/cql-track/issues&lt;/a&gt; if you run into anything.&lt;/p&gt;




</description>
      <category>devops</category>
      <category>schema</category>
      <category>database</category>
      <category>python</category>
    </item>
    <item>
      <title>Archexa: A CLI That Turns Codebases Into Architecture Docs, Impact Analysis, and Reviews</title>
      <dc:creator>Eresh Gorantla</dc:creator>
      <pubDate>Fri, 20 Mar 2026 15:10:00 +0000</pubDate>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9/archexa-a-cli-that-turns-codebases-into-architecture-docs-impact-analysis-and-reviews-9bo</link>
      <guid>https://forem.com/eresh_g_721f1d5ca1de0a5b9/archexa-a-cli-that-turns-codebases-into-architecture-docs-impact-analysis-and-reviews-9bo</guid>
      <description>&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%2Fh6jlnp1lgr5udcxtkuxp.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%2Fh6jlnp1lgr5udcxtkuxp.png" alt="archexa" width="384" height="122"&gt;&lt;/a&gt;&lt;br&gt;
Built this while working across multiple microservice codebases — would love feedback from folks solving similar problems.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Architecture documentation tends to drift over time.&lt;/p&gt;

&lt;p&gt;It’s usually written once, when the system is simpler. But as the code evolves — refactors, new services, changing APIs, updated data flows — the documentation doesn’t keep up.&lt;/p&gt;

&lt;p&gt;Updating architecture docs is not trivial. It often requires re-understanding the system end-to-end, tracing dependencies, and reconstructing how components interact. In practice, this rarely happens alongside regular development work.&lt;/p&gt;

&lt;p&gt;Over time, this creates familiar challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New engineers take longer to ramp up&lt;/li&gt;
&lt;li&gt;“Safe” changes can impact downstream systems unexpectedly&lt;/li&gt;
&lt;li&gt;Code reviews focus on diffs, not system-wide effects&lt;/li&gt;
&lt;li&gt;Important system knowledge lives in people’s heads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture knowledge does exist — it’s all in the code.&lt;br&gt;
But it’s distributed across many files and not easily accessible as a whole.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Built: Archexa
&lt;/h2&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%2F7ugxabav9rk9pvkgdwyz.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%2F7ugxabav9rk9pvkgdwyz.gif" alt="archexa Demo" width="760" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To address this, I built Archexa — a CLI tool that analyzes a codebase and generates structured engineering artifacts directly from it.&lt;/p&gt;

&lt;p&gt;Instead of maintaining documentation manually, Archexa generates it on demand.&lt;/p&gt;

&lt;p&gt;It produces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architecture documentation - (component maps, dependency diagrams, communication flows)&lt;/li&gt;
&lt;li&gt;Change impact analysis- (likely downstream effects before merging changes)&lt;/li&gt;
&lt;li&gt;Architecture-aware code reviews - (cross-file and cross-service insights)&lt;/li&gt;
&lt;li&gt;Deep technical Q&amp;amp;A - (structured answers to system-level questions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not just docstrings or README templates — actual architecture documents, with references back to source files.&lt;/p&gt;
&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Install (macOS/Linux)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ereshzealous/archexa/main/install.sh | bash

&lt;span class="c"&gt;### Initialize config&lt;/span&gt;
archexa init

&lt;span class="c"&gt;### Get an overview of your codebase&lt;/span&gt;
archexa gist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For Detailed Documentation Please visit - &lt;a href="https://github.com/ereshzealous/archexa" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Archexa
&lt;/h2&gt;

&lt;p&gt;This tool comes from recurring patterns I’ve seen in real projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Onboarding takes too long&lt;/em&gt;&lt;/strong&gt; - Understanding how a system fits together often requires exploring many files manually. That knowledge is rarely centralized.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Changes have hidden impact&lt;/em&gt;&lt;/strong&gt; - A small change — like renaming a field — can affect consumers elsewhere (APIs, events, downstream services) that aren’t obvious from local context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Code reviews miss system-level effects&lt;/em&gt;&lt;/strong&gt; - Reviews are typically scoped to changed files. But system behavior often depends on interactions beyond that diff.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of these stem from the same root issue - The architecture exists in the code, but it’s not easily visible as a system.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where Existing Tools Fit
&lt;/h2&gt;

&lt;p&gt;There are strong tools in this space, each solving part of the problem:&lt;/p&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%2Fm42kyvkecoc854vdzatg.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%2Fm42kyvkecoc854vdzatg.png" alt="Case Study on Availability of Tools" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of these tools is valuable.&lt;br&gt;
Archexa focuses on combining into a single workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;code analysis&lt;/li&gt;
&lt;li&gt;LLM-based reasoning&lt;/li&gt;
&lt;li&gt;structured output&lt;/li&gt;
&lt;li&gt;CLI-first workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Two Modes: Fast vs Deep
&lt;/h2&gt;

&lt;p&gt;Not every question needs the same depth.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pipeline Mode (Default)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Scans entire codebase using AST parsing&lt;/li&gt;
&lt;li&gt;Extracts structure: imports, classes, APIs, data flows&lt;/li&gt;
&lt;li&gt;Generates a system-level overview&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Performance:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;1–2 AI calls&lt;/li&gt;
&lt;li&gt;Seconds to minutes&lt;/li&gt;
&lt;li&gt;Low cost&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Agent Mode (--deep)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reads actual source files&lt;/li&gt;
&lt;li&gt;Traces dependencies and execution paths&lt;/li&gt;
&lt;li&gt;Follows relationships across components&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Behavior:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Multiple tool calls (10–50)&lt;/li&gt;
&lt;li&gt;More detailed insights&lt;/li&gt;
&lt;li&gt;Higher cost&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Example (FastAPI — 2,600+ files)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;When analyzing dependency injection:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pipeline mode → high-level overview&lt;/li&gt;
&lt;li&gt;Deep mode → traced execution path, identified patterns, documented lifecycle, cited source files&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Six Commands, Six Use Cases
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;gist&lt;/code&gt; — Codebase Overview
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa gist &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml
archexa gist &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--deep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Quick understanding of - architecture, components, request flow&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;query&lt;/code&gt; — Ask Questions
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa query &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"How does authentication work?"&lt;/span&gt;
archexa query &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Explain dependency injection"&lt;/span&gt; &lt;span class="nt"&gt;--deep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Returns structured answers with file references.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;analyze&lt;/code&gt; — Full Architecture Docs
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa analyze &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Generates:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Mermaid architecture diagrams&lt;/li&gt;
&lt;li&gt;Component tables with responsibilities&lt;/li&gt;
&lt;li&gt;Dependency maps and communication flows&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;impact&lt;/code&gt; — Change Impact Analysis
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa impact &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="s2"&gt;"src/routing.py"&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Adding rate limiting parameter"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Surfaces:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Directly and transitively affected files&lt;/li&gt;
&lt;li&gt;Likely downstream impact across services&lt;/li&gt;
&lt;li&gt;Risk assessment and migration strategy&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;review&lt;/code&gt; — Architecture-Aware Review
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa review &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--changed&lt;/span&gt; &lt;span class="nt"&gt;--deep&lt;/span&gt;
archexa review &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml &lt;span class="nt"&gt;--branch&lt;/span&gt; origin/main..HEAD &lt;span class="nt"&gt;--deep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Identifies:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Cross-file issues and contract mismatches&lt;/li&gt;
&lt;li&gt;System-level concerns (security, performance, error handling)&lt;/li&gt;
&lt;li&gt;Areas worth deeper inspection — with exact file and line numbers&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;chat&lt;/code&gt; — Interactive Exploration
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;archexa chat &lt;span class="nt"&gt;--config&lt;/span&gt; archexa.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Ask questions in plain English&lt;/li&gt;
&lt;li&gt;Switch between fast and deep mode per turn&lt;/li&gt;
&lt;li&gt;Maintain context across turns&lt;/li&gt;
&lt;li&gt;Save sessions to file with &lt;code&gt;/save&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Works With Multiple LLM Providers
&lt;/h2&gt;

&lt;p&gt;Archexa connects via OpenAI-compatible endpoints, so you can use the provider that works best for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI&lt;/strong&gt; — GPT-4o, GPT-4.1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic&lt;/strong&gt; — Claude Sonnet, Claude Haiku (via OpenRouter)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google&lt;/strong&gt; — Gemini Flash (via OpenRouter)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure OpenAI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; — fully local, free, code never leaves your machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any OpenAI-compatible endpoint&lt;/strong&gt; — LM Studio, vLLM, corporate proxies&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The FastAPI examples in the repo use three different models so you can compare quality across providers.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Single Binary, Runs Locally
&lt;/h2&gt;

&lt;p&gt;Archexa ships as a single binary. No Python install, no package manager — download and run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ereshzealous/archexa/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;macOS (ARM/Intel), Linux (x64/ARM).&lt;/li&gt;
&lt;li&gt;windows - Download .exe file manually from releases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your code stays on your machine. Only extracted evidence goes to the AI API — not your raw source files. Or use Ollama and nothing leaves your machine at all.&lt;/p&gt;

&lt;p&gt;A local CLI has a different risk profile from SaaS tools — it stays in your workflow without depending on a hosted product's availability or roadmap decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Output: FastAPI (2,661 files)
&lt;/h2&gt;

&lt;p&gt;Every command run against a real open-source project, with configs and generated documents published&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pipeline&lt;/td&gt;
&lt;td&gt;Gemini Flash&lt;/td&gt;
&lt;td&gt;1m 41s&lt;/td&gt;
&lt;td&gt;7.5 KB overview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gist --deep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;Gemini Flash&lt;/td&gt;
&lt;td&gt;58s&lt;/td&gt;
&lt;td&gt;10 KB with file refs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;analyze&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pipeline&lt;/td&gt;
&lt;td&gt;Claude Sonnet 4&lt;/td&gt;
&lt;td&gt;1m 55s&lt;/td&gt;
&lt;td&gt;10.5 KB, 6 diagrams, 210 citations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;query --deep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;Claude Sonnet 4&lt;/td&gt;
&lt;td&gt;2m 31s&lt;/td&gt;
&lt;td&gt;7.5 KB DI deep dive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;review --deep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;Gemini Flash&lt;/td&gt;
&lt;td&gt;1m 46s&lt;/td&gt;
&lt;td&gt;6.8 KB, 9 review findings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;impact --deep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;GPT-4.1&lt;/td&gt;
&lt;td&gt;2m 50s&lt;/td&gt;
&lt;td&gt;12.4 KB impact analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Browse the full examples with generated docs&lt;/strong&gt;: &lt;a href="https://github.com/ereshzealous/archexa/tree/main/examples/fastapi" rel="noopener noreferrer"&gt;github.com/ereshzealous/archexa/tree/main/examples/fastapi&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Full documentation — installation, config reference, provider setup for every major AI service, all commands, troubleshooting — lives in the repo:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ereshzealous/archexa" rel="noopener noreferrer"&gt;github.com/ereshzealous/archexa&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a public beta. I'd appreciate any feedback — what works, what doesn't, what you'd want it to do. Issues and stars are both welcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;License:&lt;/strong&gt; Apache 2.0&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I Built 56 Developer Tools With Zero Dependencies — Here's What I Learned</title>
      <dc:creator>Eresh Gorantla</dc:creator>
      <pubDate>Wed, 18 Mar 2026 19:23:32 +0000</pubDate>
      <link>https://forem.com/eresh_g_721f1d5ca1de0a5b9/i-built-56-developer-tools-with-zero-dependencies-heres-what-i-learned-1k05</link>
      <guid>https://forem.com/eresh_g_721f1d5ca1de0a5b9/i-built-56-developer-tools-with-zero-dependencies-heres-what-i-learned-1k05</guid>
      <description>&lt;p&gt;I kept opening random websites for small tasks — formatting JSON, decoding JWTs, converting timestamps, generating UUIDs. Different sites, different UX, most wanting sign-ups or tracking everything.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://devprix.dev" rel="noopener noreferrer"&gt;devprix.dev&lt;/a&gt; — 56 developer tools that all run in your browser. Nothing sent to any server. Zero npm dependencies for any tool logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Full Tool List&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Formatters &amp;amp; Validators&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON Formatter (beautify, minify, tree view)&lt;/li&gt;
&lt;li&gt;SQL Formatter (6 SQL dialects)&lt;/li&gt;
&lt;li&gt;Regex Tester (live highlighting, pattern library)&lt;/li&gt;
&lt;li&gt;JWT Decoder (header/payload/signature, expiry check)&lt;/li&gt;
&lt;li&gt;Cron Expression Parser (visual builder, next run times)&lt;/li&gt;
&lt;li&gt;JSON Path Evaluator &amp;amp; JSON Validator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Generators&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UUID Generator (v1, v3, v4, v5, v7)&lt;/li&gt;
&lt;li&gt;Password &amp;amp; Key Generator (RSA, ECDSA, Ed25519 key pairs)&lt;/li&gt;
&lt;li&gt;QR Code Generator (URL, WiFi, vCard — encoder built from scratch)&lt;/li&gt;
&lt;li&gt;Test Data Generator (55+ field types)&lt;/li&gt;
&lt;li&gt;Hash Generator (MD5, SHA-1/256/384/512, HMAC)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Converters&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timestamp / Epoch Converter (80+ timezones)&lt;/li&gt;
&lt;li&gt;Base64 Encoder/Decoder&lt;/li&gt;
&lt;li&gt;Color Converter (HEX/RGB/HSL, WCAG contrast checker, palettes)&lt;/li&gt;
&lt;li&gt;YAML ↔ JSON (Docker/K8s presets)&lt;/li&gt;
&lt;li&gt;JSON → TypeScript (interfaces, Zod schemas)&lt;/li&gt;
&lt;li&gt;Curl → Code (Python, JS, Go, Java, PHP, Rust, C#, Ruby)&lt;/li&gt;
&lt;li&gt;CSV ↔ JSON, Markdown ↔ HTML, Image → Base64, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DevOps &amp;amp; Config&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Run → Compose (and reverse)&lt;/li&gt;
&lt;li&gt;Chmod Calculator, IP Subnet Calculator&lt;/li&gt;
&lt;li&gt;Git Command Builder (18 commands with danger warnings)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CSS &amp;amp; Design&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS Gradient Generator (linear/radial/conic, 24 presets)&lt;/li&gt;
&lt;li&gt;Box Shadow Generator (multi-layer, Tailwind output)&lt;/li&gt;
&lt;li&gt;Tailwind CSS Lookup (500+ classes, reverse lookup)&lt;/li&gt;
&lt;li&gt;Favicon Generator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...plus diff checker, JSON diff, slug generator, ASCII art generator, line tools, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Interesting Technical Bits&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;QR Code Encoder From Scratch&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of using a library, I implemented the full QR encoding &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Myers Diff Algorithm&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The text diff checker uses Eugene Myers' 1986 algorithm — the same one behind &lt;code&gt;git diff&lt;/code&gt;. It finds the shortest edit script and then does a second pass for character-level highlighting within changed lines, so you can see exactly which characters were modified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Curl to Code Parser&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Parsing curl commands is surprisingly tricky. The parser handles single and double quoted strings, backslash line continuations, --data-raw vs -d vs --data-binary, multiple -H headers, -u for auth, and generates idiomatic code for 9 languages — not just string concatenation but proper library usage (requests for Python, fetch for JS, net/http for Go).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Client-Side Cryptography&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The password generator and hash tools use the Web Crypto API (SubtleCrypto) for RSA, ECDSA, and Ed25519 key pair generation, SHA hashing, and HMAC computation. MD5 is the one exception — since Web Crypto doesn't support MD5, that's implemented from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero dependencies is liberating.&lt;/strong&gt; &lt;br&gt;
No supply chain risk, no breaking updates, no bundle bloat. &lt;br&gt;
Every algorithm is right there in the codebase. The trade-off is development time — implementing a QR encoder takes longer than &lt;code&gt;npm install qrcode&lt;/code&gt; — but the result is faster, smaller, and fully under your control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dark mode is table stakes.&lt;/strong&gt; &lt;br&gt;
The site supports light, dark, and system-preference themes. In 2026, shipping a developer tool without dark mode is leaving users behind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try It&lt;/strong&gt;&lt;br&gt;
→ &lt;a href="https://devprix.dev" rel="noopener noreferrer"&gt;devprix.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All free, all private, all in your browser. I'd love to hear what tools you'd want added.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
