Dropped features
What didn't make the cut and why
Dropped features
These features existed in v1 and intentionally don't ship in v3. Each section explains the rationale and the recommended replacement.
Custom commands (user-defined runtime slash commands)
v1 had a custom-command-schema and built-in /customcommand admin command that let server admins create new slash commands from inside Discord, persisted to MongoDB.
Why dropped: it's a whole feature inside a feature. It complicates persistence (every adapter would have to support it), it's a moderate fraction of v1's surface area, and most users either built their own version with richer behavior or never used it at all.
Replacement: build it yourself on top of v3 if you need it. Define an admin command (/define) that writes to your own table and dynamically registers handlers via your application logic. Your data shape, your UI.
Built-in default admin commands
v1 shipped 8 commands automatically: prefix, slashcommand list/delete, togglecommand, embed, channelonly, requiredPermissions, requiredroles, etc.
Why dropped: every bot has different opinions about how admin UI should look. Shipping these as defaults forced users to either accept them or fight to disable them.
Replacement: each helper is now a one-liner you can call from your own admin command. See Persistence — setGuildPrefix, disableCommand, lockCommandToChannel, etc.
FeaturesHandler
v1's featuresDir walked a directory and called require(file)(commandHandler, client) for each entry. It worked but was bundle-hostile (uses dynamic require()) and listener leaks were common.
Why dropped: the plugin system is a strict superset — same dynamic-loading capability, plus lifecycle hooks, type safety, and cleaner unmounting on destroy().
Replacement: factory functions returning PluginManifest, registered via the plugins: [...] option.
Anti-crash mode
v1 had antiCrash: true that registered an uncaughtException handler keeping the process alive after errors.
Why dropped: this hides bugs. Production deployments should let the process crash and restart via PM2 / systemd / Docker — that way crash loops show up in monitoring and the bug actually gets noticed.
Replacement: handle it at the process level if you really want it:
process.on("uncaughtException", (err) => {
console.error("Uncaught exception:", err);
});
process.on("unhandledRejection", (err) => {
console.error("Unhandled rejection:", err);
});Or better: don't add it at all. Let your supervisor restart the bot on crash and pipe logs to a place you'll actually see them.
testServers (deploy-only-on-startup gate)
v1 had a testServers option that registered new commands against a list of guild IDs first, then promoted to global on success.
Replacement: use explicit Discord registration scopes.
createCommandHandler({
client,
commandDir: "./src/commands",
registration: {
global: "clear",
guilds: [{ id: "123456789012345678", mode: "sync" }],
},
});Guild sync is useful for private single-server bots and fast iteration, not only tests. sync overwrites the configured scope with the current command list; clear overwrites it with an empty list. Discord commands persist until overwritten or cleared.
cooldownConfig.dbRequired (5-minute DB persistence threshold)
v1 had a magic 300-second threshold above which cooldowns were persisted to MongoDB and below which they were in-memory.
Why dropped: it was a heuristic that pleased no one. v3 makes it explicit: in-memory by default; provide cacheAdapter (e.g. @djs-commands/adapter-redis) if you want distributed cooldowns regardless of duration.
Replacement: cacheAdapter: redisCacheAdapter(redis) from @djs-commands/adapter-redis.
Last updated on
