philosophy.html

319 lines
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
{{template "main.html" .}}

{{define "title"}}Philosophy — Congo{{end}}
{{define "description"}}Why Congo vendors source, distributes itself, uses SQLite in production, and renders HTML on the server. Ideas that sound weird until they don't.{{end}}

{{define "content"}}
<div class="max-w-3xl mx-auto px-6 py-16">
    <h1 class="text-3xl md:text-4xl font-bold tracking-tight mb-3">Ideas That Sound Weird Until They Don't</h1>
    <p class="text-body text-base mb-16">
        Congo makes choices most frameworks avoid. They follow from one idea about what software is.
    </p>

    <div class="space-y-0">

        <!-- Data and Processing -->
        <section class="pb-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Don't Pretend the Machine Is Something It Isn't</h2>
            <p class="text-body leading-relaxed mb-4">
                Programming languages are reflections of how we want to think. The syntax is a law for
                the code we can write. The compiler is the judge. At the core of any law are axioms,
                and different languages chose different ones. C++ says everything is a class —
                structure and behavior fused into one taxonomy. Haskell says everything is a function —
                data is just a function that hasn't been applied yet. These are branches of thought,
                not truths about machines. At the bottom, everything serializes to numbers. Classes are
                numbers. Functions are numbers. The distinction is how we choose to interpret them.
            </p>
            <p class="text-body leading-relaxed mb-4">
                We don't start from "everything is a class" or "everything is a function." We start from
                what the machine actually gives us: data and processing. A struct defines the shape of data
                in memory. An interface defines the shape of behavior. Functions are procedures — encodings
                of our flow diagrams. Methods are functions with a subject as the first argument:
                <code class="text-accent">user.Save()</code> is <code class="text-accent">Save(user)</code>.
                The method name claims a verb for the type. The arguments provide context.
                Go gives us this directly — no hidden dispatch, no class hierarchy, no pretense.
            </p>
            <p class="text-body leading-relaxed mb-4">
                This model scales. At the application level: a database is data, a server is processing.
                At the cloud level: replicated databases across machines, Go services on separate nodes.
                An AI model is an application of data and processing with an interface that takes tokens
                and returns tokens — beyond that, like any other component, it's a black box we encode
                into and out of. The cloud isn't a different paradigm. AI isn't a different paradigm.
                They're the same two concepts at different scopes.
            </p>
            <div class="quote-bar">
                <p>Congo's packages are a toolkit, not a taxonomy. <code>database</code> handles data
                storage. <code>application</code> handles HTTP processing. <code>router</code> handles
                network routing. <code>security</code> handles HTTP security. <code>assistant</code>
                handles AI integration. <code>platform</code> handles infrastructure.
                <code>frontend</code> handles client-side rendering.
                Each package is a primitive. Your application composes them.</p>
            </div>
        </section>

        <!-- Declarative and Imperative -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Declare the What, Encode the How</h2>
            <p class="text-body leading-relaxed mb-4">
                Imperative code is an encoding of procedural instructions — step by step, do this then that.
                This maps directly to how machines work: memory and instructions, structs and functions.
                Imperative code is what gets tested because it's the code that actually runs. It's assembly.
                It's C. It's the <code class="text-accent">for</code> loop.
            </p>
            <p class="text-body leading-relaxed mb-4">
                Declarative code is an encoding of composition — structures made of structures, ultimately
                defined by imperative parts. Go has a pattern for this: the
                <a href="https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis" class="text-accent hover:text-bright transition-colors" target="_blank" rel="noopener">functional options pattern</a>.
                Instead of passing a config struct, you pass functions that each configure one thing.
                The result reads like a declaration — what the application is — but compiles to
                imperative Go. This is a Congo application:
            </p>
            <div class="code-block mb-4">
                <pre><code class="language-go">router.Listen(
    router.WithLogger(),
    security.New(security.WithNonce(), security.WithHeaders()),
    application.New(views,
        application.WithController(controllers.Home()),
        application.WithController(controllers.API()),
    ),
)</code></pre>
            </div>
            <p class="text-body leading-relaxed mb-4">
                One declaration. <code class="text-accent">router.Listen</code> composes the middleware chain,
                starts the server, handles TLS. <code class="text-accent">application.New</code> takes
                functional options — each <code class="text-accent">WithController</code>,
                <code class="text-accent">WithValue</code>, <code class="text-accent">WithFunc</code>
                configures one aspect. The application registers controllers, parses templates, serves HTML.
                Security generates nonces and sets headers. Each package is imperative internally and
                declarative at its interface.
            </p>
            <div class="quote-bar">
                <p>Packages define imperative primitives. Applications compose them declaratively
                using functional options. The binary is the compiled result. This is why Congo compiles
                to a single file — the declaration becomes the program.</p>
            </div>
        </section>

        <!-- Three Declarations -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Applications Are Configurations</h2>
            <p class="text-body leading-relaxed mb-4">
                Most applications are, at their heart, a configuration of technologies composed
                into business logic. A web app configures a database, a server, security policies,
                and maybe an AI provider — then encodes business rules in controllers. The technologies
                are the constants. The business logic is the variable.
            </p>
            <p class="text-body leading-relaxed mb-4">
                Congo makes this explicit. A project has three declarations — three files that
                describe three scopes of the same system:
            </p>
            <div class="code-block mb-4">
                <pre><code class="language-go">// models/db.go — what the system remembers
