Per-project documentation
Notion is the product source of truth: vision, roadmap, decisions, open questions. Nothing gets coded before the plan is validated.
In the repository, five Markdown files live next to the code and are updated at every step. They give the AI the memory it lacks from one session to the next.
- PLAN: goals, features, steps, stack, open decisions.
- ARCHITECTURE: code organisation, choices and their rationale, data flows.
- FUNCTIONS: the key functions, their parameters, where they live.
- COMMANDS: every command of the project, step-by-step release, state of migrations per environment.
- PATTERNS: techniques met along the way, with their benefits and trade-offs.
Division of roles with the AI, solo
I wrote code by hand for years (Node.js, Haxe, Python, Go, SQL) and I read everything the AI produces. On my personal projects, Claude Code does the heavy lifting: the code, the documentation, a review of its own code and static analysis (format, lint, typecheck). I define what is expected, I review, I test with real data and real user journeys, and I validate. The cycle:
- The AI codes, documents, and delivers code that compiles and passes analysis.
- I test locally, with real data.
- I validate or report what is wrong, and we iterate.
- Commit on the AI's branch, only after validation.
Rules designed for a solo developer who is also their own user:
- No AI-written tests unless asked, but testable code: logic in pure functions, injectable dependencies.
- No comments in the code: anything worth explaining goes in the documentation.
- No tooling installed just so the AI can check itself, no cascading multi-agent reviews. Two minutes of real use say more.
- Manual actions happen one step at a time, each with its expected result.
In a team
Alone, I am the only reviewer and the only user. In a team, code has to be read, picked up and maintained by others, so the frame changes:
- Code review: every PR is reviewed by a human before merging, whether written by hand or with AI. Reviews are also how people grow: explain the why, not just the what.
- Shared conventions: style, naming, structure and AI usage written down in the repository and agreed together. Format and lint checked in CI, not by hand.
- Tests: automated tests on business logic and data flows, run in CI, blocking the merge. The "no tests unless asked" rule only applies to my prototypes.
- Comments: the team's convention wins. At the very least, the reason behind a non-obvious choice is written next to the code.
- Knowledge sharing: the PLAN, ARCHITECTURE and COMMANDS files are the base for onboarding. Decisions recorded, pairing on critical topics, nobody is the only one who knows a part of the system.
Git and environments
dev: the working branch. For projects with a dev site (dev.litterbox.redev.frfor instance), a push todevdeploys it, with its own database or schema.main: production. A push deploys automatically.- The AI works on a
claude/<topic>branch that it commits and pushes itself. Merges are mine: PR todev, then PRdev→main. - Never a force push or history rewrite on
devormain. Branches rather than worktrees.
Database and migrations
A single self-hosted Supabase on the VPS, shared between projects: one Postgres database, one schema per project, shared user accounts and storage. RLS enabled on every table exposed to the API: the public key ends up in the bundle, security comes from row-level rules.
Every schema change is two files:
schema.sql: the complete, current state. Used to create a fresh database and to read the model, never replayed on an existing database.migrations/YYYY-MM-DD_topic.sql: the difference only. The only script ever run, once per environment, dev then prod. Additive by default, any deletion is announced.
Full pg_dump every night, kept fourteen days, restorable schema by schema.
Hosting and deployment
An Ubuntu VPS: nginx and certbot in front of everything, Docker Compose for services, a deploy user without sudo. Shared services (Supabase, n8n, nginx blocks) live in a separate infrastructure repository, the reference for every project. Two deployment models, one per repository type:
- Infrastructure (compose, nginx, live data on the server): cloned on the VPS with a read-only GitHub deploy key. Deploying =
git pull --ff-onlythendocker compose up -d, by hand. GitHub can do nothing on the server. - Applications (Vite or Flutter builds, this site): GitHub Actions builds on every push to
mainand sends the result byrsyncto the folder nginx serves. Nothing to install on the VPS, the server never holds the source code.
Each project keeps what is its own in its repository: workflow, nginx block, migrations, .env.example. GitHub secrets use the same names in every repository. A single n8n instance, with [dev] and [prod] workflows per project, versioned in the project's repository and imported by its deployment.
Tracking
Jira for tickets (epics, stories, bugs) and releases, GitHub for code and PRs, Notion for knowledge and decisions. User feedback is collected directly inside the apps.
Tech stack
- Data
- BigQuerydbtTrinoPostgreSQLMySQLSQL ServerMongoDB
- Data visualisation
- SupersetTableauPower BI
- Development
- PythonNode.jsJavaScript / TypeScriptReactFlutter / DartGoHaxePHPC#REST APIs
- Automation and AI
- n8nClaude CodeGeminiHugging Face
- Infrastructure
- UbuntuDocker ComposenginxcertbotSupabaseGitHub ActionsGCP (Cloud Run)
- Organisation
- NotionJiraGitHubSlackClickUp