<?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: Hamza Syed</title>
    <description>The latest articles on DEV Community by Hamza Syed (@hamza140).</description>
    <link>https://dev.to/hamza140</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%2F1201974%2F47c79ca1-f50c-4ab9-8501-5728fd379c96.png</url>
      <title>DEV Community: Hamza Syed</title>
      <link>https://dev.to/hamza140</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hamza140"/>
    <language>en</language>
    <item>
      <title>Test the migration before it tests production: rehearsing AI-generated database changes</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Tue, 27 Oct 2026 13:26:24 +0000</pubDate>
      <link>https://dev.to/hamza140/test-the-migration-before-it-tests-production-rehearsing-ai-generated-database-changes-18mn</link>
      <guid>https://dev.to/hamza140/test-the-migration-before-it-tests-production-rehearsing-ai-generated-database-changes-18mn</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/articles/ai-generated-database-migrations"&gt;The pre-merge checklist&lt;/a&gt; catches a bad migration's shape: no rollback, a table rewrite hiding in one step, a backfill welded to a schema change. What it cannot catch is behaviour. A migration can pass every review point and still take a nine-minute lock on your busiest table, because review happens on paper and locks happen on data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.notvibescoded.com/articles/testing-ai-generated-migrations" rel="noopener noreferrer"&gt;Read the full article on NotVibesCoded&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Testing an AI-generated migration means rehearsing it. Run it, reverse it, and measure it against data that looks like yours, before production volunteers to go first. The agent wrote the migration in seconds. The rehearsal is what keeps that speed from becoming the incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a rehearsal database that can lie to you less
&lt;/h2&gt;

&lt;p&gt;A rehearsal is only worth anything if the database can fail the way production fails. Three properties matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Production-shaped data.&lt;/strong&gt; An anonymized production dump, restored. If that is impossible, generated fixtures at real volume: row counts per table within an order of magnitude of production, and the same skew (one tenant with half the rows, timestamps clustered in the last month).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The same engine.&lt;/strong&gt; Same database major version, same extensions. Behaviour around locking, defaults, and type coercion shifts between versions just enough to hide the failures you are rehearsing for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ugly rows.&lt;/strong&gt; Nulls where the agent assumes none. Names at the column's length limit. Orphaned foreign keys from the bug you fixed in March. Agents test the happy path, because the happy path is what their training data shows. Production data is mostly edge cases that survived.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is step 4 of the checklist, run it against realistic data, done properly. "Realistic" is a property you build, not a hope you have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up, down, up
&lt;/h2&gt;

&lt;p&gt;The single most skipped test: run the migration forward, run the rollback, then run it forward again. Three runs, not one.&lt;/p&gt;

&lt;p&gt;The down path is where agents bluff. Two patterns to watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The destructive stub.&lt;/strong&gt; The auto-generated rollback drops the new column and calls it done. If the application wrote data to that column between deploy and rollback, the rollback deletes it. That is not a rollback; that is data loss with good posture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The second rewrite.&lt;/strong&gt; The forward migration was carefully staged, and the rollback is a single statement that rewrites the table on the way back, taking the lock the forward path was designed to avoid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Time the rollback while you are there. If the forward run takes twenty seconds and the rollback takes forty minutes, you do not have a rollback. You have a restore from backup with extra steps, and the time to learn that is during the rehearsal, while it is still a choice. Any step that is genuinely irreversible (dropping the old column in the contract phase, for example) gets rehearsed as what it is: a point of no return, scheduled deliberately, never discovered mid-incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diff the data, not the schema
&lt;/h2&gt;

&lt;p&gt;Schema diffs are the easy part, and tools do them well. Corruption is quieter. The migration succeeds, the deploy goes green, and three percent of the rows are wrong in a way nobody notices until the invoices go out.&lt;/p&gt;

&lt;p&gt;Before and after the rehearsal run, capture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Row counts&lt;/strong&gt; for every touched table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Null counts&lt;/strong&gt; on every touched column.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggregates that must not move:&lt;/strong&gt; sums on money columns, counts of distinct keys, min and max on timestamps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invariants as queries:&lt;/strong&gt; every order still has a customer; every line item still sums to its order total; wherever the old and new columns coexist, they agree.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take the worked example from the checklist, renaming &lt;code&gt;users.name&lt;/code&gt; to &lt;code&gt;users.full_name&lt;/code&gt;. After the backfill step, the rehearsal query is one line: count the rows where &lt;code&gt;full_name&lt;/code&gt; is null and &lt;code&gt;name&lt;/code&gt; is not. Anything above zero is a failed rehearsal, no matter how clean the deploy looked. Then sample twenty rows and read them. Some checks are still eyes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it under load and watch the locks
&lt;/h2&gt;

&lt;p&gt;A migration that is instant against a quiet staging copy can still stall production, because production is never quiet. So the rehearsal runs with a concurrent writer: a small script that inserts and updates rows in the table being altered, for the whole run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Watch the waits.&lt;/strong&gt; On Postgres, &lt;code&gt;pg_locks&lt;/code&gt; and &lt;code&gt;pg_stat_activity&lt;/code&gt; show you who is waiting on whom. Set a low &lt;code&gt;lock_timeout&lt;/code&gt; during rehearsal so contention fails loudly instead of queueing silently behind the migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure the rewrite honestly.&lt;/strong&gt; If a statement rewrites the table, duration scales with size. Ten seconds on a one-million-row rehearsal table is not ten seconds on a hundred million rows, and the rehearsal report should say the extrapolated number out loud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the concurrent index actually happened.&lt;/strong&gt; A &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; that fails leaves an invalid index behind, quietly. The rehearsal verifies the index exists and is valid, not just that the command returned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the same discipline as &lt;a href="https://dev.to/articles/reviewing-ai-generated-prs-checklist"&gt;reviewing an AI-generated PR&lt;/a&gt;: the diff tells you what the author intended, and only running the thing tells you what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Break the backfill on purpose
&lt;/h2&gt;

&lt;p&gt;If the migration ships a backfill, kill it halfway through the rehearsal run. Then answer three questions. Does it resume where it stopped, restart safely from the top, or apply twice? Are the batches idempotent, keyed on ranges of the primary key rather than offsets, so a rerun skips finished work? Is progress visible, and is the rate limited enough that the backfill is not competing with the application for the database?&lt;/p&gt;

&lt;p&gt;An agent's default backfill is a single &lt;code&gt;UPDATE&lt;/code&gt; statement wearing a trench coat. On a large table that is a lock, a transaction log spike, and a very long minute. The rehearsal is where you find out which one you were sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The go or no-go gate
&lt;/h2&gt;

&lt;p&gt;Five answers, written down before the deploy starts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rollback rehearsed, and its duration is known.&lt;/li&gt;
&lt;li&gt;Data diffs clean: counts, nulls, aggregates, invariants.&lt;/li&gt;
&lt;li&gt;Worst lock wait measured under load, not guessed.&lt;/li&gt;
&lt;li&gt;Backfill killed and resumed successfully.&lt;/li&gt;
&lt;li&gt;The rollback decision (run the down migration, or restore) is made now, not during the incident.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Agents are genuinely good at writing migrations: the boilerplate, the framework idioms, the syntax. The checklist keeps the judgment about shape. The rehearsal keeps the judgment about behaviour. Both stay yours, because production data is the one thing in the pipeline that cannot be regenerated.&lt;/p&gt;

&lt;p&gt;More on shipping AI code without slop at notvibescoded.com.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>database</category>
      <category>postgres</category>
    </item>
    <item>
      <title>What 'context' actually means in AI coding tools</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Sun, 25 Oct 2026 20:44:17 +0000</pubDate>
      <link>https://dev.to/hamza140/what-context-actually-means-in-ai-coding-tools-3hbg</link>
      <guid>https://dev.to/hamza140/what-context-actually-means-in-ai-coding-tools-3hbg</guid>
      <description>&lt;p&gt;Every AI coding tool talks about context, and most explanations stop at "the files it can see". That is only the start.&lt;br&gt;
