Programming

Coding with AI

Today models write code that runs, but code that runs is not the same as code that is right, and the gap between the two is what stays with you. The professional move is not asking better questions; it is arranging a workflow where every piece of code is small, reviewable and tested before it reaches the main branch.

  • Lesson 13 of 14
  • Intermediate
  • Free, no signup

What daily users say about complex tasks

Stack Overflow Developer Survey 2025, the complex-tasks question, 33,230 responses
  1. 25.2%
  2. 14.1%
  3. 22%
  4. 17.6%
  5. 16.8%
  • Very well 4.4%
  • Good, not great 25.2%
  • Neither good nor bad 14.1%
  • Bad 22%
  • Very poor 17.6%
  • Do not use them for complex work, or do not know 16.8%

This is what people report, not a measurement of code quality; it says what developers experience. The last bucket is not on the good-to-bad scale, which is why its colour stands apart.

Last checked: Facts and tool names in this lesson are re-checked against their sources on this date.

What exactly are models good at in coding?

There is a clear pattern, and once you see it the rest of this lesson gets simple: models are good at work whose shape is repetitive and whose judgment content is low. The closer the work gets to decisions specific to your project, the more quality falls off, and it falls off quietly, because the code still runs.

Things they genuinely do well: writing code whose like has been written a thousand times, such as reading a file, validating a form, or a network request with its error handling. Translating from one language to another. Converting data from one shape to another. Writing tests for a function you wrote yourself. Explaining code somebody else wrote that you are a stranger to. And the one almost nobody uses, which pays back the most: critiquing code you wrote yourself.

The work where they fall short shares a pattern too: anywhere the right answer depends on something that is not in the conversation. Architecture decisions that turn on your scale and your team. Code that has to fit the unwritten conventions of an old project. Anything that depends on the latest changes in a library. And security, which the risks section returns to.

Our position is simple: put the model on work you could have done yourself but do not want to, not on work you do not know how to do. Because in the first case you can judge the output, and in the second you can only hope.

What do developers who use these tools every day actually say?

Instead of guessing, look at the largest public survey in the field. The 2025 Stack Overflow developer survey says 84 percent of respondents use or plan to use AI tools, and 51 percent of professional developers use them daily. So the "should we use them" argument is over.

But the same survey says a second thing that gets quoted far less. Asked how well the tools they use handle complex tasks, 4.4 percent said very well and 25.2 percent said good but not great. Against that, 22 percent said bad and 17.6 percent said very poor. Another 14.1 percent said neither good nor bad, and 16.8 percent said they do not use these tools for complex tasks or do not know. In other words, the people with these tools in their hands every day are more negative than positive about complex work.

And a third number that sums up this whole lesson: the biggest single frustration, at 66 percent, was "AI solutions that are almost right, but not quite". Almost right is the worst possible state. Completely wrong code is caught by the compiler or the first run. Almost right code passes both and shows itself months later in one particular case.

The rest of that same frustration list is worth reading too, because it shows the shape of the cost: 45.2 percent said debugging generated code takes more time, 20 percent said they have become less confident in their own problem solving, and 16.3 percent said it is hard to understand how or why the code works. At the other end of the list, only 4 percent said they had run into no problems at all.

One note about these numbers, since this is also a lesson in honesty: they are what people report, not a measurement of code quality. They tell you what developers experience, not how good the models objectively are. For this lesson the first is what matters.

What frustrates them most

Same survey, the frustrations question, 31,476 responses, multiple choice
  1. Solutions that are almost right, but not quite 66%
  2. Debugging generated code takes more time 45.2%
  3. I have become less confident in my own problem solving 20%
  4. It is hard to understand how or why the code works 16.3%
  5. I have not run into any problems 4%

Because each person could tick several boxes, these numbers do not add to a hundred and must not be summed. Two options from the source list are not shown here, "I do not use AI tools regularly" and "Other", because neither is a complaint about the tool.

Which tools, and which of them can you actually reach from Iran?

Tools come in three shapes, and choosing between them depends less on the model than on where you work.

An editor extension suggests while you type. GitHub Copilot is the best known. Its advantage is that it interrupts nothing; its cost is that because it always suggests, you get used to accepting without reading.

