<?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: jasu.dev</title>
    <description>The latest articles on DEV Community by jasu.dev (@ja_su_1e698278f65839836aa).</description>
    <link>https://dev.to/ja_su_1e698278f65839836aa</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%2F3913709%2Fd9532784-0db9-49c3-80ec-4e050a840249.png</url>
      <title>DEV Community: jasu.dev</title>
      <link>https://dev.to/ja_su_1e698278f65839836aa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ja_su_1e698278f65839836aa"/>
    <language>en</language>
    <item>
      <title>Content Transformation: The First Step Most RAG Tutorials Skip</title>
      <dc:creator>jasu.dev</dc:creator>
      <pubDate>Sat, 23 May 2026 04:27:48 +0000</pubDate>
      <link>https://dev.to/ja_su_1e698278f65839836aa/content-transformation-the-first-step-most-rag-tutorials-skip-1o8g</link>
      <guid>https://dev.to/ja_su_1e698278f65839836aa/content-transformation-the-first-step-most-rag-tutorials-skip-1o8g</guid>
      <description>&lt;p&gt;Info: This article is part of a series on building a production RAG pipeline. Start with the &lt;a href="https://jasu.dev/posts/building-a-multi-tenant-rag-system-overview/" rel="noopener noreferrer"&gt;overview&lt;/a&gt; if you haven't.&lt;/p&gt;

&lt;p&gt;The most important thing to understand when working with LLMs is: If you insert trash, you get trash back. &lt;/p&gt;

&lt;p&gt;Before you even start to build a RAG system you should think about which kind of documents you want to store in the system and which format the text should have.&lt;br&gt;
This decision highly influences how you build your system. &lt;br&gt;
What chunking mechanisms you can use, how much information you fit into one document and what technology you can use.&lt;/p&gt;

&lt;p&gt;Most systems accept multiple file types like PDF, HTML, CSV or Markdown. But in the database they all need to be the same format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown
&lt;/h2&gt;

&lt;p&gt;The text format in the database needs to have a couple of properties and markdown covers them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Readable by Humans and LLMs Natively
&lt;/h3&gt;

&lt;p&gt;The content of the inserted documents will be returned to the LLM in the retrieval process to craft an answer to a specific query.&lt;/p&gt;

&lt;p&gt;In order for the LLM to craft a useful answer, the content of the document needs to be readable natively by LLMs. That means it needs to be a text format.&lt;br&gt;
PDF and DOCX for example are containers that need extraction before being readable so they are already disqualified. Markdown is a proper text format and&lt;br&gt;
can be read without any kind of parsing. It's also pretty easy to read for humans so debugging your pipeline is easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Effective
&lt;/h3&gt;

&lt;p&gt;To save tokens and storage space the stored text needs to deliver as much information as possible in the least amount of words. &lt;/p&gt;

&lt;p&gt;Of course the text still needs to make sense and be structured somehow to preserve context (more on that in a later article) but you get my point right?&lt;br&gt;
More text for the same information means more tokens. Although much cheaper than tokens, each character in the DB column also costs money in the form of storage which can&lt;br&gt;
accumulate quickly when you serve millions of documents (rows in the db).&lt;/p&gt;

&lt;p&gt;So you need a readable text format that can be structured with as few characters as possible. Markdown turns out to be very efficient with that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Markdown vs HTML
&lt;/h3&gt;

&lt;p&gt;In the last couple of weeks HTML got &lt;a href="https://x.com/trq212/status/2052809885763747935?s=46&amp;amp;t=iJvr88mi1CMpldT29Nd9vQ" rel="noopener noreferrer"&gt;quite some attention&lt;/a&gt; on social media for being the new go-to&lt;br&gt;
format when it comes to file formats for LLMs.&lt;/p&gt;

&lt;p&gt;However I would like to push back here. Especially in terms of cost effectiveness and readability for humans, Markdown is still the king. &lt;br&gt;
It makes quite some difference if you write &lt;code&gt;#&lt;/code&gt; over &lt;code&gt;&amp;lt;h1&amp;gt;..&amp;lt;/h1&amp;gt;&lt;/code&gt; for a heading. &lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing Different File Formats into Markdown
&lt;/h2&gt;

&lt;p&gt;Now that the format is decided, each file type needs its own path to get there.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;

&lt;p&gt;Embedding website content into a RAG system is the most common use case.&lt;/p&gt;