&lt;a href="https://www.notvibescoded.com/articles/what-context-means-ai-coding" rel="noopener noreferrer"&gt;https://www.notvibescoded.com/articles/what-context-means-ai-coding&lt;/a&gt;&lt;br&gt;
Context has three layers. Layer one is the code: the file being edited, the files it imports, the tests that cover it. An assistant that can see the test will write a change that passes it. One that cannot will write a change that looks right and fails it.&lt;br&gt;
Layer two is the rules you have written down. A short context file at the root of the project, naming the exact test command, the exact lint command, the files nobody edits by hand, and the definition of done, is worth more than a hundred extra files in the model's view. Keep it short and specific, and never let it go stale: stale context is worse than no context, because the assistant follows it confidently.&lt;br&gt;
Layer three is the decisions you have already made. Why this library and not that one, why this endpoint returns that shape. An assistant that does not know those decisions will happily reopen them. Document the ones that keep getting reopened, in one paragraph: what was tried, why it failed, what is used instead.&lt;br&gt;
Before you prompt, check three things. Can it see the code and tests the task touches? Does it have your rules in writing? Does it know the decisions that would stop it reopening a settled question? If all three are yes, a short prompt works, because the context is doing the heavy lifting.&lt;br&gt;
The full piece is on NotVibesCoded.&lt;br&gt;
&lt;a href="https://www.notvibescoded.com/articles/what-context-means-ai-coding" rel="noopener noreferrer"&gt;https://www.notvibescoded.com/articles/what-context-means-ai-coding&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Write a bug report your AI assistant can actually fix</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Sun, 25 Oct 2026 20:43:35 +0000</pubDate>
      <link>https://dev.to/hamza140/write-a-bug-report-your-ai-assistant-can-actually-fix-2ani</link>
      <guid>https://dev.to/hamza140/write-a-bug-report-your-ai-assistant-can-actually-fix-2ani</guid>
      <description>&lt;p&gt;Most AI fixes fail because the report is missing the one thing the fix depends on: what you expected, what actually happened, and how to reproduce it.&lt;br&gt;
&lt;a href="https://www.notvibescoded.com/articles/bug-report-ai-can-fix" rel="noopener noreferrer"&gt;https://www.notvibescoded.com/articles/bug-report-ai-can-fix&lt;/a&gt;&lt;br&gt;
A usable bug report has three sentences. First, what you expected, specifically: "Clicking Save on the settings page should update the display name and show a confirmation." Second, what actually happened: "Clicking Save reloads the page, the display name is unchanged, and no confirmation appears." Third, how to reproduce it, the shortest path from a fresh state to the bug, including any condition that makes it happen or not happen.&lt;br&gt;
Include the error, not your theory about it. Paste the full error message, plus ten lines before it for context, and stop there. A thousand lines of log is almost as useless as no log at all. Your theory can be wrong. The log cannot be misquoted if it is right there.&lt;br&gt;
Keep it to one bug per report. Bundling three bugs into one prompt means the assistant fixes the first, half fixes the second, and silently drops the third. Split them, and each fix has one thing to get right and one way to check it.&lt;br&gt;
Finally, say what "fixed" looks like: "Fixed means the steps above now produce the expected behaviour." That stops the assistant declaring victory after changing code that merely looks related, and it gives you a thirty-second test to run before merging.&lt;br&gt;
The full piece, including a reusable template and what to leave out of a report, is on NotVibesCoded.&lt;br&gt;
&lt;a href="https://www.notvibescoded.com/articles/bug-report-ai-can-fix" rel="noopener noreferrer"&gt;https://www.notvibescoded.com/articles/bug-report-ai-can-fix&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>debugging</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Export Your Own Canva Carousel for LinkedIn</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Sun, 25 Oct 2026 05:15:25 +0000</pubDate>
      <link>https://dev.to/hamza140/how-to-export-your-own-canva-carousel-for-linkedin-38fg</link>
      <guid>https://dev.to/hamza140/how-to-export-your-own-canva-carousel-for-linkedin-38fg</guid>
      <description>&lt;p&gt;You designed a carousel in Canva. Now you need it on LinkedIn, and LinkedIn does not work like Instagram. On Instagram, a carousel is a set of separate images uploaded in swipe order. On LinkedIn, a carousel is a document post: you upload one multi-page file, and LinkedIn turns each page into a swipeable slide. Export the wrong file type and you will either get a single flat image or a broken upload. Here is the exact workflow for your own Canva design.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://carouselsmaker.com/blog/how-to-export-canva-carousel-linkedin" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcarouselsmaker.com%2Fog-image.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://carouselsmaker.com/blog/how-to-export-canva-carousel-linkedin" rel="noopener noreferrer" class="c-link"&gt;
            Export Canva Carousel for LinkedIn as PDF | Carousels Maker
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            LinkedIn carousels are PDF document posts, not separate images. Here is the exact Canva export workflow for your own design, plus the size and file limits that matter.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcarouselsmaker.com%2Ffavicon.ico" width="48" height="48"&gt;
          carouselsmaker.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Step 1: set the slide size before you export
&lt;/h2&gt;

&lt;p&gt;LinkedIn does not publish an official pixel size for carousels. It publishes file limits, and the sizes everyone uses are the industry standard that matches how the mobile feed displays images: 1080 × 1350 pixels (4:5 portrait) for maximum screen space, or 1080 × 1080 pixels (1:1 square) for a tidy desktop frame.&lt;/p&gt;

&lt;p&gt;The rule that matters more than the exact pixels is consistency. Every page in your file must be the same size. LinkedIn fits PDFs with mixed page sizes to one page size before upload, which is how text gets cropped on the odd slide out. If your Canva design mixes sizes, fix that in Canva first, not after export.&lt;/p&gt;

&lt;p&gt;If you are starting a new design in Canva, set a custom size of 1080 × 1350 before designing anything. If the design already exists at another size, resize it in Canva and check every slide for cropped text before exporting. A carousel that looks fine in Canva and broken on LinkedIn almost always broke at this step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: export as one PDF, not separate images
&lt;/h2&gt;

&lt;p&gt;In Canva, click Share, then Download. Set the file type to PDF Standard, not PDF Print, unless you have a specific reason to use print settings. PDF Standard keeps the file smaller and renders reliably on LinkedIn. Download all pages, not a page range, unless you are deliberately splitting the deck.&lt;/p&gt;

&lt;p&gt;Do not choose PNG or JPG here. That is the Instagram workflow, where separate images are exactly what you want. For LinkedIn, separate images uploaded as a normal post will not become a swipeable carousel. The document post is what makes the swipe happen, and the document is a PDF.&lt;/p&gt;

&lt;p&gt;Check the finished file before you upload it. Open the PDF and swipe through every page. Check for cropped headlines, text that runs off the edge, and any slide that looks different from the rest. LinkedIn converts each page to an image, so what you see in the PDF is very close to what readers will see, and a flaw you catch here costs nothing to fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: check the file limits
&lt;/h2&gt;

&lt;p&gt;LinkedIn's document post limits are generous, but they are hard limits. A file over 100 MB is rejected. A file over 300 pages is rejected. Most carousels should be nowhere near either limit: a strong LinkedIn carousel is usually 5 to 10 slides, and a file that large is almost always a sign of uncompressed images rather than good content.&lt;/p&gt;

&lt;p&gt;If your PDF is unexpectedly large, the usual cause is full-resolution photos on every slide. Compress the images in Canva before exporting, or export at a lower resolution, and re-check that text is still crisp at phone size. Body text below about 24 points, or headlines that only read at full screen, will vanish on a phone feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: upload it as a document post
&lt;/h2&gt;

&lt;p&gt;On LinkedIn, start a post, choose Add a document, and upload your PDF. Do not upload it as images, and do not paste a link to the Canva file and expect a carousel. The document upload is the step that creates the swipeable format.&lt;/p&gt;

&lt;p&gt;The document title you type during upload is not a throwaway filename. It appears above the carousel in the feed, so it is the second thing readers see after your post copy. Give it the same care as a headline: specific, promising a payoff, and matched to the post copy above it. A title like "presentation-final-v3" wastes that space.&lt;/p&gt;

&lt;p&gt;Write a short caption that introduces the carousel and tells readers what they will get by swiping, then post. LinkedIn turns each PDF page into a slide, in order, and readers swipe through it in the feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Instagram version, for comparison
&lt;/h2&gt;

