alex shapalov Profile picture
Building ActiveRabbit 🐇 open-source to $10k MRR — $529/mo 🚀 @Remotely_jobs 100k+ users • Daily vibe-coded builds & revenue • 🇺🇦→🇺🇸

Dec 3, 9 tweets

37signals open-sourced Fizzy, their Kanban tracker for issues & ideas. @dhh

I went through the code and stole 10 patterns you can reuse in your own Rails / SaaS app 👇

github.com/basecamp/fizzy

1️⃣ Account slug multi-tenancy (no namespaced controllers)

Fizzy uses a rack middleware (AccountSlug::Extractor) that:

Pulls the numeric account id from the URL
Rewrites SCRIPT_NAME / PATH_INFO
Wraps the request in Current.with_account

Result: the whole app is effectively mounted at /{account_id} and controllers stay clean & non-multi-tenant-aware.

2️⃣ Base36 UUIDv7 primary keys

They store UUIDv7 in binary columns but expose them as 25-char base36 strings:

Ordered by time
Short enough to copy/paste
Still globally unique

It’s a great pattern if you want “nice” IDs without giving up UUID semantics.

3️⃣ Account-aware background jobs

Fizzy extends Active Job so that every job:

Captures Current.account when enqueued
Serializes it into the payload
Restores it and runs inside Current.with_account on perform

No more “oops this job ran for the wrong tenant” in Sidekiq.

4️⃣ Sharded full-text search per account

They define 16 Search::Record::Trilogy shard classes (search_records_0…15).

Shard chosen by CRC32(account_id)
Data is stemmed before save
Queries use boolean MATCH ... AGAINST scoped with an account_key

That’s a cheap way to keep search fast as data grows.

5️⃣ Bundled email notifications with time windows

Notification::Bundle keeps a rolling window per user (starts_at..ends_at):

Collects unread notifications in that window
Sends a single digest when the window closes
Marks bundle as delivered

You get “less spammy” notifications without complex logic in each notifier.

6️⃣ Entropy-driven auto-postponing of stale cards

Boards + accounts have “entropy” settings.

Card::Entropic looks at last_active_at and can:
Find cards “postponing soon”
Auto-move overdue ones into a Not now column with auto_postpone_all_due

7️⃣ Activity spike & stall detection

Each card has an ActivitySpike record.

When last_active_at changes, Fizzy queues detection jobs and uses a stalled scope for cards that stayed quiet for 14+ days.

Great pattern if you want to highlight “truly stuck” work automatically.

8️⃣ Access revocation that actually scrubs data

Board::Accessible doesn’t just remove “access”.

When a user loses access, it scrubs:
Mentions
Notifications

Watches

via raw SQL joins so nothing leaks from other cards. An underrated privacy & UX pattern.

Share this Scrolly Tale with your friends.

A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.

Keep scrolling