&lt;p&gt;Transforming HTML to Markdown is fairly easy. There are many different libraries out there that can do the job. I personally like &lt;a href="https://docs.crawl4ai.com/" rel="noopener noreferrer"&gt;Crawl4AI&lt;/a&gt;.&lt;br&gt;
They offer crawl functionality, asynchronous behaviour and a default Markdown generator. Important things to look out for are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define tags you don't want included in your markdown (navigation, footers, headers, images)&lt;/li&gt;
&lt;li&gt;define what the markdown generator should ignore (links, images)&lt;/li&gt;
&lt;li&gt;find the right crawling strategy for your use case (consult the Crawl4AI documentation for your use case)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  PDF
&lt;/h3&gt;

&lt;p&gt;Transforming PDF documents to Markdown is the most complicated step of document transformation. &lt;/p&gt;

&lt;p&gt;Yes, there are many different libraries out there that do the job and are fairly easy to use but the problem is the process itself.&lt;/p&gt;

&lt;p&gt;Transforming PDF content into Markdown requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloading the PDF&lt;/li&gt;
&lt;li&gt;Reading the PDF page by page&lt;/li&gt;
&lt;li&gt;Extracting the PDF page by page&lt;/li&gt;
&lt;li&gt;Transforming the content to Markdown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most PDFs are several Megabytes and take a while to read. On top of that there might even be some images inside the PDF that are much harder to extract.&lt;/p&gt;

&lt;p&gt;The problem with these steps is that they are quite hungry when it comes to resources. Depending on your infrastructure you need to find a good balance between speed&lt;br&gt;
and memory/CPU usage (more on that later).&lt;/p&gt;

&lt;p&gt;After trying out multiple libraries I found that pymupdf4llm does the best job.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSV/XLS
&lt;/h3&gt;

&lt;p&gt;CSV and XLS(X) files are pretty straightforward to transform into Markdown. I found the MarkItDown library to do a solid job in transforming the content into&lt;br&gt;
proper Markdown tables. &lt;/p&gt;

&lt;h3&gt;
  
  
  MD/TXT
&lt;/h3&gt;

&lt;p&gt;Markdown and TXT files don't need to be transformed. I listed them for completeness here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Cleaning
&lt;/h2&gt;

&lt;p&gt;All your input content needs to be properly cleaned before you embed and insert it into your vector database.&lt;/p&gt;

&lt;p&gt;After transformation from different filetypes to Markdown you end up with a lot of noise. Even files that don't need transformation are worth cleaning.&lt;br&gt;
You do not want to end up with blank lines and other noise that does not add any value to the system and just occupies space.&lt;/p&gt;

&lt;p&gt;In general I recommend doing the following things in content cleaning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace any multiple consecutive occurrences of blank lines to just a maximum of two.&lt;/li&gt;
&lt;li&gt;Strip trailing and leading whitespace from each line&lt;/li&gt;
&lt;li&gt;Remove lines that are only symbols and no text&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Speed Problem
&lt;/h2&gt;

&lt;p&gt;Once you try to scrape a website which embeds a couple of PDFs you will notice one thing:&lt;/p&gt;

&lt;p&gt;It's incredibly slow.&lt;/p&gt;

&lt;p&gt;You need to download each PDF, extract it page by page and transform it. For multi-tenancy you most likely also want to cover multi-language PDFs and only save the relevant information in &lt;br&gt;
a specific language to your system. All this not only takes time, but also eats a lot of resources. To solve this, I recommend using a queue system and let that processing run somewhere&lt;br&gt;
in the background. &lt;/p&gt;

&lt;p&gt;Depending on your resources you can also try to run several processes in parallel.&lt;/p&gt;

&lt;p&gt;Luckily filling a RAG system with data is usually not super time critical and customers are willing to wait. At best, you can frame it as training the AI with their data.&lt;br&gt;
Just make sure that you remove the PDF files after processing them to save storage space.&lt;/p&gt;

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

&lt;p&gt;Content transformation is an often overlooked but crucial part of a RAG system.&lt;/p&gt;

&lt;p&gt;The quality of your input determines whether your RAG system produces high-quality output or not.&lt;br&gt;
Spending some time thinking about what input formats you want to support and how to ensure your content is clean and resource efficient saves you a lot of headaches down the line.&lt;/p&gt;

&lt;p&gt;It is also worth thinking about performance early on, especially when working with PDF files.&lt;/p&gt;

