<?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: Jestony Silvano</title>
    <description>The latest articles on DEV Community by Jestony Silvano (@jestscaledev).</description>
    <link>https://dev.to/jestscaledev</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4117355%2F6478a9b7-a096-49d3-a861-db2ea469b207.png</url>
      <title>DEV Community: Jestony Silvano</title>
      <link>https://dev.to/jestscaledev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jestscaledev"/>
    <language>en</language>
    <item>
      <title>How I Built an AI Assistant for My SaaS CRM with Scoped Agents</title>
      <dc:creator>Jestony Silvano</dc:creator>
      <pubDate>Thu, 24 Sep 2026 12:27:18 +0000</pubDate>
      <link>https://dev.to/jestscaledev/how-i-built-an-ai-assistant-for-my-saas-crm-with-scoped-agents-46i9</link>
      <guid>https://dev.to/jestscaledev/how-i-built-an-ai-assistant-for-my-saas-crm-with-scoped-agents-46i9</guid>
      <description>&lt;h1&gt;
  
  
  How I Built an AI Assistant for My SaaS CRM with Scoped Agents
&lt;/h1&gt;

&lt;p&gt;When I first started adding AI to my SaaS CRM, the obvious idea was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give the AI access to the CRM data and let users talk to it naturally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the more I worked on it, the more I realized that "an AI assistant for my CRM" isn't really one problem.&lt;/p&gt;

&lt;p&gt;A CRM has different users, organizations, roles, private data, public documentation, and actions that can change data.&lt;/p&gt;

&lt;p&gt;A personal assistant shouldn't have the same context as an organization assistant. A public product assistant definitely shouldn't have access to a customer's CRM records.&lt;/p&gt;

&lt;p&gt;So instead of building one giant AI assistant, I designed uniThread's AI system around &lt;strong&gt;scoped agents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give each agent only the context, knowledge, and tools it actually needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is how I built it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem with one giant AI agent
&lt;/h2&gt;

&lt;p&gt;Imagine having one AI agent with access to everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    AI Agent
                       │
        ┌──────────────┼──────────────┐
        │              │              │
    User Data      CRM Data       Platform Docs
        │              │              │
        └──────────────┼──────────────┘
                       │
                    Tools
                       │
                   Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, this looks convenient.&lt;/p&gt;

&lt;p&gt;The model can answer more questions and perform more actions because it has access to more information.&lt;/p&gt;

&lt;p&gt;But it creates a much bigger authorization problem.&lt;/p&gt;

&lt;p&gt;What happens when a user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me the contacts in my organization."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's different from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What is uniThread?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And both are different from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a task for John tomorrow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI needs to know not only &lt;strong&gt;what the user is asking&lt;/strong&gt;, but also &lt;strong&gt;what the user is allowed to access and what the application is allowed to execute&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is where I started separating the system into scoped agents.&lt;/p&gt;




&lt;h1&gt;
  
  
  What are scoped agents?
&lt;/h1&gt;

&lt;p&gt;Instead of having one agent responsible for everything, I defined agents around different contexts.&lt;/p&gt;

&lt;p&gt;The simplified architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         User
                           │
                           ▼
                     AI Request
                           │
                           ▼
                      Agent Scope
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          ▼                ▼                ▼
   Personal Agent   Organization Agent   CRM Agent
          │                │                │
          ▼                ▼                ▼
   Personal Context   Org Context      Platform Context
          │                │                │
          └────────────────┼────────────────┘
                           ▼
                     Available Tools
                           │
                           ▼
                      Authorization
                           │
                           ▼
                    Application / DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the agent isn't just a different system prompt.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;scope affects the context and capabilities available to the agent&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The three agent contexts
&lt;/h1&gt;

&lt;p&gt;For uniThread, I currently separate the AI system into three main contexts.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Personal Assistant
&lt;/h2&gt;

&lt;p&gt;The personal assistant operates around an individual user's context.&lt;/p&gt;

&lt;p&gt;For example, this could eventually handle things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Personal tasks&lt;/li&gt;
&lt;li&gt;Personal notes&lt;/li&gt;
&lt;li&gt;User-specific information&lt;/li&gt;
&lt;li&gt;Personal workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is that its context belongs to the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  │
  ▼