&lt;p&gt;If you also want this carousel on Instagram, that is a second export, not a reuse of the LinkedIn file. For Instagram, go back to Canva, Share, Download, choose PNG or JPG, and check the option to download pages as separate files. That gives you one image per slide, in swipe order, ready to upload as an Instagram carousel. The LinkedIn PDF and the Instagram images are two exports of the same design, built for two different formats, and confusing them is the most common export mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Slide size: 1080 × 1350 or 1080 × 1080, and the same on every slide.&lt;/li&gt;
&lt;li&gt;LinkedIn export: one multi-page PDF, PDF Standard, all pages.&lt;/li&gt;
&lt;li&gt;File limits: under 100 MB, under 300 pages, aim for 5 to 10 slides.&lt;/li&gt;
&lt;li&gt;Upload: Add a document on LinkedIn, with a real document title, not a filename.&lt;/li&gt;
&lt;li&gt;Instagram, separately: PNG or JPG, pages as separate files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Design once, export twice, and each platform gets the file format it actually wants. That is the whole trick, and it is why a carousel that looks perfect in Canva can still fail on LinkedIn: the design was right, the export was for the wrong platform.&lt;/p&gt;




&lt;p&gt;Originally published on Carousels Maker, the fastest way to make a carousel.&lt;/p&gt;

</description>
      <category>canva</category>
      <category>linkedin</category>
      <category>tutorial</category>
      <category>design</category>
    </item>
    <item>
      <title>Your vibe-coded site is not indexed. Here is how to find out why.</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Sun, 25 Oct 2026 05:14:42 +0000</pubDate>
      <link>https://dev.to/hamza140/your-vibe-coded-site-is-not-indexed-here-is-how-to-find-out-why-3o0f</link>
      <guid>https://dev.to/hamza140/your-vibe-coded-site-is-not-indexed-here-is-how-to-find-out-why-3o0f</guid>
      <description>&lt;p&gt;Your site is live. You can open it in a browser, it loads, it looks finished, and Google acts as if it does not exist. Search for your domain and nothing comes up. Search for a sentence from your homepage and nothing comes up. This is the most common invisible failure of a vibe-coded site, and it is usually fixable in an afternoon, once you stop guessing and check in the right order.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/vibe-coded-site-not-indexed-fix" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Farticles%2Fvibe-coded-site-not-indexed-fix%2Fopengraph-image" height="630" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/vibe-coded-site-not-indexed-fix" rel="noopener noreferrer" class="c-link"&gt;
            Vibe-coded site not indexed? How to fix it · NotVibesCoded
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            A new AI-built site can be live, fast and completely invisible to Google. Work through this checklist in order: access, sitemap, canonicals, content, and…
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Ficons%2Ffavicon-light.svg" width="256" height="256"&gt;
          notvibescoded.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  First, understand what "not indexed" actually means
&lt;/h2&gt;

&lt;p&gt;Indexing is not the same as ranking, and it is not the same as being live. A page is indexed when Google has crawled it, understood it, and stored it in a way that lets it appear in results. A page can be live and unindexed, indexed and ranking on page ten, or indexed and ranking well. If your site is new, the most likely story is simpler than you fear: Google has not finished processing it yet. New properties routinely show "Processing data" in Search Console for days, and that is not an error, it is a queue.&lt;/p&gt;

&lt;p&gt;Before changing anything, check whether Google knows about the site at all. Open Google Search Console, pick your property, and look at the Performance report. If it shows impressions, even a handful, Google is finding pages. If it shows nothing, and the Pages report says it is still processing data, wait a day and check again before rebuilding anything. Changing URLs, titles and structure while Google is still processing the first version only restarts the clock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: can Google reach the site at all?
&lt;/h2&gt;

&lt;p&gt;The fastest check is the robots.txt file. Open yourdomain.com/robots.txt in a browser. If it disallows everything, or disallows the pages you care about, that is your answer, and it is a common vibe-coding accident: a template, a staging configuration or an AI-generated file that blocks crawlers, shipped to production unchanged.&lt;/p&gt;

&lt;p&gt;Also check for a noindex tag. Open a page, view its source, and search for "noindex". A noindex meta tag, or an X-Robots-Tag header sending noindex, tells Google to crawl the page and then throw it away. Staging sites use noindex on purpose. Production sites inherit it by accident when the staging configuration is copied over. Remove it from production, and only from production.&lt;/p&gt;

&lt;p&gt;If robots.txt and noindex are clean, check that the site actually returns a 200 status for its pages, not a redirect loop, a 404, or a JavaScript-only shell with no content in the HTML. Google renders JavaScript, but a page that only becomes content after a long client-side load is slower to index and easier to misread than a page that ships its content in the HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: is there a sitemap, and does it match reality?
&lt;/h2&gt;

&lt;p&gt;A sitemap is a list of the pages you want indexed, submitted to Search Console. It does not guarantee indexing, but a missing, stale or wrong sitemap makes Google's job harder, especially for a new site with no links pointing at it.&lt;/p&gt;

&lt;p&gt;Check three things. First, that a sitemap exists at yourdomain.com/sitemap.xml and loads without errors. Second, that it was submitted in Search Console and shows a Success status with a recent last-read date. Third, that the URLs in it are the real, canonical URLs: the same host, the same www or non-www choice, the same trailing-slash behaviour as the live pages. A sitemap full of redirected or duplicate URLs teaches Google to distrust it.&lt;/p&gt;

&lt;p&gt;If you have both a www and a non-www sitemap, pick one canonical host and stick to it everywhere: canonical tags, sitemap, internal links and the sitemap submitted in Search Console. Mixed canonicals split a new site's authority across two versions of itself before it has any authority to split.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: are your pages worth indexing?
&lt;/h2&gt;

&lt;p&gt;Google indexes pages it thinks are useful and distinct. A vibe-coded site often fails this test in specific, fixable ways: dozens of near-identical pages generated from a template, thin pages with a paragraph of generic text, or pages that exist only to hold a link or a form. None of these are punished as a site, but none of them are indexed eagerly either.&lt;/p&gt;

&lt;p&gt;For each page you want indexed, ask three questions. Does it say something a visitor cannot get from your other pages? Does it have a real title that describes its content, under 60 characters, rather than a brand name repeated on every page? Does it have a meta description that describes the page, rather than a missing or duplicated one? Fix the pages that fail, starting with the ones that already get impressions in Search Console, because Google is already showing interest in those.&lt;/p&gt;

&lt;p&gt;Internal linking matters here too. A page with no links pointing at it from your own site is an orphan, and orphans are indexed last, if at all. Every important page should be reachable from your homepage or a main navigation section in a small number of clicks, not only from a sitemap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: ask Google to look, then leave it alone
&lt;/h2&gt;

&lt;p&gt;Once access, sitemap, canonicals and content are clean, use Search Console to request indexing on your homepage and a few key pages. This is a request, not a command, and it does not work instantly. After requesting, the unhelpful-sounding advice is the correct one: leave the URLs alone. Changing a URL, a title or a slug after requesting indexing sends Google back to the start for that page.&lt;/p&gt;

&lt;p&gt;New domains take time. One to four weeks for proper indexing is normal, and the first impressions often arrive before the first clicks, in the form of low-position appearances for very specific queries. That is progress, not failure. A page appearing at position 40 for an exact phrase is indexed. Ranking it higher is a separate job, and it starts with the content and internal links, not with more indexing requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist, in order
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Confirm the problem in Search Console, and check whether the Pages report is still processing before changing anything.&lt;/li&gt;
&lt;li&gt;Check robots.txt for a blanket block, and check pages for a noindex tag or header.&lt;/li&gt;
&lt;li&gt;Confirm pages return 200 and ship their content in the HTML.&lt;/li&gt;
&lt;li&gt;Confirm a sitemap exists, is submitted, shows Success, and lists only real canonical URLs.&lt;/li&gt;
&lt;li&gt;Fix thin, duplicated and orphaned pages, starting with pages that already get impressions.&lt;/li&gt;
&lt;li&gt;Give every page a distinct title under 60 characters and a real meta description.&lt;/li&gt;
&lt;li&gt;Request indexing on the homepage and key pages, then stop changing their URLs.&lt;/li&gt;
&lt;li&gt;Wait. Check weekly, not hourly, and judge by impressions first, clicks second.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A vibe-coded site is not indexed because it is AI-built. It is unindexed because access is blocked, the sitemap is wrong, the pages are thin, or Google simply has not finished yet. Those are four different problems with four different fixes, and the checklist above tells you which one you have before you start fixing the wrong one.&lt;/p&gt;