&lt;p&gt;In the next article we will focus on chunking.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How You Can Build a Computer From a Single Gate</title>
      <dc:creator>jasu.dev</dc:creator>
      <pubDate>Sun, 17 May 2026 03:04:22 +0000</pubDate>
      <link>https://dev.to/ja_su_1e698278f65839836aa/how-you-can-build-a-computer-from-a-single-gate-11hc</link>
      <guid>https://dev.to/ja_su_1e698278f65839836aa/how-you-can-build-a-computer-from-a-single-gate-11hc</guid>
      <description>&lt;p&gt;Most software engineers can't explain how their computer works.&lt;/p&gt;

&lt;p&gt;Me included, I work as a backend developer but know nothing about the internals of my laptop.&lt;br&gt;
In modern computer science we have so many layers of abstraction that you don't need to know what's underneath. &lt;br&gt;
But knowing a thing or two about it will certainly make you a better developer.&lt;/p&gt;

&lt;p&gt;And you can start by building your own computer from just a single logic gate. &lt;/p&gt;
&lt;h2&gt;
  
  
  Nand2Tetris
&lt;/h2&gt;

&lt;p&gt;Nand2Tetris is a course by Noam Nisan and Shimon Schocken where you build a fully functional computer by starting with just one simple logic gate - the NAND gate.&lt;/p&gt;

&lt;p&gt;The course is available for free on the website &lt;a href="https://nand2tetris.org" rel="noopener noreferrer"&gt;nand2tetris.org&lt;/a&gt; and it does not require any pre-existing knowledge about computers or programming (although it helps).&lt;br&gt;
It's divided into two parts: Hardware and Software, and includes twelve projects.&lt;br&gt;
In the first six projects you build the computer from scratch, and in projects 7 to 12 you build the software stack: a VM translator, a compiler, and a small OS.&lt;/p&gt;

&lt;p&gt;The course is widely known as one of the best courses in computer science for understanding how things work on a low level.&lt;/p&gt;
&lt;h2&gt;
  
  
  Boolean Logic
&lt;/h2&gt;

&lt;p&gt;The first project is about boolean logic. Build 15 different chips from a simple logic gate.&lt;/p&gt;

&lt;p&gt;The documents (or videos) explain everything that is needed to solve this task: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the theory of boolean logic&lt;/li&gt;
&lt;li&gt;the connection between boolean logic and electrical circuits&lt;/li&gt;
&lt;li&gt;an introduction to hardware description files and hardware simulation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are provided with stub, comparison and test files for each gate. These files provide the necessary information on how the gate/chip&lt;br&gt;
is supposed to work. The actual implementation is your part.&lt;/p&gt;

&lt;p&gt;For the Xor gate for example, these are the stub and comparison file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/1/Xor.hdl
/**
 * Exclusive-or gate:
 * if ((a and Not(b)) or (Not(a) and b)) out = 1, else out = 0
 */
CHIP Xor {
    IN a, b;
    OUT out;

    PARTS:
    //// Replace this comment with your code.
}
&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;| a | b |out|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first gates like NOT, AND and XOR were straightforward, but Mux and DMux were a different story. &lt;/p&gt;

&lt;h2&gt;
  
  
  Routing
&lt;/h2&gt;

&lt;p&gt;Mux and DMux broke my pattern.&lt;/p&gt;

&lt;p&gt;While the simpler gates are all basic boolean arithmetic, Mux and DMux do not calculate anything. &lt;br&gt;
The Mux takes two input signals (&lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;) and a select signal. &lt;br&gt;
Based on the select signal the output has either the value of &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt;. The DMux does the opposite: it takes one input and routes it to one of two outputs based on the selector. &lt;/p&gt;

&lt;p&gt;This is the truth table for the Mux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| a | b |sel|out|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tricky part here is the shift in the pattern. NOT, AND, XOR all follow the principle of combining inputs and calculating a result. Routing needs a different pattern and requires you to shift your thinking.&lt;/p&gt;

&lt;p&gt;Another challenge is the 4-Way and 8-Way Mux. I built them the same way as the Mux (but with a lot more code as you may imagine) first, before realizing that they can be built with just three Mux chips.&lt;br&gt;
I discovered that you can use the divide and conquer pattern for both, software and hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Verdict of Project 1
&lt;/h2&gt;

&lt;p&gt;In total the first project took me around five hours including going through the course material. &lt;/p&gt;

&lt;p&gt;So far it's worth it to go through this course to deepen your knowledge of how computers work and eventually become a better software engineer.&lt;/p&gt;

