<?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: Jeel Vankhede</title>
    <description>The latest articles on DEV Community by Jeel Vankhede (@jeelvankhede).</description>
    <link>https://dev.to/jeelvankhede</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%2F1050284%2F6055714a-b46c-4a13-b353-b6c807eba6ff.jpeg</url>
      <title>DEV Community: Jeel Vankhede</title>
      <link>https://dev.to/jeelvankhede</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeelvankhede"/>
    <language>en</language>
    <item>
      <title>A Verdict Is Not Evidence. Test Is Where I Learned the Difference.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Wed, 08 Jul 2026 18:28:04 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/a-verdict-is-not-evidence-test-is-where-i-learned-the-difference-5b96</link>
      <guid>https://dev.to/jeelvankhede/a-verdict-is-not-evidence-test-is-where-i-learned-the-difference-5b96</guid>
      <description>&lt;p&gt;The call-order change came back pass-with-risk. I read the recommendation, saw it had a name and a reason, and felt the task close.&lt;/p&gt;

&lt;p&gt;Then I looked at the row under it. How was this verified: not run.&lt;/p&gt;

&lt;p&gt;Nobody had run the queue. I had a label. I did not have proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is Part 6 of The Contract
&lt;/h2&gt;

&lt;p&gt;Think produced a brief. Plan produced a gate. Build executed inside it. Review scored every requirement against a verdict instead of an impression.&lt;/p&gt;

&lt;p&gt;Review reads the diff and the plan and decides whether one satisfies the other. It does not run the queue. It cannot. Its whole job is judgment about what the code should do.&lt;/p&gt;

&lt;p&gt;Test is where someone finally checks what the code actually does.&lt;/p&gt;

&lt;p&gt;I had been treating those two as the same step. They are not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test asks one question, and a verdict is not the answer
&lt;/h2&gt;

&lt;p&gt;For every active requirement, Test asks how it was verified. Command run, manual QA, or a comparison against known-good output. One of those three, or a written reason none of them ran.&lt;/p&gt;

&lt;p&gt;Not a recommendation. Not a risk level. Evidence.&lt;/p&gt;

&lt;p&gt;I built the matrix against the plan's requirements and filled in each row. Most had a command behind them. The call-order requirement had nothing. The cell read not run, and it sat directly below a pass-with-risk that already carried a name and a reason.&lt;/p&gt;

&lt;p&gt;That name had almost been enough for me. A named risk feels handled. It is not. It is a risk with a label on it, waiting for someone to actually look.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I ran the queue
&lt;/h2&gt;

&lt;p&gt;Three notifications, all with a real reason to fire within the same tick. The scheduler picked them up and ordered them by priority instead of arrival.&lt;/p&gt;

&lt;p&gt;Two landed in the sequence the requirement wanted. The third jumped ahead of a lower-priority notification that was still mid-processing.&lt;/p&gt;

&lt;p&gt;The change worked almost every time. Under one timing condition, it did not.&lt;/p&gt;

&lt;p&gt;That is the gap a verdict cannot see. Review had marked the requirement partial because the wording left the mechanism open. Running it found a real failure inside the mechanism the code had chosen. Same requirement, two different holes, and only one of them shows up when you execute.&lt;/p&gt;

&lt;p&gt;The absence of an error is not evidence. I had to run the thing to learn the error was there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The skipped check looked exactly like the ones that passed
&lt;/h2&gt;

&lt;p&gt;Here is what unsettled me. If I had left that cell at not run, the matrix would have shown three green rows and one blank, and nothing about the blank would have raised its hand.&lt;/p&gt;

&lt;p&gt;A skipped check is a risk hiding in sheep's clothing. It sits in the same table as the passing rows, formatted the same, quiet the same. Nobody flags it, because it does not look like a gap. It looks like the others.&lt;/p&gt;

&lt;p&gt;Done and unverified render identically on the page. The only way to tell them apart is to do the check.&lt;/p&gt;

&lt;h2&gt;
  
  
  I almost deleted the failure after I fixed it
&lt;/h2&gt;

&lt;p&gt;I fixed the ordering. Reran the check. It passed under the same timing condition that had broken it a minute earlier.&lt;/p&gt;

&lt;p&gt;Then my hand went to the failure row to clear it. The bug was gone. Why keep a record of something that no longer happens?&lt;/p&gt;

&lt;p&gt;I stopped, because the record was the point.&lt;/p&gt;

&lt;p&gt;The matrix is not a snapshot of the final state. It is the evidence that the check ran at all. A row that only shows the passing rerun tells the next engineer nothing. A row that shows the failure, the fix, and the clean rerun tells them exactly where the edge was and how close the code came to crossing it.&lt;/p&gt;

&lt;p&gt;I kept both rows. Deleting a fixed failure is not tidying up. It is erasing the only proof that anyone ever ran the check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Test actually gates
&lt;/h2&gt;

&lt;p&gt;Test does not gate on whether the code works. It gates on whether every requirement has evidence behind it. Command, manual QA, output comparison, or an explicit skip with a reason, a risk, and an owner.&lt;/p&gt;

&lt;p&gt;Not run is not a resting state. It resolves into evidence or into a named, owned risk, and until it does, the recommendation stays at hold.&lt;/p&gt;

&lt;p&gt;Mine resolved. The recommendation came back ship, not because the first run was clean, but because the one requirement with a crack in it now had proof under the words instead of just the words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A key takeaway:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This is not just a template for this article. It is the skill from the workflow I run after every review pass, before anything reaches Ship.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To summarise,&lt;/em&gt; &lt;code&gt;test&lt;/code&gt; &lt;em&gt;skill builds a verification matrix against every active requirement, runs configured commands where available, records manual QA and generated-output checks, and treats every skipped or not-run check as risk with a named owner rather than a silent pass. It writes a verify artifact that Ship can trust, and it will not let a recommendation past hold or hold-with-waiver until every row is accounted for.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Download it. Give it to your agent. Drop it into a task with an approved plan and a completed review. Watch it refuse to call something done until it can show you the evidence.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The verify artifact it produces is proof that Test ran inside the contract.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JeelVankhede/e6a16d915bb8ecd6fdaf2a4d14697fa6" rel="noopener noreferrer"&gt;&lt;em&gt;test.md on GitHub Gist&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>codereview</category>
      <category>softwaredevelopment</category>
      <category>softwaretesting</category>
      <category>developertools</category>
    </item>
    <item>
      <title>We shipped faster. The debt did too.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Sun, 05 Jul 2026 16:20:54 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/we-shipped-faster-the-debt-did-too-49a4</link>
      <guid>https://dev.to/jeelvankhede/we-shipped-faster-the-debt-did-too-49a4</guid>
      <description>&lt;p&gt;You open a file you have not touched in months and your first instinct is to read it like it belongs to someone else.&lt;/p&gt;

&lt;p&gt;Not because it is broken. Not because the tests are failing. Just because you do not immediately know where its edges are.&lt;/p&gt;

&lt;p&gt;You gotta believe me when I say I had that feeling recently, on a module I last touched roughly six months ago. The code ran. The logic held. But I had to re-read it like the way you re-read code you did not write: slowly, looking for the load-bearing pieces, rebuilding a picture of why it works before I could safely change it.&lt;/p&gt;

&lt;p&gt;I had written most of it. &lt;em&gt;"With help."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The AI agent had generated large sections. I had reviewed, adjusted, and approved the diffs. At the time, the pace felt like a genuine unlock. Six months later, the code felt like something from totally different era.&lt;/p&gt;

&lt;p&gt;That feeling has a name. I just do not usually expect it from a six month old code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deferral deal was reasonable when debt was slow
&lt;/h2&gt;

&lt;p&gt;Every team I have worked on had a version of the same arrangement. Not written anywhere, just understood. Debt built up, someone said "we should clean that up" before a release or during a quiet sprint, and a few of us did.&lt;/p&gt;

&lt;p&gt;It worked because the debt built up at a pace we could outrun. I could hold that pace in my head. A feature took a week, maybe two, and "later" meant a real date I could picture, not a vague promise.&lt;/p&gt;

&lt;p&gt;That is what calibration actually is. Not a policy. Not a perfect system. A feel for how much debt a stretch of work adds, built from doing the work slow enough to notice.&lt;/p&gt;

&lt;p&gt;It only needs a name now because the pace it assumed is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI changed the input rate. Nothing else did.
&lt;/h2&gt;

&lt;p&gt;The first time I generated a real feature with an agent, I was surprised by how much showed up in a short time. Not just the volume. The structure. It read like a focused week's work, done in a few hours.&lt;/p&gt;