Profile
  │
  ▼
Personal Assistant
  │
  ▼
Personal Context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It shouldn't automatically gain access to an entire organization's CRM just because the user is logged in.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Organization Assistant
&lt;/h2&gt;

&lt;p&gt;The organization assistant works within an organization's context.&lt;/p&gt;

&lt;p&gt;A user's organization membership and role become important here.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  │
  ▼
Organization Membership
  │
  ├── Organization ID
  ├── Member ID
  └── Role
          │
          ▼
   Organization Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant can then work with organization-level CRM capabilities, subject to the same authorization rules as the rest of the application.&lt;/p&gt;

&lt;p&gt;This is especially important in a multi-tenant SaaS application.&lt;/p&gt;

&lt;p&gt;A user belonging to Organization A shouldn't be able to use the AI to retrieve information from Organization B.&lt;/p&gt;

&lt;p&gt;The AI doesn't get to bypass the application's tenant boundaries just because the request is expressed in natural language.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. CRM Assistant
&lt;/h2&gt;

&lt;p&gt;The third agent is the public-facing CRM assistant for uniThread.&lt;/p&gt;

&lt;p&gt;This one has a very different purpose.&lt;/p&gt;

&lt;p&gt;It's designed to answer questions about the platform itself using documentation and other indexed knowledge.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What does uniThread do?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How does lead management work?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This agent uses retrieval rather than accessing a customer's private CRM records.&lt;/p&gt;

&lt;p&gt;The architecture is roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question
      │
      ▼
   CRM Agent
      │
      ▼
 Retrieve Relevant Docs
      │
      ▼
    Context
      │
      ▼
     LLM
      │
      ▼
 Answer + Citations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation is important because &lt;strong&gt;knowledge about the product is not the same thing as access to customer data&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The context passed into the AI system
&lt;/h1&gt;

&lt;p&gt;To keep the agent aware of its execution context, I use an agent context similar to:&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AgentContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;profileId&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="nl"&gt;orgId&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="nl"&gt;memberId&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="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;accessToken&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="nl"&gt;isPublic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;Not every agent needs every field.&lt;/p&gt;

&lt;p&gt;For example, a public CRM assistant doesn't need a customer's organization ID.&lt;/p&gt;

&lt;p&gt;An organization assistant does.&lt;/p&gt;

&lt;p&gt;A personal assistant may primarily care about the user's profile.&lt;/p&gt;

&lt;p&gt;This gives the application a structured way to describe:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who is making this request, what organization are they operating in, what role do they have, and what kind of AI interaction is this?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The architecture
&lt;/h1&gt;

&lt;p&gt;I didn't want my controller to contain all of the AI logic.&lt;/p&gt;

&lt;p&gt;The system is separated into several layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    │
    ▼
AIAgentService
    │
    ▼
Orchestrator
    │
    ▼
Model Router
    │
    ▼
Agent / Model Adapter
    │
    ▼
Tools
    │
    ▼
Authorization
    │
    ▼
Execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer has a different responsibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Controller
&lt;/h3&gt;

&lt;p&gt;Receives the request and handles the API boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  AIAgentService
&lt;/h3&gt;

&lt;p&gt;Handles the higher-level AI interaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orchestrator
&lt;/h3&gt;

&lt;p&gt;Coordinates the agent flow, context, model, tools, and execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model Router
&lt;/h3&gt;

&lt;p&gt;Allows the application to choose the appropriate model or provider without coupling the rest of the system directly to one model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent / Model Adapter
&lt;/h3&gt;

&lt;p&gt;Handles the model-specific implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;p&gt;Expose controlled application capabilities to the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization
&lt;/h3&gt;

&lt;p&gt;Determines whether the requested operation is actually allowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution
&lt;/h3&gt;

&lt;p&gt;Performs the approved operation against the application or database.&lt;/p&gt;

&lt;p&gt;This separation became increasingly important as the AI functionality grew.&lt;/p&gt;




&lt;h1&gt;
  
  
  The most important distinction: AI is not authorization
&lt;/h1&gt;

&lt;p&gt;One of the biggest things I learned while building this system is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The LLM should never be the final authority on what a user is allowed to do.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A model can interpret intent.&lt;/p&gt;

