· 5 min read ·

Commands Are Contracts: What Bot Development Shows About Spec-Driven AI Workflows

Source: hackernews

Discord bot development has a property that most application development lacks: every user-facing feature comes pre-specified as a contract. Slash commands register with Discord’s API with an explicit schema. The name, description, option types, required versus optional parameters, and valid choices are all declared before the command runs for the first time. Discord enforces that schema at the API level and will reject an invocation that does not match the registration.

That constraint, which feels like overhead when you first encounter it, turns out to be useful when working with AI coding tools. The forced contract means you have a natural spec document before a single line of handling code exists. Get Shit Done (GSD), a meta-prompting and context engineering system that appeared on Hacker News with 237 points, argues that this kind of upfront specification should be the default workflow regardless of whether your platform enforces it. Bot development is a domain where you can see why.

The Interface You Get for Free

When you define a slash command in Discord.js, the registration call is effectively a machine-readable spec:

{
  name: 'remind',
  description: 'Schedule a reminder message',
  options: [
    {
      type: ApplicationCommandOptionType.String,
      name: 'time',
      description: 'When to send the reminder (e.g., 30m, 2h, 1d)',
      required: true
    },
    {
      type: ApplicationCommandOptionType.String,
      name: 'message',
      description: 'The reminder text',
      required: true
    }
  ]
}

This registration document is already doing something close to what GSD’s spec templates ask you to create manually: naming the feature, describing its purpose, and declaring inputs with types. What it does not capture is behavior under failure, state persistence requirements, per-user limits, and edge cases. Those live implicitly in your head or, if you are working with an AI coding tool, in the model’s training distribution.

The GSD spec format adds those missing layers:

**Constraints**:
- Maximum reminder duration: 7 days
- Maximum active reminders per user: 10
- Times stored in UTC, displayed in user's registered timezone
- All pending reminders survive bot restarts (restore on `ready` event)

**Error cases**:
- Invalid duration format: reply with format examples
- Duration exceeds maximum: reply with the limit
- User at cap: reply with count and offer to list existing reminders

The restart-survival requirement is the revealing one. It is absent from the slash command registration, invisible to anyone looking only at the API schema. Without an explicit spec, a model generating the /remind implementation will produce in-memory storage: a Map keyed by user ID, perhaps, allocated at startup. That works fine until the bot process restarts after a deployment, a crash, or a routine host cycle. Users discover the missing persistence requirement in production, after the fact.

This is not a failure of model capability. It is a failure of specification. The model had no way to know that persistence across restarts was required because no one wrote it down before code generation started.

State That Accumulates Across Sessions

Bot features accumulate state in ways that stateless web endpoints do not. Guild-specific configurations, per-user preferences, pending scheduled tasks, conversation histories for AI-integrated commands, cooldown tracking for rate-limited actions: a non-trivial bot is a stateful system with multiple persistence layers. As the project grows, the interactions between those layers become complex enough that they cannot be reliably held in the implicit context of a single AI session.

Context engineering, as GSD frames it, is the practice of treating the model’s information environment as something you manage deliberately rather than let drift. For a bot codebase, that means maintaining a document about what state exists and where it lives: which tables persist which data, which Discord events are responsible for restoring in-memory state at startup, which features depend on state that may not yet be populated for new guilds. The document is written not for human readers but as structured context the model loads at the start of every session.

Research on how transformers attend to information in long contexts shows that content early in the context window is more reliably recalled than content introduced mid-session. An architectural constraint described three hours into a long development session may not be consistently applied by the time the model acts on it. The fix, injecting a stable context anchor document at the start of each session, is the same thing Claude Code’s CLAUDE.md and Cursor’s .cursorrules do for project conventions. GSD’s context anchor extends the scope to include active state and pending decisions, not just static coding conventions.

Meta-Prompting at the Feature Level

The feature where meta-prompting pays most clearly in bot development is anything with a non-trivial sequencing problem. Consider building a workflow system where users define multi-step automations triggered by Discord events: a /workflow create command that lets users chain message conditions to actions like role assignment, notifications, or timed follow-ups.

The implementation involves registering the commands, designing the workflow definition schema, building the execution engine, handling partial step failures, persisting state between steps, and restoring in-progress workflows on restart. Asking a model to implement the workflow system produces code. Asking it first to produce an implementation plan from the spec, including what decisions need to be made before each step and what information is needed to make them, produces a reviewable plan before any code is generated.

That plan might reveal that the workflow schema design decision precedes the command registration, because the slash command options will reflect what schema fields users can configure. That dependency is invisible when planning happens implicitly inside code generation. At the plan level it is explicit and reviewable before any code exists. Mistakes caught at the plan level cost a few minutes of revision. Caught at the code level, the same mistake may require reworking the schema and migrating existing data.

This is what GSD’s meta-prompting phase does: it turns implicit planning into a reviewable artifact. The model’s choices about sequencing, decomposition, and trade-offs become visible before implementation rather than embedded invisibly in generated code.

What This Generalizes To

Bot development makes these patterns legible because the constraints are unusually concrete. Slash command registration enforces explicit interfaces at the platform level. Restart survival is a testable requirement with an obvious failure mode. State accumulation across guild configurations and user preferences makes context management tangible rather than abstract.

The same pressures exist in any sufficiently complex software project, without the platform-level forcing function. API design has interface contracts that benefit from the same upfront specification. Service boundaries have state and lifecycle assumptions that drift silently when working with AI tools across long sessions. Features with non-obvious sequencing dependencies produce better results when planned explicitly before generation rather than implicitly during it.

The Hacker News reception of GSD skewed heavily toward recognition: developers who had arrived at versions of these practices independently through correction loops and production surprises. The project’s contribution is encoding those practices into templates and workflow steps before you have paid the discovery cost.

For bot development, the spec habit is nearly forced by the platform. The argument GSD is making is that you should apply the same discipline to features where nothing forces it, because the underlying reasons the platform demanded it, explicit interfaces, persistent state, defined failure modes, apply everywhere the model is filling in gaps from implicit context.

Was this interesting?