Coding Standards
TypeScript, React, styling, backend, and testing conventions for the VeriWorkly codebase.
Coding Standards
Consistency is what keeps a six-application monorepo maintainable. These are the conventions the codebase actually follows.
TypeScript
- Strict mode is mandatory. Avoid
any— useunknownplus narrowing, or a precise interface. - Validate every external input with Zod: API request payloads, imported documents,
environment configuration, and anything read back out of
localStorage. - File naming —
kebab-casefor modules (resume-store.ts,document-sync-service.ts),PascalCasefor React component files (Button.tsx,SectionVisibilitySettings.tsx). - Interfaces vs types — prefer
interfacefor object shapes that form a public API, andtypefor unions, intersections, and derived types. - Exhaustiveness — when switching over a union, add an
assertNeverdefault case. The export dispatcher does this for both document type and export format, which turns "someone added a format and forgot a branch" into a compile error.
React and frontend
- Functional components with hooks. No class components.
- Memoise the hot paths. The resume editor re-renders a full paged preview on every keystroke —
use
React.memo,useMemo, anduseCallbackin the sidebar, content panel, and preview. - State management:
- Zustand for shared application state (the active document, the session user).
- Native React state for local, ephemeral UI state.
- There is no form library in the repository — forms use controlled React state and Zod validation. Do not introduce React Hook Form or Formik without a discussion first.
- Persistence is explicit. Zustand stores do not use persistence middleware; they expose
saveToStorage/hydrateFromStorageactions that call a dedicated service module. Keep it that way — implicit writes on every state change caused real bugs previously. - Extract logic into custom hooks rather than growing component bodies.
Styling (Tailwind CSS 4)
- Utility-first. Avoid custom CSS files unless genuinely necessary (print media queries, complex keyframes).
- Use design tokens, never hardcoded hex values. Colours, spacing, and fonts come from
@veriworkly/ui. A hardcoded#2563ebwill not respond to the dark theme;var(--accent)will. - Tailwind 4 has no JS config. The theme is defined in
packages/ui/src/styles/themes.cssvia@theme. Do not add atailwind.config.ts. - Mobile-first responsive design, working from 320px up.
- Verify both themes. Every change must be checked in light and dark mode.
Accessibility
The project targets WCAG 2.2 Level AA:
- Full keyboard operability and visible focus states.
- Readable contrast in both themes.
- Status conveyed by more than colour alone.
prefers-reduced-motionhonoured for animation.- Icon-only controls carry an accessible label (
aria-labelorsr-onlytext).
Studio's internal audit still flags real gaps against this target — missing aria-expanded /
aria-controls on some accordions, icon-only social links without a text fallback, and animation
without reduced-motion handling. Treat it as an open punch list: fixing one while you are nearby is
a welcome contribution.
Backend and API
- Thin controllers. Controllers parse and validate the request, then delegate. Business logic
belongs in
apps/server/src/services/. - Centralised errors. Throw
ApiErrorwith a status and message; let the shared error middleware format the response. Do not hand-roll error JSON per route. - Consistent responses. Use
createSuccessResponse/createErrorResponseso every endpoint returns the same envelope. - Prisma discipline. Use
selectandincludeto avoid over-fetching. Prefer atomicupdateManywith a constrainingwhereclause over read-then-write, which is how the import quota and credit balance mutations avoid races. - Scope every route. Any route reachable by an API key must declare
requireApiKeyScopes(...). - Idempotency for external events. Webhooks are keyed by the provider's event ID; background flush jobs write batch markers.
Testing
- Unit tests for pure utilities and service-layer business logic, with Vitest.
- Integration tests for critical paths — auth middleware, billing webhooks, quota enforcement, and view counting — also with Vitest.
- Contract tests in Studio and the marketing site verify that shared content models and template
catalogs stay in sync between the frontend types and the backend validators. Portfolio has its own
suite under
npm test -w @veriworkly/portfolio. - Parity tests in Studio drive a real Chromium via Playwright to lay a document out in both renderers and diff the two box trees. This is the only suite that proves the live preview and the exported PDF actually agree — the contract tests can only prove the PDF is internally consistent against the shared scale.
npm test -w @veriworkly/server # backend suite
npm run test:contracts -w @veriworkly/studio # studio contract tests (fast)
npm run test:browser -w @veriworkly/studio # studio component browser tests
npm run test:contracts -w @veriworkly/site # marketing-site contract tests
npm test -w @veriworkly/portfolio # portfolio suite
npm run test:parity -w @veriworkly/studio # preview/PDF parity (boots a browser, minutes)blog-platform and docs-platform define no test scripts — they are content-only.
The parity suite needs a real Chromium
It is deliberately separate from vitest.config.ts because it boots the app and takes minutes.
tests/parity/browser.ts tries an installed Chrome, then Edge, then Playwright's bundled Chromium
— the bundled build is an unsigned download that Windows Application Control blocks on some
machines. Install Google Chrome, or run npx playwright install chromium.
There is no full browser-driven end-to-end suite — the parity harness is a rendering-measurement tool, not a user-journey runner.
Before opening a pull request
npm run lint
npm run format:write
npm test -w @veriworkly/server
npm run test:contracts -w @veriworkly/studio
npm run test:contracts -w @veriworkly/site
npm test -w @veriworkly/portfolio
npm run buildAdd npm run test:parity -w @veriworkly/studio if you touched a resume or cover letter template.
There is no root `npm test`
The repository root defines no test script. Run the workspace-scoped commands above.
See the Contributing Guide for issue claiming, branch naming, and pull request conventions.