&lt;p&gt;It can decide that a user's request probably corresponds to &lt;code&gt;create_task&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But it shouldn't decide whether the user is actually allowed to create that task.&lt;/p&gt;

&lt;p&gt;The flow should look more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Request
     │
     ▼
     LLM
     │
     │ "I want to call create_task"
     ▼
Tool Request
     │
     ▼
Authorization
     │
     ├── Not allowed → Reject
     │
     ▼
Validation
     │
     ▼
Confirmation if required
     │
     ▼
Execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model proposes an action.&lt;/p&gt;

&lt;p&gt;The application decides whether that action can happen.&lt;/p&gt;

&lt;p&gt;That distinction is critical.&lt;/p&gt;




&lt;h1&gt;
  
  
  Giving the agent tools
&lt;/h1&gt;

&lt;p&gt;Instead of giving the model unrestricted access to my backend or database, I expose specific tools.&lt;/p&gt;

&lt;p&gt;Some examples in my CRM assistant architecture include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_contacts
create_note
create_task
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the model a controlled interface.&lt;/p&gt;

&lt;p&gt;For example, if the user says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find John from Acme."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model can determine that it needs the contact search capability.&lt;/p&gt;

&lt;p&gt;If the user says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a follow-up task for John tomorrow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model can determine that &lt;code&gt;create_task&lt;/code&gt; is relevant.&lt;/p&gt;

&lt;p&gt;The model doesn't need to know how my PostgreSQL tables work.&lt;/p&gt;

&lt;p&gt;It doesn't need raw database credentials.&lt;/p&gt;

&lt;p&gt;It doesn't need unrestricted SQL access.&lt;/p&gt;

&lt;p&gt;It only needs the capabilities that the application intentionally exposes.&lt;/p&gt;




&lt;h1&gt;
  
  
  Read operations vs mutations
&lt;/h1&gt;

&lt;p&gt;Not every tool should be treated the same way.&lt;/p&gt;

&lt;p&gt;There's an important difference between:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find my contact named John."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a task for John tomorrow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first is a read operation.&lt;/p&gt;

&lt;p&gt;The second changes application state.&lt;/p&gt;

&lt;p&gt;That means mutations need stronger controls.&lt;/p&gt;

&lt;p&gt;A simplified flow can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read:

User
 ↓
AI
 ↓
Tool
 ↓
Authorization
 ↓
Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a mutation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
AI
 ↓
Tool Request
 ↓
Authorization
 ↓
Validation
 ↓
Confirmation
 ↓
Execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact confirmation rules can vary by operation, but the underlying idea is the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't let a language model silently perform important state-changing actions just because it generated a tool call.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Scoped tools
&lt;/h1&gt;

&lt;p&gt;The agent scope also affects which capabilities are available.&lt;/p&gt;

&lt;p&gt;Conceptually, the system can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Personal Agent
 ├── Personal Tasks
 └── Personal Notes

Organization Agent
 ├── Contacts
 ├── Tasks
 ├── Notes
 └── Other CRM Operations

Public CRM Agent
 └── Platform Documentation Search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the public CRM assistant doesn't suddenly get access to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_contacts
create_task
create_note
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for a customer's private organization.&lt;/p&gt;

&lt;p&gt;Its job is to answer questions about the platform, not operate someone's CRM.&lt;/p&gt;

&lt;p&gt;That's a much smaller and more controlled problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  Scoped agents are not a replacement for authorization
&lt;/h1&gt;

&lt;p&gt;This is probably the most important security point in the architecture.&lt;/p&gt;

&lt;p&gt;Having an agent called &lt;code&gt;organization-assistant&lt;/code&gt; doesn't magically make it secure.&lt;/p&gt;

&lt;p&gt;You still need authorization underneath it.&lt;/p&gt;

&lt;p&gt;My application already has organization and role boundaries, and those boundaries need to continue applying when requests come from an AI agent.&lt;/p&gt;

&lt;p&gt;The architecture therefore becomes layered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Agent Scope
      │
      ▼
Application Authorization
      │
      ▼
Organization / Member Scope
      │
      ▼
Database Security
      │
      ▼
Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI layer is another interface into the application.&lt;/p&gt;

