<?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>DEV Community: Monarch Wadia</title>
    <description>The latest articles on DEV Community by Monarch Wadia (@monarchwadia).</description>
    <link>https://dev.to/monarchwadia</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%2F221604%2Ff9cd9689-cbdc-4693-b15a-2395bd48f918.jpg</url>
      <title>DEV Community: Monarch Wadia</title>
      <link>https://dev.to/monarchwadia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/monarchwadia"/>
    <language>en</language>
    <item>
      <title>Convention as Code: Enforcing Architecture with Scripts, CI, and AI Agents</title>
      <dc:creator>Monarch Wadia</dc:creator>
      <pubDate>Sat, 14 Feb 2026 23:30:20 +0000</pubDate>
      <link>https://dev.to/monarchwadia/convention-as-code-enforcing-architecture-with-scripts-ci-and-ai-agents-hgd</link>
      <guid>https://dev.to/monarchwadia/convention-as-code-enforcing-architecture-with-scripts-ci-and-ai-agents-hgd</guid>
      <description>&lt;h2&gt;
  
  
  Conventions Drift
&lt;/h2&gt;

&lt;p&gt;Vibe coding gets it 80% right, but the remaining 20% needs enforcement via some mechanical method.&lt;/p&gt;

&lt;p&gt;Every codebase has unwritten rules. "Schemas go in their own files." "Always be explicit about strictness." They live in someone's head, or on a wiki page nobody reads. New code breaks them.&lt;/p&gt;

&lt;p&gt;Agents break such conventions very quickly — because agents don't attend standups, don't read wikis, and don't absorb tribal knowledge through osmosis.&lt;/p&gt;

&lt;p&gt;We hit this on a real project — an Atlassian Cloud integration with 19 tools across Jira, Confluence, and Bitbucket. We asked a simple question: are any of our validation schemas too loose?&lt;/p&gt;

&lt;p&gt;The answer was yes. Everywhere.&lt;/p&gt;

&lt;p&gt;Our validation library, Zod, silently strips unknown keys by default. Pass &lt;code&gt;{ name: "Alice", role: "admin" }&lt;/code&gt; to a schema that only knows about &lt;code&gt;name&lt;/code&gt;, and &lt;code&gt;role&lt;/code&gt; vanishes. No error, no warning, just gone.&lt;/p&gt;

&lt;p&gt;We found 233 problems across 20 files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No explicit strictness.&lt;/strong&gt; 119 schemas silently stripping unknown keys because nobody told them not to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No separation.&lt;/strong&gt; 114 schemas buried inline inside handler functions — unnamed, unsearchable, invisible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ESLint can't catch either of these. We needed something stronger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Time: 28 Minutes. Enforcement Period: Forever.
&lt;/h2&gt;

&lt;p&gt;We fixed all 233 violations in 28 minutes of wall-clock time — two custom rules, seven AI agents running in parallel. That's the fast part.&lt;/p&gt;

&lt;p&gt;The lasting part: those same rules now run on every commit. Any future code that breaks the convention breaks the build. No code review needed. No tribal knowledge. The convention went from "something we try to remember" to something the machine enforces — permanently.&lt;/p&gt;

&lt;p&gt;That's the pattern: encode a convention as a script, use agents to fix existing violations, then let CI enforce the rule forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the Rules
&lt;/h2&gt;

&lt;p&gt;We wrote custom rules using &lt;a href="https://ts-morph.com/" rel="noopener noreferrer"&gt;ts-morph&lt;/a&gt;, which gives you access to TypeScript's full compiler API. Unlike ESLint, it can follow method chains, track imports across files, and understand types. That's what we needed.&lt;/p&gt;

&lt;p&gt;A rule receives the full project and returns violations. Simple. We called the rule runner &lt;code&gt;topology&lt;/code&gt; — a small directory of scripts, each taking a ts-morph project and returning a list of violations.&lt;/p&gt;

&lt;p&gt;The first rule: every &lt;code&gt;z.object()&lt;/code&gt; must be followed by &lt;code&gt;.strict()&lt;/code&gt; (reject unknown keys) or &lt;code&gt;.loose()&lt;/code&gt; (allow them) — no silent defaults. The point isn't which one you pick; it's that the choice is explicit. We ran it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tsx topology/run.ts

topology: 119 violation&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; found

  src/ipc/routes.ts:45 — z.object&lt;span class="o"&gt;()&lt;/span&gt; must be followed by .strict&lt;span class="o"&gt;()&lt;/span&gt; or .loose&lt;span class="o"&gt;()&lt;/span&gt;
  src/ipc/routes.ts:52 — z.object&lt;span class="o"&gt;()&lt;/span&gt; must be followed by .strict&lt;span class="o"&gt;()&lt;/span&gt; or .loose&lt;span class="o"&gt;()&lt;/span&gt;
  src/ipc/routes.ts:78 — z.object&lt;span class="o"&gt;()&lt;/span&gt; must be followed by .strict&lt;span class="o"&gt;()&lt;/span&gt; or .loose&lt;span class="o"&gt;()&lt;/span&gt;
  ...
  src/web/chat/tools/workspace/readFile.ts:10 — z.object&lt;span class="o"&gt;()&lt;/span&gt; must be followed by .strict&lt;span class="o"&gt;()&lt;/span&gt; or .loose&lt;span class="o"&gt;()&lt;/span&gt;
  src/web/chat/tools/workspace/writeFile.ts:11 — z.object&lt;span class="o"&gt;()&lt;/span&gt; must be followed by .strict&lt;span class="o"&gt;()&lt;/span&gt; or .loose&lt;span class="o"&gt;()&lt;/span&gt;
  src/web/chat/tools/workspace/deleteFile.ts:10 — z.object&lt;span class="o"&gt;()&lt;/span&gt; must be followed by .strict&lt;span class="o"&gt;()&lt;/span&gt; or .loose&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;119 hits. Each one a file, a line number, and a message. Exit code 1.&lt;/p&gt;

&lt;p&gt;The second rule — every schema must live in a &lt;code&gt;.schema.ts&lt;/code&gt; file — caught 114 more. 233 total: a precise, machine-readable list of everything that needed fixing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispatching Agents — In Parallel
&lt;/h2&gt;

&lt;p&gt;This is where agents earn their keep. Not one agent doing everything, but multiple agents working simultaneously on partitioned work — the same way you'd split tasks across a team, except the team spins up in seconds and doesn't need onboarding.&lt;/p&gt;

&lt;p&gt;We used Claude Sonnet 4.5 in parallel sessions to do the refactoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strictness fixes:&lt;/strong&gt; One agent added &lt;code&gt;.strict()&lt;/code&gt; (or &lt;code&gt;.loose()&lt;/code&gt; where appropriate) to all 119 schemas. Each change was a single method call — one agent handled it easily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema extraction:&lt;/strong&gt; This was bigger — creating new files, naming schemas, rewriting imports. We split the 20 violating files into 6 non-overlapping groups (by directory, so no shared imports crossed boundaries) and gave each group to a separate agent. No two agents touched the same file, so there were zero merge conflicts. Seven agents total, running in parallel, done in under half an hour.&lt;/p&gt;

&lt;p&gt;Here's what a single file looked like before and after:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; — inline, no strictness:&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;// readFile.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createReadFileTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;buildTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Path to the file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;startLine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;endLine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startLine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endLine&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt; — extracted, strict, named:&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;// readFile.schema.ts (new)&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;ReadFileInputSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Path to the file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;startLine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;endLine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// readFile.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ReadFileInputSchema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./readFile.schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createReadFileTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;buildTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReadFileInputSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startLine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endLine&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema gets a name, explicit tightness, and a dedicated home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents Verify Their Own Work
&lt;/h2&gt;

&lt;p&gt;This is the key insight for anyone building agent workflows: agents don't need to understand &lt;em&gt;why&lt;/em&gt; a convention exists. They just need a pass/fail signal — exactly like a test suite.&lt;/p&gt;

&lt;p&gt;Each agent was told to run the rules as a final step:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After completing all changes, run &lt;code&gt;tsx topology/run.ts&lt;/code&gt; and confirm the output is &lt;code&gt;topology: 2 rule(s) passed&lt;/code&gt;. If any violations remain, fix them before reporting completion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We also ran 6 verification agents that compared each git diff against the original file, confirming schemas were extracted verbatim and no behavior changed. All 6 passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Merge and Enforce
&lt;/h2&gt;

&lt;p&gt;The rules go into the lint script:&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;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc --noEmit &amp;amp;&amp;amp; prettier --check . &amp;amp;&amp;amp; eslint . &amp;amp;&amp;amp; tsx topology/run.ts"&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;During the refactoring, this script found problems. After the merge, the same script prevents them. CI runs it on every commit — no agents involved, just a pass/fail check. Either &lt;code&gt;topology: 2 rule(s) passed&lt;/code&gt; or the build breaks with exact file and line numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few Things We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.strict()&lt;/code&gt; on subset schemas crashes at runtime.&lt;/strong&gt; If a schema validates a slice of a larger object, use &lt;code&gt;.loose()&lt;/code&gt; instead. The rule accepts both — it just requires the choice to be explicit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ts-morph is slow to start (~3-5 sec).&lt;/strong&gt; Fine for CI. Noticeable in local dev loops. Scope it to specific file globs if it bothers you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules need upkeep.&lt;/strong&gt; When Zod 4 deprecated &lt;code&gt;.passthrough()&lt;/code&gt; in favor of &lt;code&gt;.loose()&lt;/code&gt;, we updated the rule and dispatched another agent batch. The rule was a one-line fix; the codebase was another 28-minute session.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;This was a small project — two rules, 233 violations, 28 minutes. But the pattern is general and repeatable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify&lt;/strong&gt; a convention that lives in someone's head.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encode&lt;/strong&gt; it as a script that returns violations with file and line numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dispatch&lt;/strong&gt; agents to fix the violations, using the script as their success criterion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce&lt;/strong&gt; the script in CI so the convention can never drift again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every architectural convention that lives only in someone's head is a convention that agents will violate and humans will forget to enforce. Making it executable changes both problems at once. And it gives agents the same pass/fail signal they get from tests — they don't need to understand the reasoning, they just need to make the check pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scripts solve the knowing. Agents solve the doing. CI solves the forgetting.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>cicd</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Use OpenAI in your JavaScript project the easy way with Ragged</title>
      <dc:creator>Monarch Wadia</dc:creator>
      <pubDate>Mon, 10 Jun 2024 05:28:27 +0000</pubDate>
      <link>https://dev.to/monarchwadia/use-openai-in-your-javascript-project-the-easy-way-with-ragged-5g79</link>
      <guid>https://dev.to/monarchwadia/use-openai-in-your-javascript-project-the-easy-way-with-ragged-5g79</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome to this simple tutorial on how to use OpenAI’s API in JavaScript. In this tutorial, we will be taking the help of Ragged, which is a library that makes working with OpenAI very easy and uncomplicated.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll walk you through the steps to set up Ragged and create a basic application. By the end of this article, you’ll have a solid understanding of how to interact with OpenAI’s models using JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ragged?
&lt;/h2&gt;

&lt;p&gt;Ragged is a universal LLM client for JavaScript and TypeScript. It offers an easy-to-understand API to access various language models, including those from OpenAI. With Ragged, you can seamlessly integrate LLM capabilities into your frontend as well as backend projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, ensure you have the following installed on your machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Node.js is installed on your machine&lt;/li&gt;
&lt;li&gt;You have an OpenAI API key&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting Up Ragged
&lt;/h2&gt;

&lt;p&gt;First, let’s create a new JavaScript project. Open your terminal and run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;openai-ragged-tutorial
&lt;span class="nb"&gt;cd &lt;/span&gt;openai-ragged-tutorial
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm pkg &lt;span class="nb"&gt;set type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, install Ragged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;ragged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Writing the Hello World Application
&lt;/h2&gt;

&lt;p&gt;Now, let’s write our “Hello World” application using Ragged. Create a new file named &lt;code&gt;index.js&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Chat&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ragged/chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new Chat instance with the OpenAI provider&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Async function to handle the chat interaction&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Chat with the model&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;What is the meaning of life?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "The meaning of life is to be happy."&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Repeat what you just said, but in hindi.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "जीवन का अर्थ खुश रहना है।"&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Run the main function&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We import the &lt;code&gt;Chat&lt;/code&gt; class from Ragged.&lt;/li&gt;
&lt;li&gt;We create a new instance of &lt;code&gt;Chat&lt;/code&gt; with the OpenAI provider.&lt;/li&gt;
&lt;li&gt;We define an asynchronous &lt;code&gt;main&lt;/code&gt; function to handle the chat interaction.&lt;/li&gt;
&lt;li&gt;We chat with the OpenAI model and translate its response to Hindi.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, you need to run this application using your OpenAI API key as an environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_api_key node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you replaced &lt;code&gt;your_api_key&lt;/code&gt; with the actual key from OpenAI.&lt;/p&gt;

&lt;p&gt;You should see a response from the OpenAI model printed in your terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  401 Problems?
&lt;/h3&gt;

&lt;p&gt;If you got a “non-200 response” or an indicator that the “Status was 401”, this means you may have provided the incorrect API key. Check that the &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; variable was set correctly and try again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring More Features
&lt;/h2&gt;

&lt;p&gt;Ragged offers more than just basic chat capabilities. You can access and manipulate the message history, use tools, and create autonomous agents. To learn more, you can read Ragged’s documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we covered the basics of setting up Ragged and creating a simple “Hello World” application using OpenAI’s API. Ragged makes it easy to integrate LLM capabilities into your JavaScript projects with minimal setup and effort.&lt;/p&gt;

&lt;p&gt;Feel free to explore the Ragged documentation for more advanced features and use cases. Happy coding!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
