How I Built a Discord Bot Platform That Got 1,500 Users
The full story of VibeCord — architecture decisions, what broke, what worked, and what I'd do differently building a Discord bot platform from scratch.
In 2024 I built VibeCord — a platform that lets Discord server owners create, configure, and deploy bots without writing code. It got to 1,500 users. This is the honest account of how it happened, what broke, and what the code looked like.
What VibeCord does
The core idea: Discord server owners are not developers, but they want bots with complex behaviour — economy systems, moderation flows, games, role management. Building these requires knowing Discord.js, Node.js, webhooks, and deployment. Most people don’t.
VibeCord abstracts that. You configure your bot through a UI, and the platform runs it for you.
Architecture — how it’s structured
The system has four main parts:
1. The bot runtime — a Discord.js process that runs bots for each server. One process handles multiple servers via a token-per-bot model, not a single shared bot.
2. The configuration API — a REST API (Node.js, Express) where the frontend sends configuration changes. Changes apply to the running bot in real time without a restart.
3. The frontend dashboard — a React app where users configure their bots. Built with TypeScript and Tailwind.
4. The database — PostgreSQL. Every server’s configuration, user data, economy state, and bot settings live here.
[Discord] ←→ [Bot Runtime (Discord.js)]
↑
[Configuration API (Express)]
↑
[React Dashboard]
↑
[PostgreSQL Database]
The constraint that shaped everything: configuration changes had to be instant. Users expect to hit “save” and see the bot change behaviour within seconds, not after a restart.
The thing that broke first
The first version used a monolithic event handler — one giant switch statement that handled every Discord event. It worked for one bot. It fell apart at five.
The problem: event handlers shared state. A bug in the economy module would corrupt the moderation module’s data because they both read from the same in-memory cache without locks.
The fix was a bounded context architecture — each feature (economy, moderation, roles, games) became an isolated module with its own state, its own database tables, and its own event subscription. Modules communicate through explicit interfaces, not shared memory.
// Before — everything in one place
client.on('messageCreate', async (msg) => {
if (msg.content.startsWith('!bal')) { /* economy */ }
if (msg.content.startsWith('!warn')) { /* moderation */ }
if (msg.content.startsWith('!play')) { /* games */ }
// ... 300 more lines
});
// After — isolated modules
class EconomyModule {
private readonly db: EconomyRepository;
async handleMessage(msg: Message): Promise<void> {
if (!msg.content.startsWith('!')) return;
const command = this.parseCommand(msg.content);
await this.dispatch(command, msg);
}
}
This made testing possible. Each module could be unit tested independently, which led to the test suite growing to 2,550 tests with 85%+ coverage.
The performance problem
At ~200 active servers, the bot started lagging. Commands took 3-4 seconds to respond. Discord has a 3-second threshold before users notice.
Root cause: every command was doing a full database round-trip for configuration data that almost never changed. Server configuration (prefix, enabled modules, role IDs) was being fetched from PostgreSQL on every single message.
Fix: a tiered cache.
class ConfigCache {
private readonly memory = new Map<string, ServerConfig>();
private readonly ttl = 5 * 60 * 1000; // 5 minutes
async get(guildId: string): Promise<ServerConfig> {
const cached = this.memory.get(guildId);
if (cached && !this.isExpired(cached)) return cached.data;
const config = await this.db.getServerConfig(guildId);
this.memory.set(guildId, { data: config, timestamp: Date.now() });
return config;
}
invalidate(guildId: string): void {
this.memory.delete(guildId);
}
}
When a user saves configuration through the dashboard, the API calls configCache.invalidate(guildId) — the next command forces a fresh DB read, then caches again.
Response times went from 3-4s to under 200ms.
Getting to 1,500 users
Distribution was almost entirely word of mouth through Discord communities. The Discord bot ecosystem has a strong sharing culture — if your bot solves a real problem, people post it in bot-sharing servers.
The tipping point was a Reddit post in r/discordapp that got traction. 400 new signups in 48 hours. The infrastructure held — the caching work meant the extra load didn’t cause visible slowdowns.
What didn’t work: trying to grow through bot listing sites like top.gg. The conversion rate was low and the audience was mostly developers who’d rather build their own bot than use a platform.
The refactor that cut 62% of the code
By mid-2024 the codebase had grown to ~15,000 lines. A lot of it was defensive code — null checks, type coercions, duplicated validation logic that had accumulated over months of quick fixes.
An eight-phase refactor brought it to ~5,700 lines with no breaking changes. The key technique: extract and centralise. Every place where null checking was duplicated became a single typed guard function. Every place where Discord entities were coerced became a single resolveUser(id, guild) helper.
The test suite caught every regression. This is the only reason I could do a refactor this aggressive — without 2,500+ tests I’d never have had confidence the behaviour was preserved.
What I’d do differently
1. Start with the cache layer. The performance problems were predictable. Any bot platform at scale will hammer the database on every event. Build the cache on day one.
2. Bounded contexts from the start. The rewrite to isolated modules took two weeks. Two weeks I wouldn’t have needed if I’d structured it right initially.
3. Charge earlier. The platform was free for too long. Monetisation adds useful constraints — it forces you to understand which features users actually value vs which they tolerate.
The code is open source
The bot platform is part of my public portfolio. If you’re building something similar — a multi-tenant bot system, a Discord integration, or a real-time configuration system — I’m happy to talk through the architecture. Get in touch →
Short notes on building AI agents in production.
One email when something worth sharing ships. No fluff, no daily cadence, no recycled growth-thread noise.
Primary use: consulting updates, governed AI workflow lessons, and major project writeups.
Related Posts
How I Run Parts of calvinkennedy.com with a Governed AI System
A case study on using OpenClaw and Portarium to handle bounded business workflows on calvinkennedy.com without pretending the whole business is autonomous.
I Made $18 and Thought I Made $0: A VibeCoord Post-Mortem
My analytics said $0 revenue and 0 bot activations. Both were wrong. A full post-mortem on what I built, why it failed, and what the data actually said.
How I Used AI Agents to Automate Content Creation (End to End)
The architecture behind Content Machine — an AI agent pipeline that goes from topic to published video without human intervention. What worked, what didn't, and the real bottlenecks.