Agent Skills in Claude Code and Visual Studio Code: Setup, Benefits, Risks, and a Step-by-Step Example
AI coding assistants can generate code quickly, but they can also generate bugs quickly.
A feature may look correct at first. Then a button stops responding, a list disappears after refresh, a filter shows the wrong books, or the browser reports an error you do not understand.
The natural reaction is often to send another broad prompt:
Fix the app.
That request gives the AI very little evidence. It may guess at the cause, change several files, and introduce a second problem while trying to repair the first.
A better debugging process begins before asking the AI to edit anything. Reproduce the problem, record what happened, inspect the most recent change, identify one likely cause, and test one small correction.
This article shows how to debug AI-generated code without turning one bug into a chain of speculative changes.
By the end of this guide, you should be able to:
A bug report such as this is too vague:
The reading list is broken.It does not explain:
A useful bug report includes four basic parts:
Steps
Expected result
Actual result
EvidenceFor example:
Steps:
1. Open the reading list app.
2. Add a book with the status "Reading."
3. Select the "Reading" filter.
4. Remove the visible book.
Expected result:
The book disappears and the filtered empty-state message appears.
Actual result:
The book disappears, but the page shows a blank list with no empty-state message.
Evidence:
No console error appears. The problem happens every time.This description gives both you and the AI a clear starting point.
A symptom is what you observe.
A cause is the underlying reason it happened.
For example:
Symptom:
The empty-state message does not appear after removing the final filtered book.Possible causes include:
Do not choose one cause immediately.
Debugging is the process of gathering enough evidence to eliminate incorrect explanations.
AI assistants can be helpful during that process, but they should not be asked to guess without context.
Before changing code, confirm that the bug can be reproduced.
Write the exact sequence of actions.
For example:
1. Refresh the page.
2. Add "Kindred" with status "Finished."
3. Add "The Dispossessed" with status "Want to Read."
4. Select the Finished filter.
5. Remove "Kindred."
6. Observe the list area.Repeat the sequence.
Ask:
A reproducible bug is easier to investigate than an intermittent one.
Remove unnecessary actions.
If the problem happens with one book, do not include five books in the reproduction steps.
A minimal reproduction could be:
1. Add one Finished book.
2. Select the Finished filter.
3. Remove the book.
4. The filtered empty state does not appear.The smaller the reproduction, the fewer possible causes you need to consider.
Before debugging, run:
git statusYou need to know whether the working tree already contains uncommitted changes.
A clean state may look like:
nothing to commit, working tree cleanA dirty state may show:
modified: app.js
modified: styles.cssWhen uncommitted changes are already present, determine whether they belong to:
Mixing a bug fix with unknown existing changes makes debugging harder.
Also inspect recent commits:
git log --oneline -5The most recent relevant commit may reveal when the behavior changed.
For example:
4f2a0bc Add book removal
d8be113 Filter books by reading status
a4c76e9 Validate book titlesIf the bug appeared after adding removal, start by reviewing that change.
Open the browser’s developer tools and select the Console panel.
Then reproduce the problem again.
A JavaScript error may look like:
Uncaught TypeError: Cannot read properties of nullOr:
ReferenceError: activeFilter is not definedRecord:
Do not copy only part of the message.
A useful note might be:
Error:
ReferenceError: activeFilter is not defined
Location:
app.js:87
Triggered by:
Removing the final book while the Finished filter is activeThis is much more useful than:
There is a JavaScript error.Logic errors may not produce an exception.
The application can execute successfully while displaying the wrong result.
For example, this condition may be valid JavaScript but incorrect for filtered data:
emptyState.hidden = books.length > 0;If the full books array contains items from other statuses, the empty state remains hidden even when the filtered result is empty.
The browser does not report an error because the code is syntactically valid.
You must compare behavior with the requirement.
When the bug appears after a recent change, inspect that commit.
For the latest commit:
git show --stat
git showFor a specific commit:
git show 4f2a0bcSuppose the removal commit added:
function removeBook(bookId) {
books = books.filter((book) => book.id !== bookId);
saveBooks();
renderBooks();
}The function looks reasonable.
Now inspect renderBooks():
function renderBooks() {
const visibleBooks = getFilteredBooks();
bookList.replaceChildren();
visibleBooks.forEach((book) => {
bookList.append(createBookElement(book));
});
emptyState.hidden = books.length > 0;
}The likely problem is the final line.
It checks the total number of books rather than the number of currently visible books.
That creates a testable hypothesis.
A useful debugging hypothesis is narrow and testable.
For example:
The empty state remains hidden because
renderBooks()checksbooks.lengthinstead ofvisibleBooks.length.
This is better than:
The rendering code is bad.
A good hypothesis identifies:
You can now test it with a temporary inspection.
For example:
console.log({
totalBooks: books.length,
visibleBooks: visibleBooks.length
});Reproduce the bug.
The console may show:
totalBooks: 1
visibleBooks: 0That evidence supports the hypothesis.
Remove temporary debugging statements before committing.
The correction may be one line:
- emptyState.hidden = books.length > 0;
+ emptyState.hidden = visibleBooks.length > 0;Do not redesign the renderer, rename all functions, or add a framework to fix one condition.
A focused bug fix should change only what is necessary to correct the failing behavior.
After the edit, repeat the exact reproduction steps.
Confirm that the empty state now appears.
Then test related behavior:
This checks both the fix and nearby behavior.
Some bugs are caused by HTML or CSS rather than JavaScript.
Suppose the validation message exists but cannot be seen.
Use the Elements panel in the browser’s developer tools.
Check:
hidden attribute?display: none?For example:
<p id="title-error" class="error-message" hidden></p>The JavaScript may add text:
titleError.textContent = "Please enter a book title.";But it may forget to remove the hidden attribute:
titleError.hidden = false;The problem is not that validation failed.
The problem is that the feedback remained hidden.
When code produces the wrong result, inspect the values moving through it.
Useful checkpoints include:
For example:
console.log("Submitted title:", title);
console.log("Selected status:", status);
console.log("Current books:", books);Use logging temporarily and intentionally.
Avoid adding many logs without a question.
Weak debugging:
console.log("here");
console.log("still here");
console.log("test");Better debugging:
console.log({
activeFilter,
totalBookCount: books.length,
visibleBookCount: visibleBooks.length
});The second version provides evidence connected to a hypothesis.
A common beginner bug occurs when JavaScript looks for an element that does not exist or uses the wrong ID.
HTML:
<select id="status-filter">JavaScript:
const statusFilter = document.querySelector("#book-filter");The selector returns null because the IDs do not match.
Later code may fail:
statusFilter.addEventListener("change", handleFilterChange);The console may report:
TypeError: Cannot read properties of nullReview the selector and HTML side by side.
Check for:
This is a frequent problem after AI edits because one file may be updated while another retains the previous name.
The application expects each book to have:
id
title
author
status
createdAtA bug can occur when one code path creates a different object.
For example:
const book = {
id: crypto.randomUUID(),
name: title,
writer: author,
readingStatus: status
};The renderer may expect:
titleElement.textContent = book.title;The displayed title becomes empty because book.title does not exist.
Inspect the object when it is created:
console.log(book);Compare it with the project brief and other functions.
Data bugs often come from inconsistent names rather than complicated algorithms.
Persistence adds another possible source of problems.
Suppose books disappear after refresh.
Check whether the app saves data:
localStorage.setItem(STORAGE_KEY, JSON.stringify(books));Then check whether it loads data:
const savedBooks = localStorage.getItem(STORAGE_KEY);Review:
A key mismatch may look like:
const STORAGE_KEY = "reading-list";
localStorage.setItem("readingList", JSON.stringify(books));Saving uses readingList, while loading uses reading-list.
Both lines are valid, but they refer to different entries.
In browser developer tools, open the storage or application panel.
Find the site’s local storage and inspect the stored value.
You may see:
[
{
"id": "book-001",
"title": "Kindred",
"author": "Octavia E. Butler",
"status": "finished",
"createdAt": "2026-07-11T10:30:00.000Z"
}
]If the stored value is missing, investigate saving.
If the value exists but the interface is empty, investigate loading or rendering.
This distinction prevents unnecessary changes to the wrong part of the code.
A button that appears unresponsive may have an event problem.
Review:
type?For form submission:
function handleBookSubmit(event) {
event.preventDefault();
// ...
}Without event.preventDefault(), the browser may reload the page and clear the in-memory list.
For a remove button inside a form, the HTML should often include:
<button type="button">Remove</button>Without type="button", a button may behave as a submit button by default.
That can create a confusing bug where clicking Remove also triggers form submission.
Once you have evidence, give the AI a focused debugging request.
Weak prompt:
Fix the filtering bug.Improved prompt:
Investigate a bug in the Personal Reading List Tracker.
Steps to reproduce:
1. Add one book with status Finished.
2. Add one book with status Want to Read.
3. Select the Finished filter.
4. Remove the Finished book.
Expected result:
The filtered empty-state message appears.
Actual result:
The list is blank, but the empty-state message remains hidden.
Evidence:
- No console error appears.
- books.length is 1.
- visibleBooks.length is 0.
- The bug appears after the "Add book removal" commit.
Constraints:
- Inspect app.js only.
- Do not edit yet.
- Identify the most likely cause and explain the smallest fix.
- Do not change the data model, storage logic, styling, or HTML.After reviewing the diagnosis:
Apply only the proposed empty-state fix in app.js. Then provide the exact manual tests needed to verify filtered and unfiltered empty states.This prompt gives the AI evidence and a narrow boundary.
An AI assistant may suggest several changes at once:
- Rewrite renderBooks
- Rename the filter state
- Replace event listeners
- Add a new utility module
- Change the HTML structureThat may be excessive for one reproducible bug.
Ask:
Apply one justified fix first.
If it fails, restore or revise it before trying another theory.
After applying a fix, repeat the same steps that reproduced the problem.
Do not change the test sequence.
For example:
1. Add one Finished book.
2. Add one Want to Read book.
3. Select Finished.
4. Remove the Finished book.
5. Confirm the filtered empty state appears.Then test neighboring cases:
- Select All and confirm the remaining book appears.
- Select Want to Read and confirm the remaining book appears.
- Remove the final book and confirm the global empty state appears.
- Add another book and confirm the empty state disappears.A bug is not fixed merely because the code changed.
The original failing behavior must now pass.
A regression occurs when a fix breaks something that previously worked.
Suppose changing the empty-state condition fixes filtered results but causes the initial empty state to disappear.
Test:
The exact regression tests depend on the changed behavior.
A useful rule is:
Test the bug
+ Test the normal path
+ Test one related edge caseFor more important changes, add automated tests when the project supports them.
After the fix works, run:
git status
git diffA focused fix may show only:
modified: app.jsReview for:
console.log statements;Remove debugging leftovers unless they serve a deliberate long-term purpose.
Then stage the intended file:
git add app.js
git diff --stagedCreate a commit message that describes the corrected behavior:
git commit -m "Show empty state for filtered book lists"A bug-fix commit should make the change easy to identify later.
A broad request encourages broad edits.
Provide one reproducible bug and one review boundary.
Without a reliable reproduction, you cannot prove that the change fixed anything.
If multiple changes are applied together, you do not know which one mattered.
A full error message and line number can save considerable time.
The most recent relevant commit often narrows the search.
Debugging output can clutter the console or expose data.
Review the final diff before committing.
Repeat the failing steps yourself.
The summary is not a test result.
A small bug usually deserves a small fix.
A rewrite may introduce more uncertainty than it removes.
Record the exact steps, values, and errors.
Human memory and AI conversation context can both drift.
Use this structure when investigating a problem.
Bug title:
[Short description]
Environment:
[Browser, local project, current branch]
Starting Git state:
[Clean or list existing changes]
Steps to reproduce:
1.
2.
3.
Expected result:
[What should happen]
Actual result:
[What happens instead]
Console evidence:
[Full error and line number, or "no console error"]
Relevant recent change:
[Commit or file]
Observed values:
[Important state, inputs, or stored data]
Current hypothesis:
[One specific suspected cause]
Smallest proposed test:
[Temporary log, condition check, or isolated edit]
Fix applied:
[Exact small change]
Regression checks:
[Related behaviors tested]
Final result:
[Pass or fail]This worksheet is useful whether you debug alone or with an AI assistant.
Consider another common bug.
A removed book disappears immediately but returns after page refresh.
1. Add a book.
2. Remove it.
3. Refresh the page.
4. The removed book returns.The browser console shows no error.
Local storage still contains the removed book.
The removal function updates the in-memory array but does not save the new array.
Current code:
function removeBook(bookId) {
books = books.filter((book) => book.id !== bookId);
renderBooks();
}function removeBook(bookId) {
books = books.filter((book) => book.id !== bookId);
saveBooks();
renderBooks();
}Test:
The debugging process connects the symptom to stored evidence before changing the code.
One form submission adds the same book twice.
Check:
console.count("handleBookSubmit called");Submit once.
If the count increases by two, the handler may be registered twice.
Inspect index.html for duplicate scripts:
<script src="app.js"></script>
<script src="app.js"></script>Or inspect JavaScript for duplicate listeners:
bookForm.addEventListener("submit", handleBookSubmit);
bookForm.addEventListener("submit", handleBookSubmit);Do not add a duplicate-prevention condition until you understand why the handler runs twice.
Fix the cause rather than hiding the symptom.
A long debugging conversation may accumulate incorrect assumptions.
Start a fresh session when:
A fresh debugging prompt should include:
Do not carry every failed theory into the new session unless it helps eliminate a cause.
Sometimes the safest debugging decision is to return to the last working version.
Consider reverting when:
Inspect first:
git status
git diffTo discard uncommitted changes in one tracked file:
git restore app.jsTo reverse a committed change safely in shared history:
git revert <commit-hash>Do not use destructive reset commands casually.
A clean restart from a known checkpoint can be faster than continuing an unstable chain of patches.
In software development, the visible failure is often far from the actual cause.
A missing item on the screen may come from:
Strong debugging narrows the system one boundary at a time.
When working with AI-generated code, I prefer prompts that ask the tool to explain the evidence before editing. This makes it easier to see whether the proposed cause matches the actual repository.
The most reliable debugging loop is:
Reproduce
→ Observe
→ Hypothesize
→ Test
→ Fix
→ Verify
→ CommitSkipping directly from symptom to generated patch removes the most important reasoning steps.
git status.git diff.git diff --staged.Debugging AI-generated code is not about sending repeated “fix it” prompts.
It is a structured process of reducing uncertainty.
Start by reproducing the problem. Record the expected and actual behavior. Check the browser console, inspect recent Git changes, trace relevant values, and form one specific hypothesis. Apply the smallest justified fix, repeat the original test, check for regressions, and review the final diff before committing.
The AI assistant can help explain code, identify likely causes, and propose a correction. Your evidence determines whether that correction is credible.
The next article will focus on what to do when an entire AI coding session goes off track. We will examine context drift, uncontrolled scope expansion, contradictory changes, and how to recover without losing verified work.
Usually not.
Provide the relevant files, exact reproduction steps, error message, recent changes, and constraints. A project-level coding agent can inspect the repository, but you should still define a narrow problem.
Treat it as a logic or interface problem.
Compare actual behavior with the acceptance criteria, inspect important state values, review the relevant condition, and use the Elements panel to check whether the interface is hidden or incorrectly updated.
There is no fixed number.
Revert when the current state becomes harder to understand, the scope keeps expanding, or each speculative fix creates another issue. A known working checkpoint is often the best place to restart.
Comments
Post a Comment