A Bug Tracker You Have to Build Yourself
I've published a lot of code over the years, and I have never once learned much from cloning it.
That's the honest reason behind zarlo/bug-tracker-plan: I wanted to teach web application fundamentals, and shipping a finished bug tracker would have been the worst possible way to do it. So the repository contains a plan, some rules, and nothing else. No source code, no docker-compose.yml, no schema. The README is explicit about it planning complete, implementation not started, the repo otherwise empty by design.
Why a bug tracker
Bug trackers are a classic teaching project because they touch almost every fundamental in web application development while still being small enough to hold in your head.
The scope covers:
- Authentication and role-based access control.
- CRUD over nested resources projects contain issues, issues contain comments.
- An immutable audit log recording every state change on an issue.
- Search and filtering, including full-text.
- File uploads and blob storage.
@mentionsand the notifications they trigger.- Third-party OAuth integration with SCM providers.
- Webhooks, deferred to v1.1 so the plan isn't overloaded on day one.
None of that is exotic, and all of it is load-bearing. A small team needs all eight to do real work, which makes the project a fair proxy for the systems people actually build and deploy.
The design principles behind it are deliberately unexciting: small surface area, boring technology where it counts, no hard dependency on any cloud provider, and a preference for the approach that best explains a concept over the one that best optimises it.
Plan first, code never
Everything lives under plan/, and it's numbered so it reads in order the first time and can be jumped into afterwards:
plan/
├── 00-overview.md
├── 01-requirements.md
├── 02-architecture.md
├── 03-data-model.md
├── 04-api-design.md
├── 05-features/
├── 06-tech-stack.md
├── 07-security.md
├── 08-docker-deployment.md
├── 09-testing-strategy.md
├── 10-roadmap.md
└── 11-agent-work-conventions.md
05-features/ breaks the major areas out separately auth and users, projects and issues, comments and history, tags/labels/search, attachments and mentions, and SCM integration because those are the parts where the design decisions actually live.
It's roughly 2,900 lines. It's detailed enough that someone could implement the tracker from it without guessing at anything structural, and it's a plan rather than a spec, so there's plenty of room to disagree with it.
Decisions that don't waffle
A plan that leaves everything open teaches nothing, so some things are locked and the document says so directly:
- No JavaScript or TypeScript backends. Frontend JS is fine.
- PostgreSQL is the required database.
- Docker Compose is the deployment target.
- v1 roles are Admin, Developer, and Reporter/Viewer.
- SCM authentication in v1 is OAuth login via one configured provider.
- SCM commit-linking ("fixes #123", webhooks) is v1.1.
- Multiple instances of the same SCM provider type is v2.
06-tech-stack.md is the exception that proves the rule. It ranks nothing on purpose. Django and FastAPI, Go with sqlc, ASP.NET Core, and Laravel are all presented as a menu, each with a "why" and a "why not" framed around what you'd learn rather than which is better. Django's admin and ORM are excellent and can obscure what's actually happening; Eloquent's active record can hide what SQL is really doing; Go gives you a single binary but no built-in auth or migrations. Pick your own.
The same document includes a table of what to build yourself at least once versus what to use a library for. Sessions and the OAuth flow: do them by hand the first time so the mechanics are visible, then use the framework's. Password hashing, Markdown rendering, and HTML sanitisation: always use a library, and in the sanitisation case never hand-roll it.
AGENTS.md
A repository like this is going to get opened by AI agents. I wrote down how they're expected to behave while they're in it, because the default behaviour of most tools is to be helpful by writing the code and writing the code is exactly what defeats the purpose here.
The rules say: behave as a tutor, not an implementer. Don't produce complete files, function bodies, config blocks, or large snippets someone could paste to a finished result that includes entire Dockerfiles, migrations, and ready-to-paste SQL. When asked how to do something, describe one or more valid approaches with their tradeoffs and let the implementer choose. Explain the underlying idea rather than stopping at "do this." Review existing code by pointing at problems instead of dropping in a corrected version. Ask when intent or constraints are ambiguous.
Small sketches are fine. A function signature, a SQL column name, a config key, a pointer to the right ORM method all acceptable. The file gives a single tiebreaker to use when it's unclear whether an answer has crossed the line: would saying this rob the user of the chance to learn it themselves?
The practical effect is that the repository works whether you bring an agent or not. The plan stands on its own as a document you can read and implement by hand; the agent rules exist so that having one open in the same working directory doesn't short-circuit the exercise.
The work log
There's also an agent-work/ folder where agents keep per-session notes, following the conventions set out in 11-agent-work-conventions.md. One file per session, named with the date and time in UTC, appended to after each substantive interaction rather than written at the end, and never edited once written.
The entries record what was asked and what was pointed to. They contain no agent-generated code. It's a durable record of how the project was worked on, rather than just what came out of it.
Take it
The repository is public and yours to use, fork, or disagree with. If you find a locked decision you think is wrong, the plan is editable amend the relevant file, note it in the roadmap, and don't silently override it. That rule applies to me as much as anyone else.
If you do work through it, I'd like to hear where the plan was wrong. Those gaps are the interesting part.