Your API has a few hundred users and no shared place to ask a question. Support arrives by email, in GitHub issues that are not bugs, and in replies under a release announcement. Each answer is written once, read once, and lost, so the same three questions come back every week and nobody outside the team can find last month’s reply.
What you get
You will end up with a scored comparison of six options and a weight set that states what your first year needs. One platform comes out on top, and you commit to it until a review date you set. This is for you if you run developer relations for an API or SDK and have no community platform yet.
Short answer
Score Discord, Discourse, GitHub Discussions, Slack, and Zulip on six things that decide a first year. Those are whether answers are readable without an account, moderation tooling, whether a developer can join with an account they already hold, cost, data export, and speed. Weight them for your situation, take the top score, and stay on it for the year. A forum wins on search and export, chat wins on speed, and launching on both splits the same thirty people.
You will need
Node 22 or later, and an API or SDK with at least one external user, because a community with no members is a decision with no evidence. Verified 2026-09-25 against Node 22.22.2. The scoring is a file and a script rather than a slide, so a colleague can dispute one cell and rerun it. Slack sits in the table because most teams already have it open, and its free plan hides messages after 90 days.1
Approaches compared
| Approach | When it fits | What it costs you | When to pick something else |
|---|---|---|---|
| Discord | Users who want to talk in real time, voice included, and a team online most of the day | Every answer is invisible to a search engine, so the same question is asked and answered again each month | Search traffic matters more than presence, or nobody can moderate in the evenings |
| Discord plus Answer Overflow | An existing Discord whose help threads deserve to be found | A second service to run or pay for, and only threads in the channels you select reach the web | You have not launched anywhere yet, so a forum costs less than a bridge |
| Discourse | A small team that needs moderation to keep working while it sleeps | Hosting at 100 dollars a month or a server you run, and a slower conversation than chat | Your users are on GitHub already and the questions are about the code |
| GitHub Discussions | Users who already hold a GitHub account and questions that live near the code | No trust levels or limits on a new account, and an export that exists only through the GraphQL API | Your users are not on GitHub, or you may leave GitHub |
| Slack | A private customer group where every member is invited by name | A separate account per workspace, and on the free plan messages leave view after 90 days | The community is public, or answers must outlive a quarter |
| Zulip | Chat that must also be readable by the public, on a sponsored or self-hosted plan | Web-public channels are off by default and tied to a plan, and the topic model takes explaining | Your users expect Discord or a forum, and nobody will learn a third shape |
Chat and forum are the two shapes, and the rest is detail. Discord gives the fastest conversation and the least searchable one. Discourse gives every thread a public page and trust levels that limit what a new account can do, at the cost of a hosting bill and a slower pace. GitHub Discussions gives the reader an account they already have and no way out other than the API. Answer Overflow narrows the gap for Discord without closing it.
Score what each platform can show a search engine
The inventory is a JSON file with one entry per platform and one score per criterion, on a scale from 0 to 3. Each score carries the reason and the page the reason was read from.
"name": "Discourse",
"scores": {
"search": {
"score": 3,
"why": "A server-rendered crawler layout with structured data, and allow_index_in_robots_txt on by default",
"source": "https://meta.discourse.org/t/how-public-discourse-sites-are-indexed-by-search-engines-like-google/1184"
},
"moderation": {
"score": 3,
"why": "Five trust levels that limit new accounts and promote regulars automatically, plus flags and staff roles",
"source": "https://meta.discourse.org/t/trust-level-permissions-reference/224824"
},
A score without a source is an opinion, and the validator refuses it. Two scores carry the
argument. Discord scores 0 on search because its messages are read through
the messages API by a client holding
VIEW_CHANNEL and READ_MESSAGE_HISTORY, which a crawler does not hold. Discourse scores 3
because it detects a crawler by user agent and serves it a server-rendered layout with
structured data.2 GitHub Discussions scores 3 for a plainer reason: a discussion
inherits its repository’s visibility, so
on a public repository it is a public page.
Moderation is where the forum earns its second point. Discourse ships five trust levels, and a new account cannot send messages, post attachments, or post more than two links until it has read enough to leave level 0.3 Discord’s AutoMod blocks keywords and spam and can make a new member wait ten minutes before posting. None of GitHub’s tools, which are lock, delete, convert, and block a user, limits an account that joined this morning.
Weight the criteria for the year you are in
The weights are the only opinion in the file, and they are written down so they can be argued with.
export const PROFILES = {
'year one, a few hundred users, one part-time moderator': {
search: 3, moderation: 2, identity: 2, cost: 1, export: 2, realtime: 1,
},
'launch week, chat first, search and export set aside': {
search: 0, moderation: 1, identity: 1, cost: 1, export: 0, realtime: 3,
},
}
Search carries 3 in year one because an answer nobody can find is an answer that gets written again, by you. Export carries 2 because the year ends with a review, and a review that cannot move the archive is a review with one possible outcome. Cost carries 1 because every option here has a free path for a few hundred people, so the money is not where the cost is.
node demo.mjs
year one, a few hundred users, one part-time moderator
weights search 3, moderation 2, identity 2, cost 1, export 2, realtime 1
1. Discourse 28/33 85%
2. GitHub Discussions 25/33 76%
2. Zulip 25/33 76%
4. Discord plus Answer Overflow 19/33 58%
5. Discord 14/33 42%
6. Slack 13/33 39%
Discourse over GitHub Discussions: export, worth 4 points
Discourse over Zulip: search, worth 3 points
launch week, chat first, search and export set aside
weights search 0, moderation 1, identity 1, cost 1, export 0, realtime 3
1. Discord 15/18 83%
2. Discord plus Answer Overflow 14/18 78%
3. Slack 13/18 72%
3. Zulip 13/18 72%
5. GitHub Discussions 11/18 61%
6. Discourse 10/18 56%
Discord over Discord plus Answer Overflow: cost, worth 1 point
Discord over Slack: cost, worth 1 point
Read the second line under each ranking before the first. Discourse and GitHub Discussions tie on search, and what separates them is export. A Discourse administrator downloads a full database backup that restores onto any other Discourse. A GitHub discussion leaves through the GraphQL API or not at all. Against Discord the gap is search, whose nine points are far more than any other criterion contributes.
The second profile is the launch week, with search and export set to zero, and Discord wins it. The order moved because the weights moved. Any scoring you are shown should be able to do that, and one that cannot has the weights hidden in the scores.
Count what a split costs
The pitfall is opening two platforms on the same day, so that nobody has to choose. Thirty people who answer questions become fifteen and fifteen, which leaves every question seen by half a crowd.
export function seenWithinAnHour({ active, p, platforms = 1 }) {
if (!Number.isInteger(active) || active < 0) throw new Error(`active must be a whole number, got ${active}`)
if (!(p >= 0 && p <= 1)) throw new Error(`p must be between 0 and 1, got ${p}`)
if (!Number.isInteger(platforms) || platforms < 1) throw new Error(`platforms must be at least 1, got ${platforms}`)
const perPlatform = Math.floor(active / platforms)
return 1 - Math.pow(1 - p, perPlatform)
}
node split.mjs
30 active members, each looking in 13% of hours
1 platform 30 people each a question is seen within the hour 98% of the time
2 platforms 15 people each a question is seen within the hour 87% of the time
3 platforms 10 people each a question is seen within the hour 74% of the time
The model is a model: one look per member per hour, one in eight, independent of everything. It is not a measurement, but it is the right shape. The people are the scarce resource, the platform is not, and a second platform spends the people twice. The moderation load doubles in the same move, and the person who answers on both writes every answer twice.
Check it worked
Eleven tests pin the rules, and three matter. The year-one weights rank Discourse first and the launch weights rank Discord first, so the ranking is seen to follow the weights. Splitting thirty people across two platforms lowers the chance a question is seen within the hour. And a score with no source fails validation instead of ranking with a hole.
node --test score.test.mjs
1..11
# tests 11
# suites 0
# pass 11
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 114.243312
Rerun the tests after every edit to platforms.json. The literal Discourse in the year-one
test is the line to read: if it stops matching, a score changed, and the reason for the change
belongs in the cell beside it.
When it goes wrong
Every platform scores 3 on everything. The scores were written by the person who had already chosen. Require a source per cell, which the validator does, and have the person arguing for the losing platform write its row.
The ranking changes when a seventh criterion is added. The new criterion is carrying an opinion the weights were meant to carry. Keep the six, and change a weight instead, in the file, where the change is visible.
Discord wins in the meeting. The weights in the room were launch-week weights, and the year is not a launch week. Show both rankings, and ask which one the team will be living in by month nine.
The forum is quiet after the move. When thirty people were moved from a place with presence to a place without it, nobody told them what pace to expect. Publish an answer time, such as one working day, and meet it, because a forum runs on the expectation of a reply rather than the sight of one.
When not to do this
Do not run this scoring when the platform has been chosen for you. A partner’s Discord, a customer’s Slack Connect channel, and a company forum that already exists are constraints. The useful work is scoring what can be done about them, such as Answer Overflow on the Discord you were handed.
Do not launch on two platforms at once to avoid the decision. The split model is the whole argument, and it understates the cost, because the moderation load doubles and the same three people answer every question twice.
Do not put cost ahead of everything for a community of a few hundred people. Every option here has a free path, and the year of evenings you spend moderating is the cost the table cannot show.
Do not treat the year as a trial to abandon in the first quiet month. A defined year exists to stop the re-deciding, and a platform that is quiet in month two is quiet because it is month two. Set the review date on the day you launch, and score again on that date with the same file.
Related how-tos
Last verified
Verified 2026-09-25 against Node 22.22.2. Every output block is what the command preceding it printed. Each score in the inventory was read from the platform’s own documentation on that date, and the page it came from is in the cell beside it. The weights and the split model are this page’s own rather than the result of measuring any platform in use.
Footnotes
-
Slack’s usage limits page says a free workspace hides messages older than 90 days to make room for new ones, and deletes anything older than a year. The page does not say what the room is made of. A tip one paragraph earlier says that upgrading removes the limit, without adding any room. ↩︎ Back to text
-
Discourse renders every public page twice. The indexing reference says a crawler, detected by its user agent, is served a separate server-rendered layout with
DiscussionForumPostingmarkup and pagination links. A browser with JavaScript switched off gets anoscriptcopy of the same topic. The rendering built for machines is the one made of plain HTML. ↩︎ Back to text -
Discourse has five trust levels, numbered 0 to 4, and its own documentation has two names for the top one. The permissions reference lists level 4 as Inner Circle, and the detailed explanation lists it as Leader, reached by manual promotion only. Discourse’s 2018 post on the subject, as it reads today, adds that a site begins in bootstrap mode and grants level 1 to its first 50 accounts. The founders of a community never meet the limits they keep for everyone who arrives later. ↩︎ Back to text