Our own Go framework: how shanraq.org is built
This site runs on no off-the-shelf CMS and no web framework. Publishing, classifieds, advertising, translation, mailing and payments are assembled from modules written here, in Go.
The numbers
43,031 lines of code and 16,581 lines of tests across 271 files. 14 modules, 143 database migrations, 36 templates.
And 589 lines of JavaScript in two files, for the whole site. No React, no Vue, no bundler. The styling is 3,478 lines of our own CSS, with no Bootstrap and no Tailwind. The page you are reading loads no third-party script at all.
What a module is
The core is a one-method interface:
type Module interface {
Name() string
}
A module then adds what it needs: Routes if it has pages, Init if it must prepare something before the server starts, Start if it holds a background worker. The application collects the modules into a list and hands each of them a Runtime at boot: configuration, logger, database pool and router.
From that follows the rule that keeps the system whole: a module knows nothing of its neighbours. Articles do not import classifieds, classifieds do not import mail. Removing a module means removing one line of registration.
There are fourteen today: ai, apikeys, articles, auth, health, jobs, media, migrations, notifier, ratings, sms, syndicate, telemetry, webui.
What was taken ready-made
The list is given in full, because “our own framework” often means “ours on top of someone else’s” — which is perfectly fine for exactly as long as it is said plainly.
Routing is chi. Postgres is pgx. Logs are zap. Configuration is viper. Migrations are goose. Markdown is goldmark. Metrics and tracing are Prometheus and OpenTelemetry.
What is absent: an ORM. Every database query is written in SQL and sits beside the code that runs it.
Why almost no JavaScript
People read this site on phones, often on a slow connection and in district towns. A page that first downloads a framework in order to draw text afterwards simply does not open there.
So the HTML is assembled on the server and arrives finished. JavaScript stays only where nothing else will do: the carousel, the precipitation map, the language switch. Choosing a place in the weather forecast is an ordinary form, without a single line of script.
Three languages
Kazakh, Russian and English are not a translated interface but three equal versions of the content. An article has three texts, each with its own address and its own canonical. Machine translation exists, but it proposes and the author decides.
The code is open: https://github.com/DauletBai/shanraq.org