Skip to main content

Coding conventions

Each repository documents its own rules in CLAUDE.md or README.md; this page is the cross-repo summary. When a rule here disagrees with the repo file, the repo file wins and this page needs updating.

Everywhere​

  • Branch from develop, pull request into develop, merge develop into main for a release (Repositories).
  • Never commit a secret. Env var names in docs; values in .env, Portainer, Key Vault or GitHub secrets (Secrets management).
  • Never log or put in a URL: emails, tokens, codes, passwords, request bodies.
  • Say which environment a URL or command is for.
  • Tests ship with the change where a test runner exists (backend, Admin CRM).

Backend (emaw-django-app)​

From CLAUDE.md:

  • Use .venv\Scripts\python.exe for every command.
  • DRF denies by default; public views set AllowAny explicitly. Throttles in EMAW/throttles.py.
  • Staff views: CrmAccessViewMixin / HasCrmAccess("<capability>"), and scope_contacts() on any queryset exposing contact data. Do not hand-roll staff checks.
  • Security events through EMAW.security_log.audit(event, request, user=..., **fields); log user ids only; %s lazy formatting, no f-strings in log calls.
  • One LOGGING built by logging_utils.py; no per-environment logging dicts.
  • Verification codes via VerificationCode.verify(code).
  • Any bulk write that bypasses model signals on contacts must call invalidate_contact_lists().
  • Tests in <app>/tests/test_*.py, factories in <app>/tests/factories.py, against the throwaway Postgres.
  • New env vars: add to docker-compose.yml x-app-env, CLAUDE.md, and the production stack.
  • Changing a staff endpoint: update docs/CRM_FRONTEND_GUIDE.md.

Website (emaw-website)​

From CLAUDE.md:

  • TypeScript strict-ish: verbatimModuleSyntax, noUnusedLocals, noUnusedParameters, erasableSyntaxOnly. import type for types.
  • @/ alias over deep relative paths.
  • Internal links through LocalizedLink / LocalizedNavLink, never react-router-dom's Link.
  • New i18n namespace: update src/lib/i18n/index.ts and both locale folders.
  • Sanity through getClient(preview) and useSanityQuery; sanity.types.ts is generated, do not edit.
  • Layout fetches siteSettings once.
  • Prettier: no semicolons, single quotes, no bracket spacing, width 100. ESLint flat config.
  • No lockfile (gitignored on purpose); CI uses npm install.
  • Checkout stays in Stripe Elements mode (server-side shipping).

Web app (emaw-web-app)​

From README.md:

  • Every API call is a function in src/utils/ using the shared axios instance.
  • Sign-in requests pass {skipAuthRefresh: true}.
  • Tokens only through AuthContextProvider; refresh token in the body.
  • .env.development and .env.production are committed; override with .env.development.local.
  • Keep curriculum JSON in sync with the backend export and the mobile app.

Admin CRM (emaw-crm-app)​

From README.md and src/api/README.md:

  • Env vars only in src/config.ts.
  • Access token in memory; refresh via the HttpOnly cookie through the proxy; nothing auth-related in localStorage.
  • No emails in URLs except q; never log bodies; show X-Request-ID on error toasts; no-console is an ESLint error.
  • CSV exports only on click, never cached.
  • Dev sends only to group 36 (devGuard.ts).
  • One folder per feature with api.ts, types, screens, routes.tsx (lazy Component), tests.
  • Trailing slashes on API paths.
  • Run npm test and npm run lint before a pull request.

Mobile app (emaw-mobile-app)​

From CLAUDE.md:

  • Tokens only through store/tokenStorage.js; never log passwords, tokens or error.response of auth requests.
  • Every API call is a function in utils/ using store/AxiosInstance.js.
  • Styles from constants/styles.js, not inline.
  • Native changes go in config plugins or app.json, never in the gitignored android/ and ios/.
  • Bump versions with npm run version (it also resets the backend URL to production).
  • Do not commit a dev backend URL in constants/backendIp.js.

Documentation (emaw-documentation)​

See Writing documentation.