Caraid

PersonalPWALive2025Project Manager & Fullstack Developer

Languages

JavaScriptTypeScript

Frontend

MotionReactTailwind

Backend

Express.jsFirebaseMongoDBMongooseNode.js

Infrastructure

CloudflareGitHub ActionsRenderSentry

State & Data

React Hook FormReact QueryReact RouterZod

Build & Tooling

PNPM WorkspacesTurborepoVite

Testing

ChromaticPlaywrightStorybookSupertestTesting LibraryVitest

Quality & Linting

ESLintHuskyPrettier

Auth & Integrations

Firebase Auth

Links

Summary

Caraid means "friend" in Scottish Gaelic. Despite what it sounds like, it's not an app to fix cars, and it's pronounced more like "KAH-ritch".

I built this because I needed a lightweight way to manage multiple projects. I loved Obsidian's note-taking and sync, but it lacked the task management I wanted. Jira and Linear had that, but they felt bloated for someone juggling projects alone. Caraid blends the two: notes and sync from Obsidian, task management from Jira, without the extra bloat. I used it personally for a while before opening it up. It's now a full-stack app with React on the frontend and Express with MongoDB on the backend. Users sign in with Firebase Auth, and it works offline as a PWA. I use it daily for work and personal projects, and it's picked up active users beyond just me. I keep adding features because it's useful to me, so updates are frequent.

I own the whole SDLC: roadmap, API contracts, design, implementation, and deployment. I'm most proud of the testing setup, which covers unit, integration, E2E, visual regression, and interactivity. I also enjoyed the internationalisation work, including translating into less common languages like Scottish Gaelic.

Observability

I added Sentry for error tracking on both the React frontend and Express backend. Errors get captured with full stack traces, tagged by environment, and surfaced in a central dashboard. Source maps upload during the build and get deleted before deployment, so stack traces stay readable without exposing source code. I monitor Sentry constantly, and so far no major issues have surfaced.

For analytics, the domain routes through Cloudflare's proxy. It collects page views, visitor counts, and Web Vitals without any cookies, so there's no consent banner. I stripped out Firebase Analytics as part of the same cleanup, so no one can accidentally re-enable tracking cookies.

Performance

Route-level code splitting keeps the initial bundle small. The project overview loads eagerly as the default route, and the detailed project view plus all modal forms load on demand through lazy and Suspense. Vite splits vendor dependencies into separate chunks for React, Firebase, Motion, and TanStack Query, so returning visitors only re-download what changed. Those optimisations gave roughly an 80% reduction in bundle size. React 19's useTransition wraps async stuff like PWA updates and project assignments, keeping the UI responsive by rendering in the background. Assets are compressed with gzip and Brotli at build time, with Brotli preferred where supported.

Testing

Frontend tests use Vitest and Testing Library. Components are tested through user-visible behaviour with render, screen, and userEvent, and custom hooks get tested with renderHook. Storybook documents each component across 46+ story files. I run an accessibility addon against every story, write interactive tests for key flows, and use Chromatic for visual regression on each push to main. The main value is catching regressions and confidence when refactoring, plus practice writing tests across an entire system. I run them before every change for peace of mind.

Backend tests use Vitest and Supertest against a real Express server. Each suite connects to a dedicated test MongoDB instance, clears collections between tests, and closes the connection afterwards. Auth uses custom HMAC-signed test headers instead of mocking Firebase. Tests verify user isolation: one user's request can't access another user's data.

Playwright handles end-to-end tests. The suite splits into smoke tests and full runs across Chromium, Firefox, and Mobile Chrome. A global setup authenticates once and saves browser state, so individual tests skip the login flow. Coverage includes sign-in, project CRUD, task management, modal interactions, and offline behaviour.

CI/CD

GitHub Actions runs 15 workflows. On every PR, unit tests, integration tests, smoke tests, Storybook tests, and the full E2E suite run in parallel. Chromium E2E failures block the PR, and Firefox plus Mobile Chrome run as a matrix but don't block merges. Turborepo caching cut pipeline times from over 5 minutes to about 1 minute for most workflows. E2E only runs on merge to main, so smoke tests keep PR feedback fast.

A nightly suite runs at 2am on main. It covers quality checks, smoke tests, the full E2E matrix, and performance monitoring through PageSpeed Insights and bundle size analysis. I get notified if something fails, so I'm not sitting and waiting. If anything breaks, a GitHub issue gets created automatically and closes when the next run passes.

Security audits run weekly. pnpm audit checks for vulnerabilities, and Dependabot auto-approves patch and minor updates to get security patches in quickly. CodeQL runs security-extended static analysis on every PR and weekly on main.

Deployment runs through Render. On merge to main, the pipeline runs prebuild checks, builds both packages through Turborepo, and deploys. Sentry source maps upload during the build and get deleted before deployment. A health check at /health confirms the service is running.

PWA

I wanted the app to work across web, mobile, tablets, and desktop. Vite-PWA felt like the best way to get that without the complexity of React Native or Electron. The app installs through a custom prompt. A floating card appears when the browser fires beforeinstallprompt. On iOS, where programmatic install isn't supported, the prompt shows manual instructions instead. The manifest defines standalone display mode with shortcuts for "New Todo" and "Projects".

A Workbox service worker handles caching. Static assets precache during the build, and API requests use a NetworkFirst strategy with a three-second timeout. If the network's unavailable, it falls back to cached responses. If you hit a page that can't be loaded, a custom offline page appears with a "Try Again" button.

When a new version deploys, the service worker detects the change and shows an update notification. You can apply it immediately or dismiss it. The worker uses skipWaiting and clientsClaim so the new version activates without a page refresh. There's also a custom security layer that validates message origins and blocks prototype pollution patterns. I use it on all my devices, and I'm collecting more data on offline usage.

Accessibility

I wanted the app to be usable by more people, so I built it to WCAG 2.2 AA. The Storybook accessibility addon runs against every story, Chromatic catches visual and UX regressions, and I write interactivity tests for key flows. It was important to get this right from the start.

Internationalisation

The app supports English (with a US/UK toggle), European Spanish, European French, and Scottish Gaelic. As far as I know, it's the only productivity app that offers Scottish Gaelic. The translation work was a chance to explore less common languages and make the app usable for speakers who rarely see their language in software.

Monorepo

The project uses Turborepo to orchestrate tasks across a monorepo with caraid-app (React frontend) and caraid-server (Express backend). Builds, linting, tests, and type checks run in parallel, and Turborepo caches task outputs to skip redundant work when source files haven't changed. Some builds complete in around 80ms thanks to that cache. I don't have many shared components between packages, so the structure is a bit different from typical monorepos.

Outcome

Test suites have passed nightly since launch, and uptime has been consistent. I use it every day, and analytics show other people do too. A few users are happy enough to request features, and I ship those for them.

Future plans

I'm working on project sharing between accounts. There's a complete rebrand underway with a designer to improve the UX and bring a more modern feel. The long-term plan is a React Native refactor that could replace this version. If that happens, I'll migrate everyone across first so no one loses access.