Rzemiosło · The Craft
What is The CraftLevelsGet startedChapters Search Download PLEN
Level 2 · Technical ~4 min read

In one sentence: Proven default technology choices — so you don’t waste time on decisions settled long ago.

This chapter in plain terms

This chapter gives proven default technology choices, so you don’t waste time on debates settled long ago. It’s not “the one right path” — a sensible starting point you move away from deliberately (and write down why).

The overarching rule: boring, mature technology beats trendy. What counts is the ecosystem, the docs and how easy something is to test — not hype. The most important selection criterion: can it be easily covered by tests from the first line.

Concrete defaults: Python — A popular, readable programming language — The Craft’s default for scripts, data and the back end. A proven default: lots of ready tools and libraries, easy to drive with AI. for scripts, data and the backend; SQLite — The simplest database — the whole thing lives in one file, with no separate server. A great default to start: zero configuration, easy backup (you copy the file). As you grow, you migrate up. to start (one file, zero configuration), and PostgreSQL — A solid, “grown-up” database for bigger apps — runs as a separate server. The default step after SQLite, when you need many concurrent users and advanced features. only at scale; as little JavaScript as possible (server HTML by default); Docker / container — Packaging an app with everything it needs so it runs the same on every computer. Kills “works on my machine” — the same environment locally and on the server. for reproducibility; one VPS — A rented piece of a cloud server “just for you”, where you put an app “live”. Predictable cost and full control. One VPS easily carries a few small projects. server (e.g. Hetzner — A cheap, solid server (hosting) provider you put an app “live” on. The codex’s default server choice — predictable cost and performance without overpaying.) with nginx — A program at the front of the server that takes traffic from the internet and routes it to your app. Handles HTTPS, serves static files, balances traffic — the server’s proven “reception desk”..

An important architecture rule: the Monolith — An app built as one whole (one deploy, one database), not a patchwork of separate services. A simple, cheap default to start — easy to grasp. You split it only when scale really demands. is the default — one application and one database on one server is something you can hold in your head (one deploy, one log, one backup). Splitting into separate services (microservices) adds the network and a whole class of new failures — you reach for them only when scale really demands it.

Example: don’t start with a distributed database “because it’ll grow someday”. Start with SQLite; foresee the Migration (database) — A controlled change to the database layout — adding a column, a table or moving data — step by step. Like a renovation to plan: rebuilding data in a set order so nothing “collapses”. to Postgres in the data model, but do it when concurrent writes actually start to hurt — not sooner.

Read more: Python · when SQLite is enough · “monolith first” (Martin Fowler) · ADR — recording architecture decisions.

08 — Stack and technologies

The golden rule of altitude applied to tool choice: the simplest thing that meets the requirement; complexity is added when a metric forces it (→ 12).

The doctrine is language-agnostic — but the choice of stack decides how cheap a mistake is and how easily you can test it. This chapter is a sensible default for a project run with Claude: universal, simple, flexible, and scalable. Not dogma — a starting point you depart from deliberately (and record why, as an ADR).

Default stack (a reasonable start)

LayerDefaultWhen to change
Backend / scriptsPython — A popular, readable programming language — The Craft’s default for scripts, data and the back end. A proven default: lots of ready tools and libraries, easy to drive with AI. (+ FastAPI for the API — An agreed way for two programs to talk to each other — one asks, the other answers in a set format. Through an API your app connects to outside services (payments, maps, AI). Treat an API key like a password.)Front and back share TypeScript → Node
DatabaseSQLite — The simplest database — the whole thing lives in one file, with no separate server. A great default to start: zero configuration, easy backup (you copy the file). As you grow, you migrate up.Concurrent writes / roles / relations / scale → PostgreSQL — A solid, “grown-up” database for bigger apps — runs as a separate server. The default step after SQLite, when you need many concurrent users and advanced features.
Web / frontServer-rendered HTML + a bit of JSRich, interactive UI → Next.js
APIREST/JSON with an explicit contract (OpenAPI)gRPC only at real need
Cache — A temporarily remembered result, so the same thing isn’t computed again on every request. Speeds the app up, but can be a trap: a stale cache shows old data. / queuenone → add Redis when the traffic profile forces itnot “just in case”
PackagingDocker / container — Packaging an app with everything it needs so it runs the same on every computer. Kills “works on my machine” — the same environment locally and on the server. (reproducibility)trivial script with no dependencies → no container
Hostingone VPS — A rented piece of a cloud server “just for you”, where you put an app “live”. Predictable cost and full control. One VPS easily carries a few small projects. (Hetzner — A cheap, solid server (hosting) provider you put an app “live” on. The codex’s default server choice — predictable cost and performance without overpaying.) + nginx — A program at the front of the server that takes traffic from the internet and routes it to your app. Handles HTTPS, serves static files, balances traffic — the server’s proven “reception desk”.uneven/global traffic → Serverless — A model where you don’t manage a server — the code runs itself when someone needs it, and sleeps when not. You pay per use, it scales itself. The trap: a “cold start” — the first call can be slower. (scale-to-zero)

