AI Coding Assistants: How to Use Them Effectively
AI coding assistants write plausible code quickly, and plausible is not the same as correct. This guide summarises what research says about their speed and security, then has a small local model write six functions, scores them with hidden tests, and shows what examples and test feedback fix.
GloryTecks Team
Training & Placement Faculty
Last updated: 3 October 2026
AI coding assistants are now part of most developers' day. In the 2025 Stack Overflow Developer Survey, 84% of respondents used or planned to use AI tools in their work, and 51% of professional developers used them daily. Yet more of them distrusted the accuracy of those tools, 46%, than trusted it, 33% (Stack Overflow: 2025 survey).
Both numbers are right. Assistants are useful and often wrong. Using them effectively means building your workflow around catching the wrong parts.
What the evidence says
Speed is not guaranteed. In a randomised controlled trial, METR asked 16 experienced open-source developers to complete 246 real tasks in projects they knew well, with and without AI tools. Allowing AI made them 19% slower. The developers had expected a 24% speed-up and, afterwards, believed they had been 20% faster (Becker et al.: METR study). That was one setting, experts in familiar code, but it shows how unreliable our impression of our own productivity can be.
Security needs checking. An early study of GitHub Copilot generated 1,689 programs across 89 scenarios built around common weaknesses, and found about 40% vulnerable (Pearce et al.: Asleep at the Keyboard?). Models have improved since, but generated code still inherits patterns from the code it learned from.
A small experiment
The script asks a small local model to write six functions, each in three ways: from a description only, from a description plus example tests, and after one round of feedback listing the tests that failed. Every version runs in a separate process against hidden tests that include edge cases the model never saw.
import json
import re
import subprocess
import sys
import urllib.request
# Six small functions, each with examples a developer might show and hidden tests that include edge cases
TASKS = {
"slugify": ("slugify(title: str) -> str: lower-case the title, drop every character that is not a letter, "
"digit, space or hyphen, and join the remaining words with single hyphens",
['slugify("Hello, World!") == "hello-world"', 'slugify("Data Science 101") == "data-science-101"'],
['slugify(" Many spaces ") == "many-spaces"', 'slugify("C\'est la vie") == "cest-la-vie"',
'slugify("already-a-slug") == "already-a-slug"']),
"parse_duration": ("parse_duration(text: str) -> int: convert a duration such as '1h30m' or '45s' into seconds; "
"units are h, m and s; raise ValueError for anything else",
['parse_duration("1h30m") == 5400', 'parse_duration("45s") == 45'],
['parse_duration("1h2m3s") == 3723', 'parse_duration("2h") == 7200',
'raises(ValueError, parse_duration, "abc")']),
"merge_intervals": ("merge_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]: merge "
"overlapping or touching intervals and return them sorted",
['merge_intervals([(1, 3), (2, 6), (8, 10)]) == [(1, 6), (8, 10)]'],
['merge_intervals([(1, 2), (2, 3)]) == [(1, 3)]', 'merge_intervals([]) == []',
'merge_intervals([(5, 7), (1, 2)]) == [(1, 2), (5, 7)]']),
"roman_to_int": ("roman_to_int(numeral: str) -> int: convert a Roman numeral to an integer",
['roman_to_int("XIV") == 14'],
['roman_to_int("MCMXCIV") == 1994', 'roman_to_int("IX") == 9', 'roman_to_int("LVIII") == 58']),
"top_words": ("top_words(text: str, n: int) -> list[tuple[str, int]]: the n most frequent lower-case words "
"with their counts, most frequent first, ties broken alphabetically",
['top_words("a b b c c c", 2) == [("c", 3), ("b", 2)]'],
['top_words("x y z y z", 3) == [("y", 2), ("z", 2), ("x", 1)]', 'top_words("", 2) == []',
'top_words("A a b", 1) == [("a", 2)]']),
"is_valid_isbn10": ("is_valid_isbn10(code: str) -> bool: check an ISBN-10, ignoring hyphens; the last "
"character may be X meaning 10",
['is_valid_isbn10("0306406152") is True', 'is_valid_isbn10("0306406153") is False'],
['is_valid_isbn10("0-306-40615-2") is True', 'is_valid_isbn10("080442957X") is True',
'is_valid_isbn10("12345") is False']),
}
FENCE = "`" * 3 # the Markdown code fence the model wraps its code in
def ask(prompt):
body = {"model": "llama3.2", "stream": False, "options": {"temperature": 0, "seed": 42, "num_predict": 400},
"messages": [{"role": "user", "content": prompt}]}
req = urllib.request.Request("http://localhost:11434/api/chat", json.dumps(body).encode(),
{"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=900) as r:
reply = json.load(r)["message"]["content"]
block = re.search(FENCE + r"(?:python)?\n(.*?)" + FENCE, reply, re.S)
return block.group(1) if block else reply
def run_tests(code, tests):
# run the generated code in a separate process; report each failing assertion and why
harness = code + "\n\ndef raises(err, fn, *args):\n try:\n fn(*args)\n except err:\n return True\n return False\n"
harness += "\nimport json\nfailed = []\n" + "".join(
f"try:\n assert {t}\nexcept Exception as e:\n failed.append([{t!r}, type(e).__name__])\n" for t in tests)
harness += "print(json.dumps(failed))\n"
try:
out = subprocess.run([sys.executable, "-c", harness], capture_output=True, text=True, timeout=20)
return json.loads(out.stdout.strip().splitlines()[-1])
except Exception:
return [[t, "did not run"] for t in tests]
labels = ["description only", "with examples", "after one repair"]
totals = dict.fromkeys(labels, 0)
print(f"{'function':<17}" + "".join(f"{label:>19}" for label in labels) + " (tests passed)")
shown_repair = None
for name, (spec, examples, hidden) in TASKS.items():
tests = examples + hidden
results = []
for shown in ([], examples):
prompt = f"Write a Python function {spec}.\n"
if shown:
prompt += "It must pass these examples:\n" + "\n".join(f"assert {t}" for t in shown) + "\n"
prompt += "Reply with the code only, in one python code block."
code = ask(prompt)
results.append(run_tests(code, tests))
# the coding-agent loop: show the model which tests failed, and ask for a fix
failed = results[-1]
if failed:
report = "\n".join(f"assert {t} -> {err}" for t, err in failed)
code = ask(f"This Python function fails some tests.\n\n{FENCE}python\n{code}\n{FENCE}\n\nFailing tests:\n{report}\n\n"
"Fix the function so all tests pass. Reply with the code only, in one python code block.")
results.append(run_tests(code, tests))
if shown_repair is None:
shown_repair = (name, report, results[-1])
else:
results.append(failed)
for label, failed_tests in zip(labels, results):
totals[label] += len(tests) - len(failed_tests)
print(f"{name:<17}" + "".join(f"{f'{len(tests) - len(r)} of {len(tests)}':>19}" for r in results))
n_tests = sum(len(e) + len(h) for _, e, h in TASKS.values())
print(f"{'total':<17}" + "".join(f"{f'{totals[label]} of {n_tests}':>19}" for label in labels))
if shown_repair:
name, report, still = shown_repair
print(f"\nfeedback sent for {name}:\n{report}")
print("still failing after the repair:", [t for t, _ in still] or "none")function description only with examples after one repair (tests passed)
slugify 5 of 5 5 of 5 5 of 5
parse_duration 1 of 5 1 of 5 1 of 5
merge_intervals 3 of 4 3 of 4 4 of 4
roman_to_int 4 of 4 4 of 4 4 of 4
top_words 4 of 4 4 of 4 4 of 4
is_valid_isbn10 2 of 5 2 of 5 2 of 5
total 19 of 27 19 of 27 20 of 27
feedback sent for parse_duration:
assert parse_duration("1h30m") == 5400 -> ValueError
assert parse_duration("45s") == 45 -> ValueError
assert parse_duration("1h2m3s") == 3723 -> ValueError
assert parse_duration("2h") == 7200 -> ValueError
still failing after the repair: ['parse_duration("1h30m") == 5400', 'parse_duration("45s") == 45', 'parse_duration("1h2m3s") == 3723', 'parse_duration("2h") == 7200']What the output shows
- Plausible is not correct. The model passed 19 of 27 tests. Its failing
parse_durationcame with a tidy docstring and comments, and could not parse "1h30m" at all, because it split the text on spaces. - Examples in the prompt changed nothing here: 19 of 27 again. Showing a model what right looks like does not make it check its work.
- Feedback fixed one function. Told which
merge_intervalstest failed, the model corrected the edge case of touching intervals. - It did not fix the hardest one. Given four failing
parse_durationtests, all raising ValueError, it still produced code that failed every one. Feedback helps when the model is close; it does not rescue a wrong approach. - Only the tests revealed any of this. Read quickly, every function looked finished.
Larger models pass far more tests than a 3-billion-parameter model on a laptop. The pattern still holds: output that looks done, a gain from running tests and feeding back failures, and failures that need a person to rethink the approach.
How to use assistants well
Give the assistant a check it can run. Anthropic's guidance for its coding agent puts this first: give it tests, a build or a screenshot comparison, so it can verify its own work instead of stopping when the code "looks done" (Anthropic: Claude Code best practices). Write or approve the tests yourself.
Run the loop: generate, test, feed back, repeat. That is what coding agents automate. Cap the rounds. If two repairs fail, step in and change the approach.
Manage context. The same guidance names the context window as the most important resource: performance degrades as it fills with files, logs and conversation. Start fresh sessions for new tasks and point the assistant at the specific files that matter.
Review like a pull request from a new colleague. Check edge cases, error handling, input validation and anything touching security, secrets or data.
Use it where it is strongest: boilerplate, tests, explanations of unfamiliar code, refactoring with good test coverage, and first drafts you will review.
Measure your own speed honestly. If a task would take you five minutes, a long back-and-forth with an assistant may cost more. The METR result is a reminder to check rather than assume.
For teams
- Agree what may be shared. Proprietary code and secrets should go only to tools approved for them.
- Keep the review bar unchanged for AI-written code.
- Invest in tests and CI. They are what make assistant output safe to accept.
- Watch dependencies. Assistants can suggest packages that do not exist, which attackers can register under the invented names. Across 16 models, one study found at least 5.2% of recommended packages hallucinated for commercial models and 21.7% for open-source ones (Spracklen et al.: package hallucinations).
For the loop behind coding agents, see AI agents explained. For the tool-calling mechanism they use, see function calling and tool use. For a broader view of the AI tools developers use, see generative AI tools for developers.
Common mistakes
- Accepting code because it reads well.
- No tests, so the assistant cannot check itself and neither can you.
- Endless repair loops on an approach that is wrong.
- Pasting secrets or proprietary code into unapproved tools.
- Letting a session's context grow until the assistant loses track.
- Assuming you are faster without measuring.
Do AI coding assistants make developers faster?
Is AI-generated code secure?
How do I get better code from an AI assistant?
Should I let an AI agent run tests and fix code on its own?
Writing and testing Python for AI applications, including with AI assistants, runs through the Generative AI course.
Sources
- AI (2025 Stack Overflow Developer Survey) — Stack Overflow
- Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity (arXiv) — Becker et al., METR
- Asleep at the Keyboard? Assessing the Security of GitHub Copilot's Code Contributions (arXiv) — Pearce et al.
- We Have a Package for You! A Comprehensive Analysis of Package Hallucinations by Code Generating LLMs (arXiv) — Spracklen et al.
- Best practices for Claude Code (Claude Code documentation) — Anthropic
GloryTecks Team
Training & Placement Faculty
The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.
Want to master Generative AI?
Join GloryTecks’ expert-led Generative AI training in Hyderabad with real-time projects and 100% placement support.
Related articles
ChatGPT Complete Guide: Features, Prompts and Use Cases
ChatGPT went from a research preview to one of the most used software products in the world in under four years. This guide maps its main features and when they arrived, shows how to write prompts that work, sorts good uses from risky ones, and explains the limits every user should check for.
What Is Generative AI? A Complete Beginner's Guide
What generative AI is, in plain language: how language models produce text one token at a time (shown with a tiny runnable model), how chat models are trained, how image models differ, what the technology is reliable for, and why it confabulates.
RAG Architecture Explained: Retrieval-Augmented Generation
RAG gives a language model the right pages to read before it answers. This guide walks through the four stages with a pipeline that runs on a laptop, using open models and public documents, and shows three outcomes: a correct cited answer, a retrieval miss the model admits to, and a question outside the documents.