Skip to content

The Four Most Common Jobs: Exploring Codebases, Fixing Bugs, Refactoring, Writing Tests

📚 Series Navigation: The previous article [15 How to Ask and Give Instructions] taught you "how to speak"—breaking vague requirements into precise instructions. This article takes a different angle: 80% of daily tasks fall into four categories, and for each category, I will give you an instruction template you can copy directly, just fill in the blanks and use it.

Brothers, let's talk about something very practical today.

Think back, what exactly are you doing staring at code every day? To put it plainly, it's just four things back and forth: taking over a project you can't understand, fixing a baffling bug, refactoring a pile of messy code cleanly, and adding tests for a certain function. There are other requirements, of course, but these four categories combined take up the bulk of our daily time.

The previous article discussed general "speaking skills", and this article will land on specific scenarios—each of these four types of jobs has its standard playbook, and the routines are fixed. Using Claude Code for a long time, what finally settles down are four templates, saved in a memo, which you can call up and fill in the blanks when you encounter the corresponding scenario.

Let me put it this way: General skills are internal strength, and these four templates are martial arts moves. You have already practiced your internal strength, today I will give you the moves.

After reading this article, you will get:

  • A directly copiable instruction template for each of the four high-frequency tasks (explore / fix bug / refactor / write test)
  • The key reasoning for "why it's played this way" in each category, rather than rote memorization of templates
  • A quick reference card for the four categories of tasks, save it to call up anytime
  • A complete practical drill you can run along with, giving expected outputs (walking through the fix process with a real bug)

01 First Recognize: Four Kinds of Jobs, Four Tools

Before getting your hands dirty, first distinguish the "personalities" of these four types of jobs. Their requirements for Claude are completely different, if you use the wrong routine, the effect is far worse.

Analogy: Four tools in a toolbox, recognize their respective purposes. A screwdriver is used to turn screws, a wrench is used to turn nuts, it's naturally awkward if you take a wrench to turn a screw. Exploring, fixing bugs, refactoring, and writing tests are four different tools—the key is not "whether you know how to use Claude", but "which tool should you pull out for this job".

Their core difference lies in one dimension: Does this job touch your code?

JobTouch code?What is Claude mainly doingWhat you should watch the most
Explore codebaseNo (read-only)Reads files, explains to youWhether it explains correctly
Fix bugYesLocates root cause + fixesWhether root cause is correct, any regression test
RefactorYes (but behavior unchanged)Equivalent rewriteWhether behavior changed after modifying
Write testsAdds filesGenerates tests + covers edgesWhether edge cases are fully covered