&lt;p&gt;Originally published on NotVibesCoded. Use AI. Just don't ship slop.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>You patched PHP2Shell. How do you know it actually worked?</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Sun, 25 Oct 2026 05:13:58 +0000</pubDate>
      <link>https://dev.to/hamza140/you-patched-php2shell-how-do-you-know-it-actually-worked-34p8</link>
      <guid>https://dev.to/hamza140/you-patched-php2shell-how-do-you-know-it-actually-worked-34p8</guid>
      <description>&lt;p&gt;PHP2Shell, the pair of critical PHP flaws tracked as CVE-2026-109618 and CVE-2026-109619, was fixed on 21 October in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Both flaws are rated 9.2 on the CVSS v4 scale, and either one can let an unauthenticated attacker run code on a server that handles PHP requests. If you have already updated, good. If you have only updated the package and moved on, you may not actually be fixed yet.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/php2shell-patched-check-server-fixed" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Farticles%2Fphp2shell-patched-check-server-fixed%2Fopengraph-image" height="630" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/php2shell-patched-check-server-fixed" rel="noopener noreferrer" class="c-link"&gt;
            PHP2Shell patched? Check your server is fixed · NotVibesCoded
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            PHP2Shell was fixed on 21 October, but updating the package is only half the job. Workers must restart, containers must rebuild, and version numbers can lie.…
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Ficons%2Ffavicon-light.svg" width="256" height="256"&gt;
          notvibescoded.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This is the follow-up to our PHP2Shell article. It covers the part most teams skip: proving the fix is live, on the server that is actually serving traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only complete fix is the update
&lt;/h2&gt;

&lt;p&gt;Start here, because nothing else on this list is a substitute. Updating PHP to a fixed version, 8.5.12, 8.4.27, 8.3.36 or 8.2.35 depending on your branch, is the only complete fix. Application updates do not fix it. WordPress, plugin, theme and framework updates do not change PHP itself, and on Apache with mod_php a demonstrated exploit fires from a single unauthenticated request before any application code runs, so application-level defences never get a chance to help.&lt;/p&gt;

&lt;p&gt;If you run PHP in containers, updating a running container is not enough. Rebuild the image from a patched base, then redeploy. A patched container running on an old image is exposed again the moment it restarts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: restart every PHP worker
&lt;/h2&gt;

&lt;p&gt;This is the step that silently undoes patches. PHP-FPM workers, Apache processes and long-running workers that were started before the update are still running the old code. The update only takes effect once every worker restarts.&lt;/p&gt;

&lt;p&gt;After updating, restart PHP-FPM, restart Apache if you use mod_php, and restart any other PHP worker process you run. Then check that no old worker is still alive. If you use a process manager, confirm the old processes actually exited rather than assuming the restart command worked. On a busy server, a worker that refuses to die can keep serving requests with the old, vulnerable PHP for hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: check the version on the live server, not on your laptop
&lt;/h2&gt;

&lt;p&gt;Run the version check on the server that serves production traffic, and in the same environment that serves requests. A staging server, a build machine and a developer laptop can all show a patched version while production still runs the old one. If you deploy through containers, check inside the running container, not just the image tag in your registry.&lt;/p&gt;

&lt;p&gt;There is one trap here that catches people on Ubuntu and Debian. Those distributions backport security fixes into the version they already ship, so &lt;code&gt;php -v&lt;/code&gt; can keep showing an old number, such as 8.3.6, even after the fix is installed. On Ubuntu 24.04, the fixed package is 8.3.6-0ubuntu0.24.04.13, and its changelog names both CVEs. Check the package version and changelog, not just the PHP version number, before declaring yourself safe or vulnerable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: turn off session upload progress, and leave it off
&lt;/h2&gt;

&lt;p&gt;PHP has asked users to switch off the session upload progress feature, and it is worth doing even after patching. The feature is enabled by default, it is rarely used by modern applications, and it is one confirmed attack path for CVE-2026-109619. Setting &lt;code&gt;session.upload_progress.enabled=0&lt;/code&gt; in php.ini, a PHP-FPM pool configuration, or a server configuration that applies before request parsing removes that path. Restart every worker after changing it, for the same reason as above: the setting only applies to workers started after the change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: if you cannot update yet, say so honestly
&lt;/h2&gt;

&lt;p&gt;Some servers cannot update this week. PHP 8.1 and older get no numbered release for this fix; the PHP project published source-only backports for 7.4, 8.0 and 8.1, and some distributions and long-term support vendors ship their own patched packages. If you are on one of those branches, your route is a vendor package, a source backport you build and test yourself, or a supported patched runtime from a vendor.&lt;/p&gt;

&lt;p&gt;Interim measures can reduce exposure to specific attack paths, and they are worth doing while you arrange the update: raise the PHP memory limit, lower &lt;code&gt;post_max_size&lt;/code&gt;, limit request sizes at the web server or proxy, and disable session upload progress. Be clear about what these are, though. They reduce exposure. They do not make an installation safe. Only the update fixes it, and anyone who tells you otherwise is selling you comfort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: check for signs of compromise before you relax
&lt;/h2&gt;

&lt;p&gt;PHP2Shell was not only a theoretical flaw. Security firms reported exploitation attempts before the fixes were public, and broad exploitation after release, so a server that was internet-facing during that window should not be assumed clean just because it is patched now. Patching stops new attacks through these flaws. It does not undo an attack that already succeeded.&lt;/p&gt;

&lt;p&gt;Look for what does not belong: unexpected files in upload directories and web roots, new administrator accounts, scheduled tasks and cron entries you did not create, outbound connections to unfamiliar hosts, and changes to application files around the disclosure window. If you find anything you cannot explain, treat it as a compromise and rebuild from known-good sources rather than cleaning up files one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist, in order
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Update PHP to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a vendor package that names both CVEs.&lt;/li&gt;
&lt;li&gt;Rebuild container images from a patched base and redeploy.&lt;/li&gt;
&lt;li&gt;Restart every PHP worker, and confirm the old ones actually exited.&lt;/li&gt;
&lt;li&gt;Verify the fix on the production server, checking package versions where distributions backport fixes.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;session.upload_progress.enabled=0&lt;/code&gt; and restart workers again.&lt;/li&gt;
&lt;li&gt;If you cannot update, arrange a vendor package or backport now, and use the interim measures only as a bridge.&lt;/li&gt;
&lt;li&gt;Check for compromise before closing the incident.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Patching is not a single command. It is an update, a restart, a verification, and a check for damage, in that order. Do all four, and PHP2Shell is closed. Skip any one of them, and you are guessing.&lt;/p&gt;




&lt;p&gt;Originally published on NotVibesCoded. Use AI. Just don't ship slop.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How GitHub Is Rebuilding Its Git Storage for the Age of AI Agents</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Fri, 23 Oct 2026 17:55:14 +0000</pubDate>
      <link>https://dev.to/hamza140/how-github-is-rebuilding-its-git-storage-for-the-age-of-ai-agents-m13</link>
      <guid>https://dev.to/hamza140/how-github-is-rebuilding-its-git-storage-for-the-age-of-ai-agents-m13</guid>
      <description>&lt;p&gt;GitHub recently published a &lt;a href="https://github.blog/engineering/architecture-optimization/building-git-infrastructure-for-agent-scale-development/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; explaining that it is rebuilding the system that stores all of its Git repositories, and the reason is AI agents. Not models, not features: the storage layer underneath every repository on the platform. Here is what is breaking, and how GitHub plans to fix it.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/github-rebuilding-git-storage-ai-agents" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Fimages%2Farticles%2Fgithub-rebuilding-git-storage-ai-agents-cover.png" height="450" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/github-rebuilding-git-storage-ai-agents" rel="noopener noreferrer" class="c-link"&gt;
            GitHub rebuilds Git storage for AI agents · NotVibesCoded
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            AI agents commit and push after almost every step, and that traffic broke the scaling assumptions underneath GitHub. The company is rebuilding its Git storage…
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Ficons%2Ffavicon-light.svg" width="256" height="256"&gt;
          notvibescoded.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The Scale of the Problem
&lt;/h2&gt;

&lt;p&gt;In September, developers and agents made 7.38 billion commits on GitHub, more than five times as many as a year earlier. Pushes grew by almost the same factor, reaching 3.35 billion a month. Merged pull requests are at nearly four times last year's volume, and GitHub Actions logged 3.26 billion runs in September alone.&lt;/p&gt;

