DEV Community

Cover image for I Built a CLI to Extract Business Rules From Code — So They Never Get Lost Again
Farouk Mekkaoui
Farouk Mekkaoui

Posted on • Edited on

I Built a CLI to Extract Business Rules From Code — So They Never Get Lost Again

Run npx ruledoc and get a structured, always-up-to-date document of every business rule in your codebase. Zero install, zero config, zero dependencies. It works with TypeScript, JavaScript, Vue, and Svelte.

Here's the problem it solves.


The problem: business rules are invisible

A new developer joins the team. They're reviewing the billing module and see this:

export const FREE_PLAN_DAILY_LIMIT = 100;
Enter fullscreen mode Exit fullscreen mode

They ask: "Why 100? Who decided this? Can I change it?"

Nobody knows. The constant was added 18 months ago by someone who left. There's no Notion page, no Confluence doc, no comment explaining the reasoning. The answer lives in a Slack thread that's long gone.

This happens everywhere. Business rules — the most important logic in your app — are the least documented. They hide as constants, guard clauses, magic numbers, and config values scattered across dozens of files.

When business rules are invisible:

  • Bugs happen silently. Someone changes a limit without realizing three other features depend on it.
  • Onboarding is slow. New devs spend days asking "why does this work this way?"
  • Compliance becomes painful. When an auditor asks "show me your business rules," you can't just grep for them.
  • Documentation rots. Even if someone writes a wiki page, it goes stale the moment someone pushes a code change without updating it.

Why JSDoc, wikis, and rules engines don't solve this

You might think: "Just use JSDoc" or "We have a wiki." I looked at everything out there:

  • JSDoc / TSDoc document what a function does, not why a business decision was made. They don't give you a searchable registry of all your rules.
  • Wikis (Notion, Confluence) go stale the moment someone pushes code without updating the doc. And they always do.
  • TODO scanners (leasot, todo-tree) find tagged comments, but they have no semantic structure — no scopes, no severity levels, no grouped output.
  • Business rules engines (json-rules-engine, GoRules) execute rules at runtime. They don't document the rules already living in your code.
  • ADRs are great for architecture decisions, but too heavy for the specific limits, thresholds, and policies buried across your files.

I couldn't find a single tool that extracts and documents business rules from code comments. So I built one.

How ruledoc works

The idea is simple: annotate business rules right where they're implemented, in a structured format that a CLI tool can extract.

1. Annotate your code

// @rule(billing.plans, critical): Free plan is limited to 100 API requests per day
export const FREE_PLAN_DAILY_LIMIT = 100;

// @rule(auth.session): Session expires after 24 hours of inactivity
const SESSION_TTL_MS = 24 * 60 * 60 * 1000;

// @rule(billing.refunds, critical): Refunds must be requested within 30 days
export const REFUND_WINDOW_DAYS = 30;
Enter fullscreen mode Exit fullscreen mode

This is a regular comment — your code runs exactly the same. But now that rule is machine-readable: it has a scope (billing.plans), a severity (critical), and a description.

2. Run it

npx ruledoc
Enter fullscreen mode Exit fullscreen mode

3. Get structured documentation

ruledoc generates a BUSINESS_RULES.md grouped by scope, with a table of contents and summary table:

# Business Rules

> 10 rules · 3 scopes

## Summary

| Scope | Sub | Count | Critical | Warning |
|-------|-----|------:|---:|---:|
| Auth | Session | 2 | 1 | 1 |
| Billing | Plans | 3 | 2 | — |
| Billing | Refunds | 1 | 1 | — |
Enter fullscreen mode Exit fullscreen mode

It also generates BUSINESS_RULES.json for tooling, and optionally a standalone HTML page with search and filters.

Annotation validation and typo detection

ruledoc doesn't just extract — it validates. Misspell a severity?

// @rule(auth.session, crtical): Session expires after 24h
Enter fullscreen mode Exit fullscreen mode
⚠ auth/session.ts:4 — unknown severity "crtical", did you mean "critical"? (defaulting to info)
Enter fullscreen mode Exit fullscreen mode

No silent failures, no bad data in your docs.

Interactive diff: see what changed between runs

Each run compares with the previous output and shows what moved:

◆ ruledoc 28 rules · 5 scopes · 7 critical · 5 warning
  + Refunds must be processed within 48h [critical] billing.refund
  - Old trial rule [info] billing.trial
Enter fullscreen mode Exit fullscreen mode

You immediately see which business rules were added or removed since the last run.

Audit trail for deleted rules

This was a big one. In the first version, once someone removed a @rule() annotation, the rule just disappeared.

