Skip to content

Templates ​

Pull Request Template ​

markdown
## What

<!-- One or two sentences. What does this change? -->

## Why

<!-- Why is this change needed? Link the Jira work item. End the description with: Closes <SUBTASK-KEY> -->

## How

<!-- Anything non-obvious about the implementation. -->

## Risk

<!-- What could go wrong? How will we know? Rollback plan? -->

## Test Notes

<!-- Which test scenarios (S1, S2, ...) this MR automates, and the test names. Anything that still needs a manual check on staging. -->

## Checklist

- [ ] Docs updated (if public API or behavior changed)
- [ ] Feature flag added (if rollout is risky)
- [ ] Migration is backwards-compatible (if DB change)
- [ ] Migrations re-stamped with `restamp.sh` after the final rebase (if DB change)

Postmortem Template ​

markdown
# Postmortem: <Incident name>

**Date:** YYYY-MM-DD
**Severity:** SEV1 | SEV2 | SEV3
**Duration:** <start time> to <end time> (<total>)
**Author:** <name>
**Status:** Draft | Reviewed

## Summary

<!-- 2–3 sentences. What happened, who was affected, how we resolved it. -->

## Impact

<!-- Quantified: users affected, requests failed, revenue impact, etc. -->

## Timeline (UTC)

- HH:MM — <event>
- HH:MM — <event>

## Root cause

<!-- The actual underlying cause. Often more than one factor. -->

## What went well

<!-- Detection? Response? Communication? -->

## What went poorly

<!-- Where did the system make this hard to handle? -->

## Action items

| Item              | Owner  | Due        | Type       |
| ----------------- | ------ | ---------- | ---------- |
| Add alert for X   | @alice | YYYY-MM-DD | Detection  |
| Make Y idempotent | @bob   | YYYY-MM-DD | Prevention |

On-Call Handoff Template ​

markdown
# On-Call Handoff: <date range>

**Outgoing:** <name> → **Incoming:** <name>

## Open issues

<!-- Anything still on fire or smoldering. -->

## Notable events this week

<!-- Pages, near-misses, unusual traffic. -->

## Watch list

<!-- "Service X has been flaky after deploy Y; keep an eye on the error rate dashboard." -->

## Comp time taken

<!-- Hours of after-hours work; when you'll take it back. -->

Onboarding Checklist (copy per new hire) ​

markdown
# Onboarding: <name>

**Start date:** YYYY-MM-DD
**Buddy:** <name>

## Pre-day-1 (lead)

- [ ] Workspace email created
- [ ] GitLab account created at `gitlab.internal`
- [ ] Jira account added to the team's projects
- [ ] Slack account created and added to team channels
- [ ] Fingerprint registered for door access
- [ ] Buddy assigned
- [ ] First PR ticket picked

## Day 1

- [ ] Welcome chat with lead
- [ ] Buddy walkthrough
- [ ] Dev env running locally
- [ ] Read playbook

## Week 1

- [ ] Ship starter PR
- [ ] 1:1 with each teammate

## Week 2

- [ ] Pick up real ticket (full task workflow)
- [ ] Pair with domain owner

## Month 1

- [ ] Lead a small project end-to-end
- [ ] Give first code review

## Month 2

- [ ] Shadow on-call
- [ ] Co-author or write an ADR
- [ ] Primary on-call (first shift)

## Month 3

- [ ] Domain ownership confirmed
- [ ] Onboarding retro with lead

Jira Work Item Examples ​

Each block is the description of one Jira work item as it looks when it reaches Ready. The first is a complete detailed spec; the others are shorter, but every test scenario still has an ID and an auto or manual tag. The subtasks under each are listed last; engineers create and estimate those themselves.

New Endpoint ​

Type: Story · Epic: User activity feed · Component: activity · Owner: BE dev
Title: Add GET /v1/users/:id/activity endpoint

Goal
The mobile app needs a user activity feed. Today the client stitches it from
three separate calls; one paginated endpoint replaces them.

Flow
1. The app calls GET /v1/users/:id/activity with the user's access token.
2. The API checks the token, then that the caller is :id or an admin.
3. It returns the newest events first, one page at a time.
Failures: no token -> 401; another user's id without admin -> 403;
:id not a UUID -> 400; unknown user -> 404; limit > 100 -> 400.

Endpoints
GET /v1/users/{id}/activity   List a user's events, newest first.
                              Paging offset/limit (default 20, max 100). No filters yet.
                              Returns event type, time and metadata, plus the total count.

Rules
- Only the user themself or an admin can read the activity.
- Metadata never contains tokens or secrets.

Acceptance criteria
AC1. Given my own id, when I call the endpoint, then I get 200 and my events newest first.
AC2. Given offset=20&limit=10, then I get events 21-30 and total is the full count.
AC3. Given another user's id and no admin role, then I get 403.
AC4. Given no Authorization header, then I get 401.
AC5. Given up to 1 000 events, then p95 latency is under 200ms on staging.