&lt;p&gt;It shouldn't become a shortcut around the application's existing security model.&lt;/p&gt;




&lt;h1&gt;
  
  
  How this connects to PostgreSQL RLS
&lt;/h1&gt;

&lt;p&gt;This is also where my previous work on PostgreSQL Row Level Security became important.&lt;/p&gt;

&lt;p&gt;The AI system sits above the database security layer.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
 ↓
AI Agent
 ↓
Tool
 ↓
Application Authorization
 ↓
PostgreSQL
 ↓
RLS
 ↓
Tenant Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application can validate the request, while PostgreSQL RLS provides another boundary at the data layer.&lt;/p&gt;

&lt;p&gt;This gives the system defense in depth.&lt;/p&gt;

&lt;p&gt;If you're interested in the database side of this architecture, I wrote about it in my previous article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How I Built Multi-Tenant Data Isolation for My SaaS CRM with PostgreSQL RLS&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The two problems are closely related.&lt;/p&gt;

&lt;p&gt;The first is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I keep tenants from accessing each other's data?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I let AI interact with that data without bypassing those boundaries?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  RAG for the public CRM assistant
&lt;/h1&gt;

&lt;p&gt;For the public CRM assistant, I also needed a way to answer questions about uniThread without relying entirely on the model's built-in knowledge.&lt;/p&gt;

&lt;p&gt;That's where RAG comes in.&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documentation
     │
     ▼
Chunking
     │
     ▼
Embeddings
     │
     ▼
Vector Search
     │
     ▼
Relevant Context
     │
     ▼
LLM
     │
     ▼
Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of asking the model to simply "know" how uniThread works, the application retrieves relevant documentation and provides that information as context.&lt;/p&gt;

&lt;p&gt;This also gives me a better path toward citations and reduces the chance of the model inventing product information.&lt;/p&gt;

&lt;p&gt;Most importantly, the RAG knowledge base is separate from private customer CRM data.&lt;/p&gt;

&lt;p&gt;That's another example of why scope matters.&lt;/p&gt;




&lt;h1&gt;
  
  
  What happens when something goes wrong?
&lt;/h1&gt;

&lt;p&gt;AI systems don't always behave exactly as expected.&lt;/p&gt;

&lt;p&gt;A user might ask for something outside the agent's scope.&lt;/p&gt;

&lt;p&gt;The model might choose an inappropriate tool.&lt;/p&gt;

&lt;p&gt;A tool might receive invalid parameters.&lt;/p&gt;

&lt;p&gt;The user might not have permission to perform the requested action.&lt;/p&gt;

&lt;p&gt;The database operation might fail.&lt;/p&gt;

&lt;p&gt;So I don't treat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM → Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as a valid architecture.&lt;/p&gt;

&lt;p&gt;Instead, there are several boundaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM
 ↓
Tool Selection
 ↓
Input Validation
 ↓
Authorization
 ↓
Confirmation
 ↓
Business Logic
 ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer gets an opportunity to reject an invalid operation.&lt;/p&gt;

&lt;p&gt;This makes the AI system behave more like another application client rather than a privileged superuser.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I learned building it
&lt;/h1&gt;

&lt;p&gt;The biggest lesson was that adding AI to a SaaS application is not mainly about calling an LLM API.&lt;/p&gt;

&lt;p&gt;The difficult part is everything around the model.&lt;/p&gt;

&lt;p&gt;I had to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who is making the request?&lt;/li&gt;
&lt;li&gt;What context does the agent need?&lt;/li&gt;
&lt;li&gt;What data can it access?&lt;/li&gt;
&lt;li&gt;What tools should it have?&lt;/li&gt;
&lt;li&gt;What actions can it perform?&lt;/li&gt;
&lt;li&gt;Which operations require confirmation?&lt;/li&gt;
&lt;li&gt;How does tenant isolation continue to work?&lt;/li&gt;
&lt;li&gt;How do I prevent the AI layer from bypassing authorization?&lt;/li&gt;
&lt;li&gt;How do I provide product knowledge without exposing private customer data?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model is only one component of that system.&lt;/p&gt;




&lt;h1&gt;
  
  
  The architecture today
&lt;/h1&gt;

