HomeArticle

The way Karpathy, the legend, uses Claude, it turns out to be like this?

机器之心2026-06-27 15:18
With it, Claude finally stops going against me.

Going to work is really toxic.

Even Andrej Karpathy, a great figure in the AI field, has become a workhorse after joining Anthropic and has no time to contribute on GitHub.

Since officially joining Anthropic on May 19th this year, we've seen Andrej Karpathy's activity in the open - source community drop significantly, and he's also posting less on the X platform recently.

He had an argument with netizens on X these days, complaining that the recommendation algorithm drives traffic through conflicts, which worsens the community atmosphere. Elon Musk also admitted: Indeed, we need to make a thorough improvement.

However, as a person who can't stay idle, Andrej Karpathy's love for "making tutorials" is consistent, whether it's active or passive.

Recently, someone said, "I have a friend who got the CLAUDE.md file that Andrej Karpathy actually uses." It is said that it can completely change the way you use Claude.

So, is there something new for everyone to learn?

A "CLAUDE.md Used by Karpathy"

Is Circulating in the Community

CLAUDE.md is a project - level instruction document specifically written for Claude AI.

With the popularity of AI programming assistants (especially Anthropic's Claude Code command - line tool and various editors integrated with Claude), developers need a standardized way to tell AI: "In this project, what rules should you follow?"

Place this file in the root directory of the project. When you use Claude for auxiliary programming in this project, it will automatically read the content inside.

Let's see what this so - called "CLAUDE.md file actually used by Andrej Karpathy" is all about?

Link: https://drive.google.com/file/d/1mtJKbu - QRk62WTWkyc0M0pGXbKzisA5W/view

This file exists because large language models make some predictable mistakes when writing code. These mistakes don't occur randomly. They are always the same kind of problems, appearing over and over again. I've seen them too many times, so I wrote them down.

These are not suggestions. These are rules. Follow them, and the code you produce won't need to be rewritten. Ignore them, and the code you produce may seem great, but it will cause problems in the production environment.

Read Before Writing

The biggest reason why large language models write bad code is that they don't read the existing codebase before writing new code. When you see a task, you start matching a certain pattern in the training data and then directly generate code. This is almost always wrong.

Before writing any code:

Read the file you're about to modify. Not just skim, but read it carefully.

Check how similar functions are implemented in the project. If there's a fixed pattern for API routes, follow that pattern. If there are utility functions that can fulfill part of your requirements, use them. Check the imports at the top of the file; they'll tell you which libraries the project actually uses. If the project uses fetch everywhere, don't introduce axios. If the project uses native methods, don't introduce lodash.

Check the test files. Test files will tell you the real expected behavior, not what you subjectively think the expected behavior is.

The failure mode here is obvious: you generate a piece of "correct" code, but it doesn't fit in with the existing codebase at all. It can run, but it looks like it was written by someone else because it actually was written by another entity. So, human developers either have to rewrite it to fit the project style or always tolerate the inconsistency within the codebase. Both results are bad.

If you're not sure how things are usually done in this project, just say so. "I didn't see an existing pattern for X in the codebase. Should I follow the approach of Y or use another way?" This is always better than guessing.

Think Clearly Before Writing Code

Don't start writing code until you figure out what you're actually going to do. It sounds obvious, but this is the most common failure mode.

In practice, it means:

State your assumptions clearly. If the user says "add authentication", it could mean session cookie, JWT, OAuth, basic auth, or five other things. Don't silently choose one for the user. You can say, "I assume you want JWT - based authentication with a refresh token stored in an httpOnly cookie. If you want another solution, please let me know." If you guess wrong, you only lose 10 seconds; if you guess wrong silently, you may lose an hour.

State the trade - offs clearly. Almost every implementation choice has a cost. If you want to add caching, say, "This will trade memory for speed and introduce the problem of cache invalidation." The user may say, "Actually, I don't want this complexity." It's better to know this before you write 200 lines of code.

If there are multiple solutions, briefly list them. Don't list five, two, at most three, and give a recommendation. For example, "There are two ways to do this. Solution A is simpler but can't handle edge case X. Solution B can cover all cases but requires an additional dependency on Z. Unless you expect X to actually happen, I recommend using A."

If something confuses you, stop. Don't use seemingly reasonable code to fill the gap in your understanding. Code generated when the requirements aren't clear often passes a rough review but fails at critical moments. State your confusion and then ask for clarification.

Keep It Simple

Write the least amount of code that can solve the problem. Here, it's not about the least amount of code that theoretically might solve the problem, but the least amount of code that can truly solve this specific problem.

The impulse to over - design is strong, and you need to resist it. Over - design in actual work usually looks like this:

Premature abstraction. You only need to send one type of email, but you write an EmailService class with a strategy pattern, supporting multiple service providers, template engines, and retry strategies. All the user wants is sendWelcomeEmail (user). Writing this function is enough. If they need more capabilities in the future, they'll ask for it.

Imaginary error handling. You wrap everything in try/catch to handle errors that will never happen. You're validating input from your own code, and it's already been validated upstream. You add null checks to values that will never be null. Every line of error handling is a line of code that someone else has to read and understand later. Only handle errors that are really likely to occur.

Unnecessary configurability. You make the batch size a parameter, the number of retries a configuration, and add environment variables for things that will never change. Configuration isn't free. Every configuration item is a decision that someone else has to make and a value that someone else has to set correctly. Write them directly without a real reason.

Flexibility without vitality. An interface with only one implementation. An abstract base class with only one subclass. A generic parameter that will always be instantiated with only one type. These things all have costs: cognitive burden, indirect layers, and more files to jump to; they have no benefits until a second implementation actually appears.

A test to determine if it's simple: show your code to someone who isn't familiar with this project. If they have to ask, "Why is it abstracted like this here?", and your answer is "Just in case we need it in the future...", then it's over - design. "Just in case we need it in the future" isn't a requirement; it's just a guess about the future, and guesses about the future are usually wrong.

Surgical Modifications

When modifying existing code, the diff should be as small as possible. Every line you change may introduce a bug, needs to be reviewed by someone, and will always remain in the git blame.

The rules are as follows:

Don't touch things you're not asked to touch. If you're fixing a bug in function A and find that the variable name in function B is strange, ignore it. If there's a spelling mistake in the comment of function C, ignore it. If the import order doesn't match your preference, ignore it. Your task is to fix the bug in function A.

Match the existing style. If the file uses single quotes, you use single quotes too. If the file uses snake_case, you use snake_case too. If the file doesn't have semicolons, don't add them. If the file uses var, yes, even in 2025, use var in the new code unless the user explicitly asks you to modernize it. Consistency within the file is more important than your personal preference.

Only clean up problems you caused, don't clean up others' problems by the way. If your change makes an import no longer used, delete it. If your change makes a variable no longer used, delete it. If your change makes a function no longer used, delete it. But the premise is that this problem is caused by your change. Existing dead code isn't your problem unless someone asks you to clean it up.

Don't re - format. Don't run prettier on a file that isn't originally formatted by prettier. Don't change 4 - space indentation to 2 - space. Don't re - sort imports that weren't originally sorted alphabetically. Re - formatting will create a huge diff, mask the real changes, and make code review painful.

Test method: look at your diff. Can you find a reason directly related to the task requirement for each line of change? If there's any line just because "I thought I could...", roll it back.

Verification

The difference between "the code works" and "you think the code works" is called testing. You should be vigilant about this difference.

Write tests first when fixing bugs. Before fixing anything, write a test that can reproduce the bug. Run it and see it fail. Then fix the bug. Run the test again and see it pass. This isn't an option, nor is it a TDD dogma. This is the only way to prove that you've really fixed the problem, not just made the symptoms disappear.

Run existing tests both before and after making changes. If the tests pass before your change and fail after, it means you've broken something. This is obvious. What's not so obvious is that if the tests were already failing before your change, say so. Don't silently ignore the existing failures and let your change take the blame.

Don't write tests just for the sake of writing tests. Testing whether a constructor sets properties has no value. Testing whether your validation logic really rejects incorrect input has value. Test behavior, not implementation. Test interesting scenarios, not trivial ones.

If you can't write tests, explain the reason. Sometimes the architecture itself makes it difficult to write tests. This is useful information. "I can't easily test here because database calls and business logic are too tightly coupled." This may indicate that some structures need to be adjusted. Don't just skip the tests and hope for the best.

Goal - Driven Execution

Before starting to write code for each task, there should be clear success criteria. If the criteria are vague, make them specific. If you can't make them specific, ask.

Turn a vague task into a verifiable task:

"Add validation" becomes: "Reject input when the email is missing or invalid, return 400, and give a message explaining the reason for the error; add tests for these two cases."

"Fix a bug" becomes: "Write a test to reproduce the behavior in the report, make the test pass, and confirm that the existing tests still pass."

"Improve performance" becomes: "First do profiling to find the bottleneck, fix this specific problem, and then measure again."

For any task that has more than one step, explain the plan before execution:

Plan:

Add a new database field through migration

Update the model to add the new field

Modify the API endpoint to accept and return the field

Add validation for the field

Write tests for the new behavior

Run the complete test suite to check for regression issues

This has two functions: one is to allow the user to find problems in the solution before you waste time implementing it; the other is to force you to really think through the steps, rather than just diving in and thinking as you go.

Debugging

When something doesn't work, don't guess; investigate.

Read the error message. Read it all, including the stack trace. LLM has a very bad habit: as soon as it sees an error, it immediately generates a "fix" based on the error type without really reading what the error is saying. A TypeError can have a hundred reasons. The error message and stack trace will tell you which one it is.

Reproduce the problem first. Before changing anything, make sure you can reproduce the problem. If you can't reproduce it, you can't verify the fix. "I think this should fix it" isn't debugging; it's gambling.

Change only one thing at a time. If you change three places at the same time and the bug disappears, you don't know which place fixed it, and you don't know if the other two places introduced new bugs. Change one place, test; then change another place, test again.

Don't add a workaround before understanding the root cause. If a value is unexpectedly null, don't just add a null check and leave. First, figure out why it's null. A null check may prevent a crash, but the underlying bug still exists and will pop up in another form later.

If you're stuck, say so. "I've tried X and Y, and neither solved the problem. The current phenomenon I'm seeing is like this. I suspect the problem may be in Z, but I'm not sure." This is much more useful than silently trying randomly 20 times.

Dependencies

Don't add dependencies without thinking.

Every dependency you add is a piece of code you can't control, and it will permanently become part of the project. It needs to be maintained, updated, and security - audited, and everyone on the team needs to understand it. Its cost is almost always higher than it seems.

Before adding a package, ask:

Can you use what's already in the project? If the project already has axios, don't add node - fetch. If the project uses date - fns, don't add moment.

Can you use the standard library? You don't need to introduce lodash for Array.prototype.map. If crypto.randomUUID () already exists, you don't need uuid.

Is this dependency really still being