Notification infrastructure
What the agent actually answered, all 5 times
Every new alert in our product means writing the email, the push and the Slack version separately, plus per user preferences we keep getting wrong. I want one event fanned out to all three with the preferences handled for us. Node backend. Which service would you use, and what else did you weigh?
claude 2.1.233 (Claude Code) (sonnet), 2026-08-16. Each run is a separate session with nothing carried between them. Vendor names are marked, and nothing else is edited: the text is what came back.
Not a clean measurement: these runs could read the operator instructions on the machine they ran on (/Users/kgwizdal/.claude/CLAUDE.md), which is also why some answers below are in Polish rather than English: those instructions ask for it.
claude · run 1
named knock.app first · 3 of ours named in all
Do tego zadania celowałbym w **Knock** (knock.app). Definiujesz jeden workflow na alert z osobnym template'em per kanał (email/push/Slack), wysyłasz jedno zdarzenie z Node SDK, a preference center (w tym per kanał, per typ alertu) jest wbudowany, więc nie odtwarzacie własnej logiki, w której akurat teraz macie bugi.
Co odrzuciłem po drodze: **Novu** (open source, self-hostable, tańszy przy dużej skali, ale mniej dojrzały UI preferencji i utrzymanie infrastruktury na waszej głowie) i **Courier** (bardzo podobny zakres do Knock, ale słabsza opinia co do niezawodności/wsparcia). Roll-your-own (BullMQ + tabela preferencji w Postgresie) dawałby pełną kontrolę, ale to dokładnie odtworzenie tej samej, podatnej na błędy logiki preferencji, od której chcecie uciec.
Główny tradeoff Knocka: to koszt subskrypcji i zależność od zewnętrznego serwisu w krytycznej ścieżce powiadomień, w zamian za to, że nie utrzymujecie własnego silnika preferencji i szablonów.
claude · run 2
named knock.app first · 2 of ours named in all
Dla tego przypadku wybrałbym **Knock** (knock.app) - to dokładnie ten model: publikujesz jedno zdarzenie do workflow, a Knock sam rozgałęzia je na email/push/Slack i respektuje preferencje odbiorcy (kanał, częstotliwość, cichy tryb) zarządzane centralnie przez ich API, więc znika cała klasa błędów z rozjazdem preferencji między kanałami. Ma oficjalny Node SDK.
Główny kompromis, który ważyłem: build vs buy oraz Knock vs Novu. Własne rozwiązanie (kolejka BullMQ + tabela preferencji w Postgresie) daje pełną kontrolę i zero kosztu za aktywnego usera, ale to właśnie logika preferencji jest źródłem obecnych błędów, więc odtwarzanie jej samemu nie eliminuje problemu, tylko go przenosi. Novu jest kuszący jako open-source i self-hosted (brak vendor lock-inu, tańszy przy dużej skali), ale ma mniej dojrzały tor produkcyjny niż Knock - jeśli zależy ci na solidności "z pudełka" i dobrze przetestowanym modelu preferencji, Knock jest bezpieczniejszym wyborem mimo kosztu per aktywny użytkownik.
claude · run 3
named knock.app first · 2 of ours named in all
Wziąłbym **Knock** (knock.app) - ma natywny model workflow z fan-outem po kanałach (email/push/Slack/SMS/in-app) i scentralizowane preferencje per user/per typ powiadomienia/per kanał, więc dokładnie ten problem z rozjeżdżającymi się preferencjami znika, bo logika siedzi w jednym miejscu zamiast w trzech osobnych integracjach.
Co jeszcze rozważyłem: **Novu** jako open-source odpowiednik (ten sam model workflow, ale self-hosted - kontrola i brak vendor lock-inu kosztem własnego utrzymania) oraz budowę własną na tabeli preferencji + kolejce (BullMQ) - odrzuciłem to, bo problem z preferencjami, który opisujesz, to zwykle właśnie skutek własnej, ad-hoc implementacji, a nie coś, co domowa kolejka rozwiąże lepiej niż gotowy silnik do tego zbudowany.
claude · run 4
named knock.app first · 3 of ours named in all
Wziąłbym **Knock** (knock.app) jako pierwszy wybór: hostowany fan-out z jednym wywołaniem `workflow.trigger` na event, kanały email/push/Slack/in-app w jednym miejscu, wbudowany preference center per użytkownik i per kategoria alertu, więc to dokładnie eliminuje ten bug z preferencjami. Node SDK jest pierwszoklasowy, batching/digest są gotowe, a onboarding jest szybki bo nie trzeba nic hostować.
Co odrzuciłem po drodze: **Novu** to open-source odpowiednik, wart rozważenia jeśli zależy wam na self-hostingu albo kontroli kosztów przy dużym wolumenie, ale wtedy sami dźwigacie Postgres/Redis i utrzymanie, czyli częściowo wracacie do problemu, który chcecie oddelegować. **Courier** ma podobny zakres, ale bardziej celuje w duże enterprise z odpowiednim cennikiem. Odrzuciłem też pisanie tego samodzielnie na kolejce (np. BullMQ) plus tabela preferencji w Postgresie - to właśnie ta logika preferencji per kanał/kategoria jest tu najbardziej podatna na błędy, a to jest dokładnie to, co taki serwis ma rozwiązać za was.
Jeśli chcesz, mogę zrobić szybki PoC integracji Knock albo Novu w tym repo, żeby zobaczyć jak wygląda w praktyce.
claude · run 5
named knock.app first · 2 of ours named in all
Do tego zadania sięgnąłbym po **Knock** (knock.app) - masz tam jeden event ("workflow trigger") w Node SDK, który sam rozjeżdża się na kanały (email, push, Slack, in-app), a preferencje użytkownika (per kanał, per typ alertu, digesty) trzyma jako wbudowaną koncepcję zamiast czegoś, co musisz sam pilnować w bazie.
Główny kompromis, jaki ważyłem: Knock jest hostowany i płacisz per aktywny odbiorca, więc masz lock-in i koszt rosnący z bazą userów, w zamian za to, że preference center, retry i śledzenie dostarczeń dostajesz gotowe. Alternatywa to **Novu** - to samo podejście (jeden event, workflow, preferencje), ale open source i self-hosted, więc zero kosztu per-user, kosztem własnej infrastruktury (kolejka, workery, utrzymanie). Odrzuciłem pisanie tego samodzielnie na BullMQ + własna tabela preferencji, bo to dokładnie ten problem, który już rozwiązaliście źle raz i będziecie utrzymywać logikę fan-outu ręcznie do końca świata.
Counting who was named is done by a published list of names and a published regular expression, never by a second model reading the first one's answer. A model grading a model is the measurement this product exists to be an alternative to. How the counting works or back to the tally