Большинство команд держат обратную связь и дорожную карту в двух разных системах. Доска обратной связи, где пользователи предлагают идеи и голосуют, и публичная дорожная карта, где команда показывает, что будет дальше. Два инструмента, две базы данных и ручное копирование между ними, которое где-то на шестой неделе прекращается.
Потом дорожная карта устаревает, пользователи это замечают, и она тихо пропадает из меню.
Это одни и те же записи
Пункт дорожной карты — это пост обратной связи, который приняли в работу. Вот и вся связь между ними. Если так и устроить данные, ручное копирование исчезает: копировать нечего.
В Kookee посты обратной связи лежат на доске с колонками, которые задаёт ваша команда: «Открыто», «Запланировано», «В работе», «Выпущено» — какие подходят. Доска обратной связи показывает все колонки. Дорожная карта показывает те же посты, сгруппированные по колонкам, которые вы решили сделать публичными:
const columns = (await kookee.feedback.getColumns()).filter((column) => column.isVisible);
const { data: posts } = await kookee.feedback.list({ sort: 'top', limit: 100 });
const roadmap = columns.map((column) => ({
...column,
posts: posts.filter((post) => post.columnId === column.id),
}));
Отдельного API для дорожной карты нет, потому что он не нужен. Чтобы передвинуть карточку, её перетаскивают в другую колонку, и делает это тот же человек, который и так разбирает обратную связь. В React компонент Roadmap из @kookee/react/roadmap показывает ту же группировку как доску только для чтения — см. Дорожная карта только для чтения на компонентах React.
Голоса помогают расставить приоритеты, но не решают за вас
У постов есть счётчики голосов и комментариев, а sort: 'top' и sort: 'trending' показывают, что волнует людей сейчас:
const top = await kookee.feedback.list({ sort: 'top', category: 'feature', limit: 20 });
Стоит понимать, что это за данные. Голоса показывают, чего хотят самые громкие и вовлечённые пользователи. Это настоящий сигнал, но он не равен тому, что нужно всей вашей аудитории. Голоса помогают найти то, о чём вы не знали, но отдавать им дорожную карту целиком не стоит. Закрытая колонка «Отклонено» нужна как раз для того, чтобы отказывать публично и с объяснением причины.
Обратная связь собирается там, где идёт модерация
Посты, голоса и комментарии появляются на вашем готовом портале, куда посетители входят под своим аккаунтом. Так задумано: один человек — один голос, и вся модерация вашей команды в одном месте. Никакого спама через утёкший ключ, и не нужно со своего бэкенда ручаться, что пользователь — тот, за кого себя выдаёт.
Ваше приложение читает доску через API и показывает её как угодно: страница дорожной карты, блок «чаще всего просят» на странице цен, ссылка из пустого состояния функции на её обсуждение. Запись остаётся на портале, а читать доску можно откуда угодно.
Замкните цикл у всех на виду
Чтобы доска выглядела живой, больше всего делают две детали.
Комментарии владельцев и админов проекта помечены флагом isOfficial, поэтому ответ вашей команды выглядит иначе, чем ответ другого пользователя. А getTopContributors() возвращает людей, чьи посты собрали больше всего голосов. Это те несколько человек, которые раз за разом говорят вам правду о продукте, и такой список полезен далеко не только для рейтинга.
Цикл, который поддерживает доску живой, короткий: кто-то пишет пост, команда публично отвечает, карточка переезжает в другую колонку и появляется на дорожной карте. Потом функция выходит и попадает в журнал изменений — это ещё одна запись kookee.changelog в том же проекте, с тем же API-ключом.