Ideas That Sound Weird Until They Don't
Congo makes choices most frameworks avoid. They follow from one idea about what software is.
Don't Pretend the Machine Is Something It Isn't
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.
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:
user.Save() is Save(user).
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.
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.
Declare the What, Encode the How
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 for loop.
Declarative code is an encoding of composition — structures made of structures, ultimately defined by imperative parts. Go has a pattern for this: the functional options pattern. 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:
router.Listen(
router.WithLogger(),
security.New(security.WithNonce(), security.WithHeaders()),
application.New(views,
application.WithController(controllers.Home()),
application.WithController(controllers.API()),
),
)
One declaration. router.Listen composes the middleware chain,
starts the server, handles TLS. application.New takes
functional options — each WithController,
WithValue, WithFunc
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.
Applications Are Configurations
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.
Congo makes this explicit. A project has three declarations — three files that describe three scopes of the same system:
// 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))
// main.go — what the system does
router.Listen(
router.WithLogger(),
security.New(security.WithNonce(), security.WithHeaders()),
application.New(views,
application.WithController(controllers.Home()),
),
)
// infra/main.go — where the system lives
hosting.Launch(digitalocean.New(token),
hosting.Server("production",
hosting.WithService("web", hosting.WithBuild("./cmd/web")),
),
)
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.
Vendored Source, Not Dependencies
When you run congo init, the entire framework is copied into your project
as regular Go files in internal/. 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.
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.
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 seven packages that make up the framework.
Bring Your Own Basket
Run congo source 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.
This website is a Congo application that serves its own source code, browsable at /source. 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.
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.
Frameworks Should Teach AI About Themselves
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.
Congo takes an active approach. congo claude 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.
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.
Server-Rendered HTML, Not Single-Page Apps
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.
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.
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.
SQLite in Production
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.
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 LibSQL embedded replicas by Turso — local SQLite synced from a remote primary. See how it works in the guide.
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 Collection[T]
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.
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.
Single Binary, Complete System
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
scp and ./app. Or use
congo launch to handle Docker and infrastructure automatically.
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.
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.
Congo isn't trying to be the most popular framework. It's trying to be the most honest one.
Stay Updated
Get notified about new releases and updates.