See it? Exploration is zero-risk (it only reads, doesn't write), so you can ask boldly; fixing bugs and refactoring are going under the knife, you have to let it explain clearly before acting; writing tests is somewhere in between, it creates new files without touching your original code, but you have to watch if it's being lazy and only testing "normal cases".

Remember the judgment logic of this table, the following four sections are breaking down each tool to teach you how to hold it.

💡 One-sentence summary: First divide the personalities of the four types of jobs according to "whether they touch code" — exploration is zero-risk and can be asked casually, jobs going under the knife should have it explain first before letting it modify.


02 Exploring an Unfamiliar Codebase: From Large to Small, Ask Three Layers Deep

Let's start with the most frequent scenario: you take over a completely unfamiliar project, the first thing is to figure it out.

How did we do this in the past? Open the folder, stare blankly at dozens of directories, click in to read one by one, and still be confused after looking for two hours. Not anymore — Claude Code treats the current directory as a workspace, it can read through the whole project itself, you just ask.

Analogy: Newly arrived at a company, you wouldn't dive into the source code of a certain module right away, but find a veteran to ask three layers of questions. Layer one "What exactly does our company do as a whole, what does the grand architecture look like"; Layer two "Where is the code responsible for payment"; Layer three "From placing an order to deduction, how does the code flow for an order". From large to small, from surface to line, this is the standard rhythm of exploration.

According to the official documentation's demonstration, these three layers correspond to three types of questions respectively:

text
Give me a high-level overview of this codebase and explain its main architectural patterns
text
Which files contain the code responsible for user authentication? How do these files work together?
text
Trace the login flow, from the frontend all the way down to the database

A safe habit is, always ask the first two layers in Plan Mode—that is, press Shift+Tab twice in a row before starting to switch to planning mode (the first time enters acceptEdits, the second time reaches plan). Why? Because in the exploration stage we only want it to read and talk, we don't want it to get excited and start modifying files. In Plan Mode, it won't touch your source code, no matter how much you ask, it won't change a single line of your code.

For example, taking over a three-thousand-line old Go project, you can do this on the first day: first a "high-level overview" to figure out how many services it's divided into, then "where is the code responsible for X" to locate one by one, and finally pick a core link "trace how this request flows". You can get the hang of it in half a day, whereas it would take two or three days in the past.

Here is the exploration template directly for you, just fill in the keywords for the three layers of questions to use:

text
I just took over this project, help me get up to speed quickly. In three steps:
1. Give me a high-level overview of the architecture, explain the main modules and their responsibilities
2. In which files is the code responsible for [the feature you care about, e.g., "order payment"]
3. Trace the complete execution path of [a core flow, e.g., "from creation to payment of an order"]
Explain it in a way a beginner can understand, don't modify any code yet.

💡 One-sentence summary: There is only one rhythm for exploration — from the grand architecture to specific files and then to execution links, ask three layers deep from large to small, asking the first two layers in Plan Mode is the safest.


03 Fixing Bugs: Paste Error → Find Root Cause → Fix → Add Regression Test

Fixing bugs is another type of high-frequency job, and also the one most prone to overturning.

Why is it easy to overturn? Because the most common mistake beginners make is: pasting an error and throwing a sentence "help me fix this", and then Claude gives you a fix that "makes the error disappear". Note, "error disappearing" does not equal "problem solved"—many times it just covers up the symptoms, the root cause is still buried there, and it will explode again with a different posture next time.

Analogy: Seeing a doctor when your body hurts. You wouldn't say "give me some painkillers" as soon as you walk in the door, you would clearly state "where it hurts, when it started hurting, what actions make it hurt more", let the doctor diagnose the cause first, then write a prescription. Fixing a bug is exactly the same—let it find the root cause first, don't let it rush to "stop the pain".

An iron rule repeatedly emphasized in official best practices, worth sticking on your wall:

Fix it and verify the build passes. Solve the root cause, do not suppress errors.

So the correct process for fixing a bug is four steps, none of which can be missing:

  1. Paste error + reproduction steps: Complete error messages, stack trace, plus "what I did to trigger it"
  2. Let it locate the root cause: Don't let it modify yet, let it explain clearly "why it errored"
  3. Give a fix: If the root cause is correct, then let it act to modify
  4. Add regression test: Add a test that can reproduce this bug, to ensure the same mistake isn't made again in the future

Step four is the one beginners miss the most, but it is precisely the most valuable step. The official wording is brilliant: Let Claude "write a failing test to reproduce the issue, then fix it"—this way, when fixed, the test automatically turns green, equivalent to putting a lock on this bug; if someone accidentally changes it back later, the test will immediately sound an alarm.

Many people have suffered this loss. For example, fixing a date parsing bug, Claude fixed it swiftly at the time, and it was committed seeing the error gone. As a result, two weeks later, another colleague changed that line back during refactoring, and the same bug resurrected on the spot—because no test was left at the beginning, no one knew that line of code couldn't be touched. So fixing any bug should include the fourth step.

Here is the fix bug template directly for you:

text
I encountered a bug.
Error message: [Paste error and stack trace completely]
Reproduction steps: [What I did to trigger it, is it occasional or guaranteed]
Please:
1. First locate the root cause and explain why it errored, don't modify code yet
2. Give me a fix, solve the root cause, don't just suppress the error
3. After fixing, add a regression test that can reproduce this bug, run it once to confirm it passes

💡 One-sentence summary: Four steps to fix a bug — paste error and reproduction steps, find root cause first, then fix, finally add regression test; without the lock of regression testing, the same bug will resurrect sooner or later.


04 Refactoring: Explain Current State → Set Goal → Small Steps → Test Before and After

Refactoring is the highest risk job, because it touches "code that isn't broken".

Fixing a bug at least has a clear standard of "fixed"—the error is gone, the tests are green. Refactoring doesn't; the goal of refactoring is "the code is cleaner, but the behavior cannot change a single bit". Once the behavior changes, you are secretly introducing a bug under the guise of refactoring, this is the trickiest kind of bug, because no one goes to test a piece of code that was "just tidied up".

Analogy: Redecorating a lived-in house, but you can't ask the tenants to move out. You must guarantee water and electricity as usual, people live as usual, you just repaint the walls and straighten the wires. Refactoring is "decorating with tenants"—function (tenants' lives) must be completely unaffected throughout.