&lt;p&gt;The resulting architecture looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         User
                           │
                           ▼
                      AI Request
                           │
                           ▼
                     Agent Context
                           │
                           ▼
                       Orchestrator
                           │
             ┌─────────────┼─────────────┐
             │             │             │
             ▼             ▼             ▼
        Personal       Organization     CRM
         Agent            Agent         Agent
             │             │             │
             └─────────────┼─────────────┘
                           │
                           ▼
                      Model Router
                           │
                           ▼
                       LLM / Model
                           │
                           ▼
                         Tools
                           │
                           ▼
                     Authorization
                           │
                           ▼
                       Validation
                           │
                    ┌──────┴──────┐
                    │             │
                 Read          Mutation
                    │             │
                    │        Confirmation
                    │             │
                    └──────┬──────┘
                           ▼
                    Business Logic
                           │
                           ▼
                       Database
                           │
                           ▼
                         RLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's still evolving, but this structure gives me something much more useful than a single chatbot bolted onto the side of the CRM.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final thoughts
&lt;/h1&gt;

&lt;p&gt;Building an AI assistant inside a SaaS product changed how I think about AI architecture.&lt;/p&gt;

&lt;p&gt;The interesting question isn't just:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which model should I use?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What should this model be allowed to know and do?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For me, scoped agents became a practical way to answer that question.&lt;/p&gt;

&lt;p&gt;Each agent gets a defined purpose, context, and set of capabilities. The application remains responsible for authorization, validation, and execution.&lt;/p&gt;

&lt;p&gt;The result is an AI system that can interact with a real SaaS application without treating the LLM as an all-powerful user.&lt;/p&gt;

&lt;p&gt;And that's probably the biggest lesson I've taken from building it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI doesn't replace application architecture. It makes good application architecture even more important.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;uniThread is still evolving, and so is its AI architecture. But building it has been a great way to learn what happens when LLMs move beyond chat and start becoming actual components of a software system.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>saas</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built Multi-Tenant Data Isolation for My SaaS CRM with PostgreSQL RLS</title>
      <dc:creator>Jestony Silvano</dc:creator>
      <pubDate>Thu, 10 Sep 2026 16:00:33 +0000</pubDate>
      <link>https://dev.to/jestscaledev/how-i-built-multi-tenant-data-isolation-for-my-saas-crm-with-postgresql-rls-3e7d</link>
      <guid>https://dev.to/jestscaledev/how-i-built-multi-tenant-data-isolation-for-my-saas-crm-with-postgresql-rls-3e7d</guid>
      <description>&lt;h1&gt;
  
  
  How I Built Multi-Tenant Data Isolation for My SaaS CRM with PostgreSQL RLS
&lt;/h1&gt;

&lt;p&gt;When I started building uniThread CRM, I wanted it to be more than just a basic CRUD project. I wanted to actually understand what it takes to build a SaaS application where multiple organizations share the same system without ever seeing each other's data.&lt;/p&gt;

&lt;p&gt;That led me to one of the harder questions I had to answer while building it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I make sure users can only ever access data belonging to their own organization?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My answer ended up being a combination of PostgreSQL Row-Level Security (RLS) and authorization logic on the backend. Here's how I got there, and why I didn't just trust one layer to handle it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Obvious Requirement
&lt;/h2&gt;

&lt;p&gt;The rule is simple to state: Organization A should never be able to access Organization B's data. Simple to say, less simple to guarantee.&lt;/p&gt;

&lt;p&gt;The easy way to handle this is to just add an &lt;code&gt;organization_id&lt;/code&gt; filter to every database query in the backend. And that works... until someone forgets to add it. One missed &lt;code&gt;WHERE&lt;/code&gt; clause on one endpoint, and suddenly you've got a data leak. I didn't want tenant isolation to depend entirely on every developer (including future me) remembering to add that filter every single time.&lt;/p&gt;

&lt;p&gt;That's what got me looking into PostgreSQL RLS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing Around Organizations
&lt;/h2&gt;

&lt;p&gt;For any organization-specific CRM data, I use an &lt;code&gt;organization_id&lt;/code&gt; column to tie each record to the organization that owns it. Nothing fancy, just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;leads&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;organization_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives every lead a clear, explicit relationship to an organization. It sounds almost too simple to matter, but it's the foundation everything else builds on top of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing in Row-Level Security
&lt;/h2&gt;