&lt;p&gt;These are not numbers a system designed around human working rhythms absorbs by accident. They are what happens when the "developer" on the other end of a push is a loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Push Actually Does
&lt;/h2&gt;

&lt;p&gt;Git stores two kinds of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Objects&lt;/strong&gt; are every file, folder, and commit. Each is named by a hash of its content, and once written, an object never changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refs&lt;/strong&gt; are names that point to a commit. A branch like &lt;code&gt;main&lt;/code&gt; is just a ref, and it changes all the time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A push therefore does two things: it uploads the new objects, then moves the ref (for example, &lt;code&gt;main&lt;/code&gt;) to the new commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Agents Change
&lt;/h2&gt;

&lt;p&gt;A person might push a few times a day. An agent working in a loop can push after almost every step, so the time one push takes becomes the agent's speed limit. As GitHub's Brian Celenza puts it, an agent commits or checkpoints after nearly every action, and waits for each push to finish before continuing.&lt;/p&gt;

&lt;p&gt;In the largest repositories, GitHub says thousands of agents can be working at once, each on its own branch. All of those writes land in the same place in GitHub's system. When the work is merged, it goes through a merge queue into &lt;code&gt;main&lt;/code&gt;, so every merge competes to move the same ref.&lt;/p&gt;

&lt;p&gt;Each push also triggers thousands of reads. CI and code scanning fetch the same commit thousands of times a minute. Reads like these are easy to scale with copies and caches. Writes are the hard part, because every push has to be saved safely and seen the same way by everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  How GitHub Stores Repositories Today
&lt;/h2&gt;

&lt;p&gt;The current system is called Spokes. It keeps a full copy of every repository on five file servers, and reads are spread across those copies. When a push moves a ref, a coordinator asks the copies to agree, and a majority (three out of five) must say yes.&lt;/p&gt;

&lt;p&gt;The trouble is that these copies do two jobs: they keep the data safe, and they serve the reads. When a busy repository needs more read capacity, GitHub adds another full copy. But every copy takes part in every write, and a write waits for the slowest copy. More copies therefore means slower writes, which creates a ceiling for the busiest repositories. In Celenza's words: "Every replica participates in every write, so a push is only as fast as the slowest replica in its set."&lt;/p&gt;

&lt;p&gt;GitHub's answer is to separate the two jobs, keeping data safe and serving traffic, using two main ideas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idea One: Coordinate Only What Needs Agreement
&lt;/h2&gt;

&lt;p&gt;Today, a push saves the objects, checks them, and scans for leaked secrets in a single coordinated step, and only then moves the ref. But only the ref update actually needs the copies to agree. As things stand, a second push has to wait for the whole of the first.&lt;/p&gt;

&lt;p&gt;In the new design, the heavy steps run in parallel, so pushes only queue for that one small step. This matters most for &lt;code&gt;main&lt;/code&gt;, where every merge waits for the one before it. If moving the ref takes 300 milliseconds, &lt;code&gt;main&lt;/code&gt; can handle about three merges a second. At 10 milliseconds, it can handle about 100. Making that single step faster speeds up every merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idea Two: Separate Storage From Compute
&lt;/h2&gt;

&lt;p&gt;The second idea is to split storage, where the data lives, from compute, the machines that answer requests.&lt;/p&gt;

&lt;p&gt;The authoritative copy of each repository moves to Azure Blob Storage, which keeps it safe and replicated and becomes the single source of truth. Reads are served by lightweight workers layered on top, each keeping a cache of the data it serves.&lt;/p&gt;

&lt;p&gt;When CI fetches the same commit thousands of times, the workers answer and the push does no extra work. If a worker fails, nothing is lost. It is simply a cache miss, and a new worker fills its cache from storage.&lt;/p&gt;

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

&lt;p&gt;In GitHub's internal benchmarks, the new design handles up to 35 times more write throughput. GitHub says the next post in the series will go deeper into the new architecture, and it will be worth covering once it is out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Two principles stand out: keep the part that needs agreement as small as possible, and don't make the copies that keep your data safe do the serving as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Means for the Rest of Us
&lt;/h2&gt;

&lt;p&gt;You are not running GitHub's storage layer, but if you run agents against your own repositories, the same physics apply at your scale. Commit frequency is a load decision: an agent that pushes after every step multiplies CI runs, review notifications, and merge contention exactly the way it multiplied GitHub's. Batching agent work into fewer, larger pushes, gating merges instead of every checkpoint, and keeping the coordination points in your own workflow (approvals, required checks, the merge itself) as small and fast as possible are the same two principles, applied to a team instead of a platform.&lt;/p&gt;

&lt;p&gt;The infrastructure of software development was designed around humans who commit when they've finished thinking. Agents commit while they think. Everything downstream, storage, CI, review, is being rebuilt for that difference, starting, apparently, with GitHub itself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: GitHub Engineering, &lt;a href="https://github.blog/engineering/architecture-optimization/building-git-infrastructure-for-agent-scale-development/" rel="noopener noreferrer"&gt;Building Git infrastructure for agent-scale development&lt;/a&gt;, October 6, 2026.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;This piece is published on &lt;a href="https://www.notvibescoded.com/articles/github-rebuilding-git-storage-ai-agents" rel="noopener noreferrer"&gt;NotVibesCoded&lt;/a&gt;&lt;/strong&gt;, alongside field guides on context files for coding agents, reviewing AI-generated PRs, and shipping AI-built software without the slop.&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Reviewing AI-generated PRs: the 10-minute checklist</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Wed, 21 Oct 2026 18:55:35 +0000</pubDate>
      <link>https://dev.to/hamza140/reviewing-ai-generated-prs-the-10-minute-checklist-4gm8</link>
      <guid>https://dev.to/hamza140/reviewing-ai-generated-prs-the-10-minute-checklist-4gm8</guid>
      <description>&lt;p&gt;AI-generated PRs have a tell: they're &lt;em&gt;too&lt;/em&gt; clean. Perfect formatting, thorough comments, confident summaries. And that's exactly why they need a different review: the polish hides the same failure modes every time.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/reviewing-ai-generated-prs-checklist" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Fog%2Fog-default.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/reviewing-ai-generated-prs-checklist" rel="noopener noreferrer" class="c-link"&gt;
            Reviewing AI-generated PRs: a checklist · NotVibesCoded
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            AI-generated pull requests look confident and complete. That confidence is the hazard. A 10-minute review checklist that catches the specific ways…
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Ficons%2Ffavicon-light.svg" width="256" height="256"&gt;
          notvibescoded.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The 10-minute checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Minutes 0–2: What did it touch that it shouldn't have?
&lt;/h3&gt;

&lt;p&gt;Open the file list first, not the diff. Agents are notorious for scope creep: reformatting untouched files, "fixing" adjacent code, adding dependencies for one helper function. If the PR touches files outside the task, that's your first question, not your first approval.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minutes 2–4: The tests it wrote for itself
&lt;/h3&gt;

&lt;p&gt;Agents write tests that pass, not tests that protect. Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do the tests assert behavior or implementation?&lt;/strong&gt; A test that mirrors the code line-by-line will pass forever and catch nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are the edge cases real?&lt;/strong&gt; Empty inputs, nulls, permission failures, timeouts, or just the happy path three times?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Would the test fail if the code were wrong?&lt;/strong&gt; Mutate the logic mentally. If the test still passes, it's decoration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Minutes 4–6: Error handling and the unhappy path
&lt;/h3&gt;

&lt;p&gt;This is where AI code most consistently underperforms. Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bare &lt;code&gt;except:&lt;/code&gt; / empty catch blocks&lt;/li&gt;
&lt;li&gt;Errors swallowed and logged but never surfaced&lt;/li&gt;
&lt;li&gt;Retries without backoff or limits&lt;/li&gt;
&lt;li&gt;User-facing errors that leak stack traces or internals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ask: "what happens when this fails?" If you can't answer from reading the code, neither can the on-call engineer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minutes 6–8: Security basics
&lt;/h3&gt;

