Senior Data Science Lead

Senior Data Science Lead

How Claude Code rules actually work

A practical guide to memory, scoped rules, and predictable behaviour in real projects using Claude Code.

Jose Parreño Garcia's avatar
Jose Parreño Garcia
Mar 25, 2026
∙ Paid

This week, I spent time setting up Claude Code rules for a personal project.

It seemed straightforward at first. I added a few rules, organised them neatly, and expected Claude to behave more predictably as the project grew. Instead, I started seeing something familiar: behaviour that felt correct in one moment and subtly wrong in the next.

The more I looked into it, the clearer the pattern became. The problem wasn’t Claude. It was my mental model of how Claude Code memory and rules actually work. I was treating rules like instructions Claude would “remember”, rather than context that is discovered, loaded, scoped, and sometimes ignored.

That realisation sent me down a rabbit hole. Why are some rules always loaded? Why do others only apply in certain directories? How do scoped rules actually interact with CLAUDE.md? And how can you tell what Claude has really loaded into memory, instead of assuming?

This post is my attempt to answer those questions. I break down what Claude Code rules are, how Claude discovers and prioritises them, how scoped rules behave in practice, and what this looks like in a real data science project.

If you are using Claude Code on anything beyond a small experiment, understanding this mental model will save you a lot of confusion.

What this blog will cover

  • What Claude Code rules are, and when not to use them
    A clear explanation of what rules actually do inside Claude Code, how they differ from prompts or chat instructions, and the situations where rules create more friction than value.

  • How Claude discovers, loads, and prioritises rules
    How .claude/rules/ is scanned, how user-level and project-level rules interact, and how to verify what Claude has actually loaded instead of trusting assumptions.

  • How to use scoped rules deterministically
    How path-scoped rules work in theory and in practice, how glob patterns behave, and how to design rules that only activate when they genuinely should. In addition, using CLAUDE.md as an index and workflow guide

  • How do you tell Claude to read context that is not loaded automatically.
    In many projects, you will have external documentation you would like Claude to have access to. Claude doesn’t load it by default, but there is a way to tell it do so.

  • How can you check what Claude is loading in memory.
    Never assume that Claude is loading what you are telling it to load. Ask Claude to tell you what it loaded to memory.

  • What this looks like in a real data science project
    A concrete churn prediction example, showing how to structure rules for notebooks, feature engineering, model training, and production pipelines.

  • What can go wrong (and how to notice early)
    Common pitfalls such as rule bloat, confusing scoping, and silent failures. Also, some ideas on how to validate behaviour using small, reproducible checks.

  • Closing thoughts, why Claude Code rules exist at all
    Why rules are a response to very real scaling problems once CLAUDE.md files grow beyond a few dozen lines.

Let’s get started!

What Claude Code rules are

In my previous post, I went deep into how Claude Code memory layers work, and why the global and the project CLAUDE.md files matter.

The global CLAUDE.md file has a very specific job: defining your working relationship with Claude. One of the best practices mentioned that the global CLAUDE.md file should not describe every possible working preference. It is not the place to explain how to build a churn model, write an executive summary, or run deep research.

Those are project-level nuances and they change with context, and they belong to the project CLAUDE.md file. Now, here is where most people get things wrong. Because Claude has this specific project file, there is a temptation of writing every possible project detail here.

  • Project context

  • Architecture notes

  • Coding conventions

  • Data constraints

  • ML workflows

  • Evaluation rules

  • Deployment warnings test…

And, to an extent this is correct.

But, if you do that, you might end up with a 1,000 line long chapter of the project description. There are many many problems that branch from such long project files: context memory bloat, contradicting instructions, difficult maintenance and many more (don’t worry we will cover details on these later).

The long project file becomes a liability rather than a guide.

Claude Code rules come in to provide structure.

Rules are project-scoped memory, stored as small, modular Markdown files under .claude/rules/. They exist to capture how work should be done in this repository, but in a way that is structured, composable, and maintainable.