&lt;p&gt;I reviewed it, tested the critical paths, and shipped it. What I did not reckon with was the ratio: a few hours of review against a few days of generation, not several days of review against several days of authorship.&lt;/p&gt;

&lt;p&gt;When I write slowly, I carry the context without trying to, I know the tradeoff I made and why, because I was the one standing in front of it. That is where ownership comes from. Not from reading the diff after.&lt;/p&gt;

&lt;p&gt;When the agent writes it, that pacing disappears. The output arrives complete, and my review catches the obvious errors. What it cannot do is hand me the memory of a tradeoff I was never inside long enough to make myself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It shipped like something I authored. It arrived like something I inherited.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Six months later, nobody on the team fully "knows" that module. It is not broken. It is just so dense in a way pre-AI code of the same age rarely was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six months now reads the way three years used to
&lt;/h2&gt;

&lt;p&gt;This is not about "code quality" in the obvious sense. What I got back was honestly better than I could ever write; well-structured, function names held, nothing looked like a warning sign. Bad code announces itself. This did not.&lt;/p&gt;

&lt;p&gt;I.e. The module was an integration with an external provider handling bank transactions. I followed their documentation closely. It started clean, one responsibility per file. Then requirements arrived faster than I could sit with any single one, and since most of it was AI-assisted, the smells piled up quietly under that pace. Six months in, when something broke, I could not find the reason by reading the code. The file no longer explained the behaviour it produced.&lt;/p&gt;

&lt;p&gt;I was not holding the quality bar as tightly as I should have either. But that is the point, right? The pace made it easy not to notice I had stopped.&lt;/p&gt;

&lt;p&gt;That kind of code used to take years to arrive at, turnover, drift, decisions made by people who had already left. I got there in six months. Not because I got worse at my job but because the code arrived faster than the ownership that comes with it.&lt;/p&gt;

&lt;p&gt;Heuristic I keep coming back to: generation rate went up, review depth did not, so the gap between what shipped and what I actually understand widens quietly, because the code still looks fine.&lt;/p&gt;

&lt;p&gt;That is the condition the old deferral deal was never built for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deferring to a later that does not arrive
&lt;/h2&gt;

&lt;p&gt;The deferral strategy breaks at a specific point: when the cleanup sprint needed is larger than the capacity available for it.&lt;/p&gt;

&lt;p&gt;That used to be a slow-moving threshold. A team could accrue meaningful debt over a year and still pay it down in a focused month. The math worked because the input rate and the cleanup rate were in the same range.&lt;/p&gt;

&lt;p&gt;I did not clock this until I tried to schedule the cleanup for that six-month-old module and realised the sprint I would need did not fit anywhere on the roadmap. Not because the team was slow. Because the scope had outgrown what a sprint was ever built to absorb.&lt;/p&gt;

&lt;p&gt;The comprehension side, the reading, the refactoring, the rebuilding of shared context, still runs at the pace I run at, because it is still my work to do. Nobody had documented why the decisions were made, so there was nothing to hand the agent that would have let it re-explain its own output. The understanding I needed had never been accumulated in the first place, and there was no shortcut back to it.&lt;/p&gt;

&lt;p&gt;Six months of AI-assisted development had carried the density of what used to take two or three years to build up. The cleanup scope I was staring at was not what "we will get to it later" had ever budgeted for.&lt;/p&gt;

&lt;p&gt;I do not think this is a failure on my part. The old calibration was reasonable. It was just built for a pace that no longer exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  I do not have the answer. I have a question.
&lt;/h2&gt;

&lt;p&gt;I am not sure what the new approach looks like.&lt;/p&gt;

&lt;p&gt;I have gone looking. I have not found a team, a process, or a tool that has solved this completely, not the seeing part and not the deciding part, two different seams. Nothing tells you which modules have quietly lost their owner, or where the gap between what shipped and what anyone actually understands has drifted past what a sprint can close. And even when you can see it, there is no established way to decide what happens next that does not require a refactor window nobody can schedule.&lt;/p&gt;

&lt;p&gt;I am working on an idea in this space. Pretty early to call it a solution. But before I shape it further, a question popped in my head: is the problem I am describing real for anyone, or just real for me?&lt;/p&gt;

&lt;p&gt;If you have felt this: is the harder problem that you cannot see how much AI-generated debt you are carrying, or that you can see it but have no system for what to do next?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wanna know which one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>technicaldebt</category>
      <category>softwareengineering</category>
      <category>developertools</category>
    </item>
    <item>
      <title>Every Requirement Gets a Verdict. I Had Been Reviewing Without One.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Thu, 02 Jul 2026 18:15:19 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/every-requirement-gets-a-verdict-i-had-been-reviewing-without-one-5dgi</link>
      <guid>https://dev.to/jeelvankhede/every-requirement-gets-a-verdict-i-had-been-reviewing-without-one-5dgi</guid>
      <description>&lt;p&gt;You merge the PR. The build passes. The code does what you expected it to do. You move on.&lt;/p&gt;

&lt;p&gt;That is review for most engineers. A final read. A feeling that things looked right before the branch closed.&lt;/p&gt;

&lt;p&gt;I did it the same way for years.&lt;/p&gt;

&lt;p&gt;Three phases had already run before this one. Think had scoped the work, Plan had written the requirements, Build had shipped a diff that matched the plan exactly. I trusted that the chain held. I had never actually checked.&lt;/p&gt;

&lt;p&gt;Then I ran the Review phase, and checking turned out to mean something specific: not does this work, but does this requirement hold up, and what is my evidence.&lt;/p&gt;

&lt;p&gt;I went in expecting to approve it or send it back. The phase gave me three answers instead: covered, partial, missing. I found out what they meant one requirement at a time, starting with the one I almost got wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  I had been giving impressions, not verdicts
&lt;/h2&gt;

&lt;p&gt;The notification scheduler used a queue to manage dispatch. Every call to the external provider went through it. The provider was never exposed directly. The requirement said the provider must be notified. It was notified, exactly the way I had pictured it.&lt;/p&gt;

&lt;p&gt;I almost called it covered and moved to the next line.&lt;/p&gt;

&lt;p&gt;The Review phase stopped me there. But the requirement said &lt;em&gt;must be notified&lt;/em&gt;, not how. The queue had introduced a call order and a timing the requirement never anticipated. Nothing was broken. Something had changed shape, quietly, and nobody had written that shape down.&lt;/p&gt;

&lt;p&gt;I sat with that for longer than I expected to. Not because the code was wrong. Because I could not immediately tell you whether the change mattered.&lt;/p&gt;

&lt;p&gt;The same pass gave the shim from Plan a different verdict on the same page: covered. Mapped to the requirement it existed to satisfy, no gap between what was promised and what was in the diff.&lt;/p&gt;

&lt;p&gt;One requirement held exactly the shape it was given. The other had quietly grown a new one. Same review. Same pass. Two verdicts.&lt;/p&gt;

&lt;p&gt;Partial is not a softer word for broken. It is the verdict for &lt;em&gt;this much, no more, and here is exactly where it stops.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming the risk I almost skipped
&lt;/h2&gt;

&lt;p&gt;A partial verdict did not resolve on its own. It forced a recommendation I had never had to give before: pass, pass-with-risk, or hold. The two options I knew, approve or send back, had no room for the case that actually mattered, the one where the code worked and the requirement was still uncertain.&lt;/p&gt;

&lt;p&gt;I had to decide, in writing, whether the queue change mattered enough to hold the work. It did not feel small in the moment. The provider was being reached. The queue was deliberate architecture, not an accident I had stumbled into. But I could not confirm the call order and timing from the diff alone, and pretending I could would have been the same thing I used to do without a name for it.&lt;/p&gt;

&lt;p&gt;I wrote it down as pass-with-risk. Not because the risk had gone away. Because I had looked at it directly instead of letting it pass quietly, the way every requirement before this phase had been allowed to.&lt;/p&gt;

&lt;p&gt;Pass-with-risk is handing off the baton with the crack already named.&lt;/p&gt;

&lt;h2&gt;
  
  
  The contract was not three phases
&lt;/h2&gt;

&lt;p&gt;Review is the first phase that looks backward and forward at the same time. Backward at everything Think, Plan, and Build produced. Forward at what Test will have to verify because Review could not finish the job alone.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Looks good to me"&lt;/em&gt; was never a verdict. It was an impression wearing a verdict's clothes, and I had been handing those out for years without noticing the difference.&lt;/p&gt;

&lt;p&gt;Covered. Partial. Missing.&lt;/p&gt;

&lt;p&gt;I cannot unlearn that vocabulary now. The next time I review a diff without it, I will feel exactly what is missing, the way I never could before this phase existed.&lt;/p&gt;