&lt;p&gt;The next project will be about building a calculator from logic gates.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://jasu.dev" rel="noopener noreferrer"&gt;jasu.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systems</category>
      <category>learning</category>
    </item>
    <item>
      <title>Building a multi-tenant RAG pipeline with Postgres. Part 0: Overview</title>
      <dc:creator>jasu.dev</dc:creator>
      <pubDate>Sun, 10 May 2026 04:28:36 +0000</pubDate>
      <link>https://dev.to/ja_su_1e698278f65839836aa/building-a-multi-tenant-rag-pipeline-with-postgres-part-0-overview-1f9g</link>
      <guid>https://dev.to/ja_su_1e698278f65839836aa/building-a-multi-tenant-rag-pipeline-with-postgres-part-0-overview-1f9g</guid>
      <description>&lt;p&gt;Today I want to start with a series of articles describing my experience building a multi-tenant RAG system powered by Postgres that serves over&lt;br&gt;
millions of documents while still delivering end-to-end responses in under 4 seconds (including the latency from AI providers). This article serves as the overview&lt;br&gt;
before I will start diving deeper into the several topics in the upcoming weeks. I  put a lot of research into most of the steps until I reached a somewhat&lt;br&gt;
stable and fast system. I was heavily involved in building this at my company, but I wasn't the only one and many of the ideas came from working through problems together with the team.&lt;br&gt;
In case you are thinking about building a RAG-based system this series could help you make the decisions regarding architecture or provider choice.&lt;/p&gt;
&lt;h2&gt;
  
  
  What makes a good RAG system?
&lt;/h2&gt;

&lt;p&gt;In my opinion a good RAG system is mainly defined by recall and latency because these two things are directly impacting the end user experience.&lt;br&gt;
One could argue that recall is more important than latency, since a fast answer is worth nothing if the system is giving users false answers.&lt;br&gt;
But I think a good and well-crafted answer is also worth nothing when users need to wait ten to 20 seconds each time they ask something. The internet is a&lt;br&gt;
fast-paced environment and people don’t like to wait.&lt;/p&gt;

&lt;p&gt;Furthermore, a good RAG system should have guardrails against misuse. You are dealing with untrusted user input and therefore prompt injection is a real&lt;br&gt;
threat for RAG systems. You need to find mechanisms to prevent misuse and still make the system respond in a friendly way.&lt;br&gt;
Since LLMs tend to hallucinate (this is not exclusively LLM behavior, humans do that too), you also need a way to minimize the risk of providing false answers&lt;br&gt;
to the user and in case your documents do not provide any useful information you also need to find a good solution for this.&lt;/p&gt;

&lt;p&gt;As you can see, there are quite a few things you need to think of when building a RAG system that is somewhat publicly available. But before I jump into&lt;br&gt;
how you can overcome the listed challenges, let’s take a look at what a RAG system is made of.  &lt;/p&gt;
&lt;h2&gt;
  
  
  The Two Major Parts
&lt;/h2&gt;

&lt;p&gt;A RAG system is generally split into two major parts: Ingestion and Retrieval.&lt;/p&gt;

&lt;p&gt;Ingestion means storing documents in the vector database and retrieval is the process of getting these documents.&lt;br&gt;
Both of these steps have some nuances that highly influence how good the documents that you feed the LLM are. &lt;br&gt;
We will start with the ingestion since what you retrieve is only as good as what you put into the system in the first place. &lt;br&gt;
If you put good stuff into the system, you have a fair chance to receive a proper answer from the LLM. If you put bad stuff into the system, chances are very&lt;br&gt;
low you receive anything useful back.&lt;/p&gt;
&lt;h2&gt;
  
  
  Ingestion
&lt;/h2&gt;

&lt;p&gt;When it comes to Ingestion, there are a couple of steps that need to be done before a piece of text can be stored in a vector database. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content Transformation&lt;/li&gt;
&lt;li&gt;Content Chunking&lt;/li&gt;
&lt;li&gt;Embedding and Storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will go into detail on these three steps in the upcoming articles. For now I just want to say that it’s beneficial to think beforehand about&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What kind of files you want to support, what text format you want to use for storage.&lt;/li&gt;
&lt;li&gt;How big your chunks should be and how you want to split the documents.&lt;/li&gt;
&lt;li&gt;What storage and embedding you want to use. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Especially for the third point there are many different providers that all have their own benefits. In the course of this series I will explain why the project team&lt;br&gt;
I worked in decided to go with Postgres.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Document --&amp;gt; Transform --&amp;gt; Chunk --&amp;gt; Embed --&amp;gt; pgvector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Retrieval
&lt;/h2&gt;