So the safe playbook for refactoring is four steps, the core is "lock the behavior with tests":

  1. First let it explain the current state: Figure out exactly what this code is doing now, what hidden behaviors there are
  2. Clarify refactoring goal: Do you want to "split functions", "change to modern syntax", or "remove duplication"? Be specific
  3. Small steps: Officials explicitly suggest "refactor in small, testable increments", don't let it tear down and rewrite all at once
  4. Tests pass before and after modifying: Run tests once before refactoring to save a baseline, run again after modifying, the two results must be identical

Step four is the lifeblood of refactoring. If this code currently has no tests, then the first step of refactoring is not to modify, but to add tests first—first use tests to take a snapshot of the "current behavior", and verify against the snapshot after refactoring; it's considered successful only if the behavior hasn't changed. This is completely consistent with official best practices: Give Claude a way to verify its own work, otherwise "looks right" is the only signal, and a refactoring that looks right is exactly the easiest place to hide landmines.

There is a hard rule worth keeping here: Don't let Claude directly refactor code without test coverage. Imagine a scenario where you want it fast, letting it refactor an untested utility function, and it "optimizes" away a boundary branch—that branch looks like dead code, but actually handles a rare input. You only find out when it explodes in production. Always add tests first before refactoring, it's a bit slower, but you'll never overturn again.

Here is the refactor template directly for you:

text
I want to refactor [file / function name].
Refactoring goal: [Be specific, e.g., "split into smaller functions", "change to modern syntax", "eliminate duplication"]
Requirements:
1. First explain the current behavior of this code, including edge cases that are easy to miss
2. If it doesn't have tests yet, first add tests that cover the existing behavior
3. Refactor in small steps, keeping the external behavior completely unchanged
4. Run tests both before and after refactoring to confirm the results are identical

💡 One-sentence summary: The lifeblood of refactoring is "behavior cannot change" — first explain current state, then set goal, take small steps, use tests that pass both before and after modification to lock the behavior; if there are no tests, add tests first before acting.


05 Writing Tests: The Key is Forcing It to Cover Edge Cases

The last category: Adding tests to code.

Writing tests is actually very smooth for Claude—it will flip through your existing test files and write following the framework and assertion style you are already using, the style aligns automatically, you don't have to teach it. But there is a pitfall you must know: if you don't specifically instruct it, it defaults to only testing "normal cases".

What does it mean to only test normal cases? For example, a division function, it tests "6 divided by 2 equals 3"—it's correct, but what about dividing by 0? What if a negative number is passed? What if null is passed? These "edge cases" are where bugs really happen, and they are where tests should cover the most.

Analogy: Hiring someone to do QA for a product, you can't just let them try "normal operations". The QA that really holds value is trying those "crazy" inputs—empty, extremely long, negative, gibberish. It's always the boundaries that cause problems, not the normal paths. The core of writing tests is to force Claude to test these boundaries.

So the key to the test writing template is just one sentence: explicitly require it to cover edge cases. The good prompt given in official best practices looks like this—clearly stating "which function to test, what scenarios to test, whether to mock":

Write tests for foo.py, covering the edge case where the user has logged out. Avoid mocks.

Comparing the two ways of asking, the gap is obvious at a glance:

❌ Vague Question✅ Precise Question
"Write tests for this function""Write tests for the divide function, focusing on edge cases like division by zero, negative numbers, and non-numeric inputs"
It only tests the happy path, coverage rate is falsely highIt tests all the places that could actually blow up

There's also a small trick: use @ to point the target file directly to it (write @src/utils/math.py), it will read the whole file first before acting, much more precise than your text description "the divide function in that math file". The usage of @ references was covered in previous chapters, and it comes in handy right here.

Here is the write test template directly for you:

text
Write tests for [function name] in @[file path].
Requirements:
1. Follow the project's existing testing framework and assertion style
2. Focus on covering edge cases: [List what you can think of, e.g., "empty input, zero, negative numbers, extremely large values, incorrect types"]
3. Also help me think of any other edge cases I haven't listed, and test them as well
4. Run them once after writing, fix any failures until they pass

Point 3 is an extra trick—actively let it help you plug the gaps. The official documentation also mentions that Claude can analyze code paths and find boundary cases you might have missed. You should basically include this sentence when writing tests, it can often dig out input combinations you never even thought of, which is much more comprehensive than you listing them alone.

💡 One-sentence summary: Don't just say "write tests" when writing tests — explicitly force it to cover edge cases (null values, zero, negative numbers, type errors), and then let it help you catch edge cases you missed; the happy path is actually the least important.


06 Hands-on: Take a Real Bug and Walk Through the Fix Process

Just reading templates doesn't count as knowing, you have to run through it. Below we use the "fix bug" category for a practical drill—it has the most complete four steps, if you run through it, you will naturally know how to apply the other three categories. A real bug is specially planted here for you to fix.

Step 1: Build a toy project with a bug (Mac / Linux)

bash
mkdir bug-demo
cd bug-demo
echo 'def average(numbers):
    return sum(numbers) / len(numbers)' > calc.py

Windows users: type mkdir and cd as usual, create calc.py with Notepad, and paste the two lines of Python above into it.

This average function calculates the mean, looking fine—but if you pass in an empty list, it will crash dividing by 0. This is the bug we are going to fix.

Expectation: There is a calc.py in the bug-demo folder, the content is the two lines of the average function.

Step 2: Start Claude Code

bash
claude

Expectation: The welcome screen appears, with an input box at the bottom.

Step 3: Apply the fix bug template, paste the error and let it fix

Type in the input box (this is what the template from section 03 looks like filled in):

text
I encountered a bug.
Error message: ZeroDivisionError: division by zero when calling average([])
Reproduction steps: Pass an empty list to the average function in calc.py and it is guaranteed to happen
Please:
1. First locate the root cause and explain why it errored, don't modify code yet
2. Give me a fix, solve the root cause, don't just suppress the error
3. After fixing, add a regression test that can reproduce this bug, run it once to confirm it passes

Expectation: Claude will first tell you the root cause—when it's an empty list, len(numbers) is 0, dividing by 0 crashes; then give a diff (like returning 0 for an empty list or throwing a clearer exception), pause and wait for your approval; after approval it will also create a new test file (like test_calc.py), which contains a test case specifically for empty lists.

Step 4: Approve changes, watch it run tests

After understanding the diff, select "Agree / Yes". Claude will then run the test it just wrote.

Expectation: You can see the test results in the terminal, similar to:

text
test_calc.py::test_average_empty_list PASSED
test_calc.py::test_average_normal PASSED

Seeing tests all green = this bug has been fixed and locked—if anyone changes this line back in the future, the test will immediately turn red and sound an alarm.

Step 5: Exit, confirm files have really changed

Exit Claude (type exit or press Ctrl+D), look back at the terminal:

bash
cat calc.py

(Windows PowerShell uses type calc.py)

Expectation: The processing logic for an empty list has been added to the average function in calc.py, and a test file has been added to the directory. It matches the diff you approved = the full bug fix process ran successfully, congratulations!

⚠️ Note: If Claude didn't actively write tests in Step 3, it's highly likely you deleted point 3 from your template. Don't skimp on that sentence—the lock of regression testing is exactly the dividing line between novices and veterans.

💡 One-sentence summary: Run through the full bug fix process yourself — plant a real bug, apply the template to let it find the root cause first then fix, watch it add tests and run green, if you run through this category, you can draw a tiger using a cat as a model for the other three categories.

Quick Reference Card for Four Common Workflows


07 Summary

This article broke down 80% of your daily jobs into four categories, giving a fixed playbook and a copiable template for each category:

JobCore of One-Sentence TemplateWhat to Watch the Most
Explore codebase"Grand architecture → where is code for X → trace some flow"Ask three layers from large to small, first two in Plan Mode
Fix bug"Paste error & repro → find root cause first → fix → add regression test"Solve root cause not symptoms, don't skimp regression test
Refactor"Explain state → set goal → small steps → test before & after"Behavior can't change, if no tests add tests first
Write tests"Write tests, focus on null/zero/negative/type errors"Explicitly force edge cases, let it plug gaps

You should now be able to: Take any high-frequency task and no longer stare blankly at the cursor—directly call up the corresponding template to fill in the blanks, knowing what Claude should be asked to do first at each step, and what you should keep an eye on. These four templates are the scaffolding for the vast majority of your work from now on; once familiar, you will find that even the most complex tasks are nothing more than combinations and concatenations of these four categories.

These four templates stand the test of being called every day—no matter how fancy the task, it always circles back to these four playbooks in the end.


The next article is 17 "Images and Multimodal"—everything before was "typing instructions", but some things can't be explained clearly in words: an error screenshot, a design mockup, a database schema diagram. The next article will teach you how to feed images directly to Claude, letting it work by looking at the pictures. Think about it: if you could just throw that headache-inducing error screenshot directly to it, wouldn't it save a lot of trouble?