&lt;p&gt;Test runs next. It will find out whether pass-with-risk was the right call, or just a well-documented mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A key takeaway:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is not a template I built for this article. It is the skill I run after every plan and build pass.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;review&lt;/code&gt; skill inspects the actual diff against the brief, plan, and task artifacts, not against intent or memory. It maps every active requirement to covered, partial, or missing, runs a blocking pass and a non-blocking pass, and forces a recommendation of pass, pass-with-risk, or hold before Test is allowed to start.&lt;/p&gt;

&lt;p&gt;Download it. Give it to your agent. Drop it into a task that already has a diff and an approved plan. Watch it refuse to call something covered until the evidence says so.&lt;/p&gt;

&lt;p&gt;The review artifact it generates is proof that the diff was actually checked against the contract, not just read.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JeelVankhede/cc253b5003dbc4b53c2c5c06f7dd9ceb" rel="noopener noreferrer"&gt;review.md on GitHub Gist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>codereview</category>
      <category>softwaredevelopment</category>
      <category>engineering</category>
      <category>developer</category>
    </item>
    <item>
      <title>The Agent Did Not Drift. I Did.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Mon, 29 Jun 2026 14:21:37 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/the-agent-did-not-drift-i-did-4ihn</link>
      <guid>https://dev.to/jeelvankhede/the-agent-did-not-drift-i-did-4ihn</guid>
      <description>&lt;p&gt;The agent paused. It was waiting for me. I was not used to that.&lt;/p&gt;

&lt;p&gt;Every AI tool I had worked with before would fill the gap. Ask it to implement a feature and it would implement the feature, then the next logical thing, then the thing after that. You would come back to a diff three times larger than what you asked for. Confident. Finished. Sometimes wrong.&lt;/p&gt;

&lt;p&gt;Build did not do that.&lt;/p&gt;

&lt;p&gt;It executed Phase 1. It stopped.&lt;/p&gt;

&lt;p&gt;I reviewed. I approved. It executed Phase 2. It stopped again.&lt;/p&gt;

&lt;p&gt;I kept expecting it to continue. It never did.&lt;/p&gt;

&lt;p&gt;That patience was not a limitation. It was the contract working.&lt;/p&gt;

&lt;h2&gt;
  
  
  The work that earns the right to be boring
&lt;/h2&gt;

&lt;p&gt;This is Part 4 of The Contract. If you are starting here: Think produced a brief, Plan produced an approved sequence, and Build is where that sequence runs.&lt;/p&gt;

&lt;p&gt;Every frontend codebase has a version of this problem. One notification on launch. Then two. Then three conditions deciding which one to show. Nobody centralises it because it is never urgent enough. Until it is.&lt;/p&gt;

&lt;p&gt;I had let mine reach that point.&lt;/p&gt;

&lt;p&gt;Background items resolved on app launch. Each one could trigger a notification. The logic deciding which notification to show lived at each call site. No scheduler. No priority. Just conditions stacked on conditions, each one unaware of the others. They started colliding.&lt;/p&gt;

&lt;p&gt;I wrote a brief. I wrote a plan.&lt;/p&gt;

&lt;p&gt;Five phases. Three to consolidate everything under one scheduler and build the queue. One to wire it into the launch sequence. One to clean up.&lt;/p&gt;

&lt;p&gt;Brief done. Plan approved.&lt;/p&gt;

&lt;p&gt;By the time Build started, every structural decision had already been made. Build did not need to think. It only needed to execute.&lt;/p&gt;

&lt;p&gt;Build has entry conditions.&lt;/p&gt;

&lt;p&gt;It checks the branch state before touching a file. It verifies the plan artifact is approved. If the branch is wrong, it stops. If scope has expanded beyond what the plan defined, it stops. Build does not interpret intent. It verifies state, then executes.&lt;/p&gt;

&lt;p&gt;Phase 1 executed. Scheduler in place. Files where the plan said they would be. I reviewed it and approved Phase 2.&lt;/p&gt;

&lt;p&gt;Phase 2. Phase 3. Each one scoped, reviewable, testable on its own.&lt;/p&gt;

&lt;p&gt;That is what two phases of upstream work buys. Not code that works. Code that arrives narrow enough to be understood before it is approved.&lt;/p&gt;

&lt;p&gt;That narrowness is what let me catch what was coming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build did exactly what I asked
&lt;/h2&gt;

&lt;p&gt;Phase 4 landed.&lt;/p&gt;

&lt;p&gt;The queue was wired into the launch sequence. First run: correct. The scheduler picked up the items, ordered them, surfaced the right notification.&lt;/p&gt;

&lt;p&gt;Second run: nothing.&lt;/p&gt;

&lt;p&gt;The queue never reset between sessions. Items were scheduled once and never cleared. The scheduler treated the queue as durable when the system treated each session as disposable.&lt;/p&gt;

&lt;p&gt;The brief described the scheduling logic, the priority ordering, the collision rules. It did not say anything about queue lifetime. It did not say when to flush. It did not say what a session boundary meant to the scheduler.&lt;/p&gt;

&lt;p&gt;Build implemented exactly what the artifact said. When the artifact was silent, Build stayed silent too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Donald Knuth had this right: "Computers are good at following instructions, but not at reading your mind."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I went back to the brief and read it the way Build had read it. The flush condition was not there. Not buried or ambiguous. Just absent. I had written around the scheduler lifecycle without ever writing about it. In my head the reset was obvious. In the artifact it did not exist. The brief was missing the one line that would have made Phase 4 correct on the second run.&lt;/p&gt;

&lt;p&gt;The gap was not in the code. The gap was in what I had decided was too obvious to write down.&lt;/p&gt;

&lt;p&gt;The agent did not drift. I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate that made the gap catchable
&lt;/h2&gt;

&lt;p&gt;The moment I found it, Phase 5 had not started.&lt;/p&gt;

&lt;p&gt;Nothing downstream had been built on top of the broken assumption. That is not luck. That is the architecture of the workflow.&lt;/p&gt;

&lt;p&gt;Build waits between phases because a human checking partial output mid-sequence costs one review. Finding the same problem after Phase 5 costs five phases of rework.&lt;/p&gt;

&lt;p&gt;The exit condition is not delivery. It is confirmation. Without that gate, Build is just a slower way to get a large diff.&lt;/p&gt;

&lt;p&gt;I added the flush condition to the brief. I updated the plan. The phase re-executed. Phase 5 ran clean.&lt;/p&gt;

&lt;p&gt;One gap. Caught at the right moment. Without a cycle of iterations to find it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The brief is the contract
&lt;/h2&gt;

&lt;p&gt;Build executes what exists in the artifact. No more. No less.&lt;/p&gt;

&lt;p&gt;I had expected Build to catch my oversight. That expectation was wrong. The workflow was designed to make it wrong.&lt;/p&gt;

&lt;p&gt;Build is not a safety net for an incomplete brief. It is a precise executor of an approved one. When the brief is honest, Build produces exactly what I imagined. When the brief has a gap, Build produces exactly that too.&lt;/p&gt;

&lt;p&gt;The five phases landed at roughly 95% of what I intended.&lt;/p&gt;

&lt;p&gt;That number felt high at first. Then I looked at where the 5% came from.&lt;/p&gt;

&lt;p&gt;It came from me. Every time. A brief assumption that did not make it into the artifact. A detail that seemed too obvious to write. An edge case I held in my head instead of the plan.&lt;/p&gt;

&lt;p&gt;Build did not miss those things. I never gave them to Build.&lt;/p&gt;

&lt;p&gt;The workflow did not fail. It showed me everything I had decided was too obvious to write down.&lt;/p&gt;

&lt;p&gt;That is the most honest thing a tool has ever told me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;Build closes with a task artifact.&lt;/p&gt;

&lt;p&gt;Every changed file is recorded. Every check that ran is recorded. Every check that did not run is recorded with a reason.&lt;/p&gt;

&lt;p&gt;Review does not start with the code. It starts with that artifact. The question Review asks is not whether Build made a mistake. It is whether what Build executed was actually what the plan intended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A key takeaway:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is not a template I built for this article. It is the skill I run after every feature plan.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; skill executes exactly one approved plan phase at a time, preserves unrelated changes, inspects branch and repo state before edits, and records every changed file with manifest IDs and command evidence. It stops when scope expands. It waits when branch state is wrong. It produces a task artifact Review can trace back to the plan.&lt;/p&gt;

&lt;p&gt;Download it. Give it to your agent. Drop it into a task that already has an approved brief and plan. Watch it pause instead of fill in the gaps.&lt;/p&gt;

