<?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: Sandeep Singh</title>
    <description>The latest articles on DEV Community by Sandeep Singh (@aidevbuilds).</description>
    <link>https://dev.to/aidevbuilds</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%2F3984726%2F0d151670-f4c2-4f2d-9e95-49bd50360cdc.png</url>
      <title>DEV Community: Sandeep Singh</title>
      <link>https://dev.to/aidevbuilds</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aidevbuilds"/>
    <language>en</language>
    <item>
      <title>AI Ships Your Code in Minutes. Your Team Pays for It for Months. Here's Why.</title>
      <dc:creator>Sandeep Singh</dc:creator>
      <pubDate>Mon, 15 Jun 2026 05:47:31 +0000</pubDate>
      <link>https://dev.to/aidevbuilds/ai-ships-your-code-in-minutes-your-team-pays-for-it-for-months-heres-why-30oo</link>
      <guid>https://dev.to/aidevbuilds/ai-ships-your-code-in-minutes-your-team-pays-for-it-for-months-heres-why-30oo</guid>
      <description>&lt;h2&gt;
  
  
  AI Writes Code Fast. That's Exactly the Problem.
&lt;/h2&gt;

&lt;p&gt;Speed is not the enemy. Unmaintainable speed is.&lt;/p&gt;

&lt;p&gt;AI coding assistants can ship a working endpoint in minutes. What they can't do by default is ship one you can still safely touch six months later.&lt;/p&gt;

&lt;p&gt;I've seen this pattern repeatedly. Teams move fast, ship fast, celebrate fast. Then the codebase becomes a place people are afraid of. Every change breaks something unrelated. No one wants to be the one who touched it last.&lt;/p&gt;

&lt;p&gt;The cause is almost never complexity. It's coupling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the AI Actually Hands You
&lt;/h2&gt;

&lt;p&gt;Ask any AI assistant to build an order creation endpoint. Here's what comes back:&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;'&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM customers WHERE id = ?&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;creditLimit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order rejected: credit limit exceeded&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INSERT INTO orders (customer_id, amount) VALUES (?, ?)&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order created&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works. It'll pass a demo. The PM will be happy.&lt;/p&gt;

&lt;p&gt;Now look at what's jammed into one function: HTTP handling, raw SQL, business rule validation, and response formatting. One file. No boundaries. No separation.&lt;/p&gt;

&lt;p&gt;That's tight coupling. And tight coupling is a time bomb with a slow fuse.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Waiter Who Does Everything
&lt;/h2&gt;

&lt;p&gt;Picture a restaurant where the waiter takes your order, sprints to the pantry, cooks the food, washes the dishes, and tracks inventory.&lt;/p&gt;

&lt;p&gt;With five tables, it holds together. Barely.&lt;/p&gt;

&lt;p&gt;With fifty tables, orders get dropped. Mistakes compound. Nobody knows who's responsible. Training a new person is nearly impossible because one person owns everything.&lt;/p&gt;

&lt;p&gt;Now picture a well-run kitchen. The waiter handles the table. The chef runs the kitchen. The pantry staff manages ingredients. Each role has a clear boundary. Each person can be replaced, trained, and scaled independently.&lt;/p&gt;

&lt;p&gt;That's what layered architecture does for your codebase. Same principle. Different medium.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Layers. Three Responsibilities.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Layer 1: The Controller
&lt;/h3&gt;

&lt;p&gt;The controller handles HTTP. That's its only job.&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="c1"&gt;// controllers/orderController.js&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;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;It receives the request. It calls a service. It returns a response.&lt;/p&gt;

&lt;p&gt;What it never does: write SQL, enforce business rules, or touch the database. The moment a controller starts deciding whether an order should be approved, it has crossed a boundary it doesn't own.&lt;/p&gt;

&lt;p&gt;Controllers are translators. HTTP in, HTTP out. Nothing else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: The Service Layer
&lt;/h3&gt;