Now ruledoc keeps a BUSINESS_RULES_HISTORY.json — a tombstone file that records every deleted rule with its removal date, last known file, scope, and severity. Think of it as a changelog for your business rules.

Removed rules also show up in the Markdown output under a "Removed rules" section. When an auditor asks "did we ever have a rule about X?", you have the answer.

Protect critical business rules in CI/CD

This is my favorite feature. Add --protect critical to your CI pipeline:

npx ruledoc --check --protect critical
Enter fullscreen mode Exit fullscreen mode

If someone silently removes a critical business rule, the build fails:

✗ [critical] billing.plans: Free plan limited to 100 req/day
    was in billing/limits.ts

✗ ruledoc: 1 critical rule(s) removed — build blocked
  To allow removal, use --allow-removal or add a @rule-removed() comment.
Enter fullscreen mode Exit fullscreen mode

This forces a conscious decision. If the removal is intentional, the developer adds an acknowledgment:

// @rule-removed(billing.plans, JIRA-456): Migrated to config service
Enter fullscreen mode Exit fullscreen mode

This unblocks the build and the removal is recorded in the history with the ticket reference. No silent deletions, no surprises.

Smart file exclusion with .gitignore support

ruledoc automatically respects your .gitignore, so node_modules, dist, build output — all ignored without any config. It also skips test files (*.test.*, *.spec.*, __tests__/) by default, because mock @rule() annotations in tests shouldn't pollute your documentation.

Need more control? Add custom patterns:

{
  "extraIgnore": ["**/generated/**", "**/vendor/**"]
}
Enter fullscreen mode Exit fullscreen mode

Or override the defaults:

npx ruledoc --no-ignore-tests
npx ruledoc --no-gitignore
Enter fullscreen mode Exit fullscreen mode

LLM context export for AI-assisted coding

The context format generates a flat text file optimized for LLM prompts:

npx ruledoc --format context
Enter fullscreen mode Exit fullscreen mode

Output:

# Business Rules (auto-generated by ruledoc — do not edit)
# 28 rules · 5 scopes · Generated 2026-03-16
[critical] billing.plans: Free plan limited to 100 req/day (billing/plans.ts:1)
[critical] billing.refunds: 30-day refund window (billing/refunds.ts:8)
[warning] auth.session: Session expires after 24h (auth/session.ts:2)
Enter fullscreen mode Exit fullscreen mode

Append it to your CLAUDE.md or .cursorrules and your AI coding assistant knows every business rule before touching your code. No more AI-generated PRs that accidentally violate a business constraint.

GitHub Action for automated PR comments

Drop this into your workflow and every pull request gets a comment summarizing which business rules changed:

- uses: fmekkaoui/ruledoc/action@main
  with:
    protect: critical
Enter fullscreen mode Exit fullscreen mode

Added a rule? Removed one? The PR comment shows it. Reviewers see the business impact at a glance without digging through the diff.

CI/CD integration with one line

Add one line to your GitHub Actions workflow:

- run: npx ruledoc --check --quiet
Enter fullscreen mode Exit fullscreen mode

--check compares the generated output against the committed file and exits with code 1 if they differ. Your docs are always in sync with the code.

If you're using Turborepo, run ruledoc as a watched task alongside your dev server:

turbo watch dev rules
Enter fullscreen mode Exit fullscreen mode

The annotation format in detail

The full syntax supports scopes, severity levels, and ticket references:

// @rule(scope): Description
// @rule(scope.sub): With subscope
// @rule(scope.sub, critical): With severity
// @rule(scope.sub, critical, JIRA-123): With ticket
// @rule(scope.sub, JIRA-123, critical): Order doesn't matter
Enter fullscreen mode Exit fullscreen mode

Severity and ticket can be in any order — ruledoc figures out which is which. You can also customize the tag name, define your own severity levels, or provide a full custom regex.

Zero dependencies, zero risk

ruledoc has zero runtime dependencies. No lodash, no chalk, no commander. Just Node.js built-ins. It never modifies your source files, never phones home, and config is JSON-only — no eval, no code execution.

Get started in 30 seconds

npx ruledoc --init
Enter fullscreen mode Exit fullscreen mode

This creates a config file and shows you the annotation format. Then add @rule() comments to your codebase and run npx ruledoc.

Works with TypeScript, JavaScript, Vue, and Svelte.

GitHub: github.com/fmekkaoui/ruledoc
npm: npmjs.com/package/ruledoc


If you've ever lost a business rule in your codebase, I'd love to hear how you dealt with it. And if you try ruledoc, feedback and stars are welcome 🙏

Top comments (0)