
> **TL;DR:** Pick your riskiest module (billing, auth, or entitlements). Write down what must happen, what must not happen, and the edge cases. Put it in a `.pbc.md` file in your repo. That's it — you now have a behavior spec. This guide walks through the whole process.

Every other post on this blog explains why behavior specs matter. This one shows you how to write one.

You'll need: a code editor, 15 minutes, and one module in your product where an agent mistake would actually hurt.

## Step 1: Pick the module

Don't try to contract your entire product. Start with the module where getting it wrong costs the most.

Good first candidates:
- **Billing** — grace periods, refund windows, proration, plan limits
- **Auth** — session handling, permission checks, account lockout
- **Entitlements** — what free users can and can't do, trial limits
- **Onboarding** — the flow that shapes first impressions

Pick one. For this tutorial, we'll use billing.

## Step 2: Create the file

Create a file called `billing.pbc.md` in your repo. The `.pbc.md` extension is a convention — it's still plain Markdown. Put it wherever makes sense: the root, a `docs/` folder, or alongside the code it describes.

Start with frontmatter:

```yaml
---
id: billing
title: Billing & Subscriptions
context: billing
status: draft
updated: 2026-04-04
---
```

The `status: draft` is honest — you're writing this for the first time. It'll become `trusted` once your team has reviewed and confirmed the behaviors.

## Step 3: Define the scope

Write a short section describing what this contract covers and what it doesn't. This prevents scope creep and tells readers (human and agent) where the boundaries are.

```markdown
# Billing & Subscriptions

## Scope

- Subscription lifecycle (trial, active, past due, cancelled, expired)
- Payment failure and grace period behavior
- Plan limits and what happens when they're exceeded

## Non-goals

- Payment gateway API details (that's implementation)
- Invoice formatting (that's presentation)
- Tax calculation logic (that's a separate module)
```

## Step 4: List your terms

If your module uses domain-specific language, define it. This eliminates ambiguity — the grace period means exactly this, not whatever someone infers from the code.

```markdown
## Glossary

| Term | Definition |
| --- | --- |
| Grace period | The 14-day recovery window after a failed renewal payment. |
| Billing cycle | The recurring period (monthly or annual) between charges. |
| Plan limits | The monthly usage caps for a given subscription tier. |
```

## Step 5: Write the behaviors

This is the core of the contract. For each behavior, write:

- **Given** — the preconditions (what must be true before this happens)
- **When** — the trigger (what kicks off this behavior)
- **Then** — the outcomes (what must happen)

Start with the happy path, then add failure cases and edge cases.

```markdown
## Behaviors

### Automatic renewal

#### Given

- The subscription is active
- The current billing cycle ends today
- A valid payment method exists

#### When

The system processes the subscription for renewal.

#### Then

1. A charge is submitted to the payment processor
2. On success: subscription stays active, billing cycle advances
3. Plan limits reset for the new period

---

### Payment failure and grace period

#### Given

- The subscription is active
- A renewal charge fails

#### When

The payment processor reports a failed charge.

#### Then

1. Subscription moves to past_due status
2. User retains full access for 14 days (the grace period)
3. System retries the payment on days 1, 3, and 7
4. If payment succeeds during grace period: subscription returns to active
5. If grace period ends without payment: subscription moves to expired

#### Invariants

- Grace period is exactly 14 days — not configurable per plan
- No data deletion occurs during grace period
- User is notified on each retry attempt

#### Edge cases

- If user manually updates their payment method during grace period: immediate retry
- If user downgrades plan during grace period: grace period continues on the new plan
```

That's a behavior spec. It's readable by your team, your agents, and anyone who touches this code six months from now.

## Step 6: Add structured blocks (optional but powerful)

Plain Markdown is already useful. But if you want tools and agents to parse the spec deterministically — not just read it as prose — you can add `pbc:*` fenced blocks.

These are YAML blocks inside Markdown code fences with a `pbc:` prefix. They look like code blocks to a Markdown renderer, but a PBC-aware tool can extract them as typed data.

For example, add a structured version of the glossary:

````markdown
```pbc:glossary
- term: Grace period
  definition: The 14-day recovery window after a failed renewal payment.
- term: Billing cycle
  definition: The recurring period (monthly or annual) between charges.
- term: Plan limits
  definition: The monthly usage caps for a given subscription tier.
```
````

And a structured version of a behavior:

````markdown
```pbc:behavior
id: BIL-BHV-002
name: Payment failure and grace period
actor: billing_scheduler
description: When a renewal charge fails, the system enters a 14-day grace period with retries before expiring the subscription.
```

```pbc:preconditions
- The subscription is in active.
- A renewal charge fails.
```

```pbc:trigger
The payment processor reports a failed charge.
```

```pbc:outcomes
- Subscription moves to past_due.
- User retains full access for 14 days.
- System retries payment on days 1, 3, and 7.
- If payment succeeds: subscription returns to active.
- If grace period ends without payment: subscription moves to expired.
```
````

You don't have to add structured blocks for everything. Start with plain Markdown. Add `pbc:*` blocks to the behaviors that are most critical — the ones where you want a tool to be able to verify they're still honored.

## Step 7: Point your agents at it

Add a line to your CLAUDE.md, AGENTS.md, or whatever instruction file your agents read:

```markdown
Before modifying any billing, auth, or entitlement logic, read the relevant
`.pbc.md` file in this repo. Do not change behaviors marked as invariants
without explicit approval.
```

This is the bridge between your existing agent workflow and the behavior spec. The instruction file says "look here first." The behavior spec says "here's what the product promises."

## Step 8: Review with your team

Share the `.pbc.md` file with your team. Ask two questions:

1. **Is this accurate?** Does the spec match what the product actually does?
2. **Is anything missing?** Are there behaviors or edge cases that should be captured?

When you and your team agree the spec is accurate, change `status: draft` to `status: trusted`.

That's it. You have a living behavior spec — your product truth — in your repo.

## What you just built

In 15 minutes, you created an artifact that:

- **Humans can read** — plain Markdown, renders on GitHub, in any editor
- **Agents can reference** — structured enough to check decisions against before coding
- **Tools can parse** — `pbc:*` blocks are deterministically extractable
- **Lives in your repo** — version-controlled, diffable, evolves with your code

You didn't write documentation. You wrote product truth — a structured statement of what the product promises to do, explicit enough that both humans and agents can hold the code accountable to it.

## Next steps

- Browse the full [billing example](https://pbc.stewie.sh) in the PBC viewer to see what a complete behavior spec looks like
- Read the [PBC spec](https://github.com/stewie-sh/pbc-spec) for the full list of `pbc:*` block types (states, actors, rules, config, events)
- Add a second behavior spec for your auth or entitlements module
- If you want Stewie to extract candidate behaviors from your codebase automatically: [request early access](https://tally.so/r/PdlkLV)