A separate chat in the browser, such as Claude or Gemini. Better for thinking, critiquing and understanding than for writing, because it separates the code from the project context, and that separation forces you to state the problem yourself.

A command line agent or an agentic editor, such as Claude Code or Cursor, which read and write files themselves. The most powerful shape and the most dangerous: when a tool changes ten files without showing you each change, review becomes the only thing standing between you and an invisible bug.

Now access from Iran, which for this page reader is the real question. There is an important difference here that is rarely stated: GitHub is not like the others. GitHub own trade-controls page says it holds a licence from the US Treasury Office of Foreign Assets Control covering its cloud services for developers resident in Iran, both free and paid. We quote that sentence and claim nothing beyond it; whether payment goes through with an Iranian card is a separate matter we have not tested, and the buying guide covers that layer on its own.

On the other side, Anthropic supported-countries page does not include Iran, and Google own page about the Gemini web app gives a list of more than two hundred and thirty countries and territories that Iran is not on. Cursor publishes no supported-countries list that we could find, so we claim nothing about it; its prices can be read off its own page, a free plan and a professional plan at twenty dollars a month.

There is a fourth route that means more in Iran than elsewhere: open-weight models. You can take the weights and run them on your own hardware or at any provider, so the access question changes shape. Their quality trails the closed models and that gap keeps moving; our up-to-date ranking with a source for every number is at the best AI for coding, and we deliberately name no version here, because any number written into a lesson is stale in three months.

The workflow that holds quality: test first, small diffs, real review

Three habits separate someone who works faster with a model from someone who produces technical debt with one. None of them are about the model; all three are yours.

Test first, then code. Before you ask for an implementation, ask yourself how you will know this works. Write that answer down and you now have an objective standard neither you nor the model can argue with. Order matters here: if you take the code first and then ask the same model to write its test, you get a test that carries the same assumptions as the code, and both are wrong together. The best move is to state what the test must prove yourself, even if you hand the writing of it to the model.

Small diffs. Ask the model to do one thing in one change and stop there. A fifty-line change that alters one thing can be reviewed; a five-hundred-line change that alters four things does not get reviewed, it gets approved. Those are very different, and the difference is exactly where bugs get in.

Real review. Review means being able to say, for each line, why it is there. If you hit a line you do not understand, you have three options: ask, work it out, or delete it. The fourth, which everybody picks, is to leave it because the rest works, and that line is precisely the one you come back to in six months.

There is a cheap fourth habit almost nobody uses: ask the model to critique its own code in a separate message. In a fresh message with no conversation history, paste only the code and ask what is wrong with it. You get answers you would not have got by asking in the same thread, because inside the thread the model role is to defend what it just built.

Hand to the model

  • Code whose like has been written a thousand times
  • Translating from one language to another
  • Converting data from one shape to another
  • Writing tests for a function you wrote
  • Explaining code you are a stranger to
  • Critiquing code you wrote yourself

Keep for yourself

  • Architecture that turns on your scale and team
  • The unwritten conventions of an old project
  • Security and access-control decisions
  • Anything depending on a library latest changes
  • Defining what the test has to prove
  • The final call on every line that reaches main

The line between these columns is not fixed and moves with the project. The test is one thing: if you cannot judge the output, the work does not belong in the left column.

What must never go into the message?

This section is short because its rule is short: anything you put in the message leaves your hands. Depending on the service, the plan and the settings, it may be retained, it may be reviewed by a human, and it may go to training. Each service data-usage page is the only right place to learn this, not what people say.

So four things stay out: keys, passwords and tokens, even inside a snippet you sent only as an example; real user data, especially a log with emails and phone numbers in it; a client codebase, when your contract did not allow it; and anything you signed a confidentiality undertaking for.

The practical route is simple. Replace the key with a meaningless string, and if you ever pasted a real key into a chat, that key is no longer valid and must be rotated; not because it is certainly leaked, but because you no longer know where it is. For user data, build a few made-up rows with the same shape. The shape of the data is what the model needs, and its contents are not.

If you are a beginner, where should you start?

There is a real worry that deserves a straight answer: if the model writes the code, when do I learn?

The risk is real, and the same survey shows it: 20 percent of respondents said they have become less confident in their own problem solving. The mechanism is clear too: learning comes from being stuck, and a tool that removes being stuck has removed the thing that was teaching you.

