Working with AI Effectively

Advanced · 16 min read · ▶ live playground · ✦ checkpoint

AI is not a mind reader. The quality of its answer usually starts with the quality of your request, so this lesson gives you prompt patterns for code, explanations, refactors, debugging, tests, and docs while keeping your judgment in charge.

A prompt is the request you give an AI assistant. Treat it like a work order clipped to a task: the clearer the goal, materials, limits, and definition of done, the less guessing the assistant has to do.

Prompts need shape

A weak prompt asks for a result without enough boundaries:

Write a Python function for scores.

That leaves too many blanks. Which scores? What input shape? What output? What should happen at the edges?

A stronger prompt gives the assistant a small job with useful constraints, rules that narrow what a good answer is allowed to do:

Goal: Write a Python function named badge_for_score(score: int) -> str.
Context: This is for a beginner console game.
Rules:
- 90 and above returns "gold"
- 70 through 89 returns "silver"
- anything below 70 returns "practice"
- do not print; return a string
Return:
- the function only
- then list 3 edge cases I should test

Notice the pieces:

  • Goal: the job in one sentence.
  • Context: the surrounding situation, using the word from AI as Your Pair Programmer.
  • Constraints: the guardrails.
  • Output format: what you want back.
  • Checks: how you will judge the result.

You do not need a giant prompt. You need the right facts. A prompt is not a magic spell; it is a work order.

Generate code in small slices

Generation means asking AI to draft new code. Generation is useful, but the size of the requested code controls how easy it is to review.

Ask for one function, one test file, one refactor, or one command shape. A small answer can be checked against the rules. A whole app may look impressive while hiding a dozen choices you never agreed to.

For code generation, use this pattern:

Task: Draft one Python function.
Inputs: a list of player dictionaries with "name" and "score" keys.
Output: a list of player names sorted from highest score to lowest.
Constraints:
- do not mutate the original list
- use clear variable names
- include type hints
Before code: state any assumptions.
After code: list the cases I should test.

The assistant may still produce a bug. That is normal. Treat the result as a candidate, not a verdict.

def badge_for_score(score: int) -> str:
    if score > 90:
        return "gold"
    if score > 70:
        return "silver"
    return "practice"

This candidate looks clean, but it does not match the earlier rule: 90 should earn "gold", and 70 should earn "silver". Your review caught an off-by-one bug before the code reached your project.

A useful follow-up prompt is specific:

Compare this function against the rules above.
Find boundary mistakes only.
Explain each mismatch before suggesting code.

That keeps the assistant focused on evidence instead of rewriting everything.

Ask for explanation before refactoring

Explanation means asking AI to describe what code does. Refactoring means changing code structure without changing behavior. Keep those separate when you are learning.

If code is unfamiliar, ask for a trace first:

Explain this function for a Python learner.
Trace one example input step by step.
Name any side effects, such as mutation or printing.
Do not rewrite the code yet.

That last sentence matters. If you ask for explanation and refactoring at the same time, the assistant may change the code before you understand what it did. First understand the recipe card. Then decide whether to rewrite it.

For refactoring, give behavior limits:

Refactor this function for readability only.
Keep the same inputs, return values, and exceptions.
Do not add new packages.
After the code, summarize the behavior-preserving changes.

A behavior-preserving change is an edit that changes the shape of the code but not what callers observe. Renaming a confusing local variable can be behavior-preserving. Changing whether a function returns None or a string is not.

Your best review question after a refactor is: "What behavior could have changed by accident?" That question catches more than "does this look nicer?"

Debug with evidence

Debugging with AI works best when it follows the hypothesis loop from Debugging Like a Pro: gather evidence, form a theory, test it, repeat.

Weak debugging prompt:

My program broke. Fix it.

Better debugging prompt:

Observed: entering score 90 prints "practice".
Expected: score 90 should print "gold".
Relevant code:
<paste the smallest function or branch>
Recent change: I changed the comparison operators.
What I tried: I printed score and confirmed it is the int 90.
Ask: Give me 2 likely causes and the smallest check for each.

The useful ingredients are observed behavior, what actually happened; expected behavior, what should have happened; and a minimal reproduction, the smallest code or input that still shows the problem.

Do not ask AI to patch first when you do not understand the bug. Ask it to propose causes and checks. Then you run the check yourself.

Generate tests and documentation

Tests and documentation are great AI jobs because they force the assistant to look at behavior from the outside.

For tests:

Given this function and these rules, propose pytest test cases.
Include:
- normal cases
- boundary cases
- one regression case for the bug where score 90 returned "practice"
Return test names and expected results first.
Do not rewrite the function.

A test case is one checked example of behavior: input, action, expected result. AI can suggest cases you forgot, but you still judge whether the expected result is correct. A generated test that expects the wrong behavior preserves the bug.

For documentation:

Write a docstring for this function.
Audience: another Python developer using the function.
Include parameters, return value, raised exceptions if any, and one short example.
Do not describe implementation details unless callers need them.

Good documentation answers the caller's questions. It should not narrate every line. You learned that in Documentation and Readability: comments and docstrings should explain what matters to the reader, not decorate obvious code.

The pattern is the same across code, tests, docs, and debugging: give a small task, useful context, clear constraints, and a way to check the answer. The next lesson raises the stakes: how to verify AI-generated work responsibly, protect secrets, respect licenses, avoid dependence, and preview calling AI APIs from your own programs.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion