<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Farouk Mekkaoui</title>
    <description>The latest articles on Forem by Farouk Mekkaoui (@farouk_mekkaoui_6bc190fdb).</description>
    <link>https://forem.com/farouk_mekkaoui_6bc190fdb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3821361%2F6c7a59f6-d659-47b6-aa6e-e0d2d741064e.png</url>
      <title>Forem: Farouk Mekkaoui</title>
      <link>https://forem.com/farouk_mekkaoui_6bc190fdb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/farouk_mekkaoui_6bc190fdb"/>
    <language>en</language>
    <item>
      <title>I Built a CLI to Extract Business Rules From Code — So They Never Get Lost Again</title>
      <dc:creator>Farouk Mekkaoui</dc:creator>
      <pubDate>Fri, 13 Mar 2026 03:53:18 +0000</pubDate>
      <link>https://forem.com/farouk_mekkaoui_6bc190fdb/stop-losing-business-rules-in-your-codebase-3aj4</link>
      <guid>https://forem.com/farouk_mekkaoui_6bc190fdb/stop-losing-business-rules-in-your-codebase-3aj4</guid>
      <description>&lt;p&gt;Run &lt;code&gt;npx ruledoc&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Here's the problem it solves.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: business rules are invisible
&lt;/h2&gt;

&lt;p&gt;A new developer joins the team. They're reviewing the billing module and see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FREE_PLAN_DAILY_LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They ask: &lt;em&gt;"Why 100? Who decided this? Can I change it?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;When business rules are invisible:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why JSDoc, wikis, and rules engines don't solve this
&lt;/h2&gt;

&lt;p&gt;You might think: &lt;em&gt;"Just use JSDoc"&lt;/em&gt; or &lt;em&gt;"We have a wiki."&lt;/em&gt; I looked at everything out there:&lt;/p&gt;

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

&lt;p&gt;I couldn't find a single tool that extracts and documents business rules from code comments. So I built one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ruledoc works
&lt;/h2&gt;

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

&lt;h3&gt;
  
  
  1. Annotate your code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @rule(billing.plans, critical): Free plan is limited to 100 API requests per day&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FREE_PLAN_DAILY_LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// @rule(auth.session): Session expires after 24 hours of inactivity&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SESSION_TTL_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// @rule(billing.refunds, critical): Refunds must be requested within 30 days&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REFUND_WINDOW_DAYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  2. Run it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ruledoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Get structured documentation
&lt;/h3&gt;

&lt;p&gt;ruledoc generates a &lt;code&gt;BUSINESS_RULES.md&lt;/code&gt; grouped by scope, with a table of contents and summary table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Business Rules&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; 10 rules · 3 scopes&lt;/span&gt;

&lt;span class="gu"&gt;## Summary&lt;/span&gt;

| Scope | Sub | Count | Critical | Warning |
|-------|-----|------:|---:|---:|
| Auth | Session | 2 | 1 | 1 |
| Billing | Plans | 3 | 2 | — |
| Billing | Refunds | 1 | 1 | — |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also generates &lt;code&gt;BUSINESS_RULES.json&lt;/code&gt; for tooling, and optionally a standalone HTML page with search and filters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Annotation validation and typo detection
&lt;/h2&gt;

&lt;p&gt;ruledoc doesn't just extract — it validates. Misspell a severity?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @rule(auth.session, crtical): Session expires after 24h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠ auth/session.ts:4 — unknown severity "crtical", did you mean "critical"? (defaulting to info)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No silent failures, no bad data in your docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive diff: see what changed between runs
&lt;/h2&gt;

&lt;p&gt;Each run compares with the previous output and shows what moved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;◆ ruledoc 28 rules · 5 scopes · 7 critical · 5 warning
  + Refunds must be processed within 48h [critical] billing.refund
  - Old trial rule [info] billing.trial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You immediately see which business rules were added or removed since the last run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit trail for deleted rules
&lt;/h2&gt;

