Contributing to exponential
Thanks for helping improve exponential. This repo is a split monorepo: a Go headless API, a Next.js UI, a generated TypeScript SDK, and a CLI. The contribution path below is written for the current architecture, not the older single-app prototype.
Start Here
Before making changes, skim:
- README.md for product scope and local setup options.
- CLAUDE.md for architecture, auth, and quality guardrails.
- docs/README.md for the documentation map.
- docs/self-hosting.md for Docker and ECS deployment.
- docs/secrets.md for the optional 1Password workflow.
- apps/web/tests/README.md for test conventions.
Project Shape
apps/api/- Go API, auth, handlers, OpenAPI strict server stubs, sqlc
queries, and migration runner.
apps/web/- Next.js 16 App Router UI. Runtime business endpoints should
not be added under apps/web/src/app/api/; the Go API owns /api/* and
/v1/*.
apps/cli/- CLI that consumes the generated SDK.packages/proto/- OpenAPI contract and SQL migrations.packages/sdk/- generated TypeScript SDK plus small hand-written helpers.infra/andscripts/- Docker, ECS, validation, smoke, and deploy helpers.
Prerequisites
- Node.js 20+
- pnpm 10.24.0, via Corepack or your package manager
- Go, for
apps/api - Docker Desktop, for the local Postgres/Redis/API/web stack
- Playwright Chromium, for E2E tests
corepack enable
pnpm install
pnpm exec playwright install chromiumLocal Development
The default local ports are:
- Web:
http://localhost:7015 - API:
http://localhost:7016
Full Docker Dev Stack
This starts Postgres, Redis, the Go API, the Next.js web app, and Mailhog.
cp .env.example .env
docker compose -f docker-compose.dev.yml up --buildHost Dev Server With Docker Services
Use this when you want faster web/API iteration from your shell.
cp .env.example .env
make dev-services
EXPONENTIAL_API_DATABASE_URL=$DATABASE_URL go run ./apps/api/cmd/migrate
pnpm devpnpm dev starts the web app on port 7015 and preflights the database before
binding. Only set SKIP_DB_PREFLIGHT=true when intentionally debugging a
route that does not need the database.
Optional 1
Password Flow
If you have access to the Exponential vault, you can run commands through
.env.1password instead of maintaining local secret values:
make op-doctor
make dev-opSee docs/secrets.md for details.
Development Workflow
- Create a focused branch:
git switch -c fix/short-description- Make one logical change.
- Update source, generated artifacts, tests, and docs together.
- Run the relevant focused tests while iterating.
- Run the merge gates before opening a PR.
Use these branch prefixes when they fit: feat/, fix/, docs/,
refactor/, test/, or chore/.
Architecture Rules
API and SDK
- OpenAPI is the contract for Go business endpoints. Update
packages/proto/openapi.yaml when adding or changing public API behavior.
- Regenerate and commit generated SDK/stub/sqlc changes when the contract or
SQL queries change.
- Keep Go handlers small, use context-aware DB calls, and return RFC
7807-style JSON problems for API errors.
- Browser traffic is proxied to the Go API through
/api/*; SDK/CLI clients
use /v1/*.
Web App
- Keep
apps/webUI-only for runtime business endpoints. - Use the generated SDK for migrated runtime slices instead of hand-written
endpoint fetches.
- Preserve the terminal/editorial redesign and existing theme tokens.
- Reuse Radix primitives and Tailwind tokens before introducing new UI
dependencies or hard-coded colors.
- Keep interactions keyboard-accessible, especially command-palette and issue
management flows.
Auth
Authentication is first-party Go auth. Do not reintroduce Kratos, Better Auth, NextAuth, or password auth without an explicit new plan. Current auth surfaces include Google OAuth, magic links, sessions, and workspace invitations.
Out of Scope
Do not add paywalls, subscription checkout, payment collection, or hosted SaaS billing flows unless there is an explicit approved plan for that work. Existing workspace billing/settings and Stripe webhook code are current admin/provider surfaces; changes there should stay narrowly scoped and documented.
Verification
Run these before committing code changes:
make check
make testBefore declaring UI/runtime flows verified, also run:
make test-e2emake all runs make check and make test. It does not run Playwright E2E.
For UI changes, manually open the affected page at http://localhost:7015 and
exercise the golden path plus at least one edge case. For deployment changes,
run the deploy path with production smoke enabled:
RUN_PROD_SMOKE=true scripts/deploy-ecs.shThen verify ECS, ALB, and smoke-test output. A pushed commit is not the same thing as a deployed and live-verified change.
Test Guide
- Go API tests:
cd apps/api && go test ./...ormake test. - Vitest unit/component tests:
pnpm testormake test. - SDK tests:
pnpm --filter @namuh-eng/expn-sdk test. - CLI tests:
pnpm --filter @namuh-eng/expn-cli test. - Playwright E2E:
make test-e2e, with the dev stack running.
Run a single E2E file from the web package when iterating:
pnpm --filter @exponential/web test:e2e -- tests/e2e/smoke.spec.tsUse Playwright debug or headed mode when a browser interaction is unclear:
pnpm --filter @exponential/web exec playwright test --debug
pnpm --filter @exponential/web exec playwright test --headedCode Style
- TypeScript strict mode: no
anyand noas unknown asshortcuts. - Prefer real types and narrow helpers over type assertions.
- Biome owns formatting and linting.
- Keep Go code idiomatic, small, and explicit.
- Do not weaken or delete tests to make a change pass.
Useful commands:
make check
make fix
make formatPull Requests
Before opening a PR:
git fetch origin
git rebase origin/main
make check
make test
make test-e2eIn the PR description, include:
- What changed.
- Why it changed.
- User-visible behavior, if any.
- Tests and manual verification performed.
- Screenshots or short clips for visual UI changes.
- Any deploy or migration steps.
Main is protected, so land changes through a branch and PR. If your branch needs updates after review, rebase carefully and force-push with lease:
git push --force-with-lease origin your-branchCommit Messages
Keep commits small and focused. Use a short conventional prefix:
feat: add issue priority filter
fix: preserve callback URL after auth completion
docs: refresh local development guide
test: cover project milestone actionsAvoid vague commits like fix stuff, broad mixed changes, or unrelated
formatting churn.
Common Troubleshooting
The Web App Exits Before Starting
The web dev script preflights Postgres. Start the local services and apply migrations:
make dev-services
EXPONENTIAL_API_DATABASE_URL=$DATABASE_URL go run ./apps/api/cmd/migrate
pnpm devE2E Auth Is Failing
Playwright creates test sessions through the test-only helper under
/api/test/create-session. Make sure the local web app is running on 7015,
the API is reachable on 7016, and migrations have been applied.
make check Fails On Generated Or Cache Files
Do not edit generated outputs by hand. Regenerate the relevant SDK, OpenAPI, sqlc, or stub artifacts. If the failure points at a stale local cache, clean the cache and rerun the check:
make clean
make checkDocker Is Not Available
Use host Postgres and Redis, then set DATABASE_URL and REDIS_URL in your
environment or .env. Apply migrations with the Go migration runner before
starting the app.
Need Help?
- Open a GitHub issue for bugs or feature requests.
- Use GitHub Discussions for questions.
- For deployment and self-hosting, start with docs/self-hosting.md.
Thank you for contributing to exponential.