Add zero-tooling web development essay
New essay arguing SX eliminates the entire conventional web toolchain (bundlers, transpilers, package managers, CSS tools, dev servers, linters, type checkers, framework CLIs) and that agentic AI replaces the code editor itself. Links Carson Gross's "Yes, and..." essay with a Zen Buddhism framing of the write→read→describe progression. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
133
sx/sx/essays.sx
133
sx/sx/essays.sx
@@ -677,3 +677,136 @@
|
||||
"The claim is narrower and stronger: for a system that must simultaneously serve as markup, programming language, wire format, data notation, spec language, and metaprogramming substrate — with homoiconicity, one-pass parsing, structural validation, token efficiency, and composability — there is no alternative to s-expressions. Not because alternatives have not been tried. Not because the design space has not been explored. But because the requirements, when stated precisely, admit exactly one family of solutions, and that family is the one McCarthy discovered sixty-seven years ago.")
|
||||
(p :class "text-stone-600"
|
||||
"The name for this, borrowed from a " (a :href "https://en.wikipedia.org/wiki/There_is_no_alternative" :class "text-violet-600 hover:underline" "different context entirely") ", is " (em "TINA") " — there is no alternative. Not as a political slogan, but as a mathematical observation. When you need a minimal, homoiconic, structurally-validatable, token-efficient, tree-native, AI-compatible representation for the web, you need s-expressions. Everything else is either less capable or isomorphic."))))
|
||||
|
||||
(defcomp ~essay-zero-tooling ()
|
||||
(~doc-page :title "Zero-Tooling Web Development"
|
||||
(p :class "text-stone-500 text-sm italic mb-8"
|
||||
"SX was built without a code editor. No IDE, no manual source edits, no build tools, no linters, no bundlers. The entire codebase — evaluator, renderer, spec, documentation site, test suite — was produced through conversation with an agentic AI. This is what zero-tooling web development looks like.")
|
||||
|
||||
(~doc-section :title "No code editor" :id "no-editor"
|
||||
(p :class "text-stone-600"
|
||||
"This needs to be stated plainly, because it sounds like an exaggeration: " (strong "not a single line of SX source code was written by hand in a code editor") ". Every component definition, every page route, every essay (including this one), every test case, every spec file — all of it was produced through natural-language conversation with Claude Code, an agentic AI that reads, writes, and modifies files on the developer's behalf.")
|
||||
(p :class "text-stone-600"
|
||||
"No VS Code. No Vim. No Emacs. No Sublime Text. No cursor blinking in a source file. The developer describes intent; the AI produces the code, edits the files, runs the tests, and iterates. The code editor — the tool that has defined software development for sixty years — was not used.")
|
||||
(p :class "text-stone-600"
|
||||
"This is not a stunt. It is a consequence of two properties converging: a language with trivial syntax that AI can produce flawlessly, and an AI agent capable of understanding and modifying an entire codebase through conversation. Neither property alone would be sufficient. Together, they make the code editor unnecessary."))
|
||||
|
||||
(~doc-section :title "The toolchain that wasn't" :id "toolchain"
|
||||
(p :class "text-stone-600"
|
||||
"A modern web application typically requires a stack of tooling before a single feature can be built. Consider what a React project demands:")
|
||||
(ul :class "space-y-2 text-stone-600"
|
||||
(li (strong "Bundler") " — Webpack, Vite, Rollup, or esbuild to resolve modules, tree-shake dead code, split bundles, and minify output.")
|
||||
(li (strong "Transpiler") " — Babel or SWC to transform JSX into function calls, strip TypeScript annotations, and downlevel modern syntax for older browsers.")
|
||||
(li (strong "Package manager") " — npm, yarn, or pnpm to manage hundreds or thousands of transitive dependencies in " (code "node_modules") ".")
|
||||
(li (strong "CSS pipeline") " — Sass or PostCSS to preprocess styles, CSS Modules or styled-components for scoping, Tailwind CLI for utility generation, PurgeCSS to remove unused rules.")
|
||||
(li (strong "Dev server") " — webpack-dev-server or Vite's dev mode for hot module replacement, WebSocket-based live reload, and proxy configuration.")
|
||||
(li (strong "Linter") " — ESLint with dozens of plugins and hundreds of rules to catch mistakes the language grammar permits.")
|
||||
(li (strong "Formatter") " — Prettier to enforce consistent style, because the syntax admits so many equivalent representations that teams cannot agree without automation.")
|
||||
(li (strong "Type checker") " — TypeScript compiler running in parallel to catch type errors that JavaScript's dynamic semantics would defer to runtime.")
|
||||
(li (strong "Test runner") " — Jest or Vitest with jsdom or happy-dom to simulate a browser environment, plus Cypress or Playwright for integration tests.")
|
||||
(li (strong "Framework CLI") " — create-react-app, Next.js CLI, or Angular CLI to scaffold project structure, generate boilerplate, and manage framework-specific configuration."))
|
||||
(p :class "text-stone-600"
|
||||
"That is ten categories of tooling, each with its own configuration format, update cycle, breaking changes, and mental overhead. A fresh " (code "npx create-next-app") " pulls in over 300 packages. The " (code "node_modules") " directory can exceed 200 megabytes. The configuration surface — " (code "webpack.config.js") ", " (code "tsconfig.json") ", " (code ".eslintrc") ", " (code ".prettierrc") ", " (code "postcss.config.js") ", " (code "tailwind.config.js") ", " (code "jest.config.js") ", " (code "next.config.js") " — is itself a specialization.")
|
||||
(p :class "text-stone-600"
|
||||
"SX uses " (strong "none of them") "."))
|
||||
|
||||
(~doc-section :title "Why each tool is unnecessary" :id "why-unnecessary"
|
||||
(p :class "text-stone-600"
|
||||
"Each tool in the conventional stack exists to solve a problem. SX eliminates the problems themselves, not just the tools.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Bundlers")
|
||||
(p :class "text-stone-600"
|
||||
"Bundlers exist because JavaScript's module system requires resolution — imports must be traced, dependencies gathered, and the result packaged for the browser. SX components are defined in " (code ".sx") " files, loaded at startup into the evaluator's environment, and served as s-expressions over the wire. The wire format is the source format. There is no compilation step, no module resolution, and no bundle. The " (code "deps.sx") " spec computes per-page component sets at runtime — only the components a page actually uses are sent to the client. This is tree-shaking without the tree-shaker.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Transpilers")
|
||||
(p :class "text-stone-600"
|
||||
"Transpilers exist because developers write in one language (JSX, TypeScript, modern ES20XX) and must ship another (browser-compatible JavaScript). SX source runs directly — the evaluator walks the same AST that was authored. There is no JSX-to-createElement transform because s-expressions " (em "are") " the component syntax. There are no type annotations to strip because types are enforced at the boundary, at startup. There is no syntax to downlevel because the grammar has been the same since 1958.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Package managers")
|
||||
(p :class "text-stone-600"
|
||||
"Package managers exist because the JavaScript ecosystem fragments functionality into thousands of tiny packages with deep transitive dependency trees. SX's runtime is self-contained: approximately eighty primitives built into the spec, an evaluator bootstrapped from " (code ".sx") " files, and a renderer that produces HTML, DOM nodes, or SX wire format. No third-party packages. No dependency resolution. No lock files. No supply-chain attack surface. The planned future — content-addressed components on IPFS — replaces package registries with content verification.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "CSS build tools")
|
||||
(p :class "text-stone-600"
|
||||
"CSS preprocessors and build tools exist because CSS has a global namespace, no scoping mechanism, and no way to know which rules a page actually uses. CSSX solves all three: the server scans the rendered component tree, collects the CSS classes actually referenced, and sends only those rules. Styles are co-located in components as keyword arguments. There is no global stylesheet to collide with, no preprocessor to run, no PurgeCSS to configure. The CSS each page needs is computed from the component graph — a runtime operation, not a build step.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Dev servers and HMR")
|
||||
(p :class "text-stone-600"
|
||||
"Dev servers with hot module replacement exist because compilation creates a feedback delay — you change a file, wait for the bundler to rebuild, and watch the browser update via WebSocket. SX has no compilation. " (code ".sx") " files are checked for changes on every request via " (code "reload_if_changed()") ". Edit the file, refresh the browser, see the result. The feedback loop is bounded by disk I/O, not by a build pipeline. There is no dev server because there is nothing to serve differently from production.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Linters and formatters")
|
||||
(p :class "text-stone-600"
|
||||
"Linters exist because complex grammars permit many ways to write incorrect or inconsistent code. Formatters exist because those same grammars permit many ways to write correct but inconsistently styled code. S-expressions have one syntactic form: " (code "(head args...)") ". Parentheses open and close. Strings are quoted. That is the entire grammar. There are no semicolons to forget, no operator precedence to get wrong, no closing tags to mismatch. Formatting is just indentation of nested lists. There is nothing to lint and nothing to format.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Type systems")
|
||||
(p :class "text-stone-600"
|
||||
"TypeScript exists because JavaScript's dynamic typing defers errors to runtime, often to production. SX's boundary spec declares the signature of every primitive, every IO function, and every page helper. " (code "SX_BOUNDARY_STRICT=1") " validates all registrations at startup — a type violation crashes the application before it serves a single request. Runtime predicates (" (code "string?") ", " (code "number?") ", " (code "list?") ") handle the rest. The type system is not a separate tool running in parallel — it is the spec, enforced at the edge.")
|
||||
|
||||
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Framework CLIs and scaffolding")
|
||||
(p :class "text-stone-600"
|
||||
"Framework CLIs exist because modern frameworks have complex setup — configuration files, directory conventions, build chains, routing configurations. SX has two declarative abstractions: " (code "defcomp") " (a component) and " (code "defpage") " (a route). Write a component in a " (code ".sx") " file, reference it from a " (code "defpage") ", and it is live. There is no scaffolding because there is nothing to scaffold."))
|
||||
|
||||
(~doc-section :title "The AI replaces the rest" :id "ai-replaces"
|
||||
(p :class "text-stone-600"
|
||||
"Eliminating the build toolchain still leaves the most fundamental tool: the code editor. The text editor is so basic to programming that it is invisible — questioning its necessity sounds absurd. But the traditional editor exists to serve " (em "human") " limitations:")
|
||||
(ul :class "space-y-2 text-stone-600"
|
||||
(li (strong "Syntax highlighting") " — because humans need visual cues to parse complex grammars. S-expressions are trivially parseable; an AI does not need color-coding to understand them.")
|
||||
(li (strong "Autocomplete") " — because humans cannot remember every function name, parameter, and type signature. An AI that has read the entire codebase does not need autocomplete; it already knows every symbol in scope.")
|
||||
(li (strong "Go-to-definition") " — because humans lose track of where things are defined across hundreds of files. An AI navigates by reading, grep, and glob — the same tools the editor uses internally, without the GUI.")
|
||||
(li (strong "Error squiggles") " — because humans need real-time feedback on mistakes. An AI produces correct code from the spec, and when it does not, it reads the error, understands the cause, and fixes it in the next edit.")
|
||||
(li (strong "Multi-cursor editing") " — because humans need mechanical assistance to make the same change in multiple places. An AI makes coordinated changes across files atomically — component definition, navigation data, page route — in a single operation.")
|
||||
(li (strong "Version control integration") " — because humans need visual diffs and staging interfaces. An AI reads " (code "git diff") ", understands the changes, and commits with meaningful messages."))
|
||||
(p :class "text-stone-600"
|
||||
"Every feature of a modern IDE exists to compensate for a human cognitive limitation. When the agent doing the editing has no such limitations — it can hold the full codebase in context, produce valid syntax without visual feedback, trace dependencies without tooling, and make multi-file changes atomically — the editor is not needed.")
|
||||
(p :class "text-stone-600"
|
||||
"This is not hypothetical. It is how SX was built. The developer's interface is a terminal running Claude Code. The conversation goes: describe what you want, review what the AI produces, approve or redirect. The AI reads the existing code, understands the conventions, writes the new code, edits the navigation, updates the page routes, and verifies consistency. The " (em "conversation") " is the development environment."))
|
||||
|
||||
(~doc-section :title "Before enlightenment, write code" :id "before-enlightenment"
|
||||
(p :class "text-stone-600"
|
||||
"Carson Gross makes an important point in " (a :href "https://htmx.org/essays/yes-and/" :class "text-violet-600 hover:underline" "\"Yes, and...\"") ": you have to have written code in order to effectively read code. The ability to review, critique, and direct is built on the experience of having done the work yourself. You cannot skip the craft and jump straight to the oversight.")
|
||||
(p :class "text-stone-600"
|
||||
"He is right. And yet.")
|
||||
(p :class "text-stone-600"
|
||||
"There is a Zen teaching about the three stages of practice. Before enlightenment: chop wood, carry water. During enlightenment: chop wood, carry water. After enlightenment: chop wood, carry water. The activities look the same from the outside, but the relationship to them has changed completely. The master chops wood without the beginner's anxiety about whether they are chopping correctly, and without the intermediate's self-conscious technique. They just chop.")
|
||||
(p :class "text-stone-600"
|
||||
"Software development has its own version. " (em "Before understanding, write code.") " You must have written the bundler configuration to understand why bundlers exist. You must have debugged the CSS cascade to understand why scoping matters. You must have traced a transitive dependency chain to understand why minimal dependencies matter. The craft comes first.")
|
||||
(p :class "text-stone-600"
|
||||
(em "During understanding, read code.") " You review pull requests. You audit architectures. You read more code than you write. You develop the taste that lets you tell good code from bad code, necessary complexity from accidental complexity, real problems from invented ones.")
|
||||
(p :class "text-stone-600"
|
||||
(em "After understanding, describe intent.") " You have internalized the patterns so deeply that you do not need to type them. You know what a component should look like without writing it character by character. You know what the test should cover without spelling out each assertion. You know what the architecture should be without drawing every box and arrow. You describe; the agent executes. Not because you cannot do the work, but because the work has become transparent to you.")
|
||||
(p :class "text-stone-600"
|
||||
"This is not a shortcut. The developer who built SX through conversation with an AI had decades of experience writing code by hand — in Lisp, in Python, in JavaScript, in C, in assembly (although he was never that good at it). The zero-tooling workflow is not a substitute for that experience. It is what comes " (em "after") " that experience. The wood still gets chopped. The water still gets carried. But the relationship to the chopping and carrying has changed."))
|
||||
|
||||
(~doc-section :title "Why this only works with s-expressions" :id "why-sexps"
|
||||
(p :class "text-stone-600"
|
||||
"This approach would not work with most web technologies. The reason is the " (strong "syntax tax") " — the overhead a language imposes on AI code generation.")
|
||||
(p :class "text-stone-600"
|
||||
"Consider what an AI must get right to produce a valid React component: JSX tags that open and close correctly, curly braces for JavaScript expressions inside markup, import statements with correct module paths, semicolons or ASI boundary rules, TypeScript type annotations with angle-bracket generics, CSS-in-JS template literals with different quoting rules from the surrounding code, and hook call ordering constraints that are semantic, not syntactic. Each of these is a failure point. Each failure produces something that does not parse, let alone run.")
|
||||
(p :class "text-stone-600"
|
||||
"S-expressions have one rule: parentheses match. The syntax tax is zero. An AI that can count parentheses can produce syntactically valid SX. This means the AI spends its entire capacity on " (em "what") " to generate — the semantics, the structure, the intent — rather than on formatting, escaping, and syntactic bookkeeping.")
|
||||
(p :class "text-stone-600"
|
||||
"The combination is more than additive. SX is deliberately spartan for hand-editing — all those parentheses, no syntax sugar, no operator precedence. Developers who see it for the first time recoil. But AI does not recoil. AI does not care about parentheses. It sees a minimal, regular, unambiguous grammar and produces correct output reliably. Meanwhile, the languages that are comfortable for humans — with their rich syntax, implicit rules, and contextual parsing — are exactly the ones where AI makes the most mistakes.")
|
||||
(p :class "text-stone-600"
|
||||
"SX is optimized for the agent, not the typist. This turns out to be the right trade-off when the agent is doing the typing."))
|
||||
|
||||
(~doc-section :title "What zero-tooling actually means" :id "what-it-means"
|
||||
(p :class "text-stone-600"
|
||||
"Zero-tooling does not mean zero software. The SX evaluator exists. The server exists. The browser runtime exists. These are " (em "the system") ", not tools for building the system. The distinction matters.")
|
||||
(p :class "text-stone-600"
|
||||
"A carpenter's hammer is a tool. The house is not a tool. In web development, this distinction has been lost. The bundler, the transpiler, the linter, the formatter, the type checker, the dev server — these are all tools for building the application. The application itself is the server, the evaluator, the renderer, the browser runtime. Traditional web development has accumulated so many tools-for-building-the-thing that the tools-for-building-the-thing often have more code, more configuration, and more failure modes than the thing itself.")
|
||||
(p :class "text-stone-600"
|
||||
"SX has the thing. It does not have tools for building the thing. You write " (code ".sx") " files. They run. That is the entire workflow.")
|
||||
(p :class "text-stone-600"
|
||||
"Add an agentic AI, and you do not even write the " (code ".sx") " files. You describe what you want. The AI writes the files. They run. The workflow is: intent → code → execution, with no intermediate tooling layer and no manual editing step."))
|
||||
|
||||
(~doc-section :title "The proof" :id "proof"
|
||||
(p :class "text-stone-600"
|
||||
"The evidence for zero-tooling development is not a benchmark or a whitepaper. It is this website.")
|
||||
(p :class "text-stone-600"
|
||||
"Every page you are reading was produced through conversation with an agentic AI. The SX evaluator — a self-hosting interpreter with tail-call optimization, delimited continuations, macro expansion, and three rendering backends — was developed without opening a code editor. The specification files that define the language were written without an IDE. The bootstrappers that compile the spec to JavaScript and Python were produced without syntax highlighting or autocomplete. The test suite — hundreds of tests across evaluator, parser, renderer, router, dependency analyzer, and engine — was written without a test runner GUI. This documentation site — with its navigation, its code examples, its live demos — was built without a web development framework's CLI.")
|
||||
(p :class "text-stone-600"
|
||||
"The developer sat in a terminal. They described what they wanted. The AI produced the code. When something was wrong, they described what was wrong. The AI fixed it. When something needed to change, they described the change. The AI made the change. Across thousands of files, tens of thousands of lines of code, and months of development. Even the jokes — the " (a :href "/essays/sx-sucks" :class "text-violet-600 hover:underline" "self-deprecating essay") " about everything wrong with SX, the deadpan tone of the documentation, the essay you are reading right now — all produced through conversation, not through typing.")
|
||||
(p :class "text-stone-600"
|
||||
"No build step. No bundler. No transpiler. No package manager. No CSS preprocessor. No dev server. No linter. No formatter. No type checker. No framework CLI. No code editor.")
|
||||
(p :class "text-stone-600"
|
||||
"Zero tools."))))
|
||||
|
||||
@@ -93,7 +93,9 @@
|
||||
(dict :label "There Is No Alternative" :href "/essays/no-alternative"
|
||||
:summary "Every attempt to escape s-expressions leads back to s-expressions. This is not an accident.")
|
||||
(dict :label "sx sucks" :href "/essays/sx-sucks"
|
||||
:summary "An honest accounting of everything wrong with SX and why you probably shouldn't use it.")))
|
||||
:summary "An honest accounting of everything wrong with SX and why you probably shouldn't use it.")
|
||||
(dict :label "Zero-Tooling" :href "/essays/zero-tooling"
|
||||
:summary "SX was built without a code editor. No IDE, no build tools, no linters, no bundlers. What zero-tooling web development looks like.")))
|
||||
|
||||
(define specs-nav-items (list
|
||||
(dict :label "Architecture" :href "/specs/")
|
||||
|
||||
@@ -283,6 +283,7 @@
|
||||
"separation-of-concerns" (~essay-separation-of-concerns)
|
||||
"sx-and-ai" (~essay-sx-and-ai)
|
||||
"no-alternative" (~essay-no-alternative)
|
||||
"zero-tooling" (~essay-zero-tooling)
|
||||
:else (~essays-index-content)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user