var DB = engines.NewAuto()
var Users = database.Manage(DB, new(User))
var Todos = database.Manage(DB, new(Todo))</code></pre>
            </div>
            <div class="code-block mb-4">
                <pre><code class="language-go">// main.go — what the system does
router.Listen(
    router.WithLogger(),
    security.New(security.WithNonce(), security.WithHeaders()),
    application.New(views,
        application.WithController(controllers.Home()),
    ),
)</code></pre>
            </div>
            <div class="code-block mb-4">
                <pre><code class="language-go">// infra/main.go — where the system lives
hosting.Launch(digitalocean.New(token),
    hosting.Server("production",
        hosting.WithService("web", hosting.WithBuild("./cmd/web")),
    ),
)</code></pre>
            </div>
            <p class="text-body leading-relaxed mb-4">
                Data, application, infrastructure. Three scopes. Same pattern — functional options,
                declarative composition. The controllers are the only imperative part — they encode
                the business logic that connects data to HTTP. Everything else is configuration.
            </p>
            <div class="quote-bar">
                <p>The declarative layer is the configuration. The imperative layer is the business logic.
                The framework exists to make the configuration as readable as the logic.</p>
            </div>
        </section>

        <!-- Vendored Source -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Vendored Source, Not Dependencies</h2>
            <p class="text-body leading-relaxed mb-4">
                When you run <code class="text-accent">congo init</code>, the entire framework is copied into your project
                as regular Go files in <code class="text-accent">internal/</code>. Not downloaded from a registry. Not fetched
                at build time. Copied. You can read every line, modify anything, and your project compiles with zero network access.
            </p>
            <p class="text-body leading-relaxed mb-4">
                The conventional approach — importing packages from registries — creates invisible dependencies on infrastructure
                you don't control. Registries go down. Maintainers delete packages. Major versions introduce breaking changes
                that cascade through your dependency tree. These aren't hypothetical risks. They happen regularly.
            </p>
            <div class="quote-bar">
                <p>Your project's framework code lives in your repo. The dependencies it pulls in — SQLite, JWT, UUID generation — are small, stable libraries. Everything else is source code you own.</p>
            </div>
            <p class="text-body leading-relaxed">
                This also means you can understand your framework. It's a few thousand lines of Go. Open the files,
                read the code, see how routing works, how the ORM builds queries, how templates are parsed.
                There's no version to pin, no changelog to track, no upgrade to break things. It's your code now.
                See the <a href="/framework#packages" class="text-accent hover:text-bright transition-colors" hx-boost="true">seven packages</a> that make up the framework.
            </p>
        </section>

        <!-- Distribution -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Bring Your Own Basket</h2>
            <p class="text-body leading-relaxed mb-4">
                Run <code class="text-accent">congo source</code> and the binary extracts its own complete source tree.
                Everything — the framework, the CLI, the dev platform, the scaffold templates — lives inside
                the binary you're running. Congo carries its own DNA. Every binary can reproduce itself.
            </p>
            <p class="text-body leading-relaxed mb-4">
                This website is a Congo application that serves its own source code, browsable at
                <a href="/source" class="text-accent hover:text-bright transition-colors" hx-boost="true">/source</a>.
                Your developers keep their own forks — some on GitHub, some on GitLab, some on their own servers.
                We don't pick one platform and put all our eggs in that basket. We bring our own basket to
                every table.
            </p>
            <div class="quote-bar">
                <p>Congo distributes virally. Every binary contains the source. Every project contains the framework.
                Every developer can fork, modify, and redistribute. No single platform controls the chain.</p>
            </div>
            <p class="text-body leading-relaxed">
                Sovereignty matters. Not as a political statement — as an engineering one. When your software
                depends on a platform you don't control, you've introduced a single point of failure that
                no amount of redundancy fixes. Congo's distribution model is the same as its philosophy:
                own your stack, carry your tools, don't assume the table will be set for you.
            </p>
        </section>

        <!-- AI-First -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Frameworks Should Teach AI About Themselves</h2>
            <p class="text-body leading-relaxed mb-4">
                Most frameworks are passive when it comes to AI. You paste code into ChatGPT, and it guesses at your patterns.
                It hallucinates APIs. It suggests conventions from different frameworks. You spend more time correcting the AI
                than you saved by using it.
            </p>
            <p class="text-body leading-relaxed mb-4">
                Congo takes an active approach. <code class="text-accent">congo claude</code> launches Claude Code with a complete
                framework reference injected into the session — controller patterns, model API, template conventions, database
                conventions, deployment patterns. The AI doesn't guess. It knows.
            </p>
            <div class="quote-bar">
                <p>The framework is designed to be understood — by humans and by machines. Small API surface, consistent patterns, explicit conventions. Good for reading, good for AI.</p>
            </div>
            <p class="text-body leading-relaxed">
                This works because Congo is small and consistent. Seven packages, a handful of patterns, no magic.
                When the AI can read and understand the entire framework, it writes code that fits. When the framework
                provides its own documentation to the AI, every developer gets an expert pair programmer for free.
            </p>
        </section>

        <!-- HTMX Over SPAs -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Server-Rendered HTML, Not Single-Page Apps</h2>
            <p class="text-body leading-relaxed mb-4">
                The last decade of web development pushed everything to the client. React, Vue, Svelte — frameworks
                that run in the browser, manage their own state, and communicate with the server through JSON APIs.
                This added enormous complexity: build pipelines, hydration, serialization boundaries, duplicated
                validation, bundle optimization, client-side routing.
            </p>
            <p class="text-body leading-relaxed mb-4">
                Congo uses HTMX. The server renders HTML. HTMX swaps it into the page. No client-side state,
                no build pipeline for views, no API layer between your app and your UI. Your web application is
                a Go program that returns HTML. When you need complex interactivity — a code editor, a chart,
                a drag-and-drop interface — Congo supports React islands that mount into server-rendered pages.
            </p>
            <div class="quote-bar">
                <p>Your web app is a Go program that returns HTML. That's the whole architecture. HTMX for interactivity. React islands when you genuinely need them. Nothing else.</p>
            </div>
            <p class="text-body leading-relaxed">
                This isn't a regression. It's a correction. The industry is moving back to server rendering —
                Rails 8, Phoenix LiveView, Django + HTMX, even Next.js with Server Components. Congo starts there.
            </p>
        </section>

        <!-- SQLite in Production -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">SQLite in Production</h2>
            <p class="text-body leading-relaxed mb-4">
                The conventional wisdom says production databases need a separate server — PostgreSQL, MySQL,
                managed RDS. This adds operational complexity, network latency, connection pooling, and another
                thing that can go down at 3 AM.
            </p>
            <p class="text-body leading-relaxed mb-4">
                Congo uses SQLite. Your database is a file. Reads come from memory (nanosecond latency — faster
                than any cache). N+1 queries are not a performance problem when each query takes nanoseconds.
                For development, it's in-memory. For production, it's a file on disk with WAL mode. When you
                need distributed reads, Congo supports <a href="https://turso.tech/libsql" class="text-accent hover:text-bright transition-colors" target="_blank" rel="noopener">LibSQL</a> embedded replicas by Turso — local SQLite synced from a remote primary.
                See how it works in the <a href="/framework" class="text-accent hover:text-bright transition-colors" hx-boost="true">guide</a>.
            </p>
            <p class="text-body leading-relaxed mb-4">
                A SQL row and a Go struct are both records — named collections of fields. SQLite is
                untyped; it stores whatever you give it. Go is statically typed; the struct is the source
                of truth for what a record looks like. Congo's <code class="text-accent">Collection[T]</code>
                bridges them — the Go type asserts its shape onto the database, not the other way around.
                Tables are created from struct fields. Columns are added when new fields appear.
                The type truth lives in Go.
            </p>
            <div class="quote-bar">
                <p>N+1 queries are not a problem when your database reads from memory. No connection pooling. No database server. No network hop. Just a file.</p>
            </div>
            <p class="text-body leading-relaxed">
                Rails 8 validated this approach in 2024 by shipping Solid Cache, Solid Queue, and Solid Cable —
                all supporting SQLite as a backend. If 37signals trusts SQLite for production infrastructure, it's good enough for your app.
            </p>
        </section>

        <!-- Single Binary -->
        <section class="section-divide py-16">
            <h2 class="text-xl font-bold tracking-tight mb-4">Single Binary, Complete System</h2>
            <p class="text-body leading-relaxed mb-4">
                A Congo application compiles to one file. That file contains your business logic, your HTML templates,
                your static assets, your database driver, and your migration logic. Deployment is
                <code class="text-accent">scp</code> and <code class="text-accent">./app</code>. Or use
                <code class="text-accent"><a href="/framework" class="text-accent hover:text-bright transition-colors" hx-boost="true">congo launch</a></code> to handle Docker and infrastructure automatically.
            </p>
            <p class="text-body leading-relaxed mb-4">
                No runtime to install. No package manager. No node_modules. No virtualenv.
                Go compiles everything into a static binary that runs on any Linux server. Your deployment target
                needs nothing installed except an operating system.
            </p>
            <div class="quote-bar">
                <p>Your entire application is one file. Copy it to a server and run it. That's deployment. That's the whole ops story.</p>
            </div>
        </section>

        <!-- Summary -->
        <section class="section-divide py-16">
            <p class="text-body leading-relaxed">
                Every choice above follows from the same axiom: everything is a number, software is data
                and processing, and the tools should make that visible rather than hiding it behind ideology.
                Vendor the source so you can see the structs and functions. Use SQLite so data is a file,
                not a service. Render HTML on the server so the processing happens where you control it.
                Compile to a single binary so the deployment unit matches the mental model. Distribute
                virally so no platform owns the chain.
            </p>
            <p class="text-body leading-relaxed mt-4">
                Congo isn't trying to be the most popular framework. It's trying to be the most honest one.
            </p>
        </section>

    </div>

    <div class="text-center">
        <a href="/framework" class="btn-glow btn-glow-primary" hx-boost="true">Start building</a>
    </div>

    <div class="mt-16 max-w-md mx-auto">
        {{template "mailing-list.html" .}}
    </div>
</div>
{{end}}