&lt;p&gt;Agents don't think adversarially. Scan for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL/query construction with string interpolation&lt;/li&gt;
&lt;li&gt;Missing authorization checks (authentication ≠ authorization)&lt;/li&gt;
&lt;li&gt;Secrets or tokens in code, logs, or error messages&lt;/li&gt;
&lt;li&gt;Mass assignment / over-permissive input binding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need a full audit, just the OWASP top-10 instincts applied for two minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minutes 8–10: Would you have written it this way?
&lt;/h3&gt;

&lt;p&gt;Not style, &lt;em&gt;judgment&lt;/em&gt;. Did it pick the simple approach or the clever one? Did it add an abstraction for a single use? Did it duplicate something that exists three files over? Agents optimize for completing the task, not for the codebase's future. That's your job, and it's the part that takes the longest to learn and the least time to check once you know the smell.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to automate
&lt;/h2&gt;

&lt;p&gt;Everything above that's mechanical belongs in CI, not in your 10 minutes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scope check: fail if files outside the ticket's area change without explanation&lt;/li&gt;
&lt;li&gt;Test mutation score on AI-generated PRs (higher bar than human PRs, not lower)&lt;/li&gt;
&lt;li&gt;Secret scanning, obviously&lt;/li&gt;
&lt;li&gt;Lint rules for the error-handling patterns your team keeps seeing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Check the description against the diff
&lt;/h2&gt;

&lt;p&gt;The checklist above reviews the code. Spend one of your ten minutes reviewing the &lt;em&gt;story&lt;/em&gt; the PR tells about the code, because AI-written descriptions are fluent, confident, and occasionally fictional.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Added tests for X"&lt;/strong&gt;: open the test file. Count the assertions. Agents regularly describe the tests they intended to write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"No behavior change"&lt;/strong&gt; on a refactor: spot-check one call site of anything that moved. Refactors that rename are safe; refactors that rename &lt;em&gt;and&lt;/em&gt; adjust a condition are behavior changes wearing a costume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Handles the error case"&lt;/strong&gt;: find the error case in the diff. If the handling is a log line, the description and the code disagree, and the code is the one that ships.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't paranoia; it's calibration. The description is generated by the same process that generated the code, with the same confidence and the same blind spots. Trust it exactly as much as you'd trust a summary written by someone who skimmed.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the approach itself is wrong
&lt;/h2&gt;

&lt;p&gt;The hardest AI PRs to review are the ones where every line is locally fine and the whole thing is wrong. The agent added a utility that already exists two folders over. It solved the problem in the controller when your architecture solves it in the service layer. It introduced a second state-management pattern next to the one the codebase standardized on.&lt;/p&gt;

&lt;p&gt;Line-by-line review waves these through, because each line is defensible. The defense is the file list from minutes 0–2 and one question: &lt;strong&gt;"If a new hire had written this, where would I tell them this code belongs?"&lt;/strong&gt; If the answer is "not here," the review is a redirect, not a line edit. Ask for the approach change early and specifically, naming the existing pattern to follow. Agents take structural feedback well; it's the feedback they almost never get, because reviewers default to commenting on lines.&lt;/p&gt;

&lt;p&gt;A related tell lives in the commit history. One giant commit named "implement feature" is normal for agents and useless for review; a sequence of small commits, each doing one thing, is reviewable. You can't rewrite the agent's habits, but you can set the bar in your context file: small commits, messages that say why, no unrelated changes in the same commit. Review quality is partly a function of what the reviewer is handed, and shaping the input is cheaper than straining over a bad one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mindset shift
&lt;/h2&gt;

&lt;p&gt;Reviewing human code, you ask "is this correct?" Reviewing AI code, ask "what did it &lt;em&gt;not&lt;/em&gt; consider?" The agent did the typing. Your review is the engineering.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;This checklist is published on &lt;a href="https://www.notvibescoded.com/articles/reviewing-ai-generated-prs-checklist" rel="noopener noreferrer"&gt;NotVibesCoded&lt;/a&gt;&lt;/strong&gt;, alongside field guides on context files for coding agents, database migrations with AI, and shipping AI-built software without the slop.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>AI Found Two Critical Flaws Hiding in PHP for a Decade</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Wed, 21 Oct 2026 17:18:54 +0000</pubDate>
      <link>https://dev.to/hamza140/ai-found-two-critical-flaws-hiding-in-php-for-a-decade-cej</link>
      <guid>https://dev.to/hamza140/ai-found-two-critical-flaws-hiding-in-php-for-a-decade-cej</guid>
      <description>&lt;p&gt;PHP released emergency security updates today for two critical vulnerabilities that allow an attacker to run code on a web server without logging in. The flaws, nicknamed PHP2Shell, are tracked as CVE-2026-109618 and CVE-2026-109619, and both carry a CVSS v4 score of 9.2. They affect the PHP engine itself, which means they sit underneath WordPress, Drupal, Magento, and every other site PHP serves, no matter how careful the code on top is.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/php2shell-ai-found-critical-php-flaws" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Fog%2Fog-default.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/php2shell-ai-found-critical-php-flaws" rel="noopener noreferrer" class="c-link"&gt;
            PHP2Shell: AI found critical PHP flaws · NotVibesCoded
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Two critical remote code execution flaws sat in PHP, the language behind roughly 70% of websites, for more than a decade. AI models helped researchers find…
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Ficons%2Ffavicon-light.svg" width="256" height="256"&gt;
          notvibescoded.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The detail that should get your attention is not the severity score. It is how the bugs were found, and how quickly they were turned into working attacks once someone looked properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What PHP2Shell Actually Is
&lt;/h2&gt;

&lt;p&gt;Both flaws are memory-safety bugs in how PHP handles requests, and both end in unauthenticated remote code execution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2026-109618&lt;/strong&gt; corrupts memory during out-of-memory handling. When a request runs out of memory mid-setup, PHP's error recovery can leave request data half-initialised, and the cleanup path then treats that broken data as valid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2026-109619&lt;/strong&gt; is a use-after-free problem with global state. Data from one visitor's request can outlive its memory and be picked up by a later request. One confirmed attack route runs through PHP's session upload-progress feature, which has been enabled by default for over a decade.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every PHP installation that serves web requests is in scope: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed. The PHP team rates mod_php as the highest risk because a demonstrated exploit fires from a single unauthenticated request before any application code runs. Login screens and security plugins never get a chance to help.&lt;/p&gt;

&lt;p&gt;Wiz, the Google-owned security company that independently found both bugs, says every PHP release back to 5.4 is hit by at least one of them. The flaws have effectively been sitting in the codebase for more than a decade.&lt;/p&gt;

&lt;h2&gt;
  
  
  How AI Found Them
&lt;/h2&gt;

&lt;p&gt;In August, Ryan Austin, a researcher at the security firm Calif, was auditing PHP's source code with help from GPT-5.6 Sol, one of OpenAI's most advanced models. The audit was part of Patch the Planet, an open-source security programme OpenAI launched in June with Trail of Bits. Austin found one of the flaws. Wiz's researchers, working independently with Google's Gemini 4 Argon, GPT-5.6 Sol, and their own research system, found both.&lt;/p&gt;

&lt;p&gt;OpenAI has published its account of the response under the title "Patch the Planet", and calls the bugs "one of the most impactful security issues in recent memory". That is not just marketing. The class of bug involved had been flagged as a problem area decades ago and then largely forgotten, because exploiting it was considered too difficult. AI changed the economics of trying. By the researchers' account, the models helped turn a theoretical weakness into reliable exploitation with as few as a few dozen requests to a server: seconds on small systems, hours on complex ones. Work that would have taken a skilled team months took about a week.&lt;/p&gt;

&lt;p&gt;Calif's chief executive, Thai Duong, put the other half of the problem plainly to the Wall Street Journal: "Some dude on Twitter could take the patches and run AI against it and post some exploit script. All hell will break loose." Once a patch ships, anyone can diff it against the old code and ask a model to explain exactly what changed and where the bug was. The same capability that found the flaw can reverse-engineer it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scramble
&lt;/h2&gt;

&lt;p&gt;The coordinated disclosure did not survive contact with the internet. Details of both CVEs circulated on Hacker News and social media on October 20, a day before the planned announcement, and an exploit demonstration dubbed php2shell spread online. PHP pulled its release forward by a day.&lt;/p&gt;