&lt;p&gt;This was a big one. In the first version, once someone removed a &lt;code&gt;@rule()&lt;/code&gt; annotation, the rule just disappeared.&lt;/p&gt;

&lt;p&gt;Now ruledoc keeps a &lt;code&gt;BUSINESS_RULES_HISTORY.json&lt;/code&gt; — 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protect critical business rules in CI/CD
&lt;/h2&gt;

&lt;p&gt;This is my favorite feature. Add &lt;code&gt;--protect critical&lt;/code&gt; to your CI pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ruledoc &lt;span class="nt"&gt;--check&lt;/span&gt; &lt;span class="nt"&gt;--protect&lt;/span&gt; critical
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If someone silently removes a critical business rule, the build fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✗ [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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forces a conscious decision. If the removal is intentional, the developer adds an acknowledgment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @rule-removed(billing.plans, JIRA-456): Migrated to config service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This unblocks the build and the removal is recorded in the history with the ticket reference. No silent deletions, no surprises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smart file exclusion with .gitignore support
&lt;/h2&gt;

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

&lt;p&gt;Need more control? Add custom patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extraIgnore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"**/generated/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/vendor/**"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or override the defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ruledoc &lt;span class="nt"&gt;--no-ignore-tests&lt;/span&gt;
npx ruledoc &lt;span class="nt"&gt;--no-gitignore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LLM context export for AI-assisted coding
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;context&lt;/code&gt; format generates a flat text file optimized for LLM prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ruledoc &lt;span class="nt"&gt;--format&lt;/span&gt; context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# Business Rules (auto-generated by ruledoc — do not edit)
# 28 rules · 5 scopes · Generated 2026-03-16
&lt;/span&gt;&lt;span class="err"&gt;[critical]&lt;/span&gt; &lt;span class="py"&gt;billing.plans&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Free plan limited to 100 req/day (billing/plans.ts:1)&lt;/span&gt;
&lt;span class="err"&gt;[critical]&lt;/span&gt; &lt;span class="py"&gt;billing.refunds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30-day refund window (billing/refunds.ts:8)&lt;/span&gt;
&lt;span class="err"&gt;[warning]&lt;/span&gt; &lt;span class="py"&gt;auth.session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Session expires after 24h (auth/session.ts:2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  GitHub Action for automated PR comments
&lt;/h2&gt;

&lt;p&gt;Drop this into your workflow and every pull request gets a comment summarizing which business rules changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fmekkaoui/ruledoc/action@main&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;critical&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Added a rule? Removed one? The PR comment shows it. Reviewers see the business impact at a glance without digging through the diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD integration with one line
&lt;/h2&gt;

&lt;p&gt;Add one line to your GitHub Actions workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx ruledoc --check --quiet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--check&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;If you're using Turborepo, run ruledoc as a watched task alongside your dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;turbo watch dev rules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The annotation format in detail
&lt;/h2&gt;

&lt;p&gt;The full syntax supports scopes, severity levels, and ticket references:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @rule(scope): Description&lt;/span&gt;
&lt;span class="c1"&gt;// @rule(scope.sub): With subscope&lt;/span&gt;
&lt;span class="c1"&gt;// @rule(scope.sub, critical): With severity&lt;/span&gt;
&lt;span class="c1"&gt;// @rule(scope.sub, critical, JIRA-123): With ticket&lt;/span&gt;
&lt;span class="c1"&gt;// @rule(scope.sub, JIRA-123, critical): Order doesn't matter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero dependencies, zero risk
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started in 30 seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ruledoc &lt;span class="nt"&gt;--init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a config file and shows you the annotation format. Then add &lt;code&gt;@rule()&lt;/code&gt; comments to your codebase and run &lt;code&gt;npx ruledoc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Works with TypeScript, JavaScript, Vue, and Svelte.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/fmekkaoui/ruledoc" rel="noopener noreferrer"&gt;github.com/fmekkaoui/ruledoc&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/ruledoc" rel="noopener noreferrer"&gt;npmjs.com/package/ruledoc&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;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 🙏&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
