Free Strategy Call
ios swift ai claude-code engineering team-process

CLAUDE.md for an iOS Team: What to Put In It (and What to Leave Out)

A practical guide to writing a CLAUDE.md an iOS team actually follows, from someone who runs one across a multi-repo AI-agent pipeline daily.

My first CLAUDE.md for a client project was 400 lines long. Architecture diagrams, naming conventions, a full history of why we picked MVVM over TCA, a style guide that duplicated SwiftLint’s config. Claude Code read all 400 lines on every single turn. It also ignored about half of them, because a rule buried on line 340 competes with nothing for the model’s attention by the time it gets there.

I cut it to 60 lines over two weeks. Same team, same codebase, fewer violations of the rules that actually mattered.

The file is not documentation

The mistake is treating CLAUDE.md like a README: a place to write down everything true about the project. A README is read once, by a human, who skims for the section they need. CLAUDE.md is read in full, by a model, on every turn, and it competes with the actual task for the model’s limited attention. Every line you add is a line that dilutes every other line.

This is the same judgment call I made putting Claude Code subagents into a Fortune-500 iOS team’s review loop: deciding which few things actually needed to be said, and trusting established patterns for the rest, is what made the agent useful. That review loop measured out at about 30 minutes saved per engineer per day, and the metric held because we encoded only the parts a human would otherwise have to re-explain every single review.

What actually belongs in it

Non-obvious project conventions. Not “we use MVVM” (that’s visible in five minutes of reading the code), but “ViewModels never import UIKit, only SwiftUI” if that’s a real rule someone violated once and it cost a day. The bar is: would a competent engineer new to this repo get it wrong without being told? If yes, it goes in. If the code itself already answers the question, it doesn’t.

Hard constraints that aren’t visible in the diff. App Store review requirements, a minimum supported iOS version the codebase doesn’t obviously reflect yet, a performance budget on a specific screen because a previous ship got rejected for jank. These are exactly the kind of thing an agent has no way to infer from the code alone.

Workflow gotchas specific to this repo. Which branch triggers a real deploy. Which test suite is flaky and safe to ignore versus which one is a hard gate. Where the actual source of truth lives when two files seem to disagree. I run a merge gate across several of my own repos now (a script that only merges once CI is green and a review pass found nothing blocking) and the single highest-value line in each of those CLAUDE.md files is the one sentence explaining that the gate exists and why a raw git merge bypasses something on purpose.

What the agent should never touch unsupervised. For me, that’s anything under public/_headers, deploy configs, or a security-sensitive file. Naming the boundary explicitly is cheaper than discovering it was needed after the fact.

What to leave out

Anything git blame or the README already answers. If a new human engineer would read the file to find it, the model doesn’t need a duplicate copy competing for attention.

Aspirational rules nobody actually follows. A style guide that isn’t enforced by a linter is a wish, not a constraint, and every unenforced rule in the file trains the model (correctly) that the rules in this file are optional.

Architecture history and rationale. Why you picked SwiftUI over UIKit three years ago is a great onboarding doc. It is not information that changes what the agent does on the task in front of it right now.

Anything that duplicates what a linter, a type system, or a test suite already enforces mechanically. If SwiftLint already blocks force-unwraps, writing “avoid force-unwraps” in CLAUDE.md adds nothing except length.

This is the same principle behind treating CI/CD and modularization as their own deliverable rather than background debt on a Fortune-500 mobile program I worked on: standardize the few things that actually compound across the whole team, and leave the rest to local judgment instead of trying to legislate everything from one file.

A concrete before/after

Before, in that 400-line file: a paragraph explaining the team’s Git branching strategy, a full enumeration of every third-party dependency and why it was chosen, and a style section that repeated SwiftLint rule-for-rule.

After: one line pointing at the linter config instead of repeating it, one line naming the merge gate and why, one line on the ViewModel/UIKit boundary because that was the one rule an agent (and, honestly, a couple of humans) had actually gotten wrong before.

The file that’s harder to write is the short one. It requires deciding what actually matters instead of writing down everything you know.

Where this breaks down

None of this holds if the codebase itself is inconsistent. A CLAUDE.md pointing at “the linter enforces this” is only as good as the linter config being current and actually run in CI. If your test suite is flaky enough that “ignore it” is standard practice, that’s worth fixing before it’s worth documenting.

What’s the longest-standing rule in your own CLAUDE.md that you’re not sure is still true?

Contact

Let's
connect

Ready to discuss your startup's technical challenges? Let's talk about how I can help you build and scale.

Location Valencia, Spain CET Timezone
From Ukraine 🇺🇦 Kyiv
LinkedIn @oleh-veheria Connect with me
Languages 4 Languages EN, UA, RU, ES