Test scenarios
| ID | Scenario                                   | Type   | Test name |
| S1 | Own activity, 3 events, newest first       | auto   | TestActivity_S1_OwnEventsNewestFirst |
| S2 | offset=20&limit=10 returns the right slice | auto   | TestActivity_S2_Pagination |
| S3 | No events -> 200, empty data, total=0      | auto   | TestActivity_S3_Empty |
| S4 | Other user's id without admin -> 403       | auto   | TestActivity_S4_Forbidden |
| S5 | :id not a UUID -> 400; limit=101 -> 400    | auto   | TestActivity_S5_BadInput |
| S6 | Missing Authorization header -> 401        | auto   | TestActivity_S6_NoToken |
| S7 | p95 < 200ms with 1 000 events on staging   | manual | - |

Notes
Depends on the auth service (JWT validation), available. Reads activity_events, no
schema change. Out of scope: filtering by event type, real-time updates.

Open questions
(none)

Subtasks
- BE: endpoint, pagination, auth checks, tests S1-S6
- FE: activity feed screen against the new endpoint

New Datasource / Data Layer ​

Type: Task (label infra) · Epic: In-app notifications · Component: notifications
Title: Add notifications table and repository layer

What
Create a notifications table and a repository that supports insert, mark-as-read,
and paginated fetch by user. This is the persistence layer for the upcoming
in-app notification feature.

Why
In-app notifications are the next feature on the roadmap. Standing up the data
layer first lets the API and worker tickets proceed independently.

Acceptance criteria
- notifications table exists with columns: id, user_id, type, payload (jsonb),
  read_at (nullable), created_at
- Index on (user_id, created_at DESC) for the list query
- Repository exposes: Create, MarkRead, ListByUser(userID, limit, offset)
- Migration runs up and down cleanly on a fresh database
- No raw SQL string interpolation — parameterized queries only

Test scenarios
S1 [auto] Run migration up on empty DB — table and index created with no errors
S2 [auto] Run migration down — table dropped cleanly, no orphan objects
S3 [auto] Create a notification, then ListByUser — record appears in results
S4 [auto] MarkRead sets read_at; a second call is a no-op (idempotent)
S5 [auto] ListByUser with offset beyond total rows — returns empty slice, no error
S6 [auto] Simulate 10 000 rows for one user — query completes under 50ms (EXPLAIN ANALYZE attached)

Dependencies
- None (self-contained data layer; the API Story depends on this one)

Subtasks
- BE: migration (timestamped, re-stamped before merge) and repository with tests

Bug Fix ​

Type: Bug · Priority: High · Component: api
Title: Pagination returns duplicate items when records are inserted mid-page

What
When new records are inserted between paginated requests, offset-based pagination
returns the same item in two consecutive pages.

Why
Users see duplicate entries in the dashboard list view, causing confusion and
support tickets. Reported in #inc-20260508-dup-rows.

Root cause (to be confirmed)
Offset shifts when a new row is inserted before the current page window.
Switching to cursor-based pagination (by created_at + id) will make the
position stable regardless of inserts.

Acceptance criteria
- Fetching page 2 immediately after a new record is inserted on page 1 does not
  return any item already seen on page 1
- Existing offset/limit query params remain accepted (backwards-compatible);
  cursor param is additive
- All existing pagination tests pass

Test scenarios
S1 [auto] Fetch page 1 (limit=10), insert a new record, fetch page 2 — no duplicates
S2 [auto] Fetch all pages sequentially with no inserts — full dataset covered, no duplicates
S3 [auto] cursor param omitted — falls back to offset behaviour (existing callers unaffected)
S4 [auto] Invalid cursor value — returns 400 with a clear error message

Dependencies
- Affects: /v1/companies and /v1/users list endpoints
- No schema changes required

Subtasks
- BE: cursor pagination with offset fallback, regression test that fails before the fix
- FE: send the cursor from the list views

Performance Improvement ​

Type: Task (label tech-debt) · Component: reports
Title: Reduce p95 latency on GET /v1/reports from 1 400ms to under 400ms

What
The reports list endpoint is slow because it runs a COUNT(*) on a 2M-row table
on every request. Add a covering index and cache the count result for 60 seconds.

Why
p95 latency is at 1 400ms (baseline from Grafana, 2026-05-10). The PM dashboard
loads this endpoint on every page open; users are reporting the page feels broken.
Target: p95 < 400ms without changing the response contract.

Acceptance criteria
- p95 latency drops below 400ms under normal load (verified by load test)
- Response is identical to today's response — same fields, same pagination behaviour
- Cache miss (first request or TTL expired) still responds under 600ms
- No regression on related endpoints (GET /v1/reports/:id, POST /v1/reports)

Test scenarios
S1 [manual] Run k6 load test (100 VUs, 2 min) before and after — attach both result summaries to PR
S2 [auto] Cold cache (Redis flushed) — endpoint responds correctly and within 600ms
S3 [auto] Warm cache — response matches cold cache response byte-for-byte (minus timestamps)
S4 [auto] Cache TTL expires mid-test — no errors, stale count is refreshed automatically
S5 [manual] Run EXPLAIN ANALYZE on the query with and without the new index — attach both plans

Dependencies
- Redis (already available in the stack)
- Grafana dashboard for before/after screenshots

Subtasks
- BE: covering index, count cache, load test before and after