How to Stop AI-Generated Code From Becoming a Team's Maintenance Nightmare
Let's briefly sort out the usage scenarios of rules in Claude Code.
Here, I'll approach it from the perspective of my daily team development collaboration, because I personally feel that these rules are generally used more in team scenarios.
For example, I am currently a team lead in a project group at my company, and my team, including myself, has four backend developers. The project is developed collaboratively by the team, and my responsibilities include code review.
I've mentioned before that the company has started promoting AI-assisted development. For instance, our project team requires us to uniformly use Claude Code to write code.
I initially thought AI could help everyone improve project efficiency and would be much better than traditional team collaboration methods. However, after using it for a while, I still kept finding a bunch of problems during code reviews:
For example, for the same function to query a user, Developer A submitted code like this:
function getUserById(id: string) {
const user = await db.query('SELECT * FROM users WHERE id = ?', [id])
console.log('User fetched:', user)
return user
}
While Developer B submitted code written like this:
async function get_user_by_id(userId: string) {
try {
const user = await db.findOne({ id: userId })
logger.info('Fetched user', { userId })
return user
} catch (error) {
logger.error('Failed to fetch user', { error, userId })
throw new ServiceError('User query failed', { cause: error })
}
}
For the same project method, the code written by two people is completely different:
For example, the method name: A uses camelCase, while B uses snake_case style. For example, logging: A uses console.log, while B uses logger for output. For example, A didn't handle errors, but B used a complete try-catch. For example, A's query uses
SELECT *, while B explicitly specifies fields to query.
Every time I review, I have to point out these issues and then ask the corresponding developer to fix and recommit.
And the reality is, code written by AI, relying entirely on manual code review to check standards, is simply impossible to check thoroughly. There are always omissions, after all, I can't remember every single detailed code execution standard.
Sometimes I have to review over a dozen PRs a day, each PR modifying dozens of files. I have to check one by one for console.log usage, error handling, SELECT * usage. My eyes get blurry after looking at so many, and it's easy to miss a few.
[s]Here I'm also speechless, because the company regularly scans code repositories. If the code is too poorly written or has vulnerabilities, we'll be held accountable.[/s]
And sometimes the ones I miss happen to be critical issues. For example, a colleague didn't handle exceptions, and the code crashed directly when an exception occurred after going live. It wasn't until the post-mortem review that I realized I hadn't caught this problem during code review.
What's more troublesome is that as AI large models become more powerful, many colleagues have gradually started letting AI fully take over coding. AI writes the code, local tests pass, and they commit it directly. They might not even look at the quality of the code AI wrote.
So without clear standard constraints, the code quality produced by everyone using AI will be uneven.
Although output has gone up, quality has gone down, which actually increases the maintenance burden later.
In the old days of manual programming, you could have each developer review their own code and memorize the project development standards. But now that AI is writing code, without a clear set of standards that AI can understand and follow, code quality simply cannot be guaranteed.
Especially when newcomers join the project later, they don't know what standards the team has. I sent them several document links, telling them to read them first. As a result, they read for two days and still hadn't finished, saying there were too many documents and they didn't know which were the key points. Later, I had no choice but to let them start writing code directly with Claude Code, and the resulting code was even messier.
Although our project has a continuously maintained CLAUDE.md file, our initial approach was to constantly review problems encountered and then update the CLAUDE.md file content. But after writing it, we found that this path was actually a dead end.
Limitations of CLAUDE.md: Write Simply or in Detail?
CLAUDE.md can indeed be committed to git and shared with the team. But when writing it, you encounter a dilemma:
If written too simply, AI doesn't know what to do
For example, we might constrain the AI in CLAUDE.md like this:
# CLAUDE.md
- Don't use console.log, uniformly use logger
- Add tests when modifying interfaces
Rules without specific actions like this, AI might know the requirement during execution but not know exactly how to do it:
- How to use logger? Is it
logger.info()orlogger.log()? How should parameters be passed?- How to add tests? Unit tests or integration tests? Where to put test files? What's the naming convention?
The final result of this design is that everyone's understanding is different, and the code written is still not uniform.
If written in too much detail, you hit line limits
If we want to put all detailed rules into CLAUDE.md, we'll quickly find a problem:
Because we all know the official recommendation is generally to keep CLAUDE.md concise, aiming to control it within 200 lines. This isn't to say it can't be used if it exceeds 200 lines, but because the content of CLAUDE.md is loaded into the context. The longer it is, the more context window it occupies.
If CLAUDE.md is 500 lines, 1000 lines, it will lead to some problems:
- The context window gets filled up, leaving less space for actual conversation and code.
- AI's attention is scattered; with too many rules, AI is actually more likely to ignore the later rules.
- Loading becomes slower, resulting in loading such a long file every time Claude Code starts.
So generally, I don't recommend stuffing all team standards into CLAUDE.md. CLAUDE.md is more suitable for concise rules, precise positioning, clearly stating the most critical project facts and entry information.
So what should the positioning of CLAUDE.md be?
Generally, CLAUDE.md is more suitable for writing project fact rules, and can be kept within 200 lines as officially recommended, such as these types of rules:
- What is the tech stack?
- What are the common commands?
- What is the directory structure?
- What are the basic constraints?
So more detailed bottom-line team standards should not be written here; they should have a dedicated place for management.
This is the reason for the existence of the rules/ directory.
So our ultimate goal is how to establish our team development standards through CLAUDE.md + rules/. CLAUDE.md is mainly responsible for project facts, such as tech stack, common commands, directory structure, etc. And rules/ is mainly responsible for detailed rules, such as coding standards, security bottom lines, testing requirements, etc. This way, CLAUDE.md remains concise, and rules/ can be written in detail. Separation of duties, each performing its own role.
The benefits of doing this can be:
- AI knows what the project is like (CLAUDE.md)
- Also knows how the team writes code (rules/)
- After standards are updated, commit to git, and the whole team can get the latest rules after pulling.
- Newcomers can get the latest standards by pulling the code, no need to read a bunch of documents.
The Value of rules/: Separation of Duties + Detailed to Executable
rules/ is also placed under the project's .claude/ directory. The approximate structure is like this:
my-project/
├─ CLAUDE.md # Basic project info
└─ .claude/
└─ rules/ # Team development standards
├─ security.md # Security standards
├─ testing.md # Testing standards
└─ coding-style.md # Code style standards
So the first step we need to achieve is separation of duties. According to our team collaboration design principles:
- CLAUDE.md is mainly responsible for what the project is like, i.e., project fact rules.
- rules/ mainly records our coding bottom-line standard content.
Designed this way, CLAUDE.md only needs to remain concise, writing only basic project info. And rules/ can be written in more detail, used to store team development standards and other rules.
For example, in a team collaboration project, we can store rule rules like this:
rules/coding-style.md: Code style conventions (e.g., how to name functions, how to handle errors, how to log)rules/api-design.md: API design standards (e.g., input validation, response format, error code definitions)rules/database.md: Database operation standards (e.g., query optimization, transaction handling, index usage)
Each file specifically manages one type of rule. We'll explain in detail how to write these later.
So after doing this separation of duties as above, what problems can we solve in team collaboration development?
1. Clearer responsibilities, easier to maintain
For example, our CLAUDE.md only manages project facts, remaining concise. Detailed team standards are placed in the rules/ directory, managed in separate files. This way, each has clear responsibilities and won't be mixed up and hard to maintain.
2. Multi-person collaborative maintenance reduces conflicts
For example, in team collaboration rules, we can divide maintenance like this:
.claude/rules/
├─ coding-style.md # Maintained by Developer A
├─ security.md # Maintained by Developer B
└─ testing.md # Maintained by Developer C
Each file has a single responsibility, multiple people can maintain different rule files simultaneously, and there won't be conflicts when committing.
3. Rules can be written in more detail
Rules in rules/ can actually be written in sufficient detail, so that AI can directly execute according to the rules.
For example, we can write in the rules like this:
- Priority (P0 must comply, P1 strongly recommended)
- Positive and negative examples (Don't do this / Do this)
- Reason explanation (Why do this)
Similar to this kind of more refined rule standard content.
4. Newcomers get up to speed faster
If a newcomer joins the team project, they can see the complete standards as soon as they pull the code. They don't need to read a bunch of scattered documents or ask what standard files the team has. Later, when writing code with Claude Code, AI can at least see these standards in the context, and the code written will be closer to the team's requirements.
However, one thing to note here is that some newcomers, although they pull the code, may not necessarily know to look at .claude/rules/.
So our approach is to add a line in the team onboarding document:
After pulling the code, first look at the
.claude/rules/directory, which contains the team's coding standards. Then use Claude Code to write some code casually and observe if AI follows the team standards. If you find that the code written by AI does not comply with the standards, it means rules/ might not be effective, then come to me for troubleshooting.
This way, newcomers at least know the existence of this directory and know how to verify if the rules are effective.
5. Standard updates are synchronized to everyone
Because this rule standard needs to be continuously optimized and updated as the project iterates, when standards are updated, we can commit them to the git repository, and then have everyone pull the code. This way, new sessions or after reloading, they can get the latest rules.
However, don't misunderstand that as soon as rules are changed, all running Claude Code sessions of everyone will immediately take effect. For important rule changes, it's still recommended to sync in the group chat or team meeting, reminding everyone to pull the latest code and restart sessions. This way, we can use git for unified distribution without missing key changes because someone is still working in an old context.
Loading Methods for rules/: Global Rules and Path Rules
Then let's understand the loading methods for rules. There are three common usage types for rules in the rules/ directory: unconditional rules, path-scoped rules, and user-level rules.
First Type: Global Rules
The most common way to write them is not to specify paths in the rule file, just write the rule content directly, like this:
# Code Style Conventions
- Function naming uses camelCase
- Error handling must use try-catch
- Logging uniformly uses logger, do not use console.log
After we set such rules, Claude Code will load them into the context upon startup. No matter which file we are processing, these rules will take effect. This is more suitable for project-wide common standards, such as code style, security requirements, etc.
Second Type: Path Rules (path-scoped rules)
Path rules are a relatively important feature in rules/, which many people might not have used yet.
This actually means we can specify that a rule only takes effect when processing certain files.
So how do we specify this? For example, we can add a YAML-formatted paths declaration at the beginning of the rule file (AI-organized code is also fine, screenshot taken):
We need to pay attention to the content wrapped in --- at the top, which is the paths declaration.
It tells Claude Code that this rule is only loaded when processing .ts and .tsx files under the src/api/ directory. As for files in other directories, this rule will not be loaded and will not occupy context space.
There's another point I need to explain: why do we sometimes need path rules?
Let me give an example. Suppose our team's project separates frontend and backend, with a directory structure roughly like this:
my-project/
├── frontend/ # React frontend
├── backend/ # Node.js backend
└── .claude/
└── rules/
├── coding-style.md # Global code style
├── frontend.md # Frontend-specific standards
└── backend.md # Backend-specific standards
If we load all rules at startup, problems arise. For example, frontend rules + backend rules + testing rules are all loaded, and the context window is immediately filled up. Moreover, when processing frontend code, AI still has to remember backend standards, which is easy to confuse. Most importantly, the more rules there are, the more scattered AI's attention becomes, and the compliance effect actually worsens.
So we need to adopt path rules. After processing this way, we can process files under frontend/ and only load frontend rules, and process files under backend/ and only load backend rules. This way, the context is cleaner, and AI execution is more focused.
Simple examples of matching patterns in path rules
The paths field supports glob patterns. The commonly used ones are these (AI-organized, shown in screenshot):
We can also specify multiple patterns and use curly braces to match multiple extensions, like this:
---
paths:
- "src/**/*.{ts,tsx}"
- "lib/**/*.ts"
- "tests/**/*.test.ts"
---
Third Type: User-Level Rules (Personal Preferences)
Besides project-level .claude/rules/, there are also user-level ~/.claude/rules/, which are our personal cross-project standards.
For example, if we personally have these habits:
- Always include timestamps when logging
- Error handling must record stack traces
- Test case naming uses
should_xxx_when_yyyformat
Then we can place these rules here:
~/.claude/rules/
├── preferences.md # Personal coding preferences
└── workflows.md # Personal preferred workflows
This way, no matter which project we switch to, Claude Code will load our personal standards.
So what scenarios is this generally suitable for?
For example, if we need to switch between multiple projects but want to maintain a consistent personal style. Or, beyond team standards, we have additional personal habits, etc.
Another example is for outsourced workers or consultants who frequently deal with different clients' projects; user-level rules are very useful here.
However, one thing to note here is that user-level rules and project-level rules will take effect simultaneously. If there's a conflict, project-level rules have higher priority because they are loaded later.
Sharing Rules Across Projects, Using Symbolic Links
If we are responsible for multiple projects and the team has unified coding standards, we can actually use symbolic links to avoid duplicate maintenance.
Here's an example: suppose the company has 10 microservice projects, all using Java + Spring Boot, with identical coding standards.
In this case, we can maintain a centralized set of rules, and each project references them via symbolic links:
# Maintain rules in the company intranet Git repository
~/company-standards/
├── java-style.md
├── api-design.md
└── security.md
# Each project references via symbolic links
cd my-project/.claude/rules/
ln -s ~/company-standards/java-style.md java-style.md
ln -s ~/company-standards/api-design.md api-design.md
The benefits of doing this are:
- Rules only need to be maintained once, and 10 projects are updated synchronously.
- New projects can be connected in seconds with ln -s.
- Can be combined with Git submodule or company intranet NFS sharing.
However, one thing to note here is that symbolic links require administrator privileges on Windows. If the team has Windows users, it's recommended to use git submodule instead.
Rule Management for Multi-Team Collaboration
If it's a monorepo with multiple teams collaborating in the same repository, pay a little attention here. Don't simply assume that putting a .claude/rules/ under each subdirectory means Claude Code will automatically load rules hierarchically by team. Generally, we have two approaches for this.
First: Use subdirectory CLAUDE.md to differentiate team context
For example, we can set up the directory structure like this:
monorepo/
├── CLAUDE.md # Company-wide common standards
├── frontend/
│ └── CLAUDE.md # Frontend team specific standards
└── backend/
└── CLAUDE.md # Backend team specific standards
This method is suitable for placing each team's project facts, common commands, and directory descriptions under their respective directories. For example, when frontend developers work in the frontend/ directory, Claude Code can more easily get frontend-related context; when backend developers work in the backend/ directory, they can more easily get backend-related context.
Second: Centralize team standards under the root directory .claude/rules/, then use paths to control scope
For more detailed coding standards, I recommend placing them in the repository root's .claude/rules/ and then using path rules to differentiate frontend and backend:
monorepo/
├── CLAUDE.md
├── frontend/
├── backend/
└── .claude/
└── rules/
├── frontend.md
└── backend.md
For example, frontend.md can be written like this:
---
paths:
- "frontend/**/*.{ts,tsx}"
---
# Frontend Development Standards
- React components use PascalCase
- Prioritize existing project solutions for state management
- Supplement necessary tests when modifying components
backend.md can be written like this:
---
paths:
- "backend/**/*.java"
---
# Backend Development Standards
- Controller does not write business logic
- Database changes must go through migration
- External interfaces must validate input parameters
The benefit of organizing this way is that the root CLAUDE.md is responsible for repository-wide consensus, subdirectory CLAUDE.md is responsible for team facts, and .claude/rules/ is responsible for executable coding standards. Frontend rules only trigger when processing frontend files, backend rules only trigger when processing backend files, making rule boundaries clearer.
An Important Point to Explain Here
CLAUDE.md and rules/ are both guiding AI's behavior, not enforcing it. That is to say, they are both loaded into AI as context. AI will only try to comply but does not guarantee 100% execution. After all, as I explained before, large models have their own judgment; sometimes it thinks a certain rule is not applicable in the current scenario and will skip it.
To give a simple example, suppose we wrote in rules/coding-style.md that function naming uses camelCase, but we temporarily say in the conversation to use snake_case this time, AI will also listen to us.
So some students might not understand, Since it doesn't guarantee 100% execution, why don't we use hooks entirely to force AI to execute standards?
So let me explain here too: because hooks can only check results, but cannot teach AI how to write.
Let's give another example. Suppose our team requires error handling to be written like this:
# rules/coding-style.md
- Error handling uses try-catch
- Error logs use logger.error()
- Error objects should carry context information, such as userId, requestId
After AI sees these rules, it knows what to do when writing code, such as first adding try-catch, then using logger.error() to record, and finally bringing userId and other context information. The code written this way is basically correct.
But if we only use hooks for mandatory checking:
We can only check after AI has finished writing the code, and intercept if try-catch is not used.
Moreover, AI doesn't know what we specifically want, it can only guess: should it add try-catch? Should it use logger? What context information should it bring? Then write a version, get intercepted. Guess again, write again, get intercepted again... Repeatedly modifying like this. Efficiency is very low.
So generally, a reasonable division of labor is better like this:
- rules/: Responsible for telling AI in advance how to write, mainly guiding direction.
- hook: Responsible for checking afterwards if there are things that absolutely cannot be done, mainly acting as a safety net.
rules/ lets AI try its best to write in the right direction, and most of the time it can write correctly. hooks only intercept situations that must be intercepted, such as committing API keys, skipping lint checks, etc. This way, the two cooperate, and efficiency is higher.
Regarding the detailed usage of hooks, I shared it in the previous Claude Code Hook, When CLAUDE.md Rules Don't Work, We Still Need Mandatory Interception Mechanisms. Interested folks can take a look.
On the Continuous Evolution of Team Rules
Earlier we talked about what rules/ is and how it is loaded. So where do these rules come from?
Here, I'll share the sources from my project, mainly two.
The first type is rules set at the beginning of the project
These rules belong to technical selection and architectural conventions, basically decided when the project is established.
For example, our team had a project with these style settings:
- Response format uniformly uses JSON form
- Function naming uses camelCase
- Logging uniformly uses slf4j, not System.out
- Error handling uniformly uses try-catch
Rules like these were decided during the technical solution review and can be directly written into rules/coding-style.md and rules/api-design.md.
The second type is rules accumulated during practice
Generally, these rules are gradually discovered, summarized, and extracted during actual development and code review.
Let's give another example. At the beginning of the project, we only wrote one line in CLAUDE.md: APIs should uniformly return a format.
As a result, during development, Developer A wrote an API without input validation. The frontend passed an empty string, and the backend directly reported a 500 error.
I asked him to add validation during code review.
But after a while, Developer B made the same mistake again.
For this recurring problem, I added a section in rules/api-design.md:
## Input Validation
- All APIs must validate input parameters
- Required parameters cannot be empty
- Numeric types must validate range
- Strings must validate length
Don't do this:
public User getUser(String userId) {
return userService.findById(userId);
}
Do this:
public User getUser(@NotBlank String userId) {
if (userId == null || userId.trim().isEmpty()) {
throw new BadRequestException("userId cannot be empty");
}
return userService.findById(userId);
}
After writing this in, such problems decreased significantly.
Another point: Rules need regular cleaning
In the daily iteration of the project, after we write rules in, we also need to periodically check for outdated ones.
For example, our team now reviews rules/ once every major version.
Clean up outdated rules
For instance, we previously required all API responses to be wrapped in { code, data, message } format.
But later we integrated the company's unified gateway, which automatically wraps responses, making this rule redundant.
If not deleted, AI would still write according to this rule, resulting in double wrapping.
Resolve conflicting rules
Sometimes different rules/ files have conflicts.
For example, coding-style.md says function naming uses camelCase, but later someone added a rule in database.md saying "database fields use snake_case".
AI seeing these two might get confused. So during regular reviews, we find these conflicts and clarify the scope of application.
What to do when rules conflict?
In our team collaboration, different roles may have different understandings of rules.
For example, we encountered a situation where frontend developers wanted API responses to be flat ({data: []}), while backend developers wanted nesting for extensibility ({data: {list: [], meta:{}}}).
Both sides actually had their reasons, but if written into rules/, it would conflict.
So generally, our approach is this:
If it's a technical selection type rule, the backend lead makes the decision, and writing it into rules/ is the final decision. If it's a style preference type rule, discuss it in a team meeting first, reach a consensus, then write it in. If a consensus cannot be reached in the short term, don't write it into rules/ for now, maintain the status quo, and see which is more suitable in practice.
In any case, we absolutely cannot have two leads writing their own rules, leaving AI seeing two opposite rules and not knowing whom to listen to.
Finally, rules need to be verified with tools
I mentioned above that rules/ only guides AI on how to write but does not guarantee 100% execution. So we also need to cooperate with other tools for verification.
Hooks as a safety net for key rules
For example, we do not allow committing API keys to the repository. This kind of rule must be enforced with hooks.
So we added a check in the PreToolUse hook: if the code contains plaintext sensitive information like API_KEY =, password =, it is directly intercepted (this is just a simple example, it's not actually this simple and crude).
Lint and test verify code quality
Although code style requirements are written in rules/, ultimately we still rely on checkstyle or lint to check.
For example, we run checks in CI, and merging is not allowed if they don't pass.
Code review handles judgment-type issues
Finally, there are some rules that are judgment-type, such as whether this feature should add caching, whether this API design is reasonable, etc.
Often, AI cannot judge these rule issues, so ultimately humans still need to review them.
So how to measure if rules are effective?
As a team lead, we need to know if the team standards are really working.
Generally, our approach is this:
- During code review, statistically categorize problem types. For example, during each iteration update, count the problems found in CR: naming irregularities, missing error handling, incorrect logging, etc. If a certain type of problem repeatedly appears, it means the corresponding rule is not effective and needs improvement.
- Run static checks in CI. For example, some projects might use tools like checkstyle, eslint to check code style, and count the number of violations per week to see if the trend is rising or falling.
- Regularly spot-check AI-generated code. For example, randomly check a few PRs every month to see if the code written by AI complies with team standards. If AI is found to frequently not comply with a certain rule, it means the rule is not written clearly enough.
With this data, we can objectively evaluate the effect of the rules, rather than being unclear about whether the rules are effective or not.
An Evolution Process of a Rule
For example, our team's rules/api-design.md was gradually perfected like this.
Early project stage (architectural rules, 3 items):
Then about two months later (e.g., new problems discovered, added 5 more items):
After major version review (cleaned up and optimized, grouping clearer):
This way, the rules went from 3 items to 8 items, and then optimized into a grouped structure. This process is about continuously discovering problems, accumulating rules, and cleaning up redundancy in practice.
A Complete Example: Part of My Project's Configuration
I've talked a lot above, so now let me briefly organize the configuration from our team's previous project to give everyone a complete example for direct reference.
For example, we are a backend team using Java + Spring Boot, with about four backend developers. The directory structure is roughly like this:
my-project/
├─ CLAUDE.md
└─ .claude/
└─ rules/
├─ coding-style.md # Code style (global)
├─ api-design.md # API standards (global)
├─ database.md # Database standards (global)
└─ controller.md # Controller layer standards (path rule)
Let's look at them one by one.
CLAUDE.md: Only Project Facts
As we said above, CLAUDE.md should remain concise, only writing basic project information, roughly controlled within 200 lines:
# Order Service
## Tech Stack
- Language: Java 17
- Framework: Spring Boot 3.x
- Database: PostgreSQL
- Cache: Redis
- Build Tool: Maven
## Common Commands
- Start service: `mvn spring-boot:run`
- Run tests: `mvn test`
- Package: `mvn clean package`
- Code check: `mvn checkstyle:check`
## Directory Structure
- `src/main/java/com/xxx/controller`: API layer
- `src/main/java/com/xxx/service`: Business layer
- `src/main/java/com/xxx/mapper`: Data layer
- `src/main/resources`: Configuration files
## Basic Constraints
- Database table structure changes go through Flyway migration, do not manually modify the database.
- Do not commit local configuration files, such as application-local.yml.
- Changes involving payments or permissions, review with me first.
You can see that what's placed here are all project facts, such as what technology is used, how to start, how directories are divided, and what the red lines are.
rules/coding-style.md: Code Style
This is a global rule, no paths, loaded at startup:
# Code Style Conventions
## Naming
- Class names use PascalCase
- Method names, variable names use camelCase
- Constants use UPPER_SNAKE_CASE
## Logging
- Uniformly use slf4j, do not use System.out
- Key operations must be logged, with traceId
## Error Handling
- All external methods must handle exceptions
- Error logs use log.error(), with context
- Do not swallow exceptions, nor directly print stack traces
Don't do this:
try {
doSomething();
} catch (Exception e) {
e.printStackTrace();
}
Do this:
try {
doSomething();
} catch (Exception e) {
log.error("Order processing failed, orderId={}", orderId, e);
throw new ServiceException("Order processing failed", e);
}
rules/api-design.md: API Standards
This is also a global rule:
# API Design Standards
## API Naming
- API style: RESTful
- Paths use lowercase, resources use plural, e.g., /orders, /users
- Use HTTP methods to express operations
## Request Standards
- All APIs must validate input parameters
- Pagination parameters uniformly use page and pageSize
- Query conditions use query parameters
## Response Standards
- Uniform return format: { code, data, message }
- Error codes follow HTTP standards
- List APIs must return total count total
rules/database.md: Database Standards
This is also a global rule:
# Database Operation Standards
## Querying
- Explicitly specify fields, avoid SELECT *
- Pagination queries max 100 records per request
- Complex queries must confirm index usage
## Writing
- Batch operations use batch, do not loop single inserts
- Multi-table writes must add transactions
- Use logical deletion (deleted_at), do not physically delete
## Transactions
- Transaction methods use @Transactional
- Note that internal method calls within the same class will cause @Transactional to fail
- Do not put time-consuming operations like remote calls, sending messages inside transactions
However, I need to add a note about the second point here. @Transactional failing on internal self-calls within the same class is a very common pitfall in Java. A colleague of ours stepped on it before, and AI also made the same mistake, so it's specifically written into the rules as a reminder.
rules/controller.md: Only Loaded When Modifying the API Layer
This uses a path rule, only loaded when processing files under the controller directory:
---
paths:
- "**/controller/**/*.java"
---
# Controller Layer Standards
- Controller only does parameter validation and calls service, does not write business logic
- Input parameters use @Valid for validation
- Return values uniformly wrapped with Result
- Do not directly operate the database in Controller
So why use a path rule for this one?
Because this rule is only useful when writing the API layer. We don't need it at all when processing service layer or data layer code.
By using path rules to scope it, it won't load when processing other directories, saving more context space.
Overall Effect
After we configured this set, our team's collaboration became noticeably smoother.
If a newcomer joins, they just pull the code, and Claude Code automatically knows our standards, writing code with a basically uniform style.
During our code reviews, those low-level formatting issues and naming issues almost never need to be mentioned anymore, allowing me to focus my energy on business logic and design.
So this set of configurations was also built up over a few months of use, patched together bit by bit. Initially, there were only a few items; we added one whenever we encountered a problem, and it slowly grew into what it is now.
Final Point: Personal Development and Team Development, Usage is Different
Writing up to here, we've basically covered the rules/ system. Finally, I want to help everyone sort out how exactly to use it from the perspectives of personal development and team development.
For Personal Development, rules/ is Not Really Needed
If it's just us writing a project alone, the value of rules/ is not great.
Personally, I think one CLAUDE.md is basically enough. Project facts, common commands, a few constraints, all written inside, within 200 lines, and it doesn't cost much context to load.
After all, we know our own standards. If we want to temporarily change a rule, we can just say it directly in the conversation. There won't be issues like multi-person collaboration or newcomer alignment.
If there really are some mandatory bottom lines, like not committing keys, I think directly configuring a hook is completely sufficient.
So the combination for personal development is very simple. I suggest using CLAUDE.md as the foundation, adding hooks at key points.
For Team Development, rules/ is Truly Valuable
Once multiple people are collaborating, the situation is different.
All the problems we talked about earlier will pop up, such as everyone having different code styles, code review being unable to cover everything, newcomers not knowing the standards, rule updates not being synchronized, etc.
So at this time, relying solely on CLAUDE.md is not quite enough. Once there are many rules, CLAUDE.md bursts past 200 lines; once rules are detailed, they're all squeezed into one file and unmaintainable.
So the value of rules/ lies right here:
- Split into multiple files by responsibility, clearly knowing who is responsible for what.
- Committed into git, one copy for the whole team, changes are made together.
- Use path rules to make rules only effective in corresponding directories, keeping context cleaner.
- Newcomers pull the code, and AI automatically writes according to team standards.
So the combination for team development is: CLAUDE.md for fact rules, rules/ for standards, and hooks to guard the bottom line.
When We Use It, There Are a Few Pitfalls to Note
First, don't write project facts into rules/
Things like tech stack, startup commands are project fact rules and should be placed in CLAUDE.md. rules/ only contains standards on how to write code. Don't mix these two.
Second, rules/ is not the more the better
As I said above, piling up too many rules, AI actually can't remember them, and attention gets scattered. Only write rules that AI cannot infer on its own and that repeatedly cause errors. Things that can be enforced by lint or hooks, don't ramble about in rules/.
Third, clean up regularly
Rules become outdated and can conflict with each other. Flip through the records every once in a while, delete useless ones, and sort out conflicting rules. Otherwise, AI holding a bunch of contradictory rules is more likely to make mistakes.
Fourth, distinguish what is guidance and what is mandatory
CLAUDE.md and rules/ are both for guiding AI, they cannot guarantee 100% execution. Bottom lines that truly cannot be touched must still rely on hooks to block. Never expect that writing something into rules/ means everything is fine.
Finally, although most of the time directly using Claude Code for conversational development is already sufficient, letting AI execute directly can get things done. But Claude Code provides many mechanisms to help us do better.
For example, the rules/ discussed in this article, and the hooks discussed in the previous one, they all solve problems that are not well handled in conversational mode:
- CLAUDE.md lets AI know what kind of project this is.
- rules/ allows team standards to be modularly managed, without repeating them in every conversation.
- hooks allow mandatory bottom lines to be truly blocked.
Especially when we are in team collaboration, everyone using AI is actually a big headache. After all, everyone writes differently, and quality is uneven.
Especially as a team lead or someone leading a team, what we need to do is both enable the team to use AI to improve efficiency and ensure code quality doesn't drop.
Although these mechanisms are not mandatory, knowing what they do and when to use them allows you to bring them out to solve problems when encountered.
I'll write another one later.
Official documentation reference: https://code.claude.com/docs/zh-CN/memory#organize-rules-with-claude/rules/
Top 1 of 2 from juejin.cn, machine-translated. The original thread is authoritative.
Does every conversation carry all the rules documents?
If there's no path, all rules are loaded at once. If a path is specified, they only load when you're under that path.