&lt;p&gt;PostgreSQL RLS lets the database itself decide which rows a query is allowed to see, instead of leaving that entirely up to the application code.&lt;/p&gt;

&lt;p&gt;First, you turn it on for the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;leads&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you write a policy that only allows access to rows belonging to the current user's organization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"Users can access their organization leads"&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;leads&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;organization_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_user_organization_id&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;Once that's in place, tenant isolation stops being something only my application layer is responsible for. The database is now enforcing it too. Even if I mess something up in the app code, there's still a wall between organizations at the data layer. That extra layer of protection is really what sold me on this approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Didn't Just Rely on RLS
&lt;/h2&gt;

&lt;p&gt;Even with RLS in place, I still wanted the backend to own authorization and business logic. RLS is great at stopping cross-tenant access, but it's not really designed to handle things like "can an Agent delete a lead" or "is this user even allowed to hit this endpoint."&lt;/p&gt;

&lt;p&gt;So the backend follows a fairly standard layered structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
   ↓
Controller
   ↓
Service
   ↓
Repository
   ↓
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend handles authentication, role-based access control, organization context, business rules, and validation. Then PostgreSQL adds its own layer on top through RLS.&lt;/p&gt;

&lt;p&gt;The point isn't to make one layer do everything. It's to let each layer do what it's actually good at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roles
&lt;/h2&gt;

&lt;p&gt;Right now, uniThread has three organization-level roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Owner&lt;/li&gt;
&lt;li&gt;Manager&lt;/li&gt;
&lt;li&gt;Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A user's role determines what they're allowed to do inside their organization.&lt;/p&gt;

&lt;p&gt;Working through this part is actually what made the difference between authentication and authorization click for me in a way it never had before.&lt;/p&gt;

&lt;p&gt;Authentication asks: &lt;em&gt;who are you?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Authorization asks: &lt;em&gt;what are you allowed to do?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And tenant isolation asks a third, separate question: &lt;em&gt;which organization's data are you even allowed to touch?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They sound like variations of the same idea when you read about them, but once you're actually implementing all three at once, the differences become a lot more obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Before building uniThread, my mental model of auth was basically:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Login → JWT → Authenticated User&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it. That was the whole picture in my head.&lt;/p&gt;

&lt;p&gt;Building something multi-tenant broke that mental model pretty quickly. Getting a user authenticated is really just the starting point. After that you still have to figure out what they can access, what actions they're allowed to take, and how the database itself should be protecting that data regardless of what the app layer does.&lt;/p&gt;

&lt;p&gt;It also made me a lot more deliberate about where security checks actually belong, and made me think harder about what happens the day a developer forgets to add one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;Between organization-scoped data, backend authorization, and PostgreSQL RLS, uniThread now has a decent foundation for keeping tenant data properly separated.&lt;/p&gt;

&lt;p&gt;More than the technical result, though, this was one of the first parts of the project that pushed me past "just build the CRUD feature" and into actually thinking about architecture, security, data ownership, and what happens as the system grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I were starting uniThread over, I'd think about tenant isolation a lot earlier than I did. It's way easier to bake organization ownership into your schema from day one than to retrofit it after you've already built a pile of features on top of a shaky foundation.&lt;/p&gt;

&lt;p&gt;I'd also spend more time up front defining authorization rules, instead of discovering the edge cases one at a time while implementing individual features and going "oh... wait."&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;uniThread started out as just "I want to build a CRM." Somewhere along the way it turned into a much better lesson in how SaaS applications are actually put together under the hood.&lt;/p&gt;

&lt;p&gt;Multi-tenancy was one of those topics that looked simple from the outside and turned out to be way more interesting than expected once I actually had to implement it myself.&lt;/p&gt;