&lt;p&gt;The task artifact it generates is proof that Build ran inside the contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JeelVankhede/9fee08205fd303876b4e54854c6c1acf" rel="noopener noreferrer"&gt;build.md on GitHub Gist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>softwaredevelopment</category>
      <category>developertools</category>
      <category>aitools</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>The Plan Blocked Me. That Was the Correct Outcome.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Tue, 23 Jun 2026 04:05:24 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/the-plan-blocked-me-that-was-the-correct-outcome-279l</link>
      <guid>https://dev.to/jeelvankhede/the-plan-blocked-me-that-was-the-correct-outcome-279l</guid>
      <description>&lt;p&gt;The brief was approved. Scope defined, questions closed, acceptance criteria written. The obvious move was to start building.&lt;/p&gt;

&lt;p&gt;I ran the Plan phase first.&lt;/p&gt;

&lt;p&gt;Planning and the Plan phase are not the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planning produces direction. The Plan phase produces a gate.
&lt;/h2&gt;

&lt;p&gt;Every engineer plans. Sprint boards. Jira tickets. A rough sequence before a large refactor starts. It works well enough because a human engineer carries what the plan does not say. They know what they did not write down.&lt;/p&gt;

&lt;p&gt;An agent does not. An agent fills the gaps with its best guess. That is not inefficiency. It is drift. I covered what drift costs in the &lt;a href="https://dev.to/jeelvankhede/an-ai-agent-is-a-hammer-the-think-phase-is-where-you-aim-it-3gkd"&gt;last part of this series&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Plan phase has one property that a sprint board does not: refusal conditions.&lt;/p&gt;

&lt;p&gt;A plan that cannot refuse you is documentation. A plan with refusal conditions is a gate.&lt;/p&gt;

&lt;p&gt;Nobody brings materials to a construction site and figures out the building as they go. There is a blueprint first. Then an estimate. The estimate is not the slow part. Finding out mid-build that a wall cannot go there is the slow part.&lt;/p&gt;

&lt;p&gt;The brief is the blueprint. The Plan phase is the estimate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The impact map found what the brief described in one line
&lt;/h2&gt;

&lt;p&gt;The refactor target was a god class. It owned too much. The brief said split it.&lt;/p&gt;

&lt;p&gt;Most codebases have a version of this. A module that owns too much and the team has learned to work around. The brief names it in one line and moves on. The impact map cannot.&lt;/p&gt;

&lt;p&gt;It found that the god class held the payload formatting logic. Every notification the system sent passed through it before leaving the application boundary. Split it wrong and the notification contract breaks. Which phase owns that during the transition?&lt;/p&gt;

&lt;p&gt;I thought the brief had answered that. It had not.&lt;/p&gt;

&lt;p&gt;The impact map could not answer it either. That required the risk register.&lt;/p&gt;

&lt;h2&gt;
  
  
  The plan blocked. That was correct.
&lt;/h2&gt;

&lt;p&gt;The risk register stopped me at a question I had not prepared for.&lt;/p&gt;

&lt;p&gt;The payload format was a contract with the external notification provider. The provider expected specific field names and structure. It did not return an error when that structure changed. It silently rejected the payload. No exception. The only evidence would come downstream, weeks later, when someone noticed the silence.&lt;/p&gt;

&lt;p&gt;That kind of failure is a time bomb.&lt;/p&gt;

&lt;p&gt;There was no mitigation in the current plan. The plan blocked.&lt;/p&gt;

&lt;p&gt;My first instinct was to push through. Grant myself the waiver. Flag it low risk. Sort it out during Build. That is the exact moment the Plan phase exists to intercept. Not because the risk was catastrophic. Because the question was unanswered, and an unanswered question does not disappear. It travels into Build and becomes the agent's problem.&lt;/p&gt;

&lt;p&gt;I had always sorted things like this mid-build. This time the plan would not move until I had the answer.&lt;/p&gt;

&lt;p&gt;That cost time I had not budgeted. It cost less than tearing down a wall mid-build.&lt;/p&gt;

&lt;p&gt;So I answered it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision was made at plan time, not build time
&lt;/h2&gt;

&lt;p&gt;I made the call. Not automatically. The simpler path was to move everything in one phase. But one ordering changed the external interface before the replacement was verified. The other preserved the notification contract through the transition and required a human to confirm the payload format before the agent could proceed.&lt;/p&gt;

&lt;p&gt;No code was written. I made a sequencing decision. The agent would follow it.&lt;/p&gt;

&lt;p&gt;A human engineer would have caught this mid-build. Professional instinct. Write "migrate notification dispatch" as a task and the agent migrates it. It does not know the field names are a contract. It does not know silent rejection is the failure mode. It does not know to pause before continuing.&lt;/p&gt;

&lt;p&gt;The god class was specific to this refactor. The problem it exposed was not. Something will always look safe to move. What makes it unsafe is external, invisible to a scan, and silent when it breaks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build owns code. Plan owns sequencing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The plan was signed. That was not the end.
&lt;/h2&gt;

&lt;p&gt;Think told me what to build. Plan told me in what order. Those are not the same document and they are not the same decision.&lt;/p&gt;

&lt;p&gt;That is not a handoff. That is a gate.&lt;/p&gt;

&lt;p&gt;Build started with a plan the agent could not diverge from. Whether the plan was precise enough was a question only Build could answer. That is the next part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A key takeaway:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;This is not a template I built for this article. It is the skill that I run for my plans.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;plan&lt;/code&gt; &lt;strong&gt;&lt;em&gt;skill prepares an impact map, risk registers, phases and verification gates. It enforces that all risks are known and user is acknowledged before Build begins and produces the plan as a versioned artifact.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Use it in the sequence to the Think. Once the brief is generated, you prepare a plan and read how it differs from the regular plans that AI makes.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JeelVankhede/1a20ee3b11a354cfa2e123ccbee29247" rel="noopener noreferrer"&gt;plan.md on GitHub Gist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
      <category>developertools</category>
    </item>
    <item>
      <title>An AI Agent Is a Hammer. The Think Phase Is Where You Aim It.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Fri, 19 Jun 2026 14:16:33 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/an-ai-agent-is-a-hammer-the-think-phase-is-where-you-aim-it-3gkd</link>
      <guid>https://dev.to/jeelvankhede/an-ai-agent-is-a-hammer-the-think-phase-is-where-you-aim-it-3gkd</guid>
      <description>&lt;p&gt;The agent runs. The code appears. The feature is not what I wanted.&lt;/p&gt;

&lt;p&gt;Not because the model was wrong. Because I was vague.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed Was Never the Moat
&lt;/h2&gt;

&lt;p&gt;Before AI could write a single line of code, the best engineers were not the fastest ones.&lt;/p&gt;

&lt;p&gt;They were the ones who thought first.&lt;/p&gt;

&lt;p&gt;The engineer who paused long enough to understand the system, the constraints, the consequences, consistently shipped better work than the engineer who started typing first.&lt;/p&gt;

&lt;p&gt;Speed was the byproduct of clarity.&lt;/p&gt;

&lt;p&gt;The ones who skipped the thinking phase did not save time. They silently redirected it into debugging, rework, and escalations nobody had scheduled for.&lt;/p&gt;

&lt;p&gt;AI did not change this. It amplified it.&lt;/p&gt;

&lt;p&gt;A senior once taught me: Every complex task started with pseudocode that never shipped. By the time coding started, I had already solved the problem I needed to build. Those documents were never the output. The preparation was.&lt;/p&gt;

&lt;p&gt;The discipline that managed it was never new.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architect Did Not Build. The Architect Made Building Possible.
&lt;/h2&gt;

&lt;p&gt;I picked up a clean refactor not long ago. A couple of backend endpoints that had become legacy over time, god classes built up through delayed maintenance. The project already had rules and instructions in place. Patterns documented, constraints named, architectural decisions recorded.&lt;/p&gt;

&lt;p&gt;The refactor ran clean. Manual testing surfaced one issue: a status mismatch on a response from a newly introduced feature that was not yet documented in the code. That was the full extent of the friction.&lt;/p&gt;

&lt;p&gt;The difference between that refactor and the React Native incident I described in the previous article, where the agent implemented a plan correctly and the plan itself was wrong, was not capability. It was context. One project had a contract. The other did not.&lt;/p&gt;

&lt;p&gt;Every project that shipped well had someone building that context before a decision was locked in. Not the fastest coder. The one who understood the system as a living structure: existing patterns, load-bearing decisions already made, consequences that travel downstream.&lt;/p&gt;

&lt;p&gt;The right moment to catch a structural mistake is before the first line of code. Not in a review after the tenth.&lt;/p&gt;

&lt;p&gt;That part has a name in this workflow, the first step: Think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capability Is Not the Bottleneck. Ambiguity Is.
&lt;/h2&gt;

&lt;p&gt;The agent is not too slow. It is not too limited. It does not lack capability.&lt;/p&gt;

&lt;p&gt;It lacks direction.&lt;/p&gt;

&lt;p&gt;I noticed the pattern on the tasks that touch the most files. Performance optimisation. A third-party service integration that reflects behaviour across several endpoints. Underperforming routes moved to a Redis cache. The agent did not struggle with any of them. It moved confidently, thoroughly, and in the wrong direction.&lt;/p&gt;

&lt;p&gt;Feed an AI agent a vague prompt and it does not return silence. It returns something confident, coherent, and pointed at the wrong target. The agent fills ambiguity with inference. It makes decisions I did not know I was delegating. It assumes the shape of the feature from the words I chose, not from the system I built.&lt;/p&gt;

&lt;p&gt;The gap is not between what the agent knows and what I needed. The gap is between what the agent received and what I actually meant.&lt;/p&gt;

&lt;p&gt;The interesting question is not "can the agent write this code?" The better question is "have I given the agent something precise enough that it cannot misread the direction?"&lt;/p&gt;

&lt;p&gt;Those are not the same question. Most engineers answer the first one and wonder where the week went.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architect Persona: Gather Direction, Not Just Code
&lt;/h2&gt;

&lt;p&gt;At the Think phase, the agent takes on a specific role.&lt;/p&gt;

&lt;p&gt;Not implementer. Architect.&lt;/p&gt;

&lt;p&gt;Its job is not to generate. Its job is to understand.&lt;/p&gt;

&lt;p&gt;It reads the codebase. It maps what exists. It surfaces existing patterns so the feature does not reinvent what the system already decided. It separates what I stated from what the codebase implies. It flags what is known and what is not.&lt;/p&gt;

&lt;p&gt;It does not proceed on a guess when a decision belongs to me.&lt;/p&gt;

&lt;p&gt;By the time Think ends, the agent has not written a single line of production code.&lt;/p&gt;

&lt;p&gt;The architect persona is not a mindset I sustain through discipline. It is a structured phase, and it runs as a skill.&lt;/p&gt;

&lt;p&gt;What it produces is the brief.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Brief Is How I Aim the Hammer
&lt;/h2&gt;

&lt;p&gt;The brief is the first artifact of this workflow.&lt;/p&gt;

&lt;p&gt;It is not a ticket. It is not a prompt. It is not a description of what I want.&lt;/p&gt;

&lt;p&gt;It is a specification of direction, written precisely enough that the agent cannot misread it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What goes in it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Intent. Not "add a filter to the dashboard." The outcome the user achieves and the system condition that changes. Vague intent is how the agent builds the right-looking wrong thing.&lt;/p&gt;

&lt;p&gt;Constraints. Technical limits and architectural decisions already made upstream. They define the edge of the work before it begins.&lt;/p&gt;

&lt;p&gt;What the codebase already knows. If the pattern exists somewhere in the system, the brief names it. The agent does not invent a solution the codebase already has.&lt;/p&gt;

&lt;p&gt;Explicit unknowns. Two kinds: assumptions the agent can safely proceed on, and open questions that belong to me, marked blocking or non-blocking. The agent does not guess either way. It surfaces them and waits.&lt;/p&gt;

&lt;p&gt;Definition of done. Not a feeling. A condition the agent can verify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hammer the exact spot.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A craftsman strikes metal at precisely the right point.&lt;/p&gt;

&lt;p&gt;Not harder. Not faster. Exact.&lt;/p&gt;

&lt;p&gt;The masterpiece is not a product of force. It is a product of aim.&lt;/p&gt;

&lt;p&gt;That is what the brief produces. A single concentrated point of direction. The Think phase does not add steps to the workflow. It removes the steps that appear later when direction was absent.&lt;/p&gt;

&lt;p&gt;It hammers the exact spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Direction Is Not the Route
&lt;/h2&gt;

&lt;p&gt;The brief tells the agent where to aim. It does not tell the agent the order of the blows.&lt;/p&gt;

&lt;p&gt;That belongs to the Plan phase.&lt;/p&gt;

&lt;p&gt;The Think phase does not complete when I feel ready. It completes when the brief is specific enough that an agent working from it cannot misread the direction.&lt;/p&gt;

&lt;p&gt;That is a higher bar than it sounds. It is also the bar the Plan phase builds on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A key takeaway:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is not a template I built for this article. It is the skill I run at the start of every feature.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;think&lt;/code&gt; skill classifies the task, separates explicit from implicit requirements, and marks open questions as blocking or non-blocking. It enforces an exit condition before Plan begins and produces the brief as a versioned artifact.&lt;/p&gt;

&lt;p&gt;Download it, Give it to your agent, Drop it into your first task. Read what it produces before the agent continues.&lt;/p&gt;

&lt;p&gt;The brief it generates is proof that Think ran.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JeelVankhede/336a0007582b3800304e8ec9874df647" rel="noopener noreferrer"&gt;think.md on GitHub Gist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>developertools</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>The Agent Did Exactly What I Asked And That Was the Problem.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Thu, 18 Jun 2026 13:45:24 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/the-agent-did-exactly-what-i-asked-and-that-was-the-problem-1hek</link>
      <guid>https://dev.to/jeelvankhede/the-agent-did-exactly-what-i-asked-and-that-was-the-problem-1hek</guid>
      <description>&lt;p&gt;Back in 2022, working with an AI coding assistant meant keeping a browser tab open.&lt;/p&gt;

&lt;p&gt;You typed a question. You got a code block. You figured out where it belonged. That was it.&lt;/p&gt;

&lt;p&gt;The suggestions were useful in the way a dictionary is useful. Technically correct. Completely unaware of what you were actually building. You stitched the outputs together yourself and moved on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The context problem started to close
&lt;/h2&gt;

&lt;p&gt;Then came late 2024.&lt;/p&gt;

&lt;p&gt;MCPs arrived. Context pipelines. Codebase-aware agents that could read your patterns, follow your naming conventions, understand the domain you were working in. The agent was not just answering questions anymore. It knew the project, the structure, the terminology you had already established.&lt;/p&gt;

&lt;p&gt;This was a genuine step change. Not hype.&lt;/p&gt;

&lt;p&gt;But something still did not work on complex tasks.&lt;/p&gt;

&lt;p&gt;Single-feature work was fine. Isolated changes landed cleanly. When the scope crossed modules, crossed boundaries, crossed the full surface area of a codebase, the agent started drifting. It solved the visible problem and left behind the invisible ones.&lt;/p&gt;

&lt;p&gt;I noticed this. I assumed I needed a better plan.&lt;/p&gt;

&lt;p&gt;I was right about that. I was wrong about almost everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent did exactly what I asked. That was the problem.
&lt;/h2&gt;

&lt;p&gt;The task: a global event mechanism and component communication layer across an entire React Native application.&lt;/p&gt;

&lt;p&gt;This was not a small task. Done by hand, with a focused engineer, it would take two weeks minimum. The agent understood the domain well. I handed it the work, expecting two weeks to become two days.&lt;/p&gt;

&lt;p&gt;I created a plan. I reviewed it. I approved it.&lt;/p&gt;

&lt;p&gt;Neither the agent nor I noticed what the plan did not cover: conditionally-loaded passive components that lived outside the event mitigation scope. No impact map. No blast radius analysis. No verification scenarios. A proper engineering sprint would have treated this as a one to two sprint production artifact. We treated it as a chat output.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The agent implemented the approved plan. Correctly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence should sound like a success. It is not.&lt;/p&gt;

&lt;p&gt;It looked finished. It was not.&lt;/p&gt;

&lt;p&gt;Passive and conditional components were outside the event mitigation scope entirely. State changes in those sections did not render correctly. The UI broke in places no one had thought to check, which meant I only found them by using the parts of the app the plan had quietly skipped.&lt;/p&gt;

&lt;p&gt;The duplication was worse because it was invisible. Event handling had been wired in across several locations. I only caught it after adding console logs to trace the event flow and watching the same event fire more than once.&lt;/p&gt;

&lt;p&gt;The third failure was different. The agent used a combination of Context and Redux for event-driven architecture. It added boilerplate that was never asked for, invented an implementation pattern that was not in scope, not in the plan, and not something I wanted. I had to go back to plain text, explain how it should actually work, and ask it to correct.&lt;/p&gt;

&lt;p&gt;That raised a question I kept coming back to: &lt;strong&gt;why would AI do something not asked, not in scope?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is not a defect. It is the absence of a constraint. There was no rule saying otherwise. The agent filled the gap with its best guess. The best guess was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  No tracking. No checkpoints. No floor.
&lt;/h2&gt;

&lt;p&gt;No task tracking existed. No verification log. No regression checkpoints.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Each observation I fed back got implemented immediately.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Code layered on top of code. The chat thread grew too long.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I summarised and started a new chat. Picked up from there.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It worked briefly. Then introduced new issues.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern kept repeating.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By day three, the implementation was 70-75% aligned with the original plan. No clear record of what had been verified, what had not, and what still needed a human decision. Just a long sequence of chat messages and fragile code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I made it work by the end of day three. Satisfactory,&lt;/em&gt; &lt;strong&gt;&lt;em&gt;not clean.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding was never the problem
&lt;/h2&gt;

&lt;p&gt;I went in assuming the failures would be about code quality. Complex logic, edge cases, missed conditions.&lt;/p&gt;

&lt;p&gt;It was not that.&lt;/p&gt;

&lt;p&gt;The agent had good context. It could write the code. What it could not do is decide what the work actually is. That is not a tool limitation. It is a role boundary.&lt;/p&gt;

&lt;p&gt;Not during the process. Not at the end. At the beginning.&lt;/p&gt;

&lt;p&gt;During the process, human review catches mistakes after they happen. At the beginning, human judgment defines the surface area before anything starts.&lt;/p&gt;

&lt;p&gt;That realization sent me looking. After three days, I researched existing solutions and tested a few. &lt;em&gt;They work.&lt;/em&gt; They are well thought through. But they are built for the general case. To use them seriously, you adapt them to your stack, your conventions, your project's specific constraints. That gap is the part nobody packages for you.&lt;/p&gt;

&lt;p&gt;I did not want to recommend something I had not built and broken and rebuilt. I did not want to write about a workflow I had read about rather than lived. I was not sure the workflow would hold.&lt;/p&gt;

&lt;p&gt;The thing I was missing was not a better tool. It was a contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seven phases&lt;/strong&gt;. A human approves every handoff before the next phase begins.&lt;/p&gt;

&lt;p&gt;That is what the next part covers.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>softwareengineering</category>
      <category>developertools</category>
    </item>
    <item>
      <title>Open the PR your reviewer has not met yet</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Thu, 18 Jun 2026 13:41:35 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/open-the-pr-your-reviewer-has-not-met-yet-4gfe</link>
      <guid>https://dev.to/jeelvankhede/open-the-pr-your-reviewer-has-not-met-yet-4gfe</guid>
      <description>&lt;p&gt;I got a PR to review recently. Large diff, AI-assisted, touching a module three other features depended on. The description was one sentence. It named the file, not the reason.&lt;/p&gt;

&lt;p&gt;I spent fifteen minutes just mapping the change before I could start reviewing. What the intent was. Where the risk was. Which files mattered and which were noise.&lt;/p&gt;

&lt;p&gt;Somewhere in the middle of that fifteen minutes, I thought: I have done this to someone else.&lt;/p&gt;




&lt;h2&gt;
  
  
  The author forgets what the reviewer does not know
&lt;/h2&gt;

&lt;p&gt;When you write the code, you carry everything.&lt;/p&gt;

&lt;p&gt;You know why the module was split. You know what you tried before landing here. You know which section you shipped with a quiet doubt. You know the edge case that is technically handled but not properly tested.&lt;/p&gt;

&lt;p&gt;Then you open the PR.&lt;/p&gt;

&lt;p&gt;And you write the description from that place. From inside the full context, for the version of you who already knows. Not for the reviewer who is about to walk in without any of it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Refactored service layer."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Updated config handling."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Fixed issue with auth module."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These are descriptions written from memory, not for someone without it.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI widened the gap before I noticed
&lt;/h2&gt;

&lt;p&gt;There is a specific unease I have felt looking at my own PRs a couple of days after raising them.&lt;/p&gt;

&lt;p&gt;The code is fine. The tests pass. But I cannot reconstruct why I made the choices I made. The work was AI-assisted, the diff was large, and I had moved fast. Moving fast and understanding deeply are not the same thing.&lt;/p&gt;

&lt;p&gt;I opened one of those PRs recently. A refactoring pass. I tried to read it as a reviewer. I had a question I could not answer from the description. The answer existed. It was in my head when I wrote the code. It never made it into the PR.&lt;/p&gt;

&lt;p&gt;That was the moment I realized &lt;em&gt;the description was not a summary.&lt;/em&gt; &lt;strong&gt;&lt;em&gt;It was a test&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Write it for the version of you who will review this in three days
&lt;/h2&gt;

&lt;p&gt;That realization changed how I write descriptions.&lt;/p&gt;

&lt;p&gt;Not a template first. A question first: if I came back to this PR in three days knowing nothing, what would I need to read before opening the diff?&lt;/p&gt;

&lt;p&gt;I sat with that question long enough to realize I needed a structure. Not something I could skip when moving fast. A concrete template I would follow before any non-trivial PR opens. I built it, tested it across refactoring passes, AI-assisted features, and larger architectural changes, and refined it until it held.&lt;/p&gt;

&lt;p&gt;It has six parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intent&lt;/strong&gt; - not what changed, but why this PR exists and what problem it is solving. One paragraph. If you cannot write it clearly, stop. The PR is not ready to open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Major changes&lt;/strong&gt; - the decisions that touch architecture, existing behavior, or anything a downstream system depends on. This is where the reviewer needs to slow down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minor changes&lt;/strong&gt; - the cleanup, the renames, the noise. Named separately, so they do not sit next to structural changes and get equal weight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt; - what features, modules, or systems this PR touches. The blast radius, stated plainly. Not documentation. A map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence&lt;/strong&gt; - what was run, what was walked through manually, what coverage looked like. Not to satisfy a process. Proof that the author did the work before asking someone else to do it.&lt;/p&gt;

&lt;p&gt;And the one most descriptions never reach: what I was uncertain about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Naming uncertainty is not weakness. It is direction.
&lt;/h2&gt;

&lt;p&gt;When something works but I cannot fully explain why, I say so. Directly, in the description.&lt;/p&gt;

&lt;p&gt;For the reviewer, it is a targeting signal. They know where to read closely and where to move. Without it, they distribute attention evenly across a diff that does not deserve even attention.&lt;/p&gt;

&lt;p&gt;Writing it is a checkpoint. If I cannot name what I am uncertain about, I have not thought carefully enough about my own code. That line has stopped me from opening PRs that were not ready.&lt;/p&gt;

&lt;p&gt;The description is not the last step before review. It is the last step before I know whether the PR should open at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  The reviewer who opens this has not met your code yet
&lt;/h2&gt;

&lt;p&gt;When I compare the PRs I was proud of shipping against the ones that came back with questions, the difference is rarely the code.&lt;/p&gt;

&lt;p&gt;It is the description.&lt;/p&gt;

&lt;p&gt;A reviewer who understands your intent from the first read spends their time on the hard questions. A reviewer who has to reconstruct your intent spends it on the easy ones. Asking what things are instead of whether they are right.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/jeelvankhede/review-the-code-that-does-not-exist-yet-11f8"&gt;Part 1&lt;/a&gt; of this series argues the reviewer's side of the same problem. If you arrived here first, it is worth the detour. The argument there: reviewers now need to look past surface correctness and find intent. But intent does not appear on its own. Someone has to put it there before the review begins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write it for the reviewer who has not met your code yet.&lt;/p&gt;

&lt;p&gt;Write it as if you will not be there to answer questions.&lt;/p&gt;

&lt;p&gt;Write it as if the next person to read it is you, three days from now, with no memory of writing it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If it holds up, the PR is ready.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>productivity</category>
      <category>programmingblogs</category>
    </item>
    <item>
      <title>Review the code that does not exist yet</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Thu, 18 Jun 2026 13:40:42 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/review-the-code-that-does-not-exist-yet-11f8</link>
      <guid>https://dev.to/jeelvankhede/review-the-code-that-does-not-exist-yet-11f8</guid>
      <description>&lt;p&gt;I have reviewed code for years, and for most of that time the habit was simple. Open the diff. Read every line. Find what is wrong. It worked, and it worked for a boring reason: the diffs were small because writing code by hand is slow.&lt;/p&gt;

&lt;p&gt;The diffs are not small anymore.&lt;/p&gt;

&lt;p&gt;A pull request now arrives with twenty files in it, built in an afternoon, more code than I could have written by hand in a week. The old habit does not fit a change that size. It bends, then it breaks. So I had to ask a question I had spent years quietly avoiding.&lt;/p&gt;