Overriding rule: boring, mature technology beats trendy. What counts is the ecosystem, the documentation, and testability — not the hype.

Proven toolkit (a concrete baseline)

A toolkit battle-tested on a real project — take what the task requires, skip the rest. Everything here is mature, well-documented, and easy to test and host on a single VPS:

LayerTools
Primary tool / processAnthropic Claude — Claude Desktop / Claude Code (the agent you build with; operate it well → 16) · Git (history, → 05) · GitHub (remote repo, PR) · GitHub Issues (tasks, → 07)
Format / contentHTML5 (server-rendered), Markdown (docs), JSON (API, config), XML when an integration forces it
Data / queriesSQL — The query language for a database — the way you “ask” the database for data or change it. The universal standard for talking to a database. A badly written query can bog down the whole app. — SQLite (WAL — Write-Ahead Log: a database mode where changes go to a journal first, then to the data. Gives safety (recovery after a crash) and better read/write concurrency.) to start → PostgreSQL at scale; forward-only Migration (database) — A controlled change to the database layout — adding a column, a table or moving data — step by step. Like a renovation to plan: rebuilding data in a set order so nothing “collapses”.
Backend / scriptsPython (data, scrapers, migrations on stdlib) · Node.js + Express (web/API) · EJS (server-side templates)
Frontendminimal JS (vanilla), marked for MD rendering; SVG icons: Lucide only (one source of geometry, consistency); self-hosted fonts
Sessions / securitypersistent session store (a separate file, → 14), helmet, rate-limit, bcrypt, OAuth (passport)
MailSMTP (587 STARTTLS), verified sender domain (→ 14)
Media / imageconversion to WebP (Pillow); image comparison (OpenCV) only when truly needed
Ingest / Scraping — Automatically gathering data from websites with a program instead of copying by hand. Powerful for acquiring data, but it needs manners: respect others’ rules (robots.txt), don’t overload the server.requests + BeautifulSoup (+lxml), fuzzy-match (rapidfuzz), Playwright when a browser is needed (→ 14)
LLMAPI client (Claude / Anthropic SDK), responses streamed over SSE (→ 13)
Testspytest (Python) · Jest + supertest (Node) · Playwright (e2e) (→ 03)
Build / devesbuild, nodemon; Docker for reproducibility
Server / opsone VPS (Hetzner) + nginx (TLS, reverse proxy) + pm2/systemd; GA4 analytics

Icon rule: for SVG, stick to one set — Lucide (geometry in a <symbol>/sprite, the rest is style). Mixing icon libraries = inconsistent UI and mismatched weights/strokes.

Python as the default language

  • Why: readability (code like prose — easy to review by a human and an agent), batteries included, one language for API + scripts + data/LLM. Fewer contexts to hold in your head.
  • FastAPI for the API: types + validation (Pydantic) + auto-OpenAPI = a free contract at the front↔back boundary (→ 12). pytest for tests (→ 03).
  • Reproducible env: a virtual environment + pinned deps (lock). “Worked yesterday” goes away.
  • Format and types as a contract: ruff/black, type hints. Consistent style = cheaper review (→ 02).

