Learn Java

Lesson 68Phase 8: Tools of the trade

Working with AI coding assistants

Using AI tools to learn faster without handing over your understanding: asking well, checking what comes back, and testing it.

By Updated 35 min

  • AI assistant
  • prompting
  • verification
  • tests
  • code review
  • hallucination

AI coding assistants can write a method from a sentence, explain an error message, and suggest the next line before you type it. Used well, they make you faster and help you learn. Used badly, they hand you code that looks right, compiles, and is wrong in a way you cannot see, because you never learned what right looks like. This lesson is about the first way: asking clearly, checking every answer, and keeping the understanding for yourself.

By the end of this lesson
  • Say what an assistant is good at, where it goes wrong, and why.
  • Ask a question with the context, goal and constraints that make a good answer likely.
  • Check generated code by reading it, running it and testing its edge cases.
  • Recognise an invented method from the compiler's error, and confirm a method in the documentation.
  • Keep secrets and private data out of a prompt, and watch generated code for security and licensing problems.
From before
Two skills from earlier carry this lesson. Testing with JUnit is how you prove code does what you meant, whoever wrote it. And reading errors plus the documentation habit from annotations and tooling are how you find out whether a method you were shown really exists.

What an assistant is, and what it is not#

A coding assistant is built on a language model: a program trained on a huge amount of text and code to predict what is likely to come next. Ask for “a method that reverses a string” and it produces the kind of code that usually follows such a request. That is why it is so good at common tasks, and also why it can be confidently wrong. It does not run your code, it does not know your project unless you show it, and it has no sense of which of its sentences are true. A wrong answer reads exactly as smoothly as a right one.

Often good at
Often wrong about
Explaining an error message or a piece of code in plain words.
Edge cases: empty input, zero, null, very large numbers.
Writing routine code you could write yourself, such as a record or a loop.
Methods and options that do not exist, or existed in an older version.
Showing two or three ways to do something, to compare.
Your project: its classes, its rules, what was already decided.
Suggesting test cases you had not thought of.
Whether its own answer is correct. It cannot check, so it does not.

Learn with it, not instead of it#

The risk for a learner is not that the assistant is wrong. It is that it is right often enough that you stop thinking. If it writes every loop for you, you never get faster at loops, and you cannot tell a correct loop from a broken one. A few habits keep the learning on your side:

  • Try first. Write your own attempt, even a bad one, before you ask. Then ask the assistant to review it, and you learn from the difference.
  • Ask why, not only what. “Why does this throw a NullPointerException?” teaches you more than “fix this”.
  • Make it quiz you. “Give me three small exercises on HashMap, without answers” turns it into a practice partner.
  • Never paste code you could not explain line by line. If you cannot explain it, you cannot fix it when it breaks.

Asking well#

An assistant only knows what you tell it. The same question gets a much better answer when it says which Java you use, what you already have, exactly what you want, and what you do not want:

A vague request
A request with context
“average method java”
“I am using Java 25. Write a static method double average(int[] numbers).”
No example, so the assistant guesses what you mean.
“average(new int[]{2, 3}) should return 2.5.”
No rules for the awkward cases.
“An empty array should throw an IllegalArgumentException.”
You get a block of code and nothing else.
“Explain each line, and suggest JUnit tests for the edge cases.”

Even with a good request, treat the answer as a first draft from a quick colleague who never tests anything. Which brings us to checking.

Checking what comes back#

Here is the kind of answer a vague request might get, for “the average of some numbers”. It looks fine, it compiles, and it works on the first example anyone tries. The tests below it say what the method should do, including the cases nobody tries by hand. Run them.

StatsTest.java
1import static org.junit.jupiter.api.Assertions.*;
2import org.junit.jupiter.api.Test;
3
4class Stats {
5 // A suggested method for "the average of the numbers".
6 static double average(int[] numbers) {
7 int sum = 0;
8 for (int n : numbers) {
9 sum += n;
10 }
11 return sum / numbers.length;
12 }
13}
14
15class StatsTest {
16 @Test
17 void averageOfThree() {
18 assertEquals(2.0, Stats.average(new int[]{1, 2, 3}));
19 }
20
21 @Test
22 void averageWithAHalf() {
23 assertEquals(2.5, Stats.average(new int[]{2, 3}));
24 }
25
26 @Test
27 void noNumbersIsAnError() {
28 assertThrows(IllegalArgumentException.class, () -> Stats.average(new int[]{}));
29 }
30}
Prints
›StatsTest
› ✔ averageOfThree()
› ✘ averageWithAHalf(): expected: <2.5> but was: <2.0>
› ✘ noNumbersIsAnError(): Unexpected exception type thrown, expected: <java.lang.IllegalArgumentException> but was: <java.lang.ArithmeticException>
›3 tests: 1 passed, 2 failed

One test passes and two fail. averageWithAHalf fails with expected: <2.5> but was: <2.0>: sum and numbers.length are both int, so / is whole-number division, which throws away the half, as in operators and casting. The double return type does not help, because the division has already happened. noNumbersIsAnError fails because an empty array divides by zero, and the method throws an ArithmeticException instead of saying clearly what was wrong. Both are classic edge cases. Neither shows up when you try {1, 2, 3} and see 2.0.

Key idea

Code that compiles and works on one example is a guess. Code that passes tests for the edge cases is checked. Write the tests yourself, because they are where your understanding of the problem lives.

ExerciseStatsTest.java
Fix average so all three tests pass. Throw an IllegalArgumentException for an empty array, and make the division happen in double. Leave the tests alone.

Methods that do not exist#

Assistants sometimes use a method that sounds right but is not there, often borrowed from another language or library. Lists in Java have no sum(). The compiler catches this one for you:

Terminal
read along
1$ javac Main.java
2Main.java:6: error: cannot find symbol
3 int total = prices.sum();
4 ^
5 symbol: method sum()
6 location: variable prices of type List<Integer>
71 error

“Cannot find symbol” on a method you were given is the signal to open the API documentation for the type, here List, and see what it really offers. (The working version sums a list with a loop, or with prices.stream().mapToInt(Integer::intValue).sum(), as in streams.) The more dangerous inventions compile: an option that is quietly ignored, a method from an older version that now behaves differently, or a setting that does nothing. Only reading the documentation and testing catch those.

Secrets, security and licences#

What you type into an assistant usually leaves your computer and is sent to someone else's servers. That makes three rules worth keeping, even for a practice project.

  • Never paste secrets or private data. No passwords, API keys, tokens or connection strings, and no real customer names, emails or records. Replace them with made-up values before you ask. If a secret does get pasted, treat it as leaked and change it.
  • Check generated code for security holes. Assistants reproduce common patterns, including common mistakes: SQL built by joining strings with user input (open to SQL injection, as creating and executing queries shows in the next phase), passwords stored as plain text, or error messages that reveal too much. Read generated code that touches input, files, databases or the network as carefully as code from a stranger.
  • Follow your project's rules on licences. Generated code can closely resemble code that someone published under a licence. Many companies have a policy on which tools may be used and how; at school or at work, ask before you paste generated code into a shared project.
A routine that works
Ask with context. Read the answer and make sure you could explain every line. Run it. Write tests for the edge cases, or ask for them and check them. Look up any method you have not seen in the documentation. Only then commit it, with your name on it and your understanding behind it.
Quick check
An assistant's method compiles and gives the right answer for the one example you tried. What should you do next?
Quick check
Which of these is fine to paste into an assistant while asking for help?

Try it

Answer each one in your head or on paper first, then open the answer.

1
Rewrite the request “make a method to check passwords” so that an assistant has what it needs to give a good answer.
2
Why did return sum / numbers.length; return 2.0 for {2, 3} even though the method returns a double?
3
An assistant tells you to call scanner.nextString() to read a word. How do you find out whether that is right, and what would you use instead?

Practice

Work through these on your own. Each one builds on this page.

1
Pick an exercise from an earlier lesson that you have already solved. Ask an assistant to solve it, then compare its answer with yours line by line. Write down one thing it did better, one thing it did worse or wrong, and the test that would catch the problem.
2
Write three JUnit tests for a method int countVowels(String text) before you write the method: one ordinary case, one empty string, and one with capital letters. Then ask an assistant for the method, run your tests against its answer, and fix it if it fails.
3
Ask an assistant to explain a stack trace from one of your own programs. Check its explanation against what you learned in reading errors: does it point at the right line, and does it explain the cause or only the symptom?

Further reading#

Spotted a mistake, or a part that did not make sense? Report it by email