&lt;p&gt;This is where the business logic lives. Pricing rules, credit checks, discount logic, approval workflows all of it belongs here.&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="c1"&gt;// services/orderService.js&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;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;customerRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;creditLimit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;orderData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreditLimitExceededError&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="nx"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderData&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;One question drives this layer: How should the business behave?&lt;/p&gt;

&lt;p&gt;Not: &lt;em&gt;How does the database work? That's someone else's job.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Notice the credit limit check throws a domain error not an HTTP status code. The service layer has no idea what HTTP is. That's by design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: The Repository
&lt;/h3&gt;

&lt;p&gt;The repository owns data access. SQL queries, ORM calls, database-specific logic it all lives here and only here.&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="c1"&gt;// repositories/customerRepository.js&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;getById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&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="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM customers WHERE id = ?&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="nx"&gt;customerId&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;One question drives this layer: How do we retrieve or store data?&lt;/p&gt;

&lt;p&gt;Not: &lt;em&gt;Should this order be approved? That answer belongs two layers up.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Actually Gain
&lt;/h2&gt;

&lt;p&gt;Loose coupling means layers depend on contracts, not implementations. The call chain 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;Controller
    ↓
Service
    ↓
Repository
    ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer is independently replaceable. Here's what that buys you in practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt;. You can test business logic without a database. You can test HTTP behavior without mocking business rules. Tests become fast and targeted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactoring&lt;/strong&gt;. Migrating from MySQL to PostgreSQL means touching one layer the repository. Business logic is untouched. Nothing breaks accidentally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onboarding&lt;/strong&gt;. A new engineer reads the service layer to understand what the business does. They read the repository to understand data access. No layer bleeds into another.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-assisted development&lt;/strong&gt;. This one is underrated. When you ask an AI to regenerate a repository, it can do so without touching business logic. When you update an endpoint, you don't rewrite database code. Defined layers make AI tools significantly more precise and less dangerous.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why AI Defaults to the Mess
&lt;/h2&gt;

&lt;p&gt;AI coding assistants are trained on tutorials, quick-start guides, and Stack Overflow answers. That code is written to demonstrate a concept quickly not to model production architecture.&lt;/p&gt;

&lt;p&gt;These tools optimize for the shortest path to a visible result. The output 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;Route
 ├─ Validation
 ├─ Business Rules
 ├─ SQL Queries
 ├─ External API Calls
 └─ Response Formatting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Day one feels productive. You're shipping. It runs.&lt;/p&gt;

&lt;p&gt;Month six: every feature touches every file. Bug fixes create side effects. Nobody wants to refactor because nobody knows what else will break.&lt;/p&gt;

&lt;p&gt;The velocity you gained upfront was borrowed against your future team's sanity.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Use AI Without the Mess
&lt;/h2&gt;

&lt;p&gt;Define the architecture first. Then ask AI to fill in the layers.&lt;/p&gt;

&lt;p&gt;Be explicit with your prompts:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Generate the service layer only. Assume a repository interface exists. No database queries. No HTTP handling."&lt;/p&gt;

&lt;p&gt;"Write a repository for the orders table. Return raw data objects. No business logic."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI tools are excellent at implementing patterns when the boundaries are clear. The boundaries however are your job to set. That's not changing anytime soon.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;AI generates code. Architecture determines whether that code survives real usage.&lt;/p&gt;

&lt;p&gt;Layered architecture isn't a large-team luxury. It's the structure that lets AI-generated applications grow without becoming a liability.&lt;/p&gt;

&lt;p&gt;The faster we build, the more separation of concerns matters. Speed without structure is just debt with better marketing.&lt;/p&gt;

&lt;p&gt;Define your layers. Enforce your boundaries. Then let the AI fill them in.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your approach to structuring AI-generated code? Drop it in the comments curious what's working across different stacks and team sizes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>node</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
