Skip to main content

Coding & Design Guidelines

These guidelines keep every service aligned. Keep changes focused. Mention any deviation in your pull request. Raise follow-up issues when you discover gaps.

Contributor checklist

  • Run the formatter and linter for the area you touched. Examples: pnpm run format:webapp, pnpm run lint:server.
  • Smoke-test the critical scripts. pnpm run build:webapp must pass. If you changed migrations, run pnpm run db:generate-erd-docs.
  • Update Chromatic snapshots when you change UI components.
  • Regenerate clients and documentation when you change an API contract.
  • Rerun the quality gates that match your changes: pnpm run check, pnpm run generate:api, or cd server && ./mvnw test -P'!quick'.
  • Document what you verified in the pull request template. Link failing checks to tickets if you must defer.

Webapp (/webapp)

Platform overview

  • React 19 on Vite. Read the pinned versions from webapp/package.json; they move faster than this page.
  • React Compiler is enabled in webapp/vite.shared.ts, which every build of app source pulls in. Leave it on.
  • Routing uses TanStack Router file routes in src/routes/** with the generated routeTree.gen.ts.
  • TanStack Query v5 is initialised in src/integrations/tanstack-query/root-provider.tsx. OpenAPI helpers live in src/api/@tanstack/react-query.gen.ts.
  • Global state lives in Zustand stores under src/stores/**.
  • Tailwind CSS 4 powers styling. Tokens sit in src/styles.css. shadcn/ui primitives live in src/components/ui/**, which is a registry install over Base UI — check a change there against upstream and record a deliberate divergence in the file, because re-vendoring overwrites it.
  • Storybook, Chromatic, Vitest, oxlint, and Biome cover tooling.

Building features

  • Create routes with createFileRoute. Route modules handle auth gating (see src/routes/_authenticated.tsx), loader logic, and orchestration.
  • Keep presentational components in src/components/**. They should take props, stay pure, and avoid side effects.
  • Fetch data by spreading generated ...Options() helpers into useQuery or useMutation. The helpers provide typing, query keys, and retry defaults. A hand-written query key is a lint error (hephaestus/no-manual-query-key), because it stops matching the generated one silently.
  • Invalidate caches with the generated ...QueryKey() helpers or the same keys passed to useQuery. Use optimistic updates only when the UI must respond instantly, such as mentor chat streaming.
  • Subscribe to Zustand stores with selectors (useDocumentsStore((state) => state.documents[id])). Keep store actions pure.
  • Respect React Compiler requirements. No conditional hooks. No prop mutation. Because the compiler memoises for you, useMemo, useCallback and memo cannot be imported from react, and neither can forwardRef — React 19 passes ref as an ordinary prop.
  • Keep render deterministic. Reading the wall clock or the RNG while rendering is a lint error (hephaestus/no-nondeterministic-render); component code takes the time from @/components/common/use-now and stories from @/components/common/story-clock.
  • Stay inside the --color-* token scale declared in src/styles.css. Reach for shadcn/ui primitives before adding new component libraries. @apply lives in styles.css for base and wrapper rules, not in components.
  • Co-locate *.stories.tsx files. Show the states the component can actually reach, with realistic data. Run pnpm --filter webapp run chromatic:ci whenever stories change.
  • Defer analytics work to effects that run after paint.

Quality gates

  • Install dependencies with pnpm install at the repo root so optional binaries resolve.
  • Run pnpm run check:webapp and pnpm run build:webapp before opening a pull request.
  • Execute Vitest suites with pnpm run test:webapp. Add unit tests beside the feature you touched.
  • Keep Storybook stories in sync with design. Launch pnpm --filter webapp run storybook when pairing with design reviews.
  • Validate bundle impact by checking the Vite build stats when you add large libraries.

Spring Boot application server (/server)

Structure

  • Feature packages sit directly under de.tum.cit.aet.hephaestuspractices/, evidence/, agent/, integration/, mentor/, workspace/, leaderboard/ and the rest. Shared wiring sits in config/ and core/. Cross-cutting integration traits (webhook ingress, oauth ingress, SPI, registry) live under integration/ directly. ls the package root rather than trusting a list here.
  • Controllers return DTOs located near the controller. LeaderboardEntryDTO is the reference example.
  • Repositories return Optional<T> instead of null.
  • Services take their collaborators through the constructor, over private final fields; field injection with @Autowired is not used. If a service depends on many collaborators, split the responsibility.
  • Set up loggers with LoggerFactory.getLogger. Use structured log messages.
  • Annotate the smallest unit of write work with @Transactional.

Coding habits

  • Use Java 21 features where they simplify code. Records, pattern matching, and sealed hierarchies improve readability.
  • Keep controllers thin. Push orchestration into services and prefer constructor helpers over static util classes.
  • Expose dedicated DTO mappers in the package instead of sprinkling conversion logic across layers.
  • Treat Optional as a signal for missing data. Avoid returning null from repository methods.

Null safety

  • Add a JSpecify @NullMarked package-info.java to every handwritten Java package.
  • Bare types are non-null. Use @Nullable only when absence is part of the contract. On API DTOs, use explicit @NonNull for components that must be required in OpenAPI.
  • Type-use annotations describe exactly what is nullable: List<@Nullable String> permits null elements; String @Nullable [] permits a null array reference.
  • Fix NullAway errors at the contract or implementation boundary. Suppressions and classes without an explicit null-marking scope fail the quality gate.
  • Run pnpm run check:java-nullness for the policy check and cd server && ./mvnw test-compile -P'!quick' -DskipTests for compiler analysis.

Safe workflows

  • Configuration overrides stay in application-*.yml. Do not commit application-local.yml.
  • Share long-lived infrastructure in tests. Lean on Testcontainers and GitHub fixtures where possible.
  • After updating Liquibase migrations, regenerate the REST client with pnpm run generate:api and rerun pnpm run db:generate-erd-docs.
  • Run the modular test suites with cd server && ./mvnw verify -P'!quick' — without !quick the build skips every test and still reports success. The build uses ArchUnit checks to enforce package boundaries.
  • Prefer integration tests that extend BaseIntegrationTest, or AbstractWorkspaceIntegrationTest for anything workspace-scoped, when you touch persistence logic. See Testing.

Webhook receiver (server/.../integration/core/webhook/)

  • The provider-specific verifiers and subject parsers are Spring-free and live with their provider, under integration/scm/github/webhook/ and integration/scm/gitlab/webhook/; integration/core/webhook/ holds the shared ingress — the controller, the ingest pipeline, the JetStream publisher and its bootstrap. All are exercised by JUnit unit tests plus parameterised tests over the captured fixtures in server/src/test/resources/{github,gitlab}/.
  • HMAC verification: MessageDigest.isEqual (constant-time, length-tolerant); hex via java.util.HexFormat.of() only — enforced by ArchUnit (HexEncodingArchTest). Case-folding uses Locale.ROOT everywhere — enforced by LocaleSafetyArchTest.
  • Spring beans (controllers, JetStreamPublisher, WebhookJetStreamBootstrap, health, lifecycle) gated by RuntimeRole.WEBHOOK_PROPERTY. Production runs them in the dedicated webhook-server container (SPRING_PROFILES_ACTIVE=prod,webhook) for restart independence — see ADR 0008.
  • Configuration is shared with auto-registration: core.webhook.WebhookProperties bound to hephaestus.webhook.*. The webhook packages carry per-package JaCoCo branch floors, declared in server/pom.xml; read them there rather than from a copy.

Docs site (/docs)

  • Docusaurus 3 powers the documentation. Install dependencies with pnpm install and run pnpm run start to preview.
  • Keep front matter fields (id, title, sidebar_position) unique. Update sidebars.*.ts when adding new pages.
  • Use MDX components from docs/src/components/** to keep styling consistent.
  • Run pnpm run build before committing structural changes to confirm the static export succeeds.

Database and migrations

  • Liquibase <changeSet>s (schema deltas — not release changesets) live in server/src/main/resources/db/changelog/. A schema change also needs a release changeset — see AGENTS.md § Changesets (release notes).
  • Prefer renameColumn or modifyDataType when evolving schemas so data survives deployments.
  • Add indexes before backfilling large tables. Profile long migrations locally.
  • After schema updates, regenerate the ERD and run server tests that touch the affected tables.

Observability and security

  • Sentry is configured in both the webapp and the application server. Capture exceptions with context tags instead of console logs.
  • PostHog records product analytics. Gate new events behind environment checks so tests stay deterministic.
  • Store shared secrets in LastPass and load them via .env or the respective application-*.yml files. Never commit real secrets.
  • Rotate API keys when you onboard new external services. Document the process in the runbooks under docs/admin.
  • Sanitise logged user data. Log IDs rather than emails.

Performance reminders

  • Batch network calls and paginate large results.
  • Cache expensive computations when you reuse them. TanStack Query handles client caching. Use memoised services or projections on the server.
  • Measure before tuning. Add logging or metrics when you touch critical latency paths.
  • Profile React work with the DevTools profiler and Chrome Performance panel. Profile Java code with JFR and async-profiler.

When unsure

Record trade-offs in your pull request. Ask for a second review on risky work. Suggest improvements to this page when you find a better pattern.