&lt;p&gt;On the morning of October 21, Cloudflare's CTO reported early attacks already in progress, roughly six hours before the patches landed, and said the company had deployed emergency firewall rules for all customers, including free ones. He was explicit that this was a stopgap and that only patching solves it. CISA and the FBI urged organisations to patch immediately. In the Netherlands, the national login system DigiD, the parliament website, and the country's own cybersecurity agency took their sites offline as a precaution. Security telemetry counted more than 22.6 million internet-exposed servers running potentially vulnerable PHP.&lt;/p&gt;

&lt;p&gt;The fixes shipped at about 13:35 UTC in PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You Run PHP, Patch Now
&lt;/h2&gt;

&lt;p&gt;The PHP Foundation's position is blunt: "There are no complete mitigations, updating is the only safe choice."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Update&lt;/strong&gt; to PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35. Linux distribution and LTS users should check their vendor for packages, which were shared in advance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not rely on workarounds.&lt;/strong&gt; Setting &lt;code&gt;session.upload_progress.enabled=0&lt;/code&gt; blocks one confirmed attack path for CVE-2026-109619 and nothing else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP 8.1 and older get no upstream fix.&lt;/strong&gt; The PHP team released minimal source backports for 7.4, 8.0, and 8.1, and some vendors are backporting further, but an unpatched legacy install that cannot be updated should be taken offline until it can be. A large share of the PHP servers visible on the internet still run unsupported versions, and they are now the softest targets on the web.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Part That Matters After Today
&lt;/h2&gt;

&lt;p&gt;Strip away the incident response and one fact remains: flaws that survived a decade of human review were found and weaponised in weeks once frontier models were pointed at the code. That cuts in both directions at once. Defenders get an auditor that never gets tired and can read an entire engine looking for one bad assumption. Attackers get the same auditor, plus a patch-diffing machine the moment a fix ships.&lt;/p&gt;

&lt;p&gt;This time the defenders moved first, and the coordination worked: researchers, maintainers, hosting providers, and governments had fixes and firewall rules ready before a working exploit was public. OpenAI is explicitly proposing that kind of coordinated response as the model for what comes next, because the discovery side is only going to get faster.&lt;/p&gt;

&lt;p&gt;For anyone building software with AI, including the AI-assisted kind covered on this site, the lesson is uncomfortable and useful. The review bar just moved. Code that merely looks right has always been a liability; now there is machinery that can systematically find the places where it is not right, whether you run that machinery on your own code first or someone else runs it for you. Shipping fast is fine. Shipping unreviewed is becoming a measurable risk with a timeline attached.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.notvibescoded.com/articles/php2shell-ai-found-critical-php-flaws" rel="noopener noreferrer"&gt;NotVibesCoded&lt;/a&gt;, where the PHP2Shell piece covers what happened, how AI found the flaws, and what to patch. Read the full article there, and follow for honest, hands-on coverage of building with AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>php</category>
      <category>programming</category>
    </item>
    <item>
      <title>Context files for coding agents: the complete guide</title>
      <dc:creator>Hamza Syed</dc:creator>
      <pubDate>Wed, 21 Oct 2026 08:47:47 +0000</pubDate>
      <link>https://dev.to/hamza140/context-files-for-coding-agents-the-complete-guide-2ahp</link>
      <guid>https://dev.to/hamza140/context-files-for-coding-agents-the-complete-guide-2ahp</guid>
      <description>&lt;p&gt;Every major coding agent now reads a context file at the start of a session. Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt;. Cursor reads &lt;code&gt;AGENTS.md&lt;/code&gt; and its own rules files. Copilot reads &lt;code&gt;.github/muse-instructions.md&lt;/code&gt;. Aider can be pointed at a conventions file. Different names, same idea: a short document that tells the agent how your project works before it touches a line of code.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/context-files-coding-agents-field-guide" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Fog%2Fog-default.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.notvibescoded.com/articles/context-files-coding-agents-field-guide" rel="noopener noreferrer" class="c-link"&gt;
            Context files for coding agents: a guide · NotVibesCoded
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Every coding agent reads a context file, CLAUDE.md, AGENTS.md, Copilot instructions, and most of them are decoration. The complete guide to writing one that…
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.notvibescoded.com%2Ficons%2Ffavicon-light.svg" width="256" height="256"&gt;
          notvibescoded.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Most of these files are useless. They’re either empty, a wishlist (“write clean, maintainable code”), or a 500-line brain dump nobody maintains. The agents aren’t the problem. The files are. This guide is the merged, field-tested version of everything we’ve published on the topic: what to put in the file, what to leave out, how to structure it, and how to prompt in a way that makes the file pay off.&lt;/p&gt;

&lt;h2&gt;
  
  
  One file, several names: don’t maintain five copies
&lt;/h2&gt;

&lt;p&gt;If you use more than one agent (most developers now do), the naming sprawl becomes its own problem: &lt;code&gt;CLAUDE.md&lt;/code&gt; for Claude Code, &lt;code&gt;AGENTS.md&lt;/code&gt; as the cross-tool standard Cursor and others read, &lt;code&gt;.github/muse-instructions.md&lt;/code&gt; for Copilot, a conventions file for Aider. Five copies of the same rules &lt;em&gt;will&lt;/em&gt; drift apart, and drifted instructions are worse than missing ones, because each agent then follows a different version of your conventions.&lt;/p&gt;

&lt;p&gt;The setup that survives: &lt;strong&gt;one canonical file, thin pointers everywhere else.&lt;/strong&gt; Keep the real content in &lt;code&gt;AGENTS.md&lt;/code&gt; (the name the most tools read natively), and make each tool-specific file a stub: “Read &lt;code&gt;AGENTS.md&lt;/code&gt; in the repo root and follow it; the rules below are the only additions for this tool.” Tool quirks go in the stub, everything shared lives in exactly one place. This space moves fast, so check your tool’s current docs once a quarter, but the principle doesn’t change: one source of truth, pointers elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bar: every line prevents a real mistake
&lt;/h2&gt;

&lt;p&gt;Think of the context file as onboarding a sharp junior developer who has infinite energy, perfect memory within a session, and zero knowledge of your project. You wouldn’t hand them a 200-page wiki. You’d give them the ten things that prevent the ten most common mistakes.&lt;/p&gt;

&lt;p&gt;That’s the standard for every line: &lt;strong&gt;does this prevent a real mistake you’ve seen the agent make?&lt;/strong&gt; If a line is philosophy, cut it. Agents follow concrete constraints. They ignore aspirations, because they were already trying to “write clean code” before you asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five sections that matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Stack and commands
&lt;/h3&gt;

&lt;p&gt;The highest-value section in any context file is the boring one: how to build, test, and lint. Two or three commands the agent will run constantly, with the caveats that matter (“the full suite takes about 4 minutes; run a single file while iterating”). Without this, the agent guesses, and guesses wrong, burning loops on commands that don’t exist in your repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Architecture in five lines
&lt;/h3&gt;

&lt;p&gt;Where the code lives and how it flows. “&lt;code&gt;app/&lt;/code&gt;, Next.js routes. &lt;code&gt;lib/&lt;/code&gt;, shared business logic. &lt;code&gt;db/&lt;/code&gt;, schema and migrations.” That’s enough. The agent can read the rest of the tree itself; what it can’t infer is which parts are load-bearing and which are legacy.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Conventions that differ from the default
&lt;/h3&gt;

&lt;p&gt;Don’t document standard practice, the model already knows it. Document where &lt;em&gt;you&lt;/em&gt; diverge: “Database columns are snake_case; TypeScript is camelCase.” “All timestamps are stored as UTC and converted at the UI layer.” “Never use a raw hex value, always a design token.” Rules beat wishes, and conventions with one short example beat conventions described in prose.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Things that have bitten you
&lt;/h3&gt;

&lt;p&gt;The highest-value lines in the file, because they’re the ones no model can derive from the code: “The &lt;code&gt;orders&lt;/code&gt; table joins to users on &lt;code&gt;email&lt;/code&gt;, not &lt;code&gt;user_id&lt;/code&gt;.” “Don’t run migrations against the staging database; use the seed script.” “The payments module has no foreign key to &lt;code&gt;users&lt;/code&gt;; join on &lt;code&gt;customer_email&lt;/code&gt;.” Each line here is a debugging session you never have again.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. What not to do
&lt;/h3&gt;

