Build Featurebase
KINDAreplaces $59/mosaves $708/yrback to the verdict
A portal where users post and vote on requests, an admin view that merges duplicates while preserving every voter and comment, themes linking related requests to the evidence behind them, a roadmap generated from status rather than maintained separately, and a changelog that closes the loop with the people who asked.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the portal.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree
Why Merging needs transactions and duplicate detection needs full text search. Both are here.
Get it Install locally or run it in Docker. open ↗
Verify
psql connects and reports 16 or higher - API keyfree tiers usually cover a small portal
Why Verification links and status notifications are the whole loop. Without delivery, this is a suggestion box nobody hears back from.
Get it Create an account, add the DNS records they specify, take the credentials. Domain verification is the slow step, so start it first.
Verify
A verification link email arrives in an inbox and is not in spam - have readyroughly 10 to 15 USD a year
Why Needed for the certificate and for the email sending records. A public portal on a raw address will not be trusted.
Get it Any registrar, then add the A record and the email verification records.
Verify
The hostname resolves and serves a valid certificate - have readyfree
Why Duplicate detection, themes and distinct voter counts are all meaningless against invented data, and real feedback contains the near-duplicates the whole design is about.
Get it Import from wherever feedback currently lives: a spreadsheet, a support inbox, a chat channel. A hundred real items is enough.
Verify
At least a hundred real items are loaded, including some near-duplicates
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- users(id, email, name, verified_at, is_admin, notify_on_status) - posts(id, title, body, author_id, status, board_id, created_at, merged_into_id, pinned) · merged_into_id is what makes a merge non-destructive - votes(post_id, user_id, created_at) · unique on the pair, so one person is one vote - comments(id, post_id, author_id, body, created_at, is_internal) - themes(id, name, description), theme_posts(theme_id, post_id) · a theme groups requests as evidence for a decision - changelog(id, title, body, published_at, linked_post_ids_json) - statuses(key, label, colour, on_roadmap, notify_on_enter) · configurable, because every team's workflow differs - A merge never deletes. It points one post at another and moves the votes, so the evidence survives and the count is right.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_URLsecret | required | postgres://user:pass@localhost:5432/feedback | Your PostgreSQL connection string. |
PUBLIC_URL | required | https://feedback.example.com | Appears in verification links and notifications, so it must be correct before the first submission. |
EMAIL_SERVERsecret | required | smtp://user:pass@smtp.provider.com:587 | Transactional provider credentials. |
EMAIL_FROM | required | feedback@example.com | Must be on the verified sending domain. |
LINK_SECRETsecret | required | generate-a-long-random-value | Signs verification and unsubscribe links. Rotating it invalidates outstanding links. |
MAX_POSTS_PER_EMAIL_PER_DAY | required | 5 | Submission rate limit per address. |
ROADMAP_STATUSES | required | planned,in-progress,shipped | Which statuses appear on the roadmap. The roadmap is generated from these and maintained nowhere else. |
The build, in order
A portal people can actually post to
Someone can submit a request and vote without creating a password.
Post list sorted by votes, with a submission form asking for as little as possible.
Files
src/board.jsNo password. A signed, expiring link that confirms the address and nothing more.
Files
src/auth.jsA unique constraint on post and user. Application-level checks race and eventually double count.
Files
migrations/
done when · tick each as it passeswatch out- Requiring account creation before someone can post loses most of the feedback you would have received, because people submit in the thirty seconds they are annoyed. Verify by email link after submission rather than requiring a password before it, so the barrier comes after the contribution rather than before.
Catching duplicates before they exist
Someone about to post the fourth copy of a request sees the first three.
A generated search column with an index. Indexing at write time rather than searching with wildcards.
Files
migrations/Debounced, showing vote counts, because a request with two hundred votes is more persuasive than one with two.
Files
src/suggest.jsWithout leaving the form. Any friction here and people post the duplicate anyway.
Files
src/suggest.js
done when · tick each as it passeswatch out- Merging duplicates after the fact is real work that never finishes, and the vote counts are misleading until it is done. Search at submission time is the cheapest possible intervention and prevents most duplicates, provided voting on the suggestion is one click rather than a navigation away.
Merging without losing anything
Two requests become one, and nobody's vote or comment disappears.
Votes moved with deduplication, comments moved with provenance, the source pointed at the target. All or nothing.
Files
src/merge.jsSomeone who voted on both is one vote in the target. Test with a hand-counted case.
Files
src/merge.test.jsPeople have shared it. A dead link makes the merge look like deletion.
Files
src/routes.js
done when · tick each as it passeswatch out- Deleting the duplicate loses its comments and breaks every link anyone shared, including from support tickets and chat. Keep the post and point it at the target, so an old link redirects and the discussion history stays intact.
Themes, which is where decisions come from
Related requests group into a theme you can act on, with the evidence attached.
A problem statement plus its evidence. Themes are how you decide; posts are how you gather.
Files
src/themes.jsAcross the theme's posts, deduplicated. Test with an overlap you constructed.
Files
src/themes.test.jsEvery post in the theme, so a decision can be justified from what people actually said.
Files
src/themes.js
done when · tick each as it passeswatch out- Summing vote counts across a theme's posts double counts anyone who voted on more than one, which inflates the theme and misranks it against others. Count distinct voters, which is the number that actually answers how many people want this.
Roadmap and changelog that close the loop
Status changes are visible, and the people who asked are told when it ships.
Configurable which statuses appear. No separately maintained list anywhere.
Files
src/roadmap.jsThe link is what makes the notification meaningful and the loop closed.
Files
src/changelog.jsAn outbox with a sent record per user per entry. Publishing twice must not notify twice.
Files
src/notify.js
done when · tick each as it passeswatch out- A roadmap maintained as its own list drifts from the actual statuses within weeks, and then it lies to people. Generate it from status so it cannot disagree, and let configuring which statuses appear be the only control you need.
Moderation, deletion and running it publiclyproduct builder
A public portal survives contact with the public, and personal data can be removed.
Publicly invisible until the link is followed. This alone removes most spam from the board.
Files
src/moderation.jsPin, close, hide, ban, each recorded with who and when.
Files
src/moderation.jsPosts, votes, comments and notification records. Verify by searching for their content after.
Files
src/delete.js
done when · tick each as it passeswatch out- A public form collecting email addresses will receive spam within days of being indexed, and the first response is usually to disable submissions. Rate limit by address and by network, and require the verification link before a post is publicly visible, so spam never reaches the board.
That is the whole plan for Featurebase. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- The integrated help centre and survey suite.
- Custom domains and single sign-on.
- Model-based duplicate detection and theme suggestion.
- A participant panel beyond people who find the portal.
- integrated suite, AI, custom domains, SSO, and hosted delivery
- large participant panel
- recording and transcription infrastructure
- advanced research repository
- enterprise governance
- Private boards for internal requests alongside the public one
- A weekly digest of new posts, which is how the loop stays warm without notifications per post
- Linking a post to an issue in your tracker, so status updates without a second edit
Need the files? The project pack on the verdict page hands your agent the whole brief · more user research.