This is the shortest rule in my whole ~/.claude/rules/ folder. Maybe fifteen lines. It says: no barrel files, name components directly, follow casing convention, no emoji in code. I almost did not write a post about it because it feels too obvious.

Then I checked my last twenty git diff reviews and I noticed how often Claude Code wanted to generate an index.ts that just re-exports a component, or how often it wrote an emoji into a console log “for friendliness.” The rule is short because the problem is small, but the problem happens every single day.

The story behind the rule

Two things happened in close succession.

The first was a refactor where the model generated something like this without being asked:

src/components/UserProfile/
  index.ts          (re-exports UserProfile)
  UserProfile.tsx   (the actual component)
  types.ts          (UserProfile types)
  hooks.ts          (UserProfile hooks)

The folder existed before. I had asked for one small edit to UserProfile.tsx. The model decided to “tidy up” by introducing a barrel and now my import path was import { UserProfile } from './UserProfile' resolving through index.ts. Tree-shaking went sideways, our bundler started warning, and the diff was four times bigger than necessary.

The second was the emoji issue. The model started adding emoji to error messages, JSDoc comments, log statements. None of it was malicious. Friendly tone, just like a co-worker writing a Slack message. But the resulting console output in production logs was unreadable in some terminal emulators, and the unit tests that asserted on exact log strings began breaking.

Both of these are small. Both compound. Both are exactly the kind of thing a global rule should prevent.

The rule

The whole rule, verbatim. I keep it in ~/.claude/rules/code-style.md:

# Code Style Rules

## File Naming

- NEVER use or create barrel files. No `index.ts` files for exports,
  import directly from component files.
- ALWAYS name components directly (`UserProfile.tsx`, NEVER
  `index.tsx`).
- Use PascalCase for components (`UserProfile.tsx`, `OrderList.tsx`).
- Use camelCase for hooks (`useAuth.ts`, `useFetchData.ts`).
- Use camelCase for utilities (`formatDate.ts`, `validateEmail.ts`).

## Code Quality

- NEVER use emojis in generated code, comments, JSDoc, UI strings,
  console logs, or technical documentation. Maintain a professional
  and technical tone in all development-related outputs.

That is the entire file. No exceptions, no qualifiers. Short rules are the only ones the model reliably follows; the longer a rule gets, the more the model finds clauses to interpret.

Why no barrel files

Barrel files (index.ts or index.tsx that re-export from neighboring files) are a stylistic choice with a real cost. The argument for them is usually “cleaner import paths” or “encapsulation of internal structure.” The argument against them is concrete:

  • Tree-shaking gets harder. A barrel that does export * from './A'; export * from './B' is a function from “import anything” to “load both A and B.” Bundlers can sometimes still tree-shake it, but the conditions are fragile. Side-effect-free flag, ESM only, no module.exports, no re-export rename chain, no namespace re-export. Skip the barrel, skip the problem.
  • Circular imports are easier to create. Two files that import from each other’s barrel can produce a cycle the bundler tolerates but the runtime does not (undefined references at startup). Direct imports surface the cycle at compile time.
  • Refactoring is noisier. Renaming a file means updating every importer. With a barrel, you also update the barrel. With direct imports, the find-and-replace is mechanical.
  • The “encapsulation” claim is weak. A barrel does not actually hide internals from anyone who runs ls on the folder. It just adds a layer of indirection between the import path and the source file.

The rule does not argue any of this. It just says no. If I wanted the model to weigh trade-offs, I would write a guideline, not a rule. The whole point of a rule is “stop thinking, just do it.”

Why direct component naming

UserProfile.tsx instead of UserProfile/index.tsx for the same reasons, plus one more: editor navigation. When every component is index.tsx, your editor’s tab bar fills up with six tabs all labeled “index.tsx” and you cannot tell them apart without hovering. PascalCase filenames matching the component name make every tab self-describing.

The casing rules (PascalCase for components, camelCase for hooks, camelCase for utilities) are equally arbitrary. The model has no inherent preference, so without a rule it picks whatever the surrounding codebase suggests, which means in a fresh project it picks whatever was in the training data. Specifying the convention removes one source of inconsistency.

Why no emoji in code

This one I have argued with myself about. Emoji in chat is fine. Emoji in console.log("Server started") is not fine, for three reasons:

  • Production log aggregators. Some log shippers normalize unicode, some do not. A flame emoji that renders fine in your terminal becomes mojibake in a Datadog or CloudWatch query.
  • String assertions in tests. A test that asserts expect(log).toContain("started") is robust. A test that asserts on an emoji-prefixed string breaks whenever the model regenerates the log.
  • Code review tone. A function comment with three emoji reads like a Slack message. A function comment with no emoji reads like documentation. The cost of professionalism is zero, the cost of casual tone in a code review is some skepticism from your reviewer.

The rule lists every place emoji is not allowed: generated code, comments, JSDoc, UI strings, console logs, technical documentation. The list exists because the model is creative about where it sneaks emoji in.

When the rule does not apply

There are exactly three places where I let emoji through:

  • My own README files when I am clearly writing for humans, not for code. A personal portfolio page can have whatever decoration I want. The rule scopes to “development-related outputs,” not “anything you ever write.”
  • A .claude/skills/ description string used for skill discovery. Skill descriptions surface in a UI list. A leading emoji can help recognition. I do this sparingly.
  • Unit tests that explicitly assert on emoji output (a renderer that should output an emoji). The test code includes the emoji because the test is verifying that exact behavior.

The barrel file rule has no exceptions in my codebases. If I genuinely needed a barrel (some library author exposing a public API), I would write that library outside this rule’s scope.

Variants you might want

  • Different casing. If your team is on kebab-case for filenames (user-profile.tsx), swap the convention in the rule. The model will follow whichever you specify.
  • Allow index.ts for a specific public API folder. A common compromise is “no barrels anywhere except src/lib/index.ts which is the package’s public entry point.” If you write libraries, this carve-out makes sense. If you only write apps, the strict ban is cleaner.
  • Stricter emoji rule. Some teams ban emoji even in PR descriptions and commit messages. I keep the rule scoped to code so PR titles can still have a status indicator if your team uses one.
  • Add file size limits. A natural extension is “max 300 lines per .tsx file” or similar. I do not, because line counts are not a great proxy for complexity, but if you want one, the rule file is the right place.

Closing note

The whole rule file is fifteen lines. It probably saves me ten minutes a week in code review comments I would otherwise have to write. Ten minutes a week is eight hours a year. For fifteen lines of markdown, that is the highest return-on-investment thing I have in my ~/.claude/ folder.

If you write only one Claude Code rule, write this one. It is so small you can adapt it in five minutes, and the difference in diff cleanliness shows up in the very first session after you add it.