&lt;p&gt;I'm still learning as I go, but honestly, that's a big part of why I keep building projects like this. The interesting part was never just making the app work. It's figuring out how to make it work &lt;em&gt;correctly&lt;/em&gt; as things get more complicated.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>postgres</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Juniors! Overengineer Your Projects</title>
      <dc:creator>Jestony Silvano</dc:creator>
      <pubDate>Wed, 09 Sep 2026 11:15:17 +0000</pubDate>
      <link>https://dev.to/jestscaledev/juniors-overengineer-your-projects-42j0</link>
      <guid>https://dev.to/jestscaledev/juniors-overengineer-your-projects-42j0</guid>
      <description>&lt;p&gt;title: "Juniors! Overengineer Your Projects"&lt;br&gt;
published: true&lt;br&gt;
description: "Stop optimizing for the number of projects you can finish. Build fewer things, go deeper, use AI intelligently, break your own code, and learn how real software systems work."&lt;br&gt;
tags: "ai, beginners, webdev, programming, softwareengineering"&lt;/p&gt;




&lt;h1&gt;
  
  
  Juniors! Overengineer Your Projects
&lt;/h1&gt;

&lt;p&gt;If you're a junior developer trying to land your first software job, you've probably heard some version of this advice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Build more projects."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So you build another one.&lt;/p&gt;

&lt;p&gt;Then another.&lt;/p&gt;

&lt;p&gt;A portfolio website.&lt;br&gt;
A weather app.&lt;br&gt;
A todo app.&lt;br&gt;
A Netflix clone.&lt;br&gt;
A CRUD application.&lt;/p&gt;

&lt;p&gt;And eventually, you have a GitHub full of repositories. But how much did you actually learn? I think we should approach this differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop Optimizing for the Number of Projects
&lt;/h2&gt;

&lt;p&gt;You don't need to prove that you can start 20 projects. You need to prove that you can &lt;strong&gt;understand, build, debug, and improve software.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How quickly can I finish this?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How far can I take this?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where things get interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Don't Be Afraid to Overengineer
&lt;/h2&gt;

&lt;p&gt;Obviously, don't build a distributed microservice architecture for a todo app just because you can. That's not the point. The point is that &lt;strong&gt;complexity can create opportunities to learn.&lt;/strong&gt; Take a project further than you initially planned. Add authentication. Design a proper database. Think about authorization. Add role-based access control. Handle errors properly. Think about concurrency. Add caching. Write tests. Build background jobs. Think about how the system behaves when something fails. Deploy it. Monitor it. Then break it. Then fix it. You might spend weeks building something that could have been hacked together in three days. And that's okay. If those extra weeks made you understand something you didn't understand before, &lt;strong&gt;you didn't waste them.&lt;/strong&gt; You learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Read Your Own Code
&lt;/h2&gt;

&lt;p&gt;This one sounds obvious, but I think it's seriously underrated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the code you wrote.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not just while you're writing it.&lt;/p&gt;

&lt;p&gt;Come back to it later.&lt;/p&gt;

&lt;p&gt;Look at an old function and ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why did I build it this way?&lt;/li&gt;
&lt;li&gt;Do I still understand this?&lt;/li&gt;
&lt;li&gt;Would I design this differently now?&lt;/li&gt;
&lt;li&gt;Is this actually maintainable?&lt;/li&gt;
&lt;li&gt;What happens when this fails?&lt;/li&gt;
&lt;li&gt;Why does this abstraction exist?&lt;/li&gt;
&lt;li&gt;Did I solve the actual problem, or did I just make the code more complicated?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then change it.&lt;/p&gt;

&lt;p&gt;Refactor it.&lt;/p&gt;

&lt;p&gt;Delete it.&lt;/p&gt;

&lt;p&gt;Rewrite it.&lt;/p&gt;

&lt;p&gt;Your old code is one of the best teachers you'll ever have because it shows you exactly what you used to think you understood.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. We're Still Early in the AI Era
&lt;/h2&gt;

&lt;p&gt;This is probably one of the most interesting times to be learning software development.&lt;/p&gt;

&lt;p&gt;AI can write code incredibly quickly.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;use it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seriously.&lt;/p&gt;

&lt;p&gt;Don't pretend AI doesn't exist just because you want to "learn properly."&lt;/p&gt;

&lt;p&gt;Leverage it.&lt;/p&gt;

&lt;p&gt;Ask it to explain concepts you don't understand.&lt;/p&gt;

&lt;p&gt;Ask it to explain unfamiliar code.&lt;/p&gt;

&lt;p&gt;Ask it to compare architectural approaches.&lt;/p&gt;

&lt;p&gt;Ask it to review your database design.&lt;/p&gt;

&lt;p&gt;Ask it to find edge cases.&lt;/p&gt;

&lt;p&gt;Ask it to challenge your assumptions.&lt;/p&gt;

&lt;p&gt;Ask it questions you would normally spend hours trying to formulate yourself.&lt;/p&gt;

&lt;p&gt;But there's a catch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Let AI Become Your Brain
&lt;/h3&gt;

&lt;p&gt;The quality of the answer you get is heavily influenced by the quality of the question you ask.&lt;/p&gt;

&lt;p&gt;And the quality of your question depends on the context you can provide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build context.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Learn the problem first.&lt;/p&gt;

&lt;p&gt;Understand your system.&lt;/p&gt;

&lt;p&gt;Understand the constraints.&lt;/p&gt;

&lt;p&gt;Understand what you're trying to solve.&lt;/p&gt;

&lt;p&gt;Then ask AI.&lt;/p&gt;

&lt;p&gt;If you give it vague questions and no context, you'll often get vague solutions.&lt;/p&gt;

&lt;p&gt;If you understand the problem and provide meaningful context, AI becomes a much more powerful learning tool.&lt;/p&gt;

&lt;p&gt;And don't be afraid to use smaller models or solve something yourself before reaching for the biggest model available.&lt;/p&gt;

&lt;p&gt;Sometimes struggling with a problem for an hour teaches you more than getting the answer in ten seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Build Systems, Not Just Projects
&lt;/h2&gt;

&lt;p&gt;There's a difference between building a project and building a system.&lt;/p&gt;

&lt;p&gt;A project can be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I built a CRUD application."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A system makes you ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What happens when 10,000 users use this?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then you start thinking differently.&lt;/p&gt;

&lt;p&gt;You start thinking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;Data modeling&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;Failure recovery&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't necessarily need to implement all of these.&lt;/p&gt;

&lt;p&gt;But even thinking about them changes the way you understand software.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Build Something That Is Actually Yours
&lt;/h2&gt;

&lt;p&gt;Please, build another tutorial project if you want to. There's nothing wrong with that. But eventually, &lt;strong&gt;build something original.&lt;/strong&gt; Build something because you had an idea, because you noticed a problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I have no idea how I'm going to build this."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where the fun starts. You might fail. Good. You might rewrite half of it. Even better. You might discover that your original architecture was terrible. Perfect. Now you have something to learn from.&lt;/p&gt;

&lt;h1&gt;
  
  
  Don't Maximize Output. Maximize Learning.
&lt;/h1&gt;

&lt;p&gt;There's a strange pressure among junior developers to constantly produce.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More GitHub repositories.&lt;/li&gt;
&lt;li&gt;More projects.&lt;/li&gt;
&lt;li&gt;More technologies.&lt;/li&gt;
&lt;li&gt;More certificates.&lt;/li&gt;
&lt;li&gt;More tutorials.&lt;/li&gt;
&lt;li&gt;More AI-generated code.&lt;/li&gt;
&lt;li&gt;More, more, more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But software development isn't a race to produce the most code. You can build 30 projects and barely understand any of them. Or you can build one project that forces you to learn databases, APIs, authentication, security, deployment, architecture, testing, debugging, and system design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which developer do you think learned more?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So if you're a junior developer trying to get your first job:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't be afraid to take your project too far.&lt;/li&gt;
&lt;li&gt;Don't be afraid to spend too much time learning.&lt;/li&gt;
&lt;li&gt;Don't be afraid to read your own code.&lt;/li&gt;
&lt;li&gt;Don't be afraid to break things.&lt;/li&gt;
&lt;li&gt;Don't be afraid to rewrite things.&lt;/li&gt;
&lt;li&gt;Don't be afraid to ask AI questions.&lt;/li&gt;
&lt;li&gt;Don't be afraid to work without AI sometimes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And most importantly:&lt;/p&gt;

&lt;h1&gt;
  
  
  Don't Be Afraid to Build Something Bigger Than You Know How to Build
&lt;/h1&gt;

&lt;p&gt;Because that's how you learn how to build it.&lt;/p&gt;




</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>programming</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