&lt;p&gt;Explicit prohibitions work better than you’d expect: “Don’t add a new dependency without asking.” “Don’t refactor files unrelated to the task.” “Don’t write tests for generated code.” Anti-patterns from your actual history, stated as rules, are what separate a context file that changes behavior from one that’s decoration.&lt;/p&gt;

&lt;h2&gt;
  
  
  A worked example
&lt;/h2&gt;

&lt;p&gt;Here’s a filled-in file for a small SaaS product. It’s 46 lines. Every line is there because it prevents a specific, observed failure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Ledgerline (billing SaaS)&lt;/span&gt;

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Dev: &lt;span class="sb"&gt;`npm run dev`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Test: &lt;span class="sb"&gt;`npm test -- &amp;lt;file&amp;gt;`&lt;/span&gt; while iterating; full suite &lt;span class="sb"&gt;`npm test`&lt;/span&gt; (~4 min)
&lt;span class="p"&gt;-&lt;/span&gt; Lint: &lt;span class="sb"&gt;`npm run lint`&lt;/span&gt; (must pass before committing)

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`app/`&lt;/span&gt;, Next.js routes and API handlers
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`lib/`&lt;/span&gt;, business logic (billing in &lt;span class="sb"&gt;`lib/billing/`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`db/`&lt;/span&gt;, Prisma schema and migrations

&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Database columns snake_case; TypeScript camelCase.
&lt;span class="p"&gt;-&lt;/span&gt; All timestamps UTC in the database, converted in the UI.
&lt;span class="p"&gt;-&lt;/span&gt; Money is integer cents everywhere. Never floats for amounts.
&lt;span class="p"&gt;-&lt;/span&gt; UI colors come from design tokens in &lt;span class="sb"&gt;`lib/tokens.ts`&lt;/span&gt;, never raw hex.

&lt;span class="gu"&gt;## Gotchas&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`orders`&lt;/span&gt; joins to &lt;span class="sb"&gt;`users`&lt;/span&gt; on &lt;span class="sb"&gt;`email`&lt;/span&gt;, not &lt;span class="sb"&gt;`user_id`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Webhook handlers must be idempotent: Stripe retries deliveries.
&lt;span class="p"&gt;-&lt;/span&gt; Don’t run &lt;span class="sb"&gt;`prisma migrate`&lt;/span&gt; against staging; use &lt;span class="sb"&gt;`npm run db:seed`&lt;/span&gt;.

&lt;span class="gu"&gt;## Don’t&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Don’t add dependencies without asking.
&lt;span class="p"&gt;-&lt;/span&gt; Don’t refactor outside the task’s scope.
&lt;span class="p"&gt;-&lt;/span&gt; Don’t edit generated files in &lt;span class="sb"&gt;`lib/gen/`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what’s &lt;em&gt;not&lt;/em&gt; in it: no tutorial on how billing works, no style philosophy, no secrets. The webhook line alone is worth the whole file. Whoever wrote it clearly lost an afternoon to duplicate Stripe deliveries once, and now no agent on this repo ever will.&lt;/p&gt;

&lt;h2&gt;
  
  
  Length and layout: write for the skim
&lt;/h2&gt;

&lt;p&gt;Context files compete with your actual code for the agent’s attention. The pattern that holds up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Root file under ~100-150 lines.&lt;/strong&gt; Essentials only: commands, hard rules, layout, gotchas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Details colocated.&lt;/strong&gt; Directory-specific conventions go in a nested file in that directory (&lt;code&gt;components/AGENTS.md&lt;/code&gt;), which most agents pick up when working there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link, don’t inline.&lt;/strong&gt; “See &lt;code&gt;docs/auth.md&lt;/code&gt; for the auth flow” beats pasting the auth flow into the root file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match reality.&lt;/strong&gt; If the codebase doesn’t actually follow a convention, writing it down teaches the agent to produce code that clashes with everything around it. Aspirational rules are worse than none.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The file is half the system: prompting that uses it
&lt;/h2&gt;

&lt;p&gt;A good context file handles what’s always true. Your prompts still have to handle what’s true &lt;em&gt;this time&lt;/em&gt;. The patterns that survive contact with real, messy codebases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Point at the code, don’t describe it.&lt;/strong&gt; “In &lt;code&gt;app/auth/signup.tsx&lt;/code&gt;, add email validation matching the pattern in &lt;code&gt;app/auth/login.tsx&lt;/code&gt;” beats “add validation to the signup form.” File paths are facts; descriptions are interpretations the agent has to guess at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State the constraints before the task.&lt;/strong&gt; Agents optimize for the request as stated. “Don’t add dependencies. Keep the change under 100 lines. This table has 40M rows; the migration must not lock writes.” If you don’t state a constraint, it doesn’t exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask for a plan on anything non-trivial.&lt;/strong&gt; For changes touching more than two files: “Propose a plan first. Don’t write code yet.” Reviewing a plan costs a paragraph; catching the same misunderstanding in a 400-line diff costs an afternoon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hand over the failing state.&lt;/strong&gt; “It’s broken” produces guesses. The exact error, the stack trace, the reproduction steps, and what you already tried produce fixes. The agent can’t see your screen, so put your screen in the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demand the boring parts explicitly.&lt;/strong&gt; End significant prompts with: “Include tests for the new behavior, updated docs if the API changed, and a note on what you deliberately didn’t handle.” That last clause is the highest-value sentence in prompting. It forces the agent to surface its own blind spots instead of silently shipping them.&lt;/p&gt;

&lt;p&gt;And when the agent gets it wrong twice, suspect the prompt before the model: an unstated assumption, a missing file reference, or two tasks wearing a trench coat. Split, specify, retry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintenance: the file rots faster than the code
&lt;/h2&gt;

&lt;p&gt;Commands change, rules get superseded, tools arrive. Treat staleness as a bug:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put a last-reviewed date at the top and actually review it: when onboarding a human breaks, or the agent “forgets” the same thing twice.&lt;/li&gt;
&lt;li&gt;Delete rules that stopped mattering. A wrong instruction is worse than a missing one.&lt;/li&gt;
&lt;li&gt;Grow it by scar tissue: after any session where the agent made a preventable mistake, add the one line that would have prevented it. A month of this produces a file worth more than most documentation.&lt;/li&gt;
&lt;li&gt;Test the file, not just the code. Watch a fresh agent session and note every moment it does something the file should have prevented. Each one is a missing or unclear rule. Two sessions of this usually halves your correction rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Four ways context files die
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The wishlist.&lt;/strong&gt; Aspirations instead of rules (“write clean code”). The agent nods and changes nothing. Covered above: rules or it didn’t happen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The novel.&lt;/strong&gt; 500 lines of everything anyone ever learned about the project. The agent skims, the important rules drown, and nobody dares edit it. The fix is subtraction, not better formatting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The zombie.&lt;/strong&gt; Written once at adoption, never updated. Six months later it documents commands that no longer exist and bans a library the team adopted in March. A wrong instruction is worse than a missing one, and zombies are mostly wrong instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The mirror.&lt;/strong&gt; A copy of the README, or of another tool’s context file, drifting one edit behind the original. Two sources of truth is zero sources of truth. Point, don’t copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Build/test/lint commands, verified current, with runtimes noted&lt;/li&gt;
&lt;li&gt;[ ] Architecture in five lines or fewer&lt;/li&gt;
&lt;li&gt;[ ] 3-5 hard rules and explicit prohibitions&lt;/li&gt;
&lt;li&gt;[ ] Conventions that differ from defaults, one example each&lt;/li&gt;
&lt;li&gt;[ ] Gotchas from your actual incident history&lt;/li&gt;
&lt;li&gt;[ ] Under ~150 lines at root; details nested or linked&lt;/li&gt;
&lt;li&gt;[ ] No secrets, no tutorials, no aspirations&lt;/li&gt;
&lt;li&gt;[ ] Last-reviewed date at the top&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Context engineering isn’t prompt magic. It’s documentation discipline, the same skill that makes a good README, applied to a reader that executes what it reads. The teams getting the most from coding agents aren’t writing cleverer prompts. They’re writing better repos, and then prompting like the file is watching.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;This guide is published on &lt;a href="https://www.notvibescoded.com/articles/context-files-coding-agents-field-guide" rel="noopener noreferrer"&gt;NotVibesCoded&lt;/a&gt;&lt;/strong&gt;, where it sits with the rest of the field guides on context engineering, PR review, and shipping AI-built software without the slop.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