But the answer is not "do not use it", because the person who does not use it is both slower and ignorant of what everyone else knows. The answer is order. For anything you have not learned yet, write it yourself first, even badly and incompletely. Then ask the model to critique it. That order gets the same work done and keeps the learning, because being stuck stays where it was and only gets shorter.

And a simple test that tells you where on that spectrum you stand: close the code the model wrote and explain the same thing from memory. If you can say what it does and why it does it that way, good. If you cannot, that code is not yours yet; it is rented, and you pay the rent on the day it breaks. The rest of the foundations that make this test possible are in the programming learning path.

The fast path, with AI

The fast path is not writing a better prompt. It is <strong>writing the acceptance criteria before the code</strong> and forcing the model to hand them back to you first. Three short rounds instead of one long request: round one takes no code at all, only the list of assumptions and questions; round two takes function signatures and tests; and the code arrives in round three, once both sides agree on one definition. What makes it unusual is round one: the model tells you what it invented, and that list is exactly where almost-right code comes from. This wants a frontier model rather than a fast one; our current pick is at <a class="text-link" href="/ai/code/">the best AI for coding</a>.

  1. Round one: describe the task and say explicitly that you do not want code. Ask for assumptions, open questions and edge cases.
  2. Answer the questions and reject the wrong assumptions. That single step removes most of the bugs that would have followed.
  3. Round two: ask for function signatures and tests, with no bodies. Read the tests yourself and say which case is missing.
  4. Round three: ask for the implementation, only for those signatures, in one small change. Then run the tests yourself.
  5. In a fresh message with no history, paste that same code and ask for a critique. The answers differ from what the same thread would have given you.

Copy-ready recipe

ROUND 1 - NO CODE

What I want: {one sentence saying what the function or feature does}
Input: {type and shape, with one real example}
Output: {type and shape, with the example you expect}
Environment: {language and version} / {libraries allowed} / {libraries not allowed}
Constraints: {performance, compatibility, the project code style}

Write no code. Give me only these four things:
1. Every assumption you need to do this that I did not state.
2. Questions whose answers change the result, at most 5, most important first.
3. The edge cases: empty input, very large input, malformed input, a null value.
4. If you had to write the code right now, which of the assumptions in 1 you would be inventing.

ROUND 2 - AFTER I ANSWER THE QUESTIONS

Write the function signatures, no bodies, each with one sentence saying what it guarantees.
Then write the tests. Each test checks one thing and its name says what it checks.
Every edge case from 3 must have a test.

ROUND 3

Now write the implementation for those signatures. One change, this one job, nothing extra.
If you have to introduce an assumption that was not in round 1, write it above the code.

ROUND 4 - IN A NEW, EMPTY CONVERSATION

Critique this code. Look for: unvalidated input, a swallowed error, an untested edge case,
a name that does not say what the function does, and anywhere the behaviour on failure is undefined.
Rewrite nothing. Just the list of problems with line numbers.

Before you trust the output: This recipe was walked through for this page, and one clause was added because of it: without item four in round one, the model states its assumptions but does not say which ones it invented, and that difference is the whole value. Two things after round three are yours and are not delegated: run the tests yourself, because a model will describe unrun output with complete confidence; and any line you do not understand either gets understood or gets deleted. If a security decision is involved, that is outside this cycle too, and the risks section says why.

AI in this kind of work

This whole lesson is about AI, so this block covers what the rest of the page had no room for: the tools themselves, how they stand from Iran, and the risk specific to this work. Our position in one sentence: <strong>the model is the writing instrument and you are the deciding one; any workflow that swaps those two buys today speed with tomorrow debt.</strong>

