Onboarding a new engineer
AI-generated content
This document was generated by an AI assistant. Verify accuracy before relying on the details.
ADB is mid-migration: a Java/Spring backend that is still partly in production, a Cloudflare Workers rewrite of that same backend (services/*, packages/*) that is replacing it service by service, and two frontends (adb-ui Angular in prod, apps/ui React being built to replace it). The original team is gone; one engineer (Yevhenii Khudolii) holds the domain knowledge and is the sole editor of MODULE_OWNERSHIP.md. A new engineer's first weeks are governed by that file and by BUG_FIX_PLAYBOOK.md's trust ladder, not by seniority — everyone starts at Phase 0 on a module they don't already own.
At a glance
| Read before anything else | CLAUDE.md, MODULE_OWNERSHIP.md, AGENTS.md |
| Local stack (CF port) | .claude/scripts/start-stack.sh — 3 layers, containers → pnpm dev Workers (:8787) → apps/ui React (:4200) |
| Local stack (legacy JVM) | Run the local stack — make up from infra/, Docker only |
| Trust model | BUG_FIX_PLAYBOOK.md Phases 0 (spectator) → 4 (refactor), per module |
| Off-limits without explicit approval | adb-accounting, adb-contracts (both JVM and Cloudflare sides) |
| Recommended starting modules | adb-parts, adb-persons (per MODULE_OWNERSHIP.md's own recommendation) |
| Duration | ~10–12 weeks part-time to reach Phase 2–3 on one module and ship one cross-stack change unaided |
| Branching | feature branch, PR opened draft, marked ready only when the branch is (CLAUDE.md §3b) — never git push --no-verify |
Details
Day 0 — before opening an editor
- Read
CLAUDE.mdin full — it is the authoritative map of the repo and overrides anything in this wiki that disagrees with it (see Open questions below: two wiki pages currently do). - Read
MODULE_OWNERSHIP.md— the single source of truth for what is 🟢 known, 🟡 partially known, 🔴 black box or ⛔ frozen. Check a module's level here before touching it, always. - Skim
ENGINEER_INTAKE_FORM.md— it is the form Yevhenii filled in on 2026-05-04 and is the source for most ofMODULE_OWNERSHIP.md's notes. Reading it fills in the "why" behind the levels. - Read
AGENTS.md— every engineer here maintains this wiki as part of normal work, not as a separate documentation task. - Prerequisites: Docker Desktop (≥ 8 GB RAM allocated), Node per
.nvmrc,pnpmvia Corepack, Java 18 + Maven 3.6+ for the legacy services, and AWS credentials with read access tos3://maven-repo.life-connect.fr/(the Maven build depends on it — seeCLAUDE.md's foot-guns).
Week 1 — the map, and a stack that actually runs
- The repo holds three coexisting worlds, and mixing them up is the most common early mistake:
- The JVM services (
adb-accounting,adb-contracts,adb-parts,adb-persons,adb-files,adb-utilities,adb-aggregates,adb-views,adb-reports) — Spring Boot 3.1 / Java 18, still the production backend for some paths. - The Cloudflare Workers port (
services/*+packages/*) — a service-by-service rewrite of the same backend on Workers, fronted byapps/api(the one public gateway) and backed byservices/mongo-broker(a single pooledMongoClientbehind one multiplexed WebSocket per request — there is no per-isolate connection pool, unlike a SpringDataSource). - Two frontends:
adb-ui(legacy Angular, the one in production) andapps/ui(the React BFF replacing it, strict server-side rendering, not yet in production).adb-ui-legacyis the Angular app's own entry Worker on the Cloudflare side.
- The JVM services (
- Run the full local stack for the Cloudflare port, in order —
.claude/scripts/start-stack.sh(or theadbentry in.claude/launch.json). Its header comment is the best single description of the local architecture in the repo: containers first, thenpnpm dev(16+wrangler devprocesses, ~70 s cold), thenapps/uion:4200— only once:8787/env.jsanswers. Starting the front alone is the classic mistake: every loader 502s on a Service Binding that resolves to nothing, and it looks like a crash. - For the legacy JVM path (still needed for
adb-accounting/adb-contractswork), follow Run the local stack instead — Docker-only, no host Java/Maven required. - Deliverable: a logged-in
dev/devsession on:4200showing real seeded data, on both paths, plus your own one-page sketch of what process binds to what — checked against the code. Architecture overview now reflects both worlds; treat it as a starting map to verify, not as ground truth to copy.
Week 2 — one request, end to end
- Pick a read you can trace completely:
GET /parts/:idis a good choice (small, no accounting rules, both a JVM and a Cloudflare implementation exist). - Trace the Cloudflare path hop by hop with the stack running: browser →
apps/uiroute loader →app/server/api/parts.server.js→ Service Binding →services/parts/src/worker.ts(requireAuth→partAuthz→withData) → a route insrc/routes/parts.mjs→@adb/data/parts→ one WebSocket →services/mongo-broker→ Atlas → back up throughpartDto.mjs→ the loader's envelope → the hydrated TanStack Query cache → the screen. - Read
packages/data/README.mdandservices/mongo-broker/README.mdfor the protocol behind that one WebSocket hop. - Deliverable: an annotated trace with a real log line or probe at every hop.
Weeks 3–5 — pick a home-base module, enter the trust ladder
- Read
BUG_FIX_PLAYBOOK.mdin full — this is the actual process, not a formality. Phase 0 (read-only investigation, no commits) is mandatory before Phase 1 (a draft PR containing only a failing test) and Phase 2 (a ≤50 LOC fix with a regression test, in a 🟡 module). - Start in
adb-partsoradb-persons—MODULE_OWNERSHIP.md's own recommendation for early agent and engineer work. Never open a PR againstadb-accountingoradb-contractswithout explicit approval, on either the JVM or the Cloudflare side. - The Cloudflare port left ~171
// TODO(parity:)stubs acrossservices/*(count them:grep -rn "TODO(parity" services | wc -l) — routes that are wired but whose original JVM business logic was not yet ported. Picking one inpartsorpersonsis a good Phase-2 first change: read the Java original, write down the rule, port it, test it. - Deliverable: one Phase-1 PR (failing test only) and one Phase-2 PR (fix + regression test), both draft-then-ready per
CLAUDE.md§3b.
Week 6 — the data layer and its real traps
- Shape:
packages/data/client/(the WebSocket transport) and one folder per service underpackages/data/<service>/<collection>/{queries,mutations}.mjs. - Known traps worth reproducing rather than just reading about:
- embedded
id/targetIdreferences are stored as ObjectId, not string, in several places where code comments claim otherwise — probe the stored document before trusting a filter; - a degenerate upsert filter (a dropped
_id) turns areplaceOneinto a silent overwrite of an existing document; - Atlas
$searchdoes not work against a plain localmongod— see Local Atlas Search ($search), current and accurate; - the WebSocket bridge's
save()returns Mongo's write-result metadata, not the mutated document, unlike the real driver.
- embedded
- Deliverable: a small probe script dumping the real stored type of a document you pick.
Week 7 — tests and the quality gates
- Three suites, one budgeted test command per scope (
CLAUDE.md's test table): a single vitest file, one workspace member, orpnpm test(turbo, cached) — neverpnpm test:all/pnpm -r testfrom a worktree. - Read Test coverage — unit gates and capability coverage, Cross-service BDD scenario catalog, and Staging end-to-end suite (Playwright) — these three are generated/measured pages and stayed current through the migration.
- Learn why a green
turbo run testcan be a cache replay (turbo.json's declared inputs miss things like a live service or the clock) — usepnpm test:allwhen you need the cold truth. - Deliverable: the full 8-step
BUG_FIX_PLAYBOOK.mdcycle on one real bug, start to finish.
Weeks 8–9 — the front end as a consumer of the API
- Read
apps/ui/CLAUDE.mdfirst, especially "the one rule: STRICT BFF" — no browser request ever names a backend service directly; only SSR page loaders,/api/*(the RPC manifest allowlist) and/auth/*.adb-uiandapps/ui-legacyare read-only references for visual parity, never edited. - Read the ADRs under
apps/ui/docs/adr/for the why behind SSR-from-day-one, the isolated@adb/ui-kitcomponent kit + Storybook, and the single-integration-PR agent orchestration model. - Use the repo's own porting skill (
.claude/skills/adb-ui-port/SKILL.md) to port one small Angular screen end to end, with its Storybook story and a parity screenshot. - Deliverable: one small screen ported, reviewed against the parity harness in
apps/ui/e2e/.
Week 10 — auth, session, and the public API
packages/auth(Keycloak JWT verification, the scope-by-role matrix) and the Worker-side OIDC flow + Session Durable Object inapps/ui/workers/session.js.apps/api— the one publicly reachable Worker, its two tiers keyed on the token'sazp, and whyextranet(a different Cloudflare organisation) cannot use a Service Binding.- The wiki's Authentication page covers the Keycloak/JWT fundamentals, which are still accurate; it predates
apps/api's two-tier model, so treat that part as a raw- source read (apps/api/README.md) rather than the wiki. - Deliverable: given a raw 401/403/502 from any layer, name the file that produced it.
Week 11 — infrastructure and environments
- Cloudflare side:
wrangler.jsoncper member,env/*.vars,tools/cf-config(the one resolver Pulumi and Workers Builds both call), and theinfra-cf/Pulumi stacks (staging,production,access,sentry,cdc). Alwayspnpm infra:previewbeforepnpm infra:up. - Legacy side:
infra/(Docker Compose for local, Terraform-ish for AWS + Atlas). - Deliverable: ship a harmless change to staging, verify it live, triage one real Sentry issue to a file and a line.
Week 12 — working with agents, and graduation
- This repo runs a large share of its work through Claude Code agents and skills (
.claude/agents/*,.claude/skills/*). Learn to write a brief an agent cannot misread (exact input, exact output, a verifiable success criterion) and to review agent output adversarially — a review's finding is measured, its prescription is often reconstructed from memory; "no-op" or "unreachable" is a hypothesis until it is probed live. Hunting for a lost business rule, not a lint error, is the job. - Graduation: take a real cross-stack ticket (React screen → BFF loader → service Worker →
@adb/data→ mongo-broker → Atlas, or the JVM equivalent) unaided, end to end, and update your own row inMODULE_OWNERSHIP.mdtogether with Yevhenii once a module moves from 🟡 to 🟢.
Open questions
— fixed: Architecture overview now documents the real Cloudflare port (architecture/overview.mdandservices/adb-web.mddescribedadb-webas the current Cloudflare frontendservices/*,packages/*,apps/ui,apps/ui-legacy,apps/api,infra-cf/) and adb-web is rewritten to explain that it is a separate, frozen, 🔴 black-box scaffold — read it once so you recognise it and don't mistake it for the real port if you stumble on the directory.- Whether the Cloudflare port has actually taken over any part of the production path, or is still staging-only, is genuinely unresolved (see Architecture overview's own Open questions) — confirm with the engineer rather than assuming either way.
- Root
CLAUDE.mddescribes hosting as "Azure (West Europe), Kubernetes"; the wiki's ECS Fargate deploy lifecycle and Current AWS state describe AWS ECS Fargate. Not reconciled here; worth a dedicated ingest pass comparing both against whatever actually serves production traffic today. index.md's service catalog and directory tree predateservices/,apps/,packages/andinfra-cf/entirely. Until it is refreshed, useCLAUDE.md's module table andls services apps packagesas the map of what currently exists, not the wiki's own tree diagram.