&lt;p&gt;What is a code review actually for?&lt;/p&gt;

&lt;h2&gt;
  
  
  The first thing I lost was the point
&lt;/h2&gt;

&lt;p&gt;Few years ago, I was the senior reviewer on a small team. I took the role seriously. Maybe too seriously.&lt;/p&gt;

&lt;p&gt;I opened each pull request hunting for faults. Indentation. Naming. A function that could be three lines shorter. A pattern we had agreed not to use. I found them. I was good at finding them. It felt like the job. Every comment was proof that I had been paying attention.&lt;/p&gt;

&lt;p&gt;One day I left seventeen comments on a frontend pull request.&lt;/p&gt;

&lt;p&gt;It was a CSS-heavy change, and CSS changes are hard to review on a diff. The developer had not followed the UI design standards we had set as a team. Almost none of them. So I went through it line by line and marked every single thing I could find. I sent it back.&lt;/p&gt;

&lt;p&gt;I felt like a jerk afterward. But I also thought I had done the right thing.&lt;/p&gt;

&lt;p&gt;Then I looked at what I had actually been reviewing.&lt;/p&gt;

&lt;p&gt;I had read every line. I had not read the work.&lt;/p&gt;

&lt;p&gt;The developer had built exactly what was asked. The feature worked. It was a clear, self-contained piece of UI. And I had spent the entire review arguing with the formatting, as if the formatting were the thing we shipped to users.&lt;/p&gt;

&lt;p&gt;That is the day I started reviewing code for a different thing. Not for what it does the moment it lands. For what someone will do to it next.&lt;/p&gt;

&lt;p&gt;This is not an argument for shipping mess. I still send back work that buries its intent under careless code, because code that is hard to understand is hard to change, and hard to change is where the real cost lives. The difference is that I keep room for an exception now. I do not treat every rule as a wall I am paid to defend.&lt;/p&gt;

&lt;p&gt;A rule is a tool for protecting intent. When I started treating the rule as the point, I forgot what it was protecting.&lt;/p&gt;

&lt;p&gt;I had been reviewing the rules. I had stopped reviewing the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the diffs got faster than I could read
&lt;/h2&gt;

&lt;p&gt;For a long time that lesson stayed quiet. Useful, but never urgent.&lt;/p&gt;

&lt;p&gt;Most pull requests were small, because authoring was slow, and a reviewer could keep pace with an author without much strain. One person wrote for a day, another read for an hour, and the arithmetic worked. The whole practice of review was built on that arithmetic.&lt;/p&gt;

&lt;p&gt;Then people started building with AI.&lt;/p&gt;

&lt;p&gt;Now a change lands with twenty files. Different layers, different concerns, all in one push, all produced faster than I have ever produced anything in my life. And my first thought is almost always the same.&lt;/p&gt;

&lt;p&gt;This is over-engineered.&lt;/p&gt;

&lt;p&gt;Sometimes it is. Often it is not. The instinct fires not because I have judged the design, but because the sheer size of the thing is unfamiliar, and unfamiliar reads as wrong. The honest problem is mine. I could never have written that much code in a day. Reviewing it in a day was the part that unsettled me most.&lt;/p&gt;

&lt;p&gt;The bottleneck moved.&lt;/p&gt;

&lt;p&gt;AI made writing code cheap. It did not make reviewing it cheap.&lt;/p&gt;

&lt;p&gt;Authoring used to be the slow and expensive step, and review was the quick sanity check at the end. That order has flipped. The code now arrives at almost no cost. The judgment about whether the code is right costs exactly what it always did, because judgment was never the part AI made faster.&lt;/p&gt;

&lt;p&gt;The bottleneck did not disappear. It moved onto my desk.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I changed what I look for first
&lt;/h2&gt;

&lt;p&gt;If I cannot read twenty files the way I read two, then reading every line first is the wrong opening move. It is slow. Worse than slow, it is misaimed. It spends my sharpest attention on the smallest things, early, while the largest things wait, unread.&lt;/p&gt;

&lt;p&gt;So I stopped starting with the code.&lt;/p&gt;

&lt;p&gt;I start with intent. What is this change trying to do, and does the shape of it match that goal? Does the structure tell the same story the description promised? Only after those questions do I care how any individual line is written.&lt;/p&gt;

&lt;p&gt;This is what I mean by reviewing the code that does not exist yet.&lt;/p&gt;

&lt;p&gt;The diff in front of me is temporary. It is the least permanent thing in the whole exchange. Someone will change it in six months. Someone will debug it at two in the morning with half the context I have right now. Someone will push it past the scale the author ever imagined, and watch it fail in a way nobody designed for.&lt;/p&gt;

&lt;p&gt;Those people are who the review is really for. Not the author, who already understands the change. Not me, who will forget it by next week. The stranger who inherits it.&lt;/p&gt;

&lt;p&gt;The diff is what changed. The future is what I am approving.&lt;/p&gt;

&lt;h2&gt;
  
  
  The order is the method
&lt;/h2&gt;

&lt;p&gt;Over time my questions settled into an order, and the order turned out to be the whole point. It is not a list to tick off until every box is full. It is a path, and most of it happens before I judge a single line.&lt;/p&gt;

&lt;p&gt;It runs like this.&lt;/p&gt;

&lt;p&gt;What is this trying to do? → How did they solve it, and would I have solved it differently? → What trade-offs or unknowns are sitting in plain sight? → Does this actually improve the thing, or is it added weight, a time bomb waiting for the day nobody is watching? → Am I sure?&lt;/p&gt;

&lt;p&gt;Each question guards against a different failure.&lt;/p&gt;

&lt;p&gt;The first keeps me from reviewing a change I do not understand. The second keeps me honest about the difference between a real problem and my personal taste. The third is where most of the danger hides, because the dangerous parts of a change are rarely the loud ones. The fourth is the one that only gets answered in production, usually later than you wanted. And the last question, am I sure, is the one I added for myself, because the worst approvals I have given were the confident ones.&lt;/p&gt;

&lt;p&gt;Only after all of that do I open the files and read them line by line, with no mercy.&lt;/p&gt;

&lt;p&gt;Look at where the strict part lives. At the very end.&lt;/p&gt;

&lt;p&gt;Everything before it is about understanding the work and arguing with my own judgment. The line-level pass is not the review. It is the last step of the review, and it only earns its place once the rest has cleared.&lt;/p&gt;

&lt;p&gt;You earn the right to nitpick. You do not start there.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is not permission to ship slop
&lt;/h2&gt;

&lt;p&gt;I can hear the objection, because I would raise it myself. Does intent-first mean a lower bar? Does it mean waving through sloppy code as long as the idea behind it is clear enough?&lt;/p&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;The strict pass still happens. It happens last instead of first, but it happens, and nothing on the list above is a substitute for it. Intent-first is not standards-free. It is the same standards in a better order.&lt;/p&gt;

&lt;p&gt;And there is a newer risk that makes the order matter more, not less.&lt;/p&gt;

&lt;p&gt;Volume hides intent.&lt;/p&gt;

&lt;p&gt;A twenty-file change can be correct in every single file and still be dangerous, because no one person can hold the whole of it in their head at once. The failure is not in any one line. It lives in the space between the files, in an assumption made in one place and quietly broken in another.&lt;/p&gt;

&lt;p&gt;About nine months ago a pull request landed on my desk with forty-five files in it. I was lenient. I skimmed the parts that looked safe, approved what looked obvious, and moved on.&lt;/p&gt;

&lt;p&gt;Six months later the APIs started creating duplicate transactions in production. We could not find the root cause. The file at the center of the problem had grown to over five thousand lines of code, and the issue was buried inside of it.&lt;/p&gt;

&lt;p&gt;It took a long time to dig out. That change had looked fine in every file I checked. I had just never asked whether it was wise.&lt;/p&gt;

&lt;p&gt;Reading for intent is how I catch that before it merges. A checklist cannot — it looks at each file alone.&lt;/p&gt;

&lt;p&gt;A checklist can tell me the code is correct. It cannot tell me the code is wise.&lt;/p&gt;

&lt;p&gt;The fear I started this with never left me. I have just stopped fighting it, and started letting it sharpen the right questions.&lt;/p&gt;

&lt;p&gt;I did not learn to read faster. I learned to read in the right order.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually approve
&lt;/h2&gt;

&lt;p&gt;This is what I think a review is.&lt;/p&gt;

&lt;p&gt;I am not the spell-checker. I am not the gate that catches a missing semicolon or an unused import. I am the person asking what this change will do to whoever inherits it, long after the author and I have both moved on.&lt;/p&gt;

