The problem with repeating your coding rules
I've been using AI more and more when writing code, but I don't want every project to slowly turn into whatever style the model happens to prefer that day.
I might want Django views kept small, business logic moved into services, imports ordered a certain way and tests written using patterns that already exist in the project. Maybe I don't want class-based views. Maybe I do. The important thing is that the AI should follow the decisions already made in the codebase.
You can put all of this into every prompt:
Create a Django view for updating a customer.
Use function-based views.
Use snake_case.
Keep business logic out of the view.
Use our service layer.
Use get_object_or_404.
Don't add new dependencies.
Follow our existing test structure.
...
That works, but I'm not going to write that every time I ask Claude to add a view.
A better option is to store those instructions in the repository.
Claude Code has a few ways of doing that. You can use CLAUDE.md for instructions that should always be available, and you can create Skills for more detailed instructions that only need to be loaded for certain work.
For a larger coding style guide, I prefer a Skill. It keeps the general project instructions small while giving Claude access to the detailed rules whenever it's actually writing or reviewing code.
What we're going to create
For this example, let's say I have a Django project and I want Claude to follow my coding style whenever it creates, edits, refactors or reviews code.
I'm going to create a project Skill called code-style. Because it belongs to the project, I'll store it here:
.claude/
skills/
code-style/
SKILL.md
references/
python.md
django.md
tests.md
The SKILL.md file tells Claude what the Skill does and how to use it. The files in references contain the more detailed rules.
You don't have to split a Skill into several files. A small Skill can contain everything in SKILL.md. I like separating larger style guides because the main file stays readable, and Claude can open the detailed guide that matches the code it's working on.
Start Claude Code in your project
Open the repository in your terminal and start Claude Code:
cd ~/projects/my-django-project
claude
We could create all the Skill files by hand, but there's not much reason to do that when Claude Code can inspect the project and create them for us.
I wouldn't immediately tell Claude to "create a style guide" though. If you do that without giving it anything concrete to work from, there's a good chance you end up with generic rules such as "write clean code" and "follow best practices". Those aren't useful instructions.
I want the Skill to describe how I actually write code.
Let Claude inspect the existing code first
If you already have a written style guide, you can skip most of this and point Claude at that file. If you don't, your existing codebase is usually the best place to start.
I might begin with a prompt like this:
I want to create a Claude Code skill that makes you follow
the coding style used in this repository.
Do not change any files yet.
Inspect the project first. Read representative examples of:
- Django models
- views
- forms
- services
- tests
- templates
- utility functions
Also check pyproject.toml, requirements files and any existing
linting or formatting configuration.
Identify conventions that appear consistently in the codebase.
Do not invent conventions just because they are considered
Python or Django best practices.
Give me the proposed rules before creating the skill.
That last part matters. I don't want Claude to turn its own preferences into my style guide.
If most of my views are function-based, that's useful information. If three files use one pattern and another three use something completely different, Claude shouldn't confidently declare that one of them is the project standard.
I'd rather have it tell me that the project is inconsistent so I can decide which convention I want.
Use your existing style guide if you have one
If your company already has coding standards, the process becomes easier. Maybe you have:
docs/
coding-style.md
Or:
CONTRIBUTING.md
In that case, I'd tell Claude to use those rules as the source rather than trying to reconstruct them from the code:
Read docs/coding-style.md and CONTRIBUTING.md.
I want to turn the coding conventions in those files into
a project-level Claude Code skill.
The skill should be used whenever you create, edit, refactor
or review code in this repository.
Keep the existing rules intact. If the documentation conflicts
with the current codebase, point out the conflict instead of
choosing one yourself.
That gives you a much better starting point than asking the model to decide what your coding style should be.
Create the Skill
Once I'm happy with the proposed rules, I can ask Claude to create the actual Skill:
Create the project skill now.
Put it in:
.claude/skills/code-style/
Use SKILL.md for the main instructions.
Put detailed Python rules in:
references/python.md
Put Django-specific rules in:
references/django.md
Put testing conventions in:
references/tests.md
Keep SKILL.md short. It should tell you when to use the
reference files instead of duplicating all of their contents.
The skill should automatically apply whenever you create,
modify, refactor or review code.
Claude can now create the directory and files directly inside the repository.
Because the Skill is stored under .claude/skills/, I can also commit it to Git. Everyone working on the project can then use the same instructions instead of maintaining their own collection of prompts.
The SKILL.md file
A simple version of our SKILL.md could look like this:
---
name: code-style
description: Apply this repository's coding conventions whenever creating, editing, refactoring or reviewing code. Use for Python, Django, templates and tests.
---
# Coding style
Follow the existing architecture and conventions in this repository.
Before changing code:
1. Inspect nearby files for local patterns.
2. Read the relevant style reference below.
3. Prefer existing project conventions over introducing a new pattern.
4. Do not add dependencies unless the task requires them.
## Python
For Python code, read:
[references/python.md](references/python.md)
## Django
For Django models, views, forms, services, management commands
and templates, also read:
[references/django.md](references/django.md)
## Tests
When creating or changing tests, read:
[references/tests.md](references/tests.md)
After making changes, review the edited code against the
relevant style guide before finishing.
The description at the top is more important than it might look. Claude uses that description to decide when the Skill is relevant, so I want it to describe both what the Skill contains and when it should be used.
I wouldn't use something vague like:
description: Our coding style
That doesn't give Claude much help deciding when to load it.
I'd rather explicitly mention creating, editing, refactoring and reviewing code.
Write rules that actually change the output
The detailed reference files are where I'd put the real conventions.
A bad Python style guide might contain this:
# Python style
- Write clean code.
- Follow best practices.
- Use meaningful variable names.
- Keep code maintainable.
- Avoid unnecessary complexity.
There isn't much Claude can do with that. Two developers could read those rules and still produce completely different code.
I prefer instructions that affect a concrete decision.
For example, references/python.md could contain:
# Python style
## Functions
Keep functions focused on one operation.
Prefer early returns over deeply nested conditionals.
Bad:
if user:
if user.is_active:
if user.has_access:
return dashboard(user)
Preferred:
if not user:
return None
if not user.is_active:
return None
if not user.has_access:
return None
return dashboard(user)
## Imports
Use this order:
1. Python standard library
2. Third-party packages
3. Django imports
4. Local project imports
Separate each group with a blank line.
## Type hints
Use type hints on service functions and reusable utilities.
Do not add type hints to Django model fields or framework
methods where they add noise without useful information.
## Dependencies
Do not introduce a third-party package when the same result
can be implemented clearly with Python or Django already
installed in the project.
Those rules tell Claude what I would actually choose during a code review.
Add Django-specific rules
I would keep Django conventions separate because they tend to grow faster than general Python rules.
For example:
# Django style
## Views
Use function-based views unless an existing part of the
application already uses class-based views for the same pattern.
Keep request handling in the view and move substantial business
logic into services.
Use get_object_or_404 when retrieving an object that should
return HTTP 404 when missing.
Do not catch DoesNotExist only to manually return a 404.
## Models
Keep model methods for behaviour that naturally belongs to
the model.
Do not put request-specific logic in models.
Always give ForeignKey and OneToOneField relationships an
explicit on_delete value.
Use related_name when the reverse relationship will be used
in application code.
## Queries
Watch for N+1 queries when accessing related objects.
Use select_related for single-value relationships when the
related object is needed.
Use prefetch_related for collections when appropriate.
Do not add either automatically when the related data is not
used.
## Forms
Use ModelForm when a form maps directly to a model.
Keep reusable validation in clean_* methods or model
validation rather than duplicating it in views.
## Templates
Keep business logic out of templates.
Prepare complicated values in Python before rendering.
Use Django URL names rather than hardcoded internal URLs.
These are only example rules. The point isn't that everybody should use exactly these conventions. Your Skill should contain the decisions you want Claude to make in your project.
Give Claude examples
I've found that examples are useful when a rule can be interpreted several ways.
Suppose I tell Claude:
Keep views thin.
Claude and I might have very different definitions of "thin".
I can make the rule much clearer with a small example:
Bad:
def create_subscription(request):
customer = Customer.objects.get(
pk=request.POST["customer"]
)
subscription = Subscription.objects.create(
customer=customer,
plan=request.POST["plan"],
)
send_subscription_email(subscription)
update_customer_status(customer)
return redirect("subscriptions:detail", subscription.pk)
Preferred:
def create_subscription(request):
form = SubscriptionForm(request.POST)
if form.is_valid():
subscription = create_subscription_for_customer(
**form.cleaned_data
)
return redirect(
"subscriptions:detail",
subscription.pk,
)
return render(
request,
"subscriptions/create.html",
{"form": form},
)
Now Claude can see what I mean instead of trying to interpret a vague sentence.
I wouldn't fill the Skill with hundreds of examples though. A few examples around decisions that Claude regularly gets wrong are much more useful than turning the Skill into a copy of the entire codebase.
Tell Claude to inspect nearby code too
A style guide can't describe every decision in a real project. Naming is a good example. I could spend pages describing how serializers, services and templates should be named and still miss something.
That's why I include an instruction telling Claude to inspect the surrounding code before creating something new.
If I'm asking it to add another payment service and the repository already contains:
payments/
services/
create_payment.py
refund_payment.py
cancel_payment.py
I want Claude to notice that structure before deciding to create:
payments/payment_service_manager.py
The existing repository is part of the style guide too.
Make the Skill check its own work
I also like adding a small review step to the Skill. Without it, Claude can load the correct instructions, write a fairly large change and still miss one of the rules halfway through.
The end of my SKILL.md might contain:
## Before finishing
Review every changed file against the relevant style guide.
Check specifically for:
- naming that differs from nearby code
- business logic placed in Django views
- unnecessary dependencies
- duplicated utilities
- missing tests for changed behaviour
- N+1 database queries introduced by the change
Fix violations before presenting the finished work.
I don't need a twenty-point checklist. I want a short list of mistakes that actually show up in this project.
Let Claude run your existing tools
Written conventions shouldn't replace formatters, linters or tests that can check things automatically.
If the project already uses Ruff, I don't want to write several paragraphs explaining import sorting and formatting when Ruff can check those rules directly.
Instead, the Skill can tell Claude what to run after changing Python code:
## Validation
After changing Python code, run:
ruff check .
ruff format --check .
Run the relevant tests for the code that changed.
Do not finish with failing lint checks or tests unless the
failure existed before the change. If it did, report it.
The same idea applies if your project uses Black, mypy, pytest, Django's test runner, ESLint or another tool.
I prefer putting subjective decisions in the style guide and leaving mechanical rules to software that can check them reliably.
Use CLAUDE.md for the few rules that should always be present
I wouldn't move every project instruction into a Skill.
If a rule matters during practically every Claude Code session, CLAUDE.md is still useful. I might keep a very small file at the root of the project:
# Project instructions
This is a Django application.
Use the project's code-style skill whenever creating,
modifying, refactoring or reviewing code.
Never commit secrets or .env files.
Run tests relevant to the files you change.
The detailed conventions stay in the Skill, while the small number of instructions I always want Claude to see stay in CLAUDE.md.
This also prevents CLAUDE.md from becoming a huge file full of instructions about Django forms, test naming and import ordering when I'm only asking Claude to explain a database error.
Test whether the Skill actually changes the code
After creating the Skill, I wouldn't assume it's working because the files exist.
Give Claude a normal coding task:
Add an endpoint where a logged-in user can update
the name of one of their projects.
Then inspect what it creates. Did it use the same view structure as the rest of the application? Did it put business logic in the right place? Did it follow the test structure? Did it run the checks defined in the Skill?
You can also invoke the Skill directly:
/code-style
And ask Claude to review an existing file:
/code-style
Review apps/projects/views.py and tell me where it
doesn't follow our current coding conventions.
This is a useful way to test the Skill before trusting automatic invocation.
Use the Skill to clean up old code
Once the Skill describes the style I actually want, it becomes useful for more than generating new code.
I can ask:
Review the payments app against our code-style skill.
Do not change anything yet.
Give me a list of files that don't follow the current
conventions and explain the specific rule each file breaks.
Then I can decide which parts are worth changing.
This is safer than asking Claude to "clean up the payments app", which leaves far too much room for it to decide what clean means.
Commit the Skill with the project
For project-specific coding conventions, I want the Skill in Git:
git add .claude/skills/code-style
git commit -m "Add Claude Code style skill"
That gives the Skill the same history as the code it describes. If the project stops using function-based views six months from now, we can change the rule in the same repository.
It also means another developer can clone the project and get the same Skill instead of maintaining a private prompt somewhere on their laptop.
You can also create a personal coding Skill
A project Skill is what I would use for rules that belong to a particular codebase. Claude Code also supports personal Skills under:
~/.claude/skills/
For example:
~/.claude/
skills/
my-python-style/
SKILL.md
That Skill is available across projects on the machine.
I could use this for preferences I want almost everywhere, such as how I structure Python functions or how I want tests written. I would still keep framework and application-specific conventions inside each repository.
Otherwise it's easy to end up forcing a preference from one project into another project where it doesn't fit.
Don't make the Skill too large
It's tempting to keep adding instructions every time Claude makes a mistake. Eventually you can end up with a giant Skill full of rules that contradict each other or describe problems that haven't existed for a year.
I treat the Skill like code. If a rule isn't useful anymore, I remove it. If two rules overlap, I combine them. If a formatter can enforce something, I usually let the formatter handle it instead of spending instructions on it.
The main SKILL.md should mostly explain what Claude needs to do and which reference file it should read. Detailed conventions can stay in the reference files where they're easier to maintain.
Update the Skill when Claude gets something wrong
The first version won't cover everything, and I don't think it needs to.
Suppose Claude creates a service and catches Exception around the whole function, but that's something I never want in this project. Instead of correcting it in every future conversation, I can update the Skill:
Add this rule to our Python coding style:
Do not catch Exception around an entire service function.
Catch the specific exception you can handle. If the code
cannot recover from an exception, allow it to propagate.
Find the best place for this rule in the existing style
references. Keep the wording concise.
Now the correction becomes part of the repository instead of disappearing when I close the conversation.
That's the main reason I like using Skills for this. I'm not trying to write one enormous prompt that makes Claude generate perfect code. I'm gradually recording the decisions I already make when reviewing code, then giving those decisions back to Claude the next time it works on the project.
After a while, asking Claude to add a Django feature requires much less cleanup because it starts from the same conventions I would have applied myself.