&lt;p&gt;The Retrieval part of a RAG system includes significantly more steps than the ingestion part. &lt;/p&gt;

&lt;p&gt;I personally split the retrieval part into six different steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input Processing&lt;/li&gt;
&lt;li&gt;Document Retrieval&lt;/li&gt;
&lt;li&gt;Context Preparation&lt;/li&gt;
&lt;li&gt;Reranking&lt;/li&gt;
&lt;li&gt;Response Generation&lt;/li&gt;
&lt;li&gt;Output Processing and Delivery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While most RAG tutorials focus on the core steps like document retrieval and response generation, I think a production RAG system needs way more than that.&lt;br&gt;
Especially if you want to build it for multi-tenancy. It needs input and output guardrails, multiple retrievers and optimization regarding token cost and latency,&lt;br&gt;
like the context preparation step that I included in my list. Simpler use cases may not need everything. In general having one RAG system for one specific use case only&lt;br&gt;
will always lead to the best results possible. However, in the real world you cannot build and maintain a custom system for each and every customer due to time and cost restrictions.&lt;/p&gt;

&lt;p&gt;Most of the steps mentioned above include several sub-steps. The input processing for example includes spam guards,&lt;br&gt;
query rewriting and, depending on your use case, maybe even routing to or away from the document retrieval. I try to write one article for each of the steps listed above and&lt;br&gt;
dive deep into the sub-steps so you have a proper understanding of what is needed for what.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query --&amp;gt; Input Processing --&amp;gt; Document Retrieval --&amp;gt; Context Preparation ──┐
┌───────────────────────────────────────────────────────────────────────────┘
└──→ Reranking → Response Generation → Output &amp;amp; Delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;In the next couple of articles I will go over the ingestion part of the RAG system. I will explain how to transform different file formats into a text format, how to split&lt;br&gt;
documents into chunks that make sense for retrieval and how to store them in Postgres using pgvector. I will also provide some code examples.&lt;br&gt;
We will be using Python and Langchain for this series.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://jasu.dev" rel="noopener noreferrer"&gt;jasu.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>postgres</category>
      <category>langchain</category>
    </item>
    <item>
      <title>How to make Claude Code actually follow your rules</title>
      <dc:creator>jasu.dev</dc:creator>
      <pubDate>Tue, 05 May 2026 10:32:21 +0000</pubDate>
      <link>https://dev.to/ja_su_1e698278f65839836aa/how-to-make-claude-code-actually-follow-your-rules-28cg</link>
      <guid>https://dev.to/ja_su_1e698278f65839836aa/how-to-make-claude-code-actually-follow-your-rules-28cg</guid>
      <description>&lt;p&gt;Coding Agents are great and fast evolving. I personally use Claude Code on every project. It’s super powerful, but it still needs a lot of handholding, especially when it comes to code consistency. Often times the implementation they are coming up with works properly but the code it produces is not optimal. If you are a person that just wants something that works &lt;em&gt;somehow&lt;/em&gt;, then you don’t need to care. But in most professional environments you want the codebase to have some kind of consistency in style and to be easy to maintain. So everyone follows some rules or principles on how certain things should be done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Layered Architecture in Laravel
&lt;/h2&gt;

&lt;p&gt;One example of such a rule is following the layered architecture when building projects in Laravel. The layered architecture consists of a presentation layer, an application/service layer,&lt;br&gt;
a domain layer and an infrastructure/persistence layer. In Laravel terms this means: Controller, Service, Repository, Model.&lt;/p&gt;

&lt;p&gt;The controller (in other languages often called the handler) naturally stays thin and is just for validating the request and returning a success or error response. The service handles the actual business logic and calls the repository to update models.&lt;/p&gt;

&lt;p&gt;In order to follow this architecture, you have a bunch of rules. Here are some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the controller should never execute business logic. It should always call a service.&lt;/li&gt;
&lt;li&gt;the service should never update a model or store something in the database. It should always call a repository for that.&lt;/li&gt;
&lt;li&gt;the repository should never execute business logic. It should only be responsible for editing/creating or deleting models.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  CLAUDE.md rules don't stick
&lt;/h2&gt;

&lt;p&gt;Naturally, I also include these rules in my &lt;code&gt;Claude.md&lt;/code&gt; file (the file at your project root for project-specific instructions   like coding conventions, architecture decisions and workflow preferences) and expect Claude Code to follow them. But despite being &lt;em&gt;very&lt;/em&gt; clear about these rules, Claude still tries to sneak some business logic into the controller sometimes or wants to update a model inside a service. This can be quite annoying and slows me down a lot, since I need to constantly be aware what Claude is doing and steer it in the right direction. Even though it stores these corrections in the memory, mistakes like these keep happening. &lt;/p&gt;
&lt;h2&gt;
  
  
  Path-specific rules
&lt;/h2&gt;

&lt;p&gt;I was quite upset about this and told a colleague. He suggested to me that I should try out rules.&lt;br&gt;
Rules are a special set of instructions for files or paths that have a very high priority and are only loaded when accessing these files. So for example if I want Claude to not update models inside a service class, I can set a rule like this:&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/Services/*Service.php"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Service Class Rules&lt;/span&gt;

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

Service classes MUST NOT interact with the database directly. Delegate all persistence and query logic to a Repository.

&lt;span class="gu"&gt;## Forbidden in Service classes&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Eloquent model statics that hit the DB: &lt;span class="sb"&gt;`Model::create()`&lt;/span&gt;, &lt;span class="sb"&gt;`::find()`&lt;/span&gt;, &lt;span class="sb"&gt;`::findOrFail()`&lt;/span&gt;, &lt;span class="sb"&gt;`::where()`&lt;/span&gt;, &lt;span class="sb"&gt;`::first()`&lt;/span&gt;, &lt;span class="sb"&gt;`::firstOrCreate()`&lt;/span&gt;, &lt;span class="sb"&gt;`::updateOrCreate()`&lt;/span&gt;, &lt;span class="sb"&gt;`::all()`&lt;/span&gt;, &lt;span class="sb"&gt;`::query()`&lt;/span&gt;, &lt;span class="sb"&gt;`::destroy()`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Instance persistence: &lt;span class="sb"&gt;`$model-&amp;gt;save()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;delete()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;update()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;forceDelete()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;restore()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;push()`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Relationship persistence: &lt;span class="sb"&gt;`-&amp;gt;create()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;save()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;attach()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;detach()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;sync()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;associate()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;dissociate()`&lt;/span&gt;, &lt;span class="sb"&gt;`-&amp;gt;updateExistingPivot()`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Query builder / raw SQL: &lt;span class="sb"&gt;`DB::table()`&lt;/span&gt;, &lt;span class="sb"&gt;`DB::select/insert/update/delete()`&lt;/span&gt;, &lt;span class="sb"&gt;`DB::statement()`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`DB::transaction()`&lt;/span&gt; allowed ONLY to compose multiple Repository calls atomically — reads/writes inside MUST still go through Repositories.

&lt;span class="gu"&gt;## Required pattern&lt;/span&gt;

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;php
&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;ExampleRepository&lt;/span&gt; &lt;span class="nv"&gt;$repository&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;doThing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ExampleData&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Example&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&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="p"&gt;```&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Inject Repository via constructor.
&lt;span class="p"&gt;-&lt;/span&gt; Pass DTOs (Spatie Data) or typed params — never arrays.
&lt;span class="p"&gt;-&lt;/span&gt; Service = business logic. Repository = persistence.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This file should be saved inside the &lt;code&gt;.claude/rules&lt;/code&gt; folder and can be called anything you like. I tend to name the files according to the layer/type they describe the rules for. I name a rule file for services &lt;code&gt;services.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The rule is always present in the context, but only the frontmatter. When a file that matches the &lt;code&gt;path: ...&lt;/code&gt; is loaded, the full rule is loaded into context and Claude uses these rules  with a higher priority. &lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Since I implemented a couple of these rules, Claude makes fewer “mistakes” and follows the principles more strictly. &lt;/p&gt;

&lt;p&gt;Previously I found that Claude tried to sneak in some violations of the rules approximately once every feature. &lt;br&gt;
Since I added some files to the &lt;code&gt;.claude/rules&lt;/code&gt; folder, I worked on five features and haven’t seen Claude trying to sneak in some violation so far.&lt;/p&gt;

&lt;p&gt;If you want to try out rules and want to take a deeper look at what is possible you can visit the &lt;a href="https://code.claude.com/docs/en/memory#organize-rules-with-claude%2Frules%2F" rel="noopener noreferrer"&gt;Claude Code Documentation on Rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://jasu.dev" rel="noopener noreferrer"&gt;jasu.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
