Вы выпустили новую фичу, но через месяц её использует лишь 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('ru-RU')}
</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-продуктов разного масштаба — это наш опыт.







