Keep practicing the parts of development that let you judge an AI suggestion: understanding the requirement, planning an approach, reading the code, testing it, and investigating failures. Use AI for help with specific gaps or repetitive work while continuing to exercise those skills yourself.
Set aside short, regular periods to solve a small problem without generated code. Then compare your approach with documentation, a colleague’s feedback, or an AI suggestion. Research on retrieval practice supports returning to knowledge and applying it before looking at the answer.
At work, check that you can explain a change and identify a meaningful failure case before merging it. You can use substantial assistance and still understand the result. If that understanding is missing, pause for a targeted review or learning step rather than accepting the diff because it compiles.
What dependence actually looks like
Dependence is not needing a reference for a method name, a framework option, an error message, or a rarely used command. Professional developers consult documentation, source code, issue trackers, teammates, search engines, and sometimes AI. Pretending otherwise turns memory into the wrong measure of competence.
The warning sign is loss of agency at the important boundaries. You may be becoming dependent when you cannot start without asking for a full solution, accept a patch you cannot trace, replace a failing test rather than understand it, or become stuck when the model’s answer conflicts with the program. The capability worth protecting is not memorization. It is the ability to form and revise a model of the system, choose evidence, and recover when a tool is unavailable or wrong.
The Stack Overflow question behind this article came from a computer-science student who understood theory and could build simpler projects, but used AI beyond a difficulty threshold and worried about career readiness. That is a normal and repairable gap. Do not respond by attempting a permanent ban. Respond by changing the order of work so that your own reasoning happens before the assistant’s output.
Separate four kinds of AI help
Not all AI use has the same effect on learning. The useful distinction is whether the tool helps you retrieve and evaluate knowledge, or silently performs the part you need to practise.
| Mode | What the AI does | Learning-friendly use | Dependence risk |
|---|---|---|---|
| Retrieval | Locates a fact, API, error explanation, or relevant source | Ask for official documentation and then read the relevant section yourself | Treating a summary as authority or never learning where primary docs live |
| Explanation | Teaches a concept or compares approaches | State your prediction first, then ask it to explain the mismatch using a small example | Asking for an explanation before forming any question of your own |
| Critique | Reviews your design, code, tests, or reasoning | Give it your draft and ask for edge cases, missing assumptions, and counterexamples | Letting it approve work that you have not inspected or tested |
| Delegation | Produces code, tests, migrations, configuration, or documentation | Use it for bounded, low-risk scaffolding after you define the contract and review the diff | Accepting a complete implementation when you cannot maintain it |
The first three modes can strengthen skill if they follow your own attempt. Delegation is valuable too, especially for routine work, but it moves the learning opportunity from authoring to specification, review, testing, and maintenance. Do not call an AI response “help” if it removes every hard decision before you have engaged with it.
The attempt-first loop
Use the same loop for exercises, tickets, and personal projects. It makes AI use visible and gives your brain a chance to do the retrieval and problem decomposition you want to keep.
- Define the expected behavior and try the task unaided.
- Explain your prediction before asking a focused question.
- Review every proposed change.
- Test and debug the result, then record what you learned.
- Revisit a similar task later with less help.
State the contract
Before opening chat, write a tiny design note. Include the input, output, constraints, non-goals, examples, likely failure cases, and how you will know the work is correct. For a bug, record the expected behaviour, actual behaviour, a reproducible input, and the smallest version of the system that still fails. This is not bureaucracy. It converts an unbounded request such as “build auth” into questions you can investigate and tests that can reject a wrong answer.
Attempt unaided
Set a modest time box, typically 20 to 45 minutes for a learner exercise and a shorter time box when a work deadline is real. Turn off inline completion or work in a separate scratch file if it is too tempting. Read existing code, consult the language or library documentation, draw the data flow, write a failing test, and make the most plausible change you can. The attempt may fail. A failed, observable attempt gives later feedback something to attach to.
Time boxing is important. “Never ask for help” wastes time and can turn learning into avoidance. When the timer expires, state precisely where you are stuck: perhaps you do not know an API’s cancellation semantics, cannot derive a loop invariant, or do not know why an integration test sees stale data. That is a high-quality question for documentation, a colleague, or AI.
Predict and explain
Make your prediction before you run a command or ask a model. Write one or two sentences such as: “I expect this test to fail because the cache is keyed only by user ID,” or “I think this query must be parameterized because the filter is user controlled.” Then run the test or read the source. If you were wrong, explain the difference in your own words.
This small habit fights a common illusion: code can look familiar when an assistant generated it, even when you cannot reproduce the reasoning. It also makes debugging factual rather than emotional. You are not asking whether you are a “real programmer.” You are comparing a prediction with evidence and updating your model of the program.
Ask for the smallest useful assistance
Do not prompt “solve this ticket” unless complete delegation is truly appropriate. Ask for one constrained contribution and retain the rest of the problem.
- “I have this failing test and this hypothesis. List three likely causes, without code.”
- “Explain the lifecycle of this framework hook using the official documentation. Then ask me two questions that check my understanding.”
- “Review this design for race conditions and name the assumptions it relies on. Do not rewrite it.”
- “Generate three edge-case test ideas for this function. I will implement them.”
- “Compare these two approaches against these constraints. Cite the official API documentation.”
For a new language or library, start from its official tutorial and reference, then use AI to make a specific example or check your explanation. For example, the MDN JavaScript Guide distinguishes a concept-oriented guide from the JavaScript reference, which provides the detailed facts you will repeatedly need while writing code. Use the equivalent primary documentation for your own stack.
Read generated code as if it were a pull request from a hurried stranger
Never jump from generated output to running it in a production-like environment. Read the diff in small chunks and be able to answer these questions for each meaningful block:
- What requirement does this satisfy?
- What values can enter here, and what assumptions are being made about them?
- What happens on an empty, malformed, slow, concurrent, or unauthorized input?
- What state changes, network calls, file writes, permissions, dependencies, or cost does it introduce?
- What test proves the intended behaviour, and what test might reveal a regression?
If you cannot answer, reduce the request, ask for an explanation of one block, consult the source documentation, or rewrite a small version yourself. A vendor’s safeguards do not remove this responsibility. GitHub notes that suggestions may be inaccurate, insecure, or unable to identify larger design issues, and tells users to review and validate them. GitHub Copilot limitations and practices
Use tests to check your understanding
Tests are not merely a gate after code exists. They force you to define behaviour independently of the implementation. Write at least one test or executable check before accepting a nontrivial generated implementation. When possible, create the key cases before the assistant sees its own proposed code. That reduces the risk of accepting tests that only prove the code agrees with itself.
Start with the ordinary case, then add boundaries, invalid inputs, failure paths, and a property or invariant when the domain permits it. For example, a function that allocates seats should never allocate more seats than exist, and a retry function should not treat every failure as safe to retry. For a user-facing change, test permission boundaries and observable behaviour, not merely whether a helper function was called.
AI can help brainstorm cases or draft test fixtures, but keep authorship of the acceptance criteria. Watch for a particularly dangerous pattern: a tool “fixes” a failure by deleting, weakening, skipping, or changing the test without a sound product reason. GitHub’s review guidance specifically calls out hallucinated APIs, ignored constraints, and deleted or skipped tests as AI-specific pitfalls. Reviewing AI-generated code
Run the normal local test suite, formatter, linter, type checker, and security scanners before review. In a team, make those repeatable in continuous integration so a reviewer does not have to rediscover routine problems manually. Automated checks catch a class of mistakes, not every business-rule or security failure. They support judgment; they do not replace it.
Example
Hypothetical setup: You are adding a function that applies a promotional discount to a shopping cart. The rules say a code is valid only before its expiry time, may have a minimum order amount, and must never make the total negative.
Action: Before asking AI for implementation help, you write tests for a valid code, an expired code, an order below the minimum, an exact-boundary order, a discount larger than the subtotal, and two applications of the same single-use code. You sketch the decision order on paper and implement the simple path. When you get stuck on time-zone comparison, you ask for an explanation of the date-time library’s semantics and a critique of your proposed edge cases, rather than a complete function.
Takeaway: If AI later generates a cleaner implementation, you can evaluate it against requirements you already own. If it changes the order of checks, mutates the cart unexpectedly, or drops a boundary case, your tests and design note make the error visible. You also have practised the portable skill: turning a vague feature into a model and a testable contract.
Debug from first principles before asking for a fix
Debugging is one of the skills most easily hidden by a “paste error, accept patch” workflow. Use a short evidence ladder first:
- Reproduce the failure reliably and preserve the exact input, error, environment, and expected result.
- Reduce it to the smallest failing case. Remove unrelated UI, data, or configuration until the failure remains.
- Inspect the values and control flow at the relevant boundary. State an invariant such as “this ID is present before persistence” or “the response is parsed only after the status is checked.”
- Form a hypothesis, change one variable, and run the smallest test that could disprove it.
- Read the language, library, database, or protocol documentation for the disputed behaviour.
- Only then ask AI to challenge your hypothesis, explain a documented rule, or suggest the next discriminating experiment.
This sequence does not mean you must identify every cause alone. It ensures that an assistant’s answer is evidence you can interrogate instead of a ritual you hope will work. Keep a short bug journal: symptom, root cause, failed hypothesis, final test, and the principle you missed. Review it weekly. The journal becomes tailored spaced repetition for the errors you actually make.
A practice system that scales
Schedule AI-free practice blocks
Reserve two to four short blocks each week for deliberate practice with no code-generating AI. Start with 30 minutes. The block should contain a well-scoped task slightly beyond what feels automatic: parse a file format, implement a small data structure, add pagination to a toy API, trace a race condition, or re-create a familiar feature from a written specification.
Use normal non-generative tools that are part of the skill: editor, compiler, debugger, test runner, REPL, language reference, and official documentation. For a strict learning block, search is acceptable only after you have written your own plan and question. The constraint is not purity. It is protecting the first attempt and the feedback loop.
End each block with a three-minute recall note: what was the task, what did you predict, what went wrong, what is the general rule, and what will you retry later? The evidence supports repeated practice, not one prescribed schedule. The practical point is repeated effortful recall plus feedback, spread across time, rather than binge-reading explanations. Annual Review of Psychology
Use spaced repetition for concepts worth retaining
Do not make cards for every API name. Make small prompts for durable ideas that would help you reason in an unfamiliar codebase: “What condition makes a binary search loop terminate?”, “When should an operation be idempotent?”, “What makes a SQL query injection-safe?”, “What is the difference between a process and a thread in this runtime?”, or “Which cache key preserves this feature’s isolation?”
Review the cards or recall notes after roughly one day, several days, and a couple of weeks. If a concept is easy, increase the interval. If it is hard, make the card more concrete or attach it to a tiny coding exercise. A review of applied classroom research found benefits of retrieval practice across varied education levels, formats, and delays, while also cautioning that its evidence base is not equally representative of every setting. Systematic review
Increase project difficulty deliberately
Use projects as a ladder, not as a series of giant, AI-authored portfolios. Move on when you can explain, test, and modify the previous rung without a generated solution.
| Rung | Suitable project shape | What you should own unaided | Where limited AI help fits |
|---|---|---|---|
| 1 | Small CLI, parser, game rule, or data-structure exercise | Control flow, data model, tests, and debugging | Concept explanation after an attempt |
| 2 | One-service application with persistence and a small API | Request flow, error handling, migrations, and a basic test strategy | Review edge cases and explain documentation |
| 3 | Feature in an existing codebase | Reading unfamiliar code, change plan, regression tests, and pull-request explanation | Find relevant files, critique the plan, draft routine prose |
| 4 | Multi-component project with real constraints | Tradeoffs, observability, permissions, failure handling, and release checks | Bounded scaffolding and review, with explicit human approval |
At each rung, write a short retrospective after shipping. Name one decision you made yourself, one thing you misunderstood, one source you should have consulted earlier, and one task to retry unaided. Repeating the same difficulty only improves comfort. Progress comes from a slightly harder constraint, new failure mode, or unfamiliar codebase.
Take periodic unaided skill checks
Every two to four weeks, choose a 60 to 120 minute task that resembles the work you want to do. Work with documentation and standard development tools, but without generative assistance. Save your plan, commits, tests, and a note on what stopped you. Compare this result with the previous check on four measures: how quickly you began, how specific your tests were, how well you diagnosed failures, and how clearly you can explain tradeoffs.
Do not grade yourself on line count or whether you remembered obscure syntax. If you can use documentation to make a correct, testable change and explain it, that is real professional competence. If a skill check reveals a gap, turn the gap into next week’s practice task rather than into a verdict about your career.
A two-week routine for a junior developer
This example assumes a full-time junior developer or student who can protect about 45 to 60 minutes on weekdays. Adapt the time, language, and project to your constraints. The routine uses AI in normal work, but makes its role explicit.
| Day | Main activity | AI boundary | Evidence to keep |
|---|---|---|---|
| 1 | Choose a small feature or bug and write its contract, examples, risks, and acceptance tests | No AI until the first design note is complete | One-page design note and test list |
| 2 | Implement the smallest happy path in an AI-free block | Documentation after your attempt is allowed | A passing happy-path test and one question you could not answer |
| 3 | Read the relevant official documentation and revise the design | Ask AI only to explain the mismatch between your prediction and the documentation | A corrected prediction in your notes |
| 4 | Add boundary and failure tests, then use AI to critique missing cases | Do not show it its own implementation if you want independent test ideas | A list of accepted and rejected test ideas |
| 5 | Review the diff line by line and write a short pull-request description in your own words | AI may critique the description, not author your understanding | Requirement-to-test mapping |
| 6 | Rest day or a 15-minute recall review of notes and cards | No code generation | Three recalled principles |
| 7 | Rebuild one small piece from memory in a scratch project | No AI during the rebuild | Before-and-after comparison |
| 8 | Take a real or simulated bug through the evidence ladder | AI only after a reproduction and hypothesis exist | Minimal reproduction and root-cause note |
| 9 | Make a small contribution in an unfamiliar codebase or module | AI may help locate code, then you explain the proposed change first | Change plan and dependency map |
| 10 | Add tests and run the full local checks | AI may propose edge cases after your list exists | Test output and review checklist |
| 11 | Delegate a low-risk, repetitive task such as test data or a documentation draft | You define inputs, constraints, review criteria, and diff scope | Prompt, diff, and edits you made |
| 12 | Audit a previous AI-assisted change and explain each nontrivial line or replace one part manually | Ask for a critique only after your explanation | Explanation note and one refactor |
| 13 | Rest day or spaced review | No code generation | Updated cards or bug-journal entries |
| 14 | Do a 60 to 90 minute unaided skill check and retrospective | Documentation and normal tools allowed, generative AI off | Plan, tests, result, and next learning target |
The routine is not an exam you must pass perfectly. If work intrudes, keep the structure rather than the calendar: attempt first, ask narrowly, validate independently, revisit later. After two weeks, repeat with a task one rung harder or with less assistance at the point where you previously needed it.
Healthy use at work
Workplace pressure is real. You may be expected to use AI tools because they reduce time on boilerplate, help navigate a large repository, or are built into the approved development environment. Refusing every AI tool can be counterproductive when it means slower delivery, inaccessible workflows, missed collaboration norms, or spending scarce attention on low-value repetition.
Use a risk-based division of labour instead. It is usually sensible to delegate formatting, test-data scaffolding, routine transformations, first-pass documentation, code search, or an explanation of an unfamiliar subsystem. Keep human ownership of requirements, architecture, user impact, access control, security boundaries, data migrations, release decisions, and review. For a high-risk change, reduce both the AI’s scope and its permissions. Require small diffs, tests, a human reviewer, and rollback options.
Make this visible to your manager or team. A useful statement is: “I can use the assistant to generate the repetitive fixture layer, but I will own the acceptance tests and submit a small reviewable diff.” If a deadline leaves no time to read, test, and understand a change, the issue is not merely your tool choice. It is a delivery-risk conversation. Ask for a narrowed scope, a staged release, pairing, or an explicit reviewer rather than hiding unreviewed generation behind apparent speed.
Accessibility is not dependence
For some developers, AI can be an accessibility support: converting speech to a structured draft, explaining dense text in plainer language, supporting a non-native language, helping with executive-function barriers, or offering another route into a complex codebase. Do not impose a blanket “no AI” rule that removes a needed accommodation.
The relevant test remains agency. A developer using assistive AI can still set the goal, choose constraints, ask for clarification, review output, run tests, and make the final decision. Design the learning system around accessible evidence of understanding, such as a spoken explanation, a diagram, a recorded walkthrough, or a smaller typed exercise. The target is reliable control, not a particular physical way of typing code.
Security, privacy, and provenance
Treat code assistants as an external contributor with imperfect context and an unknown chance of error. Before sharing code or allowing an agent to act, know your organization’s approved tools, data-handling rules, retention settings, and permissions. Do not paste secrets, customer data, private keys, access tokens, production logs containing personal data, or confidential business information into a tool unless it is explicitly approved for that data.
Context can be broader than the active file. OWASP warns that coding assistants may send open files, project structure, and terminal output to a provider, and recommends reviewing context settings and excluding sensitive paths. OWASP Secure Coding with AI Cheat Sheet Use tool-specific exclusion controls where available, and remember that a .gitignore entry is not automatically an AI-context exclusion.
Review generated dependencies, package scripts, build files, container definitions, CI workflows, database migrations, and infrastructure changes with more scrutiny than ordinary application code. These can run automatically or gain access to sensitive systems. OWASP specifically advises treating AI changes to build and deployment files as security-critical and requiring explicit human review. OWASP supply-chain guidance for AI coding
Keep a lightweight provenance record when generated material is substantial or policy requires it: the task and constraints you supplied, tool and model if known, date, generated files or diff, sources or dependencies it cited, checks run, reviewer, and substantive edits you made. This is not proof that the result is correct or legally clear. It is an audit trail that helps a team investigate a later defect, reproduce a decision, or check a license and dependency. Use your organization’s policy and legal guidance for intellectual-property decisions. GitHub notes that users remain responsible for generated-code risks, including bugs, vulnerabilities, and intellectual-property concerns. GitHub responsible-use guidance
For agentic tools that can run commands or modify repositories, give the smallest practical permission set, inspect planned actions, and avoid unattended access to production credentials. Treat instructions found in issues, documentation, logs, generated text, or repository files as untrusted input, especially when they can influence an agent’s commands. OWASP’s current LLM guidance identifies prompt injection and improper output handling as significant risks. OWASP GenAI LLM Top 10 2026
Signs the balance has tipped
Use this checklist honestly once a week. Several “yes” answers are a signal to adjust the workflow, not a reason for shame.
- Do I ask for an implementation before writing a requirement, sketch, or first attempt?
- Do I accept code that I could not explain to a teammate line by line at the level that matters?
- Do I lack an independently written test, test case, or observable check for a nontrivial generated change?
- When something fails, do I paste the error into AI before reproducing it and making a hypothesis?
- Have I stopped reading official documentation, source code, or existing tests?
- Would I be unable to make a small change in my main language if the assistant were unavailable for an afternoon?
- Do my prompts routinely include more repository or user data than is needed for the task?
- Do I use an assistant to make security, permission, migration, or deployment changes without an appropriate human review?
- Am I using AI to avoid the part of the task I most need to learn, rather than to remove routine friction?
If the answer is yes to one or two questions, add an attempt-first rule and a weekly AI-free block. If the answer is yes to many, temporarily reduce full-code generation. Spend two weeks in retrieval, explanation, and critique modes, with small unaided rebuilds and skill checks, then reintroduce bounded delegation. This is a training adjustment, not a moral purification exercise.
A practical default policy
For learning tasks, keep the first 20 to 45 minutes AI-free, use official docs after a written hypothesis, and ask the assistant to explain or critique rather than generate. For ordinary implementation work, define acceptance tests first, delegate only a bounded slice, inspect the diff, run checks, and make the final engineering judgment yourself. For sensitive, irreversible, or production-affecting changes, tighten permissions, use small reviewable steps, add human review, and follow your organization’s security and data rules.
Review whether your routine is improving your ability to plan, evaluate changes, and debug failures. Adjust the amount or type of assistance when one of those skills gets less practice.
Evidence
Sources used for this answer.
Question signals show what people need. Primary documentation supports the answer. Both remain visible.
- 01How do I code without being completely dependent on AI?Stack Overflow · question signal · checked 4 Sept 2026
- 02retrieval practiceannualreviews.org · primary evidence · checked 4 Sept 2026
- 03Systematic reviewlink.springer.com · primary evidence · checked 4 Sept 2026
- 04GitHub Copilot limitations and practicesdocs.github.com · implementation guidance · checked 4 Sept 2026
- 05MDN JavaScript Guidedeveloper.mozilla.org · primary evidence · checked 4 Sept 2026
- 06JavaScript referencedeveloper.mozilla.org · primary evidence · checked 4 Sept 2026
- 07Reviewing AI-generated codedocs.github.com · implementation guidance · checked 4 Sept 2026
- 08OWASP Secure Coding with AI Cheat Sheetcheatsheetseries.owasp.org · primary evidence · checked 4 Sept 2026
- 09OWASP GenAI LLM Top 10 2026genai.owasp.org · primary evidence · checked 4 Sept 2026
- 10NIST SP 800-218A, Secure Software Development Practices for Generative AI and Dual-Use Foundation Models, July 2024doi.org · primary evidence · checked 4 Sept 2026