FirstDevJob Docs
Architecture

Tech Stack

Every dependency and service FirstDevJob runs on, with the version declared in package.json

This page lists what the project actually depends on and why. Versions are the specifiers declared in package.json at the root of the repo; bun.lock holds the exact resolved versions. If a version here disagrees with package.json, package.json wins.

Runtime dependencies

PackageVersionRole
next16.3.5App Router framework, pinned exactly rather than to a range
react, react-dom^19.2.4UI runtime
convex^1.31.7Database, queries, mutations, actions, crons, and the React client
@clerk/nextjs^6.37.1Auth provider, sign-in UI, clerkMiddleware, server auth()
@convex-dev/resend^0.2.3Convex component that queues and sends email through Resend
fumadocs-core^16.5.0Docs content loader and search backend
fumadocs-ui^16.5.0Docs layout, sidebar, search dialog, MDX components
fumadocs-mdx^14.2.6Compiles content/docs/**/*.mdx into the .source bundle
mermaid^12.0.0Renders the diagrams in these docs, loaded lazily client-side
lucide-react^0.513.0Icon set
next-themes^0.4.6Theme state for the docs layout and diagram colours

Build and development tooling

PackageVersionRole
typescript^5.9.3Strict typing end to end, including generated Convex types
tailwindcss + @tailwindcss/postcss^4.1.18Styling, configured through PostCSS rather than a tailwind.config file
eslint + eslint-config-next^9.39.2 / 15.5.11Linting
prettier^3.8.1Formatting
jest, jest-environment-jsdom^30Test runner
ts-jest^29.4.6TypeScript transform for Jest
@testing-library/react, @testing-library/jest-dom, @testing-library/user-eventsee package.jsonComponent tests
@next/bundle-analyzer^15.5.11Opt-in bundle report, enabled by ANALYZE=true

The package manager is Bun, pinned to 1.4.2 by the packageManager field and by BUN_VERSION in .github/workflows/ci.yml. Use bun install, bun run <script> and bunx, not npm or npx — the repo ships bun.lock and CI installs with bun install --frozen-lockfile.

Services

ServiceWhat it does hereConfigured by
ConvexHosts the database and all backend functions. Separate dev and prod deploymentsCONVEX_DEPLOYMENT, NEXT_PUBLIC_CONVEX_URL, convex.json
ClerkSign-in, sessions, OAuth, and the JWT template named convexNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY, CLERK_JWT_ISSUER_DOMAIN
ResendSends staff and subscriber notification email from Convex actionsRESEND_API_KEY, RESEND_TEST_MODE, EMAIL_FROM, EMAIL_REPLY_TO, STAFF_NOTIFICATION_EMAILS
VercelHosts the Next.js app; the Git integration builds and deploys the frontendVercel project settings
GitHub ActionsRuns CI on every pull request and deploys Convex on pushes to Master.github/workflows/ci.yml, .github/workflows/deploy.yml
CloudflareDNS for the production domainManaged outside this repo — nothing in the codebase references it

The Resend integration is registered as a Convex component in convex/convex.config.ts (app.use(resend)) and instantiated in convex/resend.ts. It defaults to test mode: real mail only leaves the system when RESEND_TEST_MODE is explicitly set to false, 0 or no.

Why these choices

Convex over a REST or GraphQL API. Queries are subscriptions, so the job list, dashboard and moderation panel stay current without polling or cache invalidation code. Function arguments are validated by v.* validators at the boundary and the generated api object gives the frontend compile-time errors when a function is renamed. Crons and scheduled actions come with the platform, so there is no separate worker to deploy.

Clerk over rolling auth. Convex has first-party Clerk support: convex/auth.config.ts declares the issuer domain and application ID, and ConvexProviderWithClerk keeps the token fresh. Password, email-code verification and OAuth all come from one provider.

Resend through the Convex component rather than a direct HTTP call. The component persists the send in Convex and handles queueing and retries, so a transient Resend failure does not need to be handled inside each mutation.

Fumadocs for docs because the content stays as MDX in the repo, next to the code it describes, and ships as part of the same Next.js build. Search is a local index built at request time from source.getPages() — no external search service.

Bun for install and script speed; CI uses the same version as local development to keep lockfile resolution identical.

Testing

Jest with ts-jest and the jsdom environment, configured in jest.config.js. bun run test:coverage produces the coverage report; CI runs bun run test:ci. The pure helper modules in convex/ (applicationIntegrity.ts, jobSubmissionSecurity.ts, jobHelpers.ts) exist partly so that submission validation and status-transition rules can be tested without a Convex context.

Security headers

next.config.ts sets Strict-Transport-Security, X-Frame-Options: DENY, X-Content-Type-Options, Referrer-Policy, a restrictive Permissions-Policy, and a Content-Security-Policy limited to frame-ancestors 'none'. A full CSP would need an allowlist covering Clerk, Convex and Vercel, so only the clickjacking protection is enforced today — that is a deliberate, documented gap in the config, not an oversight.

On this page