How basecamp/fizzy works
1. Overall shape
- This is a Ruby on Rails monolith for a Kanban-style product called Fizzy.
- The repo is split into a core application at the root and a SaaS extension/engine under saas/, which overlays billing, storage limits, native push devices, admin stats, and related deployment/runtime differences.
2. Primary request path
- Most user interactions follow the standard Rails flow: controller → model/service logic → view.
- The app uses Hotwire/Turbo, Stimulus, Action Text, and Importmap rather than a separate SPA frontend, so server-rendered HTML and incremental updates are the main UI pattern.
- Many actions also emit Turbo Streams for live updates in place.
3. Domain model boundary
- The core business domain is organized around accounts, users, identities/sessions, boards, columns, cards, comments, tags, reactions, watches, pins, notifications, events, and webhooks.
- The app is explicitly multi-tenant: most models and controllers are scoped through account/board/card concerns, and there are dedicated multi-tenant helpers and initializers.
- The board/card domain is the center of the product; almost every other subsystem feeds into it or derives from it.
4. Board/card workflow subsystem
- Boards contain columns and cards, with specialized states such as streams, not-nows, and closures.
- Card operations are decomposed into many small controllers and model concerns: assignments, self-assignments, closures, triage, notes/comments, steps, pins, watches, goldness, tagging, and publishing.
- This is where the main product data flow happens: users manipulate cards, which update board state, activity history, and notifications.
5. Activity, events, and timelines
- The app has a first-class event layer, separate from direct card/board records.
- Events drive activity feeds, day timelines, and user-centric views, and they are also used as input for notifications and webhooks.
- This means “what happened” is modeled independently from “current board state,” which is important for diagrams showing both operational state and audit/activity history.
6. Search and filters
- Search is its own subsystem with query parsing, stemming, highlighting, results, and backend-specific record implementations.
- There is also a structured filter model with fields/resources/params/summaries, used to refine boards, cards, people, tags, and activity views.
- Search appears to be indexed in background jobs and supported by database-specific full-text/search record tables.
7. Notifications and messaging
- Notifications are multi-channel: in-app trays, email, web push, and in the SaaS layer also native push devices.
- Notification delivery is asynchronous and organized through bundling, push targets, and notifier classes.
- Mailers cover sign-in, exports/imports, account events, and bundled notifications; there are also unsubscribe flows and notification settings.
8. Authentication and account access
- Authentication supports magic links and passkeys/WebAuthn, with dedicated controllers, mailers, and a custom lib/action_pack/passkey implementation.
- The app also supports identities, access tokens, join codes, user transfers, and role/verification workflows.
- Access control is centralized through controller concerns and tenant/account scoping rather than being embedded in individual views.
9. Files, rich content, and storage
- The app relies on Active Storage and Action Text for attachments and rich text content.
- Storage is treated as a real domain concern: there are storage totals, tracked entries, materialization/reconciliation jobs, and purge-on-last-attachment behavior.
- The SaaS layer adds storage enforcement and exceptions, so storage is both an infrastructure concern and a product boundary.
10. Asynchronous processing
- Background jobs are a major part of the architecture.
- Jobs handle imports/exports, notification delivery, webhook dispatch, search reindexing, storage reconciliation, tag cleanup, and activity-spike detection.
- This separates user-facing requests from slower side effects and external integrations.
11. External integrations
- The main external integrations visible in the tree are email delivery, web push notifications, WebAuthn/passkeys, webhooks, and object storage.
- There is also support for PWA assets, service worker, and mobile-facing client configuration endpoints, which suggests the app is designed to work well on browsers and installed clients.
12. Public vs authenticated surfaces
- There are distinct controller/view areas for public access, authenticated app usage, admin, account settings, and my/ personal settings.
- Public board/card endpoints expose read-only or shareable views, while internal routes manage editing, notifications, preferences, and administrative operations.
- This separation is important for diagramming trust boundaries.
13. SaaS overlay
- The saas/ tree is not just configuration; it is a parallel engine with its own models, controllers, views, jobs, tests, and migrations.
- It introduces billing/subscription concepts, device registration, storage-limit handling, admin auditing/stats, and native push notification support.
- In architecture terms, think of it as a product-variant layer that extends the same core domain with paid-plan and operational controls.
14. Deployment/runtime
- The repo supports Docker and Kamal deployments, with separate app and SaaS deployment configs.
- The app is built as a conventional Rails server with background jobs and a cable layer, plus database, cache, queue, and file storage support.
- Tests are broad and layered: controller, model, job, integration, system, mailer, and library tests, mirroring the subsystem boundaries above.
Main entry points: board.rb Boards & cards · application_job.rb Jobs & mailers · storage_limited.rb Limits & devices · notification.rb Notifications
Components 22
UI layer
Hotwire, controllers, views, and client-side Stimulus behavior.
- Application controller · Top-level request entry with auth, tenant, and UI concerns.
app/controllers/application_controller.rb - Board actions · Board and card workflow endpoints for editing, moving, and updating work items.
app/controllers/cards_controller.rb - Card workflows · Assignments, closures, triage, comments, pins, watches, tags, steps, reactions, and drafts.
app/controllers/cards/assignments_controller.rb - Column actions · Board columns plus card drops into columns and special states.
app/controllers/boards/columns_controller.rb - Auth flow · Sessions, magic links, passkeys, signups, transfers, and join codes.
app/controllers/sessions_controller.rb - Account area · Account settings, exports/imports, identities, roles, verifications, avatars, and personal settings.
app/controllers/account/settings_controller.rb - Search & inbox · Search, filters, activity timelines, notification trays/settings, and read states.
app/controllers/searches_controller.rb - Public surface · Public board and card views for shareable or read-only access.
app/controllers/public/boards_controller.rb - Hotwire client · Stimulus controllers, importmap application, Turbo updates, Action Text, and PWA/client config.
app/javascript/application.js
Domain model
Board/card collaboration, activity history, identity, search, notifications, and storage.
- Boards & cards · Core tenant-scoped board, column, card, comment, tag, pin, watch, reaction, and step records.
app/models/board.rb - Users & identity · Users, identities, sessions, access tokens, magic links, passkeys, joins, roles, and verification.
app/models/user.rb - Events & timelines · Event layer, day timelines, activity summaries, and promptable descriptions.
app/models/event.rb - Search & filters · Query parsing, stemming, highlights, results, and structured filter resources.
app/models/search.rb - Notifications · Notification bundles, push targets, notifiers, readings, and user notification settings.
app/models/notification.rb - Storage ledger · Attachments, storage totals, tracked entries, and account storage accounting.
app/models/storage.rb
Async & integrations
Jobs, mailers, webhooks, push, and external delivery paths.
- Webhooks · Webhook definitions, deliveries, delinquency tracking, and trigger logic.
app/models/webhook.rb - Jobs & mailers · Background delivery for imports, exports, reindexing, notifications, webhooks, and account cleanup.
app/jobs/application_job.rb
SaaS overlay
Parallel engine that adds billing-like limits, admin stats, and native push support.
- SaaS engine · Parallel extension that adds subscriptions, storage limits, native push devices, and admin tooling.
saas/lib/fizzy/saas/engine.rb - Limits & devices · Storage-limited card paths, native push devices, subscriptions, and storage exceptions.
saas/app/models/account/storage_limited.rb - Admin stats · Admin auditing and usage statistics for the SaaS variant.
saas/app/controllers/admin/stats_controller.rb
Runtime
Deployment, initialization, and shared library/runtime extensions.
- Rails extensions · Custom Action Pack passkeys/WebAuthn code, Rails extensions, deployment helpers, and web push utilities.
lib/action_pack/passkey.rb - Core config · Multi-tenant, passkey, push, storage, CSP, and environment initialization.
config/initializers/multi_tenant.rb
Connections 29
- Application controller → Auth flow: authenticates · Shared controller concerns route users through session and identity flows.
- Application controller → Boards & cards: scopes · Controllers operate inside tenant/account and board/card boundaries.
- Board actions → Boards & cards: mutates · Core card CRUD and board updates persist into domain records.
- Card workflows → Boards & cards: updates · Assignments, comments, pins, watches, tags, steps, and reactions all touch the card model.
- Column actions → Boards & cards: reorders · Column actions move cards across board state and special lanes.
- Public surface → Boards & cards: reads · Public views render board and card data without full edit access.
- Account area → Users & identity: manages · Account settings and personal settings mutate identity and user records.
- Auth flow → Users & identity: verifies · Magic links, passkeys, join codes, and transfers establish identity and session state.
- Search & inbox → Search & filters: queries · Search and filters build structured queries and refine results.
- Search & inbox → Events & timelines: views · Activity and day-timeline endpoints present event history.
- Boards & cards → Events & timelines: emits · Board/card changes generate first-class events and timelines.
- Boards & cards → Notifications: notifies · Card activity produces in-app, email, and push notifications.
- Events & timelines → Notifications: feeds · Event payloads are used for notification content and routing.
- Boards & cards → Webhooks: triggers · Publishing and eventing dispatch webhook activity.
- Jobs & mailers → Notifications: delivers · Bundled and push notifications are sent asynchronously.
- Jobs & mailers → Webhooks: dispatches · Webhook delivery jobs handle external callbacks.
- Jobs & mailers → Search & filters: reindexes · Search indexing and refresh work runs in background jobs.
- Jobs & mailers → Storage ledger: reconciles · Storage materialization and reconciliation happen out of band.
- Hotwire client → Application controller: requests · Turbo/Hotwire client interactions hit Rails controllers.
- Hotwire client → Boards & cards: updates live · Turbo streams and Stimulus interactions keep board/card state fresh.
- Rails extensions → Auth flow: supports · Custom passkey/WebAuthn plumbing backs authentication.
- Core config → Boards & cards: configures · Tenanting and storage/search/runtime initializers shape model behavior.
- Core config → Rails extensions: loads · Boot-time config enables custom Rails and security extensions.
- SaaS engine → Limits & devices: adds · Engine layer introduces paid-plan limits and native push device handling.
- SaaS engine → Admin stats: adds · SaaS variant includes admin dashboards and audits.
- Limits & devices → Storage ledger: constrains · SaaS storage limits overlay the core storage ledger.
- Limits & devices → Notifications: extends · Native push becomes another notification target.
- Limits & devices → Boards & cards: guards · Storage-limit concerns block or alter card creation and publishing paths.
- Admin stats → Jobs & mailers: observes · Admin tooling reflects asynchronous billing and usage activity.