Tools that actually help

  • GitHub Copilot It works inside the editor, and for an Iranian reader its access story is the most unexpected: GitHub own trade-controls page says a US Treasury licence covers its cloud services for developers resident in Iran, free and paid. We quote that and make no claim about payment. Its free plan runs to two thousand completions a month with automatic model selection only, and paid plans start at ten dollars a month. It also has a feature the risks section needs: suggestions matching public code can be blocked.
  • Claude Code A command line agent that sees the repository, so for work touching several files it is ahead of an ordinary chat. That same power is why this lesson is strict about small diffs. It installs free but does not run without a Claude subscription or an Anthropic Console account, and Iran is not on Anthropic supported-countries list.
  • Cursor An editor with a built-in agent, for someone who wants everything in one window. Its own pricing page shows a free plan with limited requests and a professional plan at twenty dollars a month. We claim nothing about Iran: we found no supported-countries list from them and we have not tested it from Iran ourselves.
  • Claude A separate chat, suited to exactly what this lesson recommends: round one with no code, and a code critique in an empty conversation. Iran is on neither of Anthropic two supported-countries lists; we read that on their own page rather than measuring it.
  • Gemini Good for understanding large, heavily documented libraries, and its study mode asks questions instead of handing over answers; for someone worried about losing their learning, that mode is the useful one. Google own page says the Gemini web app works in more than two hundred and thirty countries and territories, and Iran is not on that list.

Where it backfires

Three real risks in this work, in order of weight.
One: almost-right code. That 66 percent frustration is exactly the risk no linter catches. Code that runs, passes a simple test, and is wrong only in one edge case. The only defence is what the workflow section said: define what the test must prove before the code exists.
Two: security. A model writes code that works, and that is not the same as code that is safe. The common pattern is unvalidated input and a missing access check, and neither of them errors. The OWASP category on security misconfiguration describes this class, and the link is below. Simple rule: wherever code touches user input or access, the model proposes and you decide.
Three: who owns the code. These models were trained on public code and can produce a fragment resembling existing code under a restrictive licence. That GitHub offers a setting to block suggestions matching public code shows the vendor itself takes the concern seriously. For client work, settle this once in the contract and then forget it; not settling it is the thing that comes back years later. How to pay for each of these tools from Iran is in the buying guide.

Sources: Stack Overflow 2025 Developer Survey: AI GitHub and Trade Controls GitHub Copilot plans Cursor pricing Anthropic: supported countries Claude Code: overview and install Google: where the Gemini web app is available OWASP Top 10: security misconfiguration

Where this advice stops

Everything in this lesson is about code you can read and judge yourself. If you do not know the language or the domain you are working in, the advice here will not save you, because all of it depends on a capable reviewer and that reviewer is you. Three things are deliberately left out: tuning a model on a company codebase, building autonomous agents that put code on the main branch with no human, and numeric model comparisons, whose place is the ranking page and which are updated there. And one honesty about the numbers on this page: the Stack Overflow survey sample is self-selected, meaning respondents chose to take part, and Iranian developers are under-represented in it. The numbers show the experience of a large population, not a measurement of model quality.

From our own work

The best example we have of "the code works and its meaning is wrong" is in this site own code, in the very engine that draws the chart at the top of this page. This theme infographic library computed each chart segment colour from that segment position in the list; sound logic when segments are ordered. On one chart in the AI section of this site, the segments were named categories rather than an order, and the largest category, "current version", happened to land first and came out red, while "preview" came out green. Nothing errored, no test failed, the page rendered fine; the chart merely displayed a judgment nobody had written. The fix is exactly what this lesson preaches: an item may state its own colour explicitly, and rgb_dg_item_hue_vars() in inc/diagram-chart.php prefers that explicit colour when it is there. Today you can see in inc/diagram-sections.php that the five version statuses carry fixed hues: retired 0, legacy 0.3, announced 0.55, preview 0.7 and live 1. The cover chart on this very lesson carries fixed hues for the same reason rather than positional ones.

Real follow-up questions

If I write code with AI, who owns it?

Each service terms decide this separately and you have to read those, not a second-hand summary. There is a more practical issue that is not about who owns the output: the output can resemble existing public code which carries its own licence. That is why GitHub has a setting to block suggestions matching public code, and for client work it is better to settle this in the contract.

To start with, which is better: an editor extension or a separate chat?

If you are still learning, the separate chat. Because it forces you to write the problem yourself and that writing is half the learning, and because an in-editor suggestion arrives so fast that you get used to accepting without reading. Once you can close a piece of code and explain it from memory, the editor extension is faster.

The model says it ran the code and it worked. Is that true?

Unless you are using a tool that genuinely executes and shows you the real output, no. An ordinary chat runs nothing, and what it presents as output is its prediction of a likely output. The difference shows up the moment you run it yourself, and that single habit prevents a large share of mistakes.