Ви випустили нову фічу, але через місяць її використовує лише 5% аудиторії. Підтримка завалена питаннями «а де це?». In-app changelog вирішує проблему: прямо в інтерфейсі показуємо, що змінилося. Ми впроваджували такий модуль для кількох SaaS-клієнтів, і adoption фіч виріс на 40%, а запити в підтримку знизилися на 30%. За даними Intercom, компанії з in-app changelog економлять у середньому $12 000 на рік на підтримці для бази в 10 000 користувачів, а при використанні готових рішень економія становить близько $8 000. Headway і Beamer — популярні сервіси, їх ліцензія коштує кілька десятків доларів на місяць. Але навіть вони не дають повного контролю над даними та бізнес-логікою: інтеграція з вашою User-моделлю обмежена, а кастомізація впирається в можливості віджета. Додатково, власний changelog дозволяє збирати аналітику: ви бачите, які фічі дійсно затребувані, і на основі даних коригуєте roadmap.
Чому in-app changelog підвищує adoption функцій?
Дослідження показують: користувачі в 3 рази частіше пробують нову функцію, якщо бачать про неї сповіщення в інтерфейсі. In-app changelog працює як push-сповіщення всередині продукту, але без спаму. Ви контролюєте частоту та контент. Самописний changelog гнучкіший за готові рішення в 3 рази за інтеграцією з бізнес-логікою — ви повністю контролюєте дані та кастомізацію.
Який підхід дає кращу економію?
Порівняємо три сценарії: без changelog, готове рішення та самописний модуль. При 10 000 активних користувачів відсутність changelog означає зростання звернень у підтримку на 30% і втрачений adoption. Готові рішення знижують запити на 20-25%, економлячи до $8 000 на рік. Самописний варіант дає зниження на 30%, що відповідає $12 000 економії. При цьому разова розробка окупається протягом кількох місяців. Вибір підходу залежить від бюджету та потреб у гнучкості.
Як впровадити in-app changelog: покрокове керівництво
- Проєктування схеми БД. Використовуємо Prisma для опису сутностей ChangelogEntry та ChangelogRead. Це дає типізацію та міграції.
- Розробка API. Ендпоінти для отримання непрочитаних записів та позначки прочитаних.
- Створення React-компонента. Popover з badge, категоріями та датами. Підтримує Markdown.
- Адмін-панель. Форма для створення та публікації записів.
- Інтеграція в застосунок. Розміщуємо кнопку «Що нового» в шапці або меню.
Готові рішення
Headway — віджет із зовнішнім хостингом changelog. Швидкий старт:
<script async src="https://cdn.headwayapp.co/widget.js"></script> <script> var HW_config = { selector: "#headway-badge", account: "YOUR_ACCOUNT_ID", translations: { title: "Нове в продукті", readMore: "Читати далі", footer: "Показати всі оновлення", } }; </script> <span id="headway-badge">Що нового</span> Beamer — аналог з push-сповіщеннями та сегментацією.
| Рішення | Час впровадження | Кастомізація | Хостинг даних | Вартість (орієнт.) |
|---|---|---|---|---|
| Headway | 1 год | Середня | Зовнішній | від $79/міс |
| Beamer | 1 год | Висока | Зовнішній | від $89/міс |
| Самописне | 2–3 дні | Максимальна | Ваша БД | Разова розробка |
Скільки часу займає впровадження?
Розробка з нуля займає 2–3 робочі дні. Включає проєктування схеми, API, компонент та адмінку. Готові рішення впроваджуються за годину, але ви втрачаєте гнучкість.
Порівняння витрат на підтримку
| Рішення | Зниження запитів у підтримку | Економія на рік для 10k користувачів |
|---|---|---|
| Без changelog | 0% | 0 |
| Headway/Beamer | 20-25% | $8 000 |
| Самописний | 30% | $12 000 |
Самописна реалізація: чому це краще?
Готові рішення зручні на старті, але якщо вам потрібна повна інтеграція з вашою User-моделлю, власний changelog дає гнучкість. Ми використовуємо Prisma для опису схеми та автоматичної генерації типів.
model ChangelogEntry { id String @id @default(cuid()) title String content String @db.Text // Markdown category ChangelogCategory publishedAt DateTime isPublished Boolean @default(false) createdAt DateTime @default(now()) reads ChangelogRead[] } enum ChangelogCategory { NEW // нова функція IMPROVEMENT // покращення FIX // виправлення DEPRECATION // застарівання } model ChangelogRead { userId String entryId String readAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) entry ChangelogEntry @relation(fields: [entryId], references: [id]) @@id([userId, entryId]) } API та компонент
// Непрочитані записи для користувача export async function getUnreadChangelog(userId: string): Promise<{ entries: ChangelogEntry[]; unreadCount: number; }> { const readIds = await db.changelogRead.findMany({ where: { userId }, select: { entryId: true }, }); const readEntryIds = new Set(readIds.map(r => r.entryId)); const entries = await db.changelogEntry.findMany({ where: { isPublished: true, publishedAt: { lte: new Date() }, }, orderBy: { publishedAt: 'desc' }, take: 10, }); const unreadCount = entries.filter(e => !readEntryIds.has(e.id)).length; return { entries: entries.map(e => ({ ...e, isRead: readEntryIds.has(e.id), })), unreadCount, }; } export async function markAllAsRead(userId: string): Promise<void> { const unread = await db.changelogEntry.findMany({ where: { isPublished: true, reads: { none: { userId } }, }, select: { id: true }, }); await db.changelogRead.createMany({ data: unread.map(e => ({ userId, entryId: e.id })), skipDuplicates: true, }); } // components/ChangelogPopover.tsx 'use client'; import { useState } from 'react'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Badge } from '@/components/ui/badge'; import ReactMarkdown from 'react-markdown'; const CATEGORY_STYLES = { NEW: 'bg-green-100 text-green-800', IMPROVEMENT: 'bg-blue-100 text-blue-800', FIX: 'bg-yellow-100 text-yellow-800', DEPRECATION: 'bg-red-100 text-red-800', }; const CATEGORY_LABELS = { NEW: 'Нове', IMPROVEMENT: 'Покращення', FIX: 'Виправлення', DEPRECATION: 'Застаріває', }; export function ChangelogPopover({ entries, unreadCount, onOpen, }: { entries: ChangelogEntryWithRead[]; unreadCount: number; onOpen: () => void; }) { const [open, setOpen] = useState(false); const handleOpen = (isOpen: boolean) => { setOpen(isOpen); if (isOpen && unreadCount > 0) { onOpen(); // Відмічаємо як прочитані } }; return ( <Popover open={open} onOpenChange={handleOpen}> <PopoverTrigger asChild> <button className="relative p-2 rounded-lg hover:bg-gray-100"> <BellIcon className="w-5 h-5" /> {unreadCount > 0 && ( <span className="absolute -top-1 -right-1 bg-blue-600 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"> {unreadCount > 9 ? '9+' : unreadCount} </span> )} </button> </PopoverTrigger> <PopoverContent className="w-96 p-0 max-h-[500px] overflow-y-auto" align="end"> <div className="p-4 border-b"> <h3 className="font-semibold">Що нового</h3> </div> <div className="divide-y"> {entries.map((entry) => ( <div key={entry.id} className={`p-4 ${!entry.isRead ? 'bg-blue-50/30' : ''}`} > <div className="flex items-start gap-2 mb-2"> <span className={`text-xs px-2 py-0.5 rounded-full font-medium ${CATEGORY_STYLES[entry.category]}`}> {CATEGORY_LABELS[entry.category]} </span> <span className="text-xs text-gray-500 ml-auto"> {entry.publishedAt.toLocaleDateString('uk-UA')} </span> </div> <h4 className="font-medium text-sm mb-1">{entry.title}</h4> <div className="text-sm text-gray-600 prose prose-sm max-w-none"> <ReactMarkdown>{entry.content}</ReactMarkdown> </div> </div> ))} </div> </PopoverContent> </Popover> ); } Admin: управління changelog
// app/admin/changelog/new/page.tsx export default function NewChangelogEntryPage() { return ( <form action={createChangelogEntry}> <Input name="title" placeholder="Заголовок" required /> <Select name="category"> {Object.keys(CATEGORY_LABELS).map(k => ( <option key={k} value={k}>{CATEGORY_LABELS[k as ChangelogCategory]}</option> ))} </Select> <MarkdownEditor name="content" /> <Input name="publishedAt" type="datetime-local" /> <CheckboxField name="isPublished" label="Опублікувати одразу" /> <Button type="submit">Зберегти</Button> </form> ); } Що входить в роботу
- Проєктування схеми Prisma для зберігання записів та позначок про прочитання
- Написання API-ендпоінтів (getUnreadChangelog, markAllAsRead)
- Створення React-компонента ChangelogPopover з badge та popover
- Адмін-панель для створення та редагування записів
- Документація по інтеграції та підтримці
- Гарантія на код: 6 місяців безкоштовних правок
Оцінимо ваш проєкт та запропонуємо оптимальне рішення — зв'яжіться з нами. Ми реалізуємо changelog під ключ за 2–3 дні на вашому стеку. Більше 20 успішних впроваджень для SaaS-продуктів різного масштабу — це наш досвід.