&lt;p&gt;That question works at every level of experience, because it is a question and not a rule. A new engineer can ask it on their very first review. A senior engineer asks it on their thousandth. Nobody is too junior to wonder what happens to this code in a year. Seniority does not exempt you from the question.&lt;/p&gt;

&lt;p&gt;And it has a limit I have started to feel.&lt;/p&gt;

&lt;p&gt;A reviewer can only do so much when the pull request is fighting them. If the intent is buried, if twenty files arrive with no story to tie them together, if the risky change is hidden in the middle of a dozen safe ones, then no amount of patient reading will rescue the review. The reader cannot supply a clarity the author refused to provide.&lt;/p&gt;

&lt;p&gt;Which means half of this problem was never the reviewer's at all. It belongs to the person opening the pull request. That is the other &lt;a href="https://dev.to/jeelvankhede/open-the-pr-your-reviewer-has-not-met-yet-4gfe"&gt;half of the story&lt;/a&gt;, and it deserves its own piece.&lt;/p&gt;

&lt;p&gt;Review is not about the code in front of you. It is about the code that comes after.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>programmingblogs</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Most Engineers Use AI. Few Engineer With It.</title>
      <dc:creator>Jeel Vankhede</dc:creator>
      <pubDate>Wed, 17 Jun 2026 23:25:52 +0000</pubDate>
      <link>https://dev.to/jeelvankhede/most-engineers-use-ai-few-engineer-with-it-3pd</link>
      <guid>https://dev.to/jeelvankhede/most-engineers-use-ai-few-engineer-with-it-3pd</guid>
      <description>&lt;p&gt;Most software engineers I know use AI in some form now.&lt;/p&gt;

&lt;p&gt;Maybe it is for debugging, boilerplate, tests, docs, SQL queries, shell commands, or quick code reviews. Some use it daily. Some use it quietly. &lt;em&gt;Even the skeptical ones have probably pasted a confusing stack trace into a chat window once.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I do not think the interesting question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;“Do engineers use AI?”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;“Has AI changed how they engineer?”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because those are not the same thing.&lt;/p&gt;

&lt;p&gt;Using AI is easy.&lt;/p&gt;

&lt;p&gt;Engineering with AI is much harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard part was not code generation
&lt;/h2&gt;

&lt;p&gt;I noticed this while using AI around real repository tasks where a wrong change was not just “bad output”; it could break structure, tests, naming, or future maintainability.&lt;/p&gt;

&lt;p&gt;The code generation part was easy. A broad prompt could produce a lot of code quickly. Sometimes the output even looked clean at first glance.&lt;/p&gt;

&lt;p&gt;But the useful results came only when I had already done the boring engineering work: define the requirement, limit the scope, explain constraints, and decide how I would verify the change.&lt;/p&gt;

&lt;p&gt;The hard part was not asking AI to write code.&lt;/p&gt;

&lt;p&gt;The hard part was giving enough context without dumping everything into the prompt. It was making the task small enough. It was asking for tradeoffs before implementation. It was reviewing the output without getting impressed by clean formatting.&lt;/p&gt;

&lt;p&gt;Most importantly, it was checking whether the solution actually belonged in the system.&lt;/p&gt;

&lt;p&gt;That changed how I looked at AI-assisted development.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prompting was not the real skill.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shaping the work was.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AI makes output faster, not verification stronger
&lt;/h2&gt;

&lt;p&gt;There is nothing wrong with using AI for small tasks.&lt;/p&gt;

&lt;p&gt;I use it for debugging direction, naming, boilerplate, test ideas, documentation drafts, and exploring unfamiliar code. Those are useful tasks. They reduce friction.&lt;/p&gt;

&lt;p&gt;But the risk starts when faster output gets mistaken for better engineering.&lt;/p&gt;

&lt;p&gt;AI can generate code quickly. It can produce clean-looking functions, structured files, confident explanations, and reasonable test cases. But it does not automatically know whether the change fits your product, your architecture, your constraints, or your long-term maintenance cost.&lt;/p&gt;

&lt;p&gt;That part is still engineering work.&lt;/p&gt;

&lt;p&gt;The problem is not simply that AI may produce bad code. Engineers already deal with bad code from humans, libraries, tutorials, Stack Overflow answers, and their own tired brains.&lt;/p&gt;

&lt;p&gt;The bigger problem is that AI increases the speed of output without increasing the quality of verification.&lt;/p&gt;

&lt;p&gt;If code becomes faster to generate, unclear requirements become more expensive. Weak review becomes more dangerous. Missing tests become more painful. Poor architecture becomes easier to copy and harder to undo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI does not automatically improve engineering.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It amplifies the engineering loop that already exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think twice, code once
&lt;/h2&gt;

&lt;p&gt;AI makes it easier to produce code before we have &lt;strong&gt;fully understood the problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is useful when the task is clear.&lt;/p&gt;

&lt;p&gt;It is dangerous when the task is vague.&lt;/p&gt;

&lt;p&gt;If the requirement is unclear, AI will still produce something. If the architecture is messy, AI will still copy the mess. If the engineer cannot review the output properly, speed becomes risk.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is why I think &lt;strong&gt;“AI will replace engineers”&lt;/strong&gt; is the least useful version of the conversation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better question is:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“What parts of engineering become more important when code becomes cheaper to generate?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My answer is: thinking clearly before implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI makes the old advice more important, not less:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Think twice, code once.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before asking AI to build, define the problem.&lt;/p&gt;

&lt;p&gt;Before accepting the answer, check the tradeoffs.&lt;/p&gt;

&lt;p&gt;Before merging the change, verify the behaviour.&lt;/p&gt;

&lt;p&gt;The value of engineering is shifting away from simply writing code and more toward shaping the right change.&lt;/p&gt;

&lt;h2&gt;
  
  
  What engineering with AI looks like
&lt;/h2&gt;

&lt;p&gt;For me, engineering with AI means treating it less like &lt;strong&gt;a magic answer box&lt;/strong&gt; and more like a collaborator that needs structure.&lt;/p&gt;

&lt;p&gt;The work starts before implementation: write the requirement, narrow the scope, give useful context, ask for risks, and force a plan before code.&lt;/p&gt;

&lt;p&gt;After implementation, the responsibility comes back to the engineer: review the diff, run the checks, and decide whether the change belongs in the system.&lt;/p&gt;

&lt;p&gt;A useful AI-assisted loop can be simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Requirement → gaps → plan → small change → review → checks → notes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not the glamorous version of AI development.&lt;/p&gt;

&lt;p&gt;But it is closer to real engineering.&lt;/p&gt;

&lt;p&gt;Because real engineering is not just producing code.&lt;/p&gt;

&lt;p&gt;It is producing reliable change.&lt;/p&gt;

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

&lt;p&gt;The next shift in software engineering will not be about who can generate the most code.&lt;/p&gt;

&lt;p&gt;That advantage is already becoming cheaper.&lt;/p&gt;

&lt;p&gt;The harder advantage is knowing what should be built, how it should fit into the system, and how to verify that it works.&lt;/p&gt;

&lt;p&gt;That is where AI-assisted engineering becomes interesting.&lt;/p&gt;

&lt;p&gt;Not because AI replaces engineering judgment.&lt;/p&gt;

&lt;p&gt;Because it makes engineering judgment harder to skip.&lt;/p&gt;

&lt;p&gt;Most major engineering tools became valuable only after teams changed the workflow around them. Git changed collaboration. Cloud changed infrastructure. CI/CD changed shipping.&lt;/p&gt;

&lt;p&gt;AI is broader, messier, and moving faster than those shifts, so I do not want to pretend it will follow the exact same path.&lt;/p&gt;

&lt;p&gt;But the lesson still feels familiar:&lt;/p&gt;

&lt;p&gt;The tool matters.&lt;/p&gt;

&lt;p&gt;The workflow around the tool matters more.&lt;/p&gt;

&lt;p&gt;The engineers who benefit most will not necessarily be the ones who use AI the most. They will be the ones who design better loops around it: clearer specs, smaller changes, stronger reviews, better tests, and more deliberate decisions.&lt;/p&gt;

&lt;p&gt;So yes, most engineers use AI now.&lt;/p&gt;

&lt;p&gt;But only a few are starting to engineer with it.&lt;/p&gt;

&lt;p&gt;The best engineers in this phase may not be the fastest prompt writers.&lt;/p&gt;

&lt;p&gt;They may be the ones who can slow the problem down before speeding the implementation up.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>softwareengineering</category>
      <category>programing</category>
    </item>
  </channel>
</rss>
