Thank you for investing time in TeachLink. This document is the single source of truth for how code moves from idea to production. Read it fully before opening your first PR.
- Code of Conduct
- Before You Start
- Branch Strategy
- Branch Protection Rules
- Setting Up Locally
- Development Workflow
- Commit Message Convention
- Pull Request Requirements
- PR Review Policy
- CI Pipeline
- Merging
- After Merge
- Getting Help
All contributors are expected to be respectful, constructive, and professional. Discriminatory language, personal attacks, or dismissive comments will result in removal from the project.
Every change — no matter how small — must be linked to a GitHub Issue. This keeps the project's backlog accurate and ensures contributors are not duplicating work.
Steps before writing a single line of code:
- Search open issues to confirm the problem or feature isn't already being worked on.
- If no issue exists, open one and describe the problem, the proposed fix, and the modules affected.
- Comment on the issue to claim it. A maintainer will assign it to you.
- Only then create a branch and start coding.
PRs without a linked issue will not be reviewed and will be closed.
main ← production-ready code only; protected
develop ← integration branch; all feature PRs target here
feature/* ← new features and enhancements
fix/* ← bug fixes
hotfix/* ← urgent production fixes that target main directly
chore/* ← non-functional changes (deps, docs, config)
feature/issue-42-wallet-auth-refresh
fix/issue-88-notification-race-condition
hotfix/issue-101-dao-vote-overflow
chore/issue-55-update-nestjs-deps
Rules:
- Always prefix with the issue number:
issue-<N>-<short-slug> - Use kebab-case, all lowercase, no spaces
- Keep slugs under 40 characters
- Never commit directly to
mainordevelop— branch protection will block it
The rules below are enforced by GitHub Branch Protection on both main and develop. They cannot be bypassed by any contributor, including repository admins.
| Rule | Setting |
|---|---|
| Direct push | ❌ Disabled for everyone including admins |
| Require PR | ✅ Required |
| Required approvals | 2 approvals from project maintainers |
| Dismiss stale reviews | ✅ Enabled — new commits reset existing approvals |
| Require review from code owners | ✅ Enabled (see CODEOWNERS) |
| Required status checks | ✅ Must pass before merge |
| Require branches to be up to date | ✅ Branch must be current with main before merge |
| Require conversation resolution | ✅ All PR comments must be resolved |
| Restrict merge methods | ✅ Squash merge only |
| Allow force push | ❌ Disabled |
| Allow deletions | ❌ Disabled |
| Rule | Setting |
|---|---|
| Direct push | ❌ Disabled for everyone including admins |
| Require PR | ✅ Required |
| Required approvals | 1 approval from any team member |
| Dismiss stale reviews | ✅ Enabled |
| Required status checks | ✅ Must pass before merge |
| Require branches to be up to date | ✅ Branch must be current with develop before merge |
| Require conversation resolution | ✅ All PR comments must be resolved |
| Restrict merge methods | ✅ Squash merge only |
| Allow force push | ❌ Disabled |
| Allow deletions | ❌ Disabled |
All of the following GitHub Actions jobs must report success before any merge button is enabled. These correspond to the jobs defined in .github/workflows/ci.yml:
CI Passed ← aggregate gate (set this as the sole required check)
The CI Passed job only passes when all upstream jobs succeed:
- ESLint (
--max-warnings 0) - Prettier format check
- TypeScript type check (
tsc --noEmit) - NestJS build
- Unit tests (Jest, coverage ≥ 70 %)
- E2E tests (Supertest against Postgres + Redis)
| Tool | Minimum version |
|---|---|
| Node.js | 20 LTS |
| npm | 10+ |
| PostgreSQL | 15+ |
| Redis | 7+ |
| Docker (optional) | 24+ |
git clone https://github.com/<org>/teachlink-backend.git
cd teachlink-backend
npm ci
cp .env.example .env
# Fill in .env values before startingnpm run lint:ci # ESLint — zero warnings allowed
npm run format:check # Prettier — must pass with no changes
npm run typecheck # TypeScript — zero type errors
npm run test:ci # Unit tests + coverageAll four commands must exit with code 0 before a PR will be accepted.
1. Claim issue on GitHub
2. git checkout develop && git pull origin develop
3. git checkout -b feature/issue-<N>-<slug>
4. Write code + tests
5. npm run lint:ci && npm run format:check && npm run typecheck
6. npm run test:ci
7. git add . && git commit -m "feat(module): short description (#<N>)"
8. git push origin feature/issue-<N>-<slug>
9. Open PR on GitHub targeting `develop`
10. Fill in the PR template completely
11. Wait for CI to pass and reviewers to approve
12. Address all review comments
13. Maintainer squash-merges
This project follows Conventional Commits.
<type>(<scope>): <short description> (#<issue-number>)
[optional body]
[optional footer]
| Type | When to use |
|---|---|
feat |
New feature or endpoint |
fix |
Bug fix |
refactor |
Code change that neither fixes a bug nor adds a feature |
test |
Adding or updating tests |
chore |
Build process, dependency, or tooling changes |
docs |
Documentation only |
perf |
Performance improvement |
ci |
CI/CD configuration |
auth, users, courses, payments, notifications, messaging, gamification, assessment, search, caching, queues, security, health, common, config, ci
feat(auth): add wallet-based JWT refresh endpoint (#42)
fix(notifications): resolve WebSocket race condition on reconnect (#88)
test(payments): add stripe webhook handler unit tests (#95)
chore(deps): upgrade NestJS to v11 (#55)
ci: add coverage threshold enforcement to pipeline (#12)
- Subject line maximum 72 characters
- Use imperative mood — "add endpoint", not "added endpoint" or "adds endpoint"
- Always include the issue number in parentheses at the end of the subject line
- Do not end the subject line with a period
Every PR must satisfy all of the following before a reviewer will read the code:
The GitHub pull request template (.github/pull_request_template.md) is required for all contributors.
Do not delete sections or checklist items. Unchecked items without a clear explanation in the PR description are treated as merge blockers.
Key items the template enforces:
-
Linked issue
- This PR closes
#<issue-number>usingCloses #<N>/Fixes #<N>/Resolves #<N>
- This PR closes
-
Branch & commits
- Branch is up to date with the target branch (
developormain) - Branch name follows the convention:
feature/issue-<N>-<slug>/fix/issue-<N>-<slug>/hotfix/issue-<N>-<slug> - All commits and the PR title follow the Conventional Commits format and include the issue number
- Branch is up to date with the target branch (
-
Code quality & tests
-
npm run lint:cipasses with zero warnings -
npm run format:checkpasses -
npm run typecheckpasses with zero errors -
npm run test:cipasses and coverage threshold is maintained - All new code paths have appropriate unit and/or e2e tests
-
-
Error handling & NestJS best practices
- DTOs use
class-validator/class-transformerand are wired through NestJS validation pipes - Inputs are validated at controller/module boundaries (no unvalidated raw payloads reaching services)
- Proper NestJS HTTP exceptions are thrown instead of generic
Error - Logging uses the shared logger with meaningful, structured messages
- Guards (auth/role/permission or custom) protect all endpoints that require authentication/authorization
- DTOs use
-
Documentation / Swagger
- Swagger / OpenAPI decorators are present and updated for all new/changed endpoints, including error responses
- The author has verified locally that Swagger (e.g.
/api) reflects the changes -
README.mdorCONTRIBUTING.mdis updated if contributor workflow or API behaviour changes
-
Breaking changes
- Any breaking API change is explicitly called out in the dedicated template section
- The linked issue is flagged with an appropriate label (for example,
breaking-change)
In addition, the template includes a Test evidence section where authors must paste the exact commands run and any relevant manual/API verification steps.
PRs without concrete test evidence will be asked for changes and are not eligible for approval.
feat(auth): add wallet refresh token rotation (#42)
^ ^ ^ ^
type scope description issue
The PR title must match the same Conventional Commits format as individual commit messages.
The PR description must contain one of:
Closes #<N>
Fixes #<N>
Resolves #<N>
GitHub will automatically close the linked issue when the PR is merged. PRs that do not contain this line will be rejected during review.
A CODEOWNERS file at the repository root maps module directories to their responsible maintainers. GitHub automatically requests review from the relevant code owner when a PR touches their module.
# CODEOWNERS
* @teachlink/backend-maintainers
src/auth/ @teachlink/auth-team
src/payments/ @teachlink/payments-team
src/web3/ @teachlink/web3-team
.github/ @teachlink/devops-team
| Branch target | Required approvals | SLA |
|---|---|---|
develop |
1 | 2 business days |
main |
2 (including 1 code owner) | 3 business days |
Reviewers are expected to check:
- Correctness of logic — does the code do what the issue asked for?
- Security — no secrets in code, input validation present, auth guards applied
- Test coverage — all new paths have tests
- API contract — no silent breaking changes to existing endpoints
- Error handling — DTO validation, proper NestJS HTTP exceptions, logging via the shared logger, guards/filters applied consistently
- Module boundaries — NestJS dependency injection used correctly
- Performance — no N+1 queries, no unbounded loops on collections from DB
- Respond to every comment, even if just to acknowledge
- Do not resolve reviewer threads yourself — the reviewer resolves after they are satisfied
- Do not force-push during review — add commits, so reviewers can see what changed
- If a suggestion is rejected, explain why in the thread
Pushing new commits to a PR under review automatically invalidates all existing approvals. The reviewer must re-approve after reviewing the new commits.
Defined in .github/workflows/ci.yml. Full documentation in README.md.
| Job | Description | Blocks merge |
|---|---|---|
install |
npm ci with cache |
Yes |
lint |
ESLint zero-warning check | Yes |
format |
Prettier check (no rewrite) | Yes |
typecheck |
tsc --noEmit |
Yes |
build |
nest build |
Yes |
unit-tests |
Jest + coverage ≥ 70% | Yes |
e2e-tests |
Supertest + Postgres + Redis | Yes |
ci-success |
Aggregate gate | Yes |
# Lint failure
npm run lint:ci
# Format failure
npm run format:check
# Type error
npm run typecheck
# Unit test / coverage failure
npm run test:ci
# E2E failure (requires local Postgres + Redis)
npm run test:e2eIf CI fails on your PR, fix the issue locally, confirm it passes, then push. Do not ask reviewers to approve while CI is red.
Only repository maintainers with the Maintainer GitHub role can merge PRs. Contributors cannot self-merge.
Squash and merge only. This produces a single, clean commit per feature on the target branch. The squash commit message must follow the Conventional Commits format and include the issue reference.
All of the following must be true simultaneously:
- ✅
CI Passedstatus check is green - ✅ Required approvals are present (1 for
develop, 2 formain) - ✅ No unresolved review conversations
- ✅ Branch is up to date with the target branch
- ✅ PR description contains
Closes #<N>(or equivalent)
If any condition is not met, the merge button is disabled by branch protection.
Hotfixes bypass develop and target main directly:
git checkout main && git pull origin main
git checkout -b hotfix/issue-<N>-<slug>
# fix the issue
git push origin hotfix/issue-<N>-<slug>
# Open PR targeting main — requires 2 approvals and CI passingAfter the hotfix merges to main, immediately back-merge to develop:
git checkout develop && git pull origin develop
git merge --no-ff main
git push origin develop- The linked GitHub Issue is automatically closed by the
Closes #Nreference. - Delete your feature branch — GitHub will prompt you on the merged PR page.
- If your change affects any documented API endpoint, update the Swagger annotations and re-verify
/apirenders correctly. - Monitor the deployment pipeline for any unexpected failures post-merge.
| Channel | Purpose |
|---|---|
| GitHub Issues | Feature requests, bug reports, task tracking |
| GitHub Discussions | Architecture questions, RFC proposals |
| Telegram | Community support, quick questions |
For security vulnerabilities, do not open a public issue. Email the maintainers directly with full reproduction details.