Database — from simple to scale

  • Start: SQLite. Zero-ops, one file, great for an MVP and an always-on VPS. Don’t start with a distributed database “because it’ll grow someday”.
  • Scale: PostgreSQL — when concurrent writes, roles, complex relations, or extensions start to hurt. Anticipate the SQLite→PG migration in the model from the start (→ 11).
  • Engine-independent rules: forward-only migrations + a backup before each (→ 04), Slug — A readable, short part of a page address that describes its content in words instead of a mysterious number. Better for humans and SEO; a stable slug doesn’t break links when things change. instead of ID in references to user data, Index (database) — A lookup in the database that makes searching instant instead of scanning everything in order. The first move for slow queries — like an index at the back of a book instead of reading 400 pages. after measurement, not on a hunch (→ 13).
  • NoSQL / distributed — only when relational genuinely doesn’t suffice, not before.

Web and API — the contract as the boundary

  • The less JS, the better. Server-rendered HTML by default; SPA/Next.js only when the interaction truly requires it. A light front = faster and cheaper to maintain (→ 13).
  • The API contract (OpenAPI) is the boundary between front and back — it lets you swap one side without touching the other (→ 12). Version it; return structured errors.
  • Streaming — Sending a large response in chunks, as it goes, instead of loading everything into memory at once. The app doesn’t choke on huge data — the user sees a result sooner. / SSE for long responses (chat, LLM token-by-token) — the user sees the effect right away, not emptiness (→ 13).

Docker — reproducibility, not a cult

  • What for: the same image locally / in CI / in prod. “Works on my machine” ceases to exist.
  • Hygiene: a small image (multi-stage, slim base, pinned versions), .dockerignore, a non-root process. docker-compose for local assembly (app + database).
  • Don’t containerize by force a single script. Docker where dependencies hurt — not as a ritual.

Server and hosting — one box, good habits

The Monolith — An app built as one whole (one deploy, one database), not a patchwork of separate services. A simple, cheap default to start — easy to grasp. You split it only when scale really demands. is the default architecture — favor it as the easiest to maintain. One application (web + API + jobs) and one database on one VPS is something you can hold in your head: one deploy, one log, one backup, one Rollback — Reverting a change to the previous, working state — “Ctrl+Z” for a deployment. When a new version breaks production, a rollback restores the previous one in seconds instead of fixing in a panic.. Debugging means reading a single process, not correlating traces across a network. Splitting into services/serverless adds the network, contract versioning, and a whole class of new failures (→ 14) — reach for them only when a metric (traffic, team, failure isolation) genuinely requires it, not “because that’s how it’s done.”

  • Default: one VPS (Hetzner) + nginx (reverse proxy, TLS) + systemd/pm2/Docker for processes. Cheap, predictable, full control, zero cold start. Many projects on one box = a separate vhost + a separate directory, the same deploy rules (→ 05).
  • Scale-to-zero (Cloud Run / serverless) when traffic is uneven/global and the cold start is acceptable; always-on VPS when traffic is predictable. A deliberate cost/latency tradeoff, recorded as an ADR (→ 12).
  • Managed services (database, mail, storage) when they take ops off your plate for less than maintaining it yourself would cost.
  • Secrets in env / a secret store — never in the repo (→ 09).

TDD and change coverage — the hard core

The most important criterion for choosing a stack: it must be testable from the first line. A technology you can’t easily wrap in a test (test-first, a fast and deterministic suite, gating CI) is a bad choice — even if it’s trendy. The mechanics of TDD — Test-Driven Development: first you write a test (what you expect), then the code that satisfies it. The test stays in the project and guards that a change broke nothing — proof instead of “it probably works”. and change coverage = the canon in → 03; here, just the hard consequence: choose tech that makes it possible, and treat “a commit without a test” as incomplete (→ 00).

The technology-choice rule

  1. The simplest thing that meets today’s requirement, with an explicit growth path (SQLite→PG, VPS→serverless).
  2. Testability and ecosystem matter more than novelty.
  3. Every non-trivial choice = an ADR: one “why” + the rejected alternatives. The next session (human or agent) should know why it’s done this way.

Anti-patterns

  • 🚫 Microservices / Kubernetes / a distributed database on an MVP — complexity no one needs yet (→ 12).
  • 🚫 A stack for the CV/the trend instead of for the problem and testability.
  • 🚫 Changing behavior without a test (“I’ll add it later” — you won’t).
  • 🚫 Secrets in the repo; no version pins → “worked yesterday”.
  • 🚫 SPA / heavy JS where server-side HTML would do.
  • 🚫 Docker/Cloud as a cult instead of a tool fit to the problem.

The canonical doctrine is written in English.