Claude Code Team Workflows: Sharing Skills and Conventions
Why "it works on my Claude" is a real problem
Every team that adopts Claude Code eventually hits the same wall. One engineer has spent three weeks teaching their local setup how the codebase likes migrations written, how PRs should be titled, which test command actually matters, and which one is a decoy left over from a framework migration two years ago. Their Claude Code sessions are fast, accurate, and quiet. Meanwhile a teammate who joined last week is fighting the same assistant, asking it to re-discover conventions that already live in the first engineer's head — and now, invisibly, in their personal CLAUDE.md file and a folder of skills nobody else has seen.
This is the AI-era version of "works on my machine." Except instead of a Node version mismatch, it's tribal knowledge about how your team does code review, tests API endpoints, or structures a migration — knowledge that used to leak out slowly through pairing sessions and Slack threads, but now gets silently absorbed by one person's AI assistant and never redistributed.
The fix isn't complicated, but it does require treating your Claude Code configuration as a first-class part of the repository, not a personal preference file. This article walks through the concrete mechanics: what goes in a shared CLAUDE.md, how to package repeatable workflows as skills, how hooks enforce conventions instead of just suggesting them, and how to structure all of it so it survives onboarding, offboarding, and the inevitable drift that happens when five people are all editing the same "rules" file.
Start with CLAUDE.md as team infrastructure, not a wishlist
The single highest-leverage file in a Claude Code-enabled repo is CLAUDE.md at the project root. Claude Code reads it automatically at the start of every session in that directory, which means anything you put there is context every teammate's assistant gets for free — no copy-pasting, no "did you see the doc I wrote," no re-explaining in every PR description.
The mistake most teams make is treating CLAUDE.md like a README: prose, aspirational, vague. A CLAUDE.md that actually changes behavior reads more like a runbook. Concrete commands, concrete file paths, concrete "never do X" statements.
A pattern that works well for a mid-sized web app repo:
# CLAUDE.md
## Build and test
- Install: `pnpm install`
- Dev server: `pnpm dev` (port 3000)
- Run unit tests: `pnpm test`
- Run a single test file: `pnpm test path/to/file.test.ts`
- Type check before any commit: `pnpm typecheck`
## Database
- Schema lives in `prisma/schema.prisma`
- Never run `prisma migrate reset` against the shared dev database
- New migrations: `pnpm prisma migrate dev --name <description>`
- Seed data: `pnpm prisma db seed`
## Conventions
- API routes return `{ data, error }` shape, never throw raw errors to the client
- Use `zod` schemas for all request body validation, colocated in `schemas/`
- Component files are named `PascalCase.tsx`; hooks are `useCamelCase.ts`
- No default exports except for Next.js page files
## Git
- Commit messages: imperative mood, no ticket numbers in the subject line
- Never force-push to `main` or `staging`
- PRs must update `CHANGELOG.md` under "Unreleased" if they touch public API routesNotice what's missing: no philosophy, no "we value clean code" filler. Every line is either a command Claude Code can run or a rule it can check itself against. That's the bar. If a line in your CLAUDE.md couldn't plausibly change what Claude does in the next five minutes, it probably belongs in a wiki instead.
Commit this file to the repo. Treat edits to it like edits to a linter config — small, reviewed PRs, not a growing pile of paragraphs nobody prunes. When conventions change, the PR that changes the convention should also update CLAUDE.md in the same commit. Otherwise you get drift: the code says one thing, the file says another, and now the assistant is actively teaching new hires the wrong pattern.
Layering: project, personal, and directory-scoped memory
Claude Code supports memory at more than one level, and understanding the layering is what makes team sharing possible without stepping on individual preferences.
- Project memory (
CLAUDE.mdat repo root) — shared, committed, applies to everyone who opens the repo. - Personal global memory (
~/.claude/CLAUDE.md) — private, per-developer, applies across every project on that machine. This is where an individual's own habits live: how they like commit messages formatted for their own scratch repos, personal shortcuts, nothing project-specific. - Directory-scoped memory — nested
CLAUDE.mdfiles in subfolders, which Claude Code picks up when working in that subtree. Useful for a monorepo wherepackages/api/CLAUDE.mdhas API-specific rules andpackages/web/CLAUDE.mdhas frontend-specific ones, layered on top of the root file.
The team workflow problem specifically lives in the first bucket. If your team convention is sitting in someone's personal ~/.claude/CLAUDE.md, it's invisible to everyone else and it evaporates the day that person is on vacation, or leaves. The fix is a standing rule: anything that should be true regardless of who's typing goes in the repo's `CLAUDE.md`, not a personal one. Personal files are for personal style, not project truth.
For monorepos, push convention files down to the narrowest scope that's still shared. A packages/payments/CLAUDE.md documenting "always mock the Stripe webhook signature in tests, never hit the real API" is something every engineer touching that package needs — new hires included — so it belongs at that directory level, committed, reviewed like code.
Skills: packaging a workflow instead of re-explaining it
CLAUDE.md is good for facts and rules. It's a poor fit for multi-step workflows — the kind of thing where you'd normally say "first check X, then run Y, then format the output like Z." That's what skills are for.
A skill is a self-contained folder (typically under .claude/skills/ in the repo, or a shared plugin directory) with a SKILL.md describing when to trigger and what to do, plus any supporting scripts or templates. The team benefit is that once one engineer builds a skill for a recurring task, everyone on the repo gets it the moment they pull the branch — no re-teaching, no tribal knowledge lost to attrition.
A concrete example: teams that do a lot of database migrations often build a skill that codifies the safe migration workflow, because "safe migration" in most codebases is actually five separate steps that are easy to skip under deadline pressure.
---
name: safe-migration
description: Use when creating or reviewing a database migration in this repo. Triggers on "add a migration", "change the schema", "new column", or "migrate the database".
---
# Safe migration workflow
1. Check for an existing migration touching the same table in the last 30 days.
If one exists, ask whether this should be squashed into it instead of adding a new one.
2. Write the migration with an explicit `down` method — never leave it empty.
3. For any column that adds a NOT NULL constraint on a table with existing rows,
generate a two-step migration: add nullable first, backfill, then add the constraint
in a follow-up migration. Never do it in one step on a table with production data.
4. Run `pnpm prisma migrate dev --name <description>` and confirm the generated SQL
matches the intent by reading the generated file before committing.
5. Add a one-line note to `CHANGELOG.md` under "Unreleased" describing the schema change.That's a skill, not a paragraph in CLAUDE.md, because it's conditional and procedural — it only applies when someone is actually touching the schema, and it has branching logic ("if a migration exists... if the table has existing rows..."). Cramming that into the always-loaded project memory would waste context on every single session, even ones that never touch the database. Skills load on demand, which is exactly the right cost model for workflows that are specific but occasionally needed.
Where to put shared skills so the whole team gets them
The practical mechanics matter here. Skills can live in a few places, and the placement decision is the difference between "everyone has this" and "only I have this."
.claude/skills/<skill-name>/SKILL.mdinside the repo — committed to version control, so it ships with the codebase. Anyone who clones the repo and runs Claude Code in it inherits every skill in that folder automatically.- A shared internal plugin, installed the same way across the team's machines, for skills that need to work across multiple repos (e.g., a company-wide "how we write incident postmortems" skill that isn't tied to one codebase).
- Personal
~/.claude/skills/— for an individual's own shortcuts that aren't ready for the team yet, or genuinely never will be (a personal changelog formatter, say).
The rule of thumb that keeps this clean: if you find yourself explaining the same multi-step process to Claude Code more than twice, and it's specific to this codebase, it's a candidate for .claude/skills/ in the repo, not your personal folder. Committing it means code review catches bad steps before they become team habit, the same way you'd review a CI config change.
A good habit for teams scaling this past two or three skills: keep a short index in CLAUDE.md itself, listing what skills exist and one line on when each fires. Claude Code discovers skills on its own, but a human skimming the repo for the first time benefits from the same map:
## Available skills (.claude/skills/)
- safe-migration — schema changes, always two-step for NOT NULL on populated tables
- api-endpoint-scaffold — new REST route, includes zod schema + test stub
- release-notes — formats CHANGELOG entries from merged PR titlesHooks: making conventions mandatory instead of optional
CLAUDE.md and skills both rely on Claude reading and following instructions. That's usually enough, but for anything that absolutely cannot slip — no committing .env files, no skipping the linter, no pushing straight to main — teams lean on hooks instead.
Hooks are shell commands that Claude Code's harness executes at defined points (before a tool runs, after a tool runs, when a session starts, and so on), configured in settings.json. Unlike a rule in CLAUDE.md, a hook isn't a suggestion the model might deprioritize under a long context — it's an actual gate the harness enforces.
A common team pattern is a pre-commit-style hook that blocks Claude Code from running git commit if the linter or type checker hasn't passed, so a rushed session can't slip a broken build into history:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "scripts/check-before-commit.sh"
}
]
}
]
}
}And scripts/check-before-commit.sh does the actual gating:
#!/usr/bin/env bash
# Blocks git commit if typecheck or lint is failing.
if echo "$CLAUDE_TOOL_INPUT" | grep -q "git commit"; then
pnpm typecheck || { echo "Typecheck failing, commit blocked" >&2; exit 1; }
pnpm lint || { echo "Lint failing, commit blocked" >&2; exit 1; }
fi
exit 0This is the piece that separates a team convention that's "usually followed" from one that's actually enforced. Commit settings.json and the hook scripts to the repo alongside CLAUDE.md and skills, and every teammate's Claude Code session inherits the same guardrails — including the guardrail against the assistant itself skipping steps under pressure to just get something done.
Onboarding: the real test of whether this is working
The honest test of a shared Claude Code setup isn't whether the person who built it likes it — it's what happens on day one for someone new. If a new engineer clones the repo, opens Claude Code, and asks it to "add an endpoint for updating user preferences," does the resulting code already match team conventions without anyone explaining anything?
If the answer is yes, CLAUDE.md, the skills folder, and the hooks are doing their job. If the answer is "well, they'd need to know to ask about the zod schema pattern," that's a gap — and it's a gap you can close the same day you notice it, by adding a line to CLAUDE.md or turning the missing step into a skill.
A useful team habit: treat every "wait, you didn't know we do it that way?" moment in a PR review as a signal that something belongs in shared config, not just in the reviewer's head. That single habit, repeated over months, is what turns a repo's Claude Code setup from a thin config file into something that actually captures how the team works. It also catches convention drift — when two senior engineers disagree in a PR thread about whether migrations need a down method, that's exactly the moment to update the skill and settle it for good, rather than re-litigating it in every future PR.
Reviewing changes to shared AI configuration like you review code
Because CLAUDE.md, .claude/skills/, and settings.json are just files in the repo, they go through the same PR process as everything else — which is the point. But it's worth being deliberate about what reviewers should look for, because reviewing a skill is a slightly different exercise than reviewing application code.
For a CLAUDE.md change, ask: is this rule specific enough that Claude Code can actually check itself against it, or is it vague enough that it'll get ignored under context pressure? "Write clean code" fails that test; "no default exports except Next.js page files" passes it.
For a new skill, ask: does the trigger description avoid false positives? A skill described as "Use when working with dates" will fire constantly and clutter every session; "Use when generating an invoice PDF with line-item tax calculations" fires only when actually relevant. Overly broad triggers are the most common way team skills go from helpful to annoying, and it's an easy thing to catch in review before it ships to everyone.
For a hook change, ask the boring but critical question: what happens when this hook itself fails or hangs? A hook that blocks every commit because a flaky network check times out will get disabled by the first frustrated engineer who hits it, and then nobody re-enables it. Keep hooks fast, deterministic, and narrowly scoped to the one thing they're gating.
Handling drift across a growing team
The failure mode that shows up six months in isn't "we never set this up" — it's "we set it up once and it slowly stopped matching reality." Frameworks upgrade, API shapes change, a service gets migrated off a vendor, and the CLAUDE.md file still confidently tells every new session to use the old pattern.
A few practices keep this in check without turning into its own maintenance burden:
- Tie config updates to the PR that changes the underlying reality. If a PR changes how auth tokens are validated, the same PR updates the relevant skill or
CLAUDE.mdsection. Don't let it become a separate, deprioritized follow-up ticket that never gets picked up. - Prune, don't just add. Skills and rules accumulate easily and get deleted rarely. Periodically — a quarterly pass is enough for most teams — read through
.claude/skills/and ask whether each one still matches current practice. Delete or rewrite the ones that don't. - Watch for the assistant re-litigating settled decisions. If Claude Code keeps proposing a pattern the team explicitly moved away from, that's a strong signal a rule needs updating, not that the model is being stubborn. The config is the source of truth it's working from; if the config is stale, the output will be too.
- Assign light ownership. Someone doesn't need to be a full-time maintainer, but having one person who glances at diffs to
CLAUDE.mdand.claude/skills/the way a build cop watches CI catches quality drift before it spreads to every new session across the team.
None of this is exotic process — it's the same discipline teams already apply to linter configs and CI pipelines, applied to the layer that now shapes how an AI assistant behaves inside the codebase. The teams that get real leverage from Claude Code aren't the ones with the cleverest individual prompts; they're the ones who treated shared configuration as seriously as they treat shared code.
Bringing it together
None of the individual pieces here are complicated on their own — a markdown file, some folders of instructions, a JSON config for hooks. What makes them powerful is treating them as a system: CLAUDE.md for facts and standing rules, skills for multi-step workflows that fire conditionally, hooks for the handful of things that must never slip regardless of what the model decides in the moment. Layer project memory under personal memory so team truth stays shared and individual preference stays private. Review changes to all of it the way you'd review a CI pipeline, because that's functionally what it's become.
Do this well and the payoff compounds. A new engineer's first PR looks like it was written by someone with six months of context, because in a very real sense it was — that context just lived in files instead of a person's memory. The alternative, where every developer's Claude Code is a slightly different assistant shaped by whatever they individually figured out, is the thing worth actively avoiding as teams scale past one or two people using these tools daily.
If you're getting started with Claude Code and want a structured path through the fundamentals before tackling team-scale configuration, the Claude Code Tutorial for Beginners course on teachyou.ai walks through the CLI from first principles up through exactly the workflows covered here — CLAUDE.md, skills, and hooks — with hands-on exercises you can apply directly to your own team's repo.
AI CodingShip full-stack AI apps at conversation speed — specs, agents, deploys, all from the terminal.
Claude CodeGo from zero to confident with Claude Code, the terminal agent that reads, edits, runs, and verifies real code.
Related reading