For example, we could have a repo will some rule markdown files like the ones below:

.claude/rules/
├── python-style.md
├── experiments.md
├── data-privacy.md
└── ml/
    ├── training.md
    └── pipelines.md

You might be thinking, “well, can’t I just write these inside the project CLAUDE.md file?”. I get the question, only if you think that Claude loads all rules at once. This is not entirely true. We will cover how to help Claude load context rules more intelligently later.

For now, just stick with my deliberately high level summary:

At a high level, you can think of Claude Code rules as a way of decomposing project memory. Instead of one large file that tries to explain everything, you get smaller, topic-specific files that can evolve independently.

That is what the next section is about.

How Claude discovers, loads, and prioritises rules

Claude rules are much more nuanced and powerful than this, so, if you are going to rely on rules, you need to understand how Claude actually finds them, loads them, and decides which ones apply.

So, let’s start by asking some simple questions:

  • If I create a .claude/rules/ directory with 5 rule files in it, what actually happens?

  • Does Claude load all of them at the start of every session?

  • Does it wait and load them intelligently when they are needed?

  • Or does it do something in between?

Well, the answer is pretty clear.

By default, Claude loads all project rules at the start of a session.

If you have 5 Markdown files under .claude/rules/, Claude will see all 5 as soon as it starts working in that repository. Everything is in memory.

This means that, without doing any tricks, claude rules are only useful to us humans as an organisational aid. If you are careless with the rules, you can still end up with a bloated context memory.

These “loaded by default rules” are called unconditional rules.

What unconditional rules are good for

Unconditional rules are… unconditional. If they exist, they are loaded. Period.

So, if they are always loaded, then what are the best use of them?

Unconditional rules work best when they encode constraints that should apply regardless of what you are doing in the repository. Here are some examples that I follow:

  1. Baseline coding and quality standards.

    Things like formatting, typing expectations, testing requirements, or logging conventions that apply everywhere in the codebase. These are stable and rarely controversial — which is exactly why they belong here.

  2. Non-negotiable constraints.

    Security requirements, data privacy rules, or compliance constraints that must always be respected, no matter which file or task Claude is working on.

  3. Shared project assumptions.

    For example: “this repository targets batch workloads, not real-time systems”, or “this project optimises for offline evaluation correctness over iteration speed”. These assumptions shape many downstream decisions, so having them always present avoids subtle drift.

Notice what is missing from this list.

Unconditional rules are not the place for:

  • Step-by-step workflows

  • Instructions tied to a specific directory

  • ML-specific modelling choices

  • Experimental or evolving practices

If a rule only applies when touching a particular part of the codebase, it should not be unconditional. Keeping it unconditional just because it is convenient is how context memory gets fuzzy.

So far, Claude loads everything. Next, we will look at how to tell Claude what not to load until it is actually needed, and how to make rules truly contextual rather than just organised.

That is where scoped rules come in.

How to use scoped rules deterministically

Up to this point, rules have behaved in a very blunt way. They exist, so, they load.

That is useful for baseline constraints, but it is not enough for real projects. Most repositories contain very different kinds of work living side by side: experiments, pipelines, infrastructure, documentation. If all rules apply everywhere, you are back to context bloat.

So, how do you tell Claude when a rule should apply, and just as importantly, when it should stay out of the way?

How path-scoped rules work

Claude Code supports scoped rules through YAML frontmatter at the top of a rule file. The idea is simple. You tell Claude “this rule only applies when you are working on files that match these paths”.

For example, imagine you have a repository like the one below.

churn-model/
├── CLAUDE.md
├── .claude/
│   └── rules/
│       ├── python-style.md
│       ├── experiments.md
│       ├── data-privacy.md
│       ├── notebooks.md. 
│       └── production-ml.md
├── notebooks/
│   ├── 01_eda.ipynb
│   └── 02_feature_ideas.ipynb
├── src/
│   ├── features/
│   │   ├── build_features.py
│   │   └── feature_catalogue.yml
│   ├── models/
│   │   └── churn/
│   │       ├── train.py
│   │       ├── evaluate.py
│   │       └── predict.py
│   └── pipelines/
│       ├── training_pipeline.py
│       └── batch_scoring.py
└── tests/
    └── test_models_churn.py

Now, we want different rules depending on what Claude is touching:

  • Notebook work should be lightweight, fast, and explicit about “prototype” status.

  • Production model code should be strict: tests, reproducibility, no data leakage.

So, as part of the markdown rules for notebooks.md and production-ml.md, we would include a path to guide Claude.

This is the .claude/rules/notebooks.md file.

By adding paths, this tells Claude:
-> When you are working inside notebooks/, Claude sees this rule.
-> When you are editing src/models/..., it should not apply.

---
paths:
  - "notebooks/**"
---
# Notebook rules

- Treat notebooks as exploratory, not production.
- Prefer small, runnable cells and short outputs.
- If code becomes stable, migrate it into `src/` with tests.
- Do not hardcode secrets or dataset paths; use environment variables or config.
This is the .claude/rules/production-ml.md file

Similar to the above, these constraints only appear when Claude is touching production code paths (models and pipeline folders).

---
paths:
  - "src/models/churn/**"
  - "src/pipelines/**"
---
# Production ML rules (churn)

- Any change must preserve reproducibility:
  - fixed random seeds
  - deterministic splits
  - versioned features
- Never train on labels that leak future information.
- Before finalising changes:
  - run `pytest -q`
  - run the relevant training pipeline locally (or in CI if configured)
- Always report evaluation using the project’s standard metrics (see `experiments.md`).

The paths in the rules above are defined via 'glob patterns’. They are pretty easy to define (if you haven’t worked with them, just ask Claude to generate them for you).

Help Claude explore your rules with an index

Now, whilst scoped rules give you a lot of control, they only trigger if Claude happens to need to read or work with the paths you specified. What if we could tell Claude what our rules are about to help it understand the project context better?

This is where a simple index can help. A good project CLAUDE.md might say things like.

## How to work in this repository

This project contains multiple types of work. Before starting, clarify what kind of task you are doing and follow the relevant guidelines below.

### Exploratory and experimental work

If you are exploring data, prototyping features, or testing modelling ideas:

- Follow the experimentation guidelines in `.claude/rules/experiments.md`
- Prefer notebooks under `notebooks/`
- Expect looser constraints and faster iteration

### Production ML changes

If you are modifying production model code:

- Expect stricter constraints to apply
- Scoped rules under `src/models/` and `src/pipelines/` will be active
- Reproducibility, evaluation, and testing are mandatory

None of that is enforceable on its own. And that is fine.

What it does is prime Claude’s planning step. This is where determinism matters, but in a different way.

Scoped rules are deterministic in application.
CLAUDE.md is deterministic in expectation.

Another way to see the split:

Scoped rules prevent local mistakes
CLAUDE.md prevents global misunderstandings

A couple of examples below:

  • If you rely only on scoped rules, Claude might technically obey constraints while still approaching the task in the wrong way. For example, diving into production code when you wanted a design discussion, or optimising code when you wanted an experiment.

  • If you rely only on CLAUDE.md, Claude might understand intent but miss critical guardrails once it starts editing.

  • Used together however, they close the loop.

Up to now, we have focused on memory that Claude loads automatically: global files, project files, and rules. But real projects often have important context that does not live in CLAUDE.md or .claude/rules/

How do you tell Claude to read context that is not loaded automatically?

Real projects almost always have important context that does not live in those places.

Think about files like:

  • README.md

  • Architecture overviews

  • Design documents, RFCs, PRDs

  • Experiment summaries

  • Runbooks or operational notes

Claude will not load these by default. Unless you explicitly point to them, Claude may never read them at all.

User's avatar

Continue reading this post for free, courtesy of Jose Parreño Garcia.

Or purchase a paid subscription.
© 2026 Jose Parreño Garcia · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture