Claude Projects: How I Finally Organized My Codebase Context
For the longest time, my workflow with ChatGPT and Claude was an unmitigated disaster. Every time I started a new feature branch, I found myself manually copying and pasting five different React components, my Prisma database schema, my Zod validation files, and my Tailwind configuration just to give the model enough context to write a helpful answer. That completely changed the day I started using Claude Projects.
If you are on the Claude Pro or Team plan, you have access to "Projects". Think of a Project as a persistent, isolated workspace with its own dedicated vector/knowledge base extending up to 200,000 tokens per chat. However, how you format and upload your data into that workspace determines whether Claude becomes a senior engineer or a hallucinating intern. Here is my exact methodology for organizing context, along with the Python context-compilers I use daily.
The Limits of the 200k Token Window
To build an effective Claude Project, you must first respect the math. Claude 3.5 Sonnet offers a 200,000-token context window. A token is roughly 4 characters in English. This means you have about 150,000 words or 750 pages of raw text available per Project.
For a standard Next.js application, 200k tokens is usually more than enough to upload your entire src/ directory, your package.json, your environment variable templates, and your database schema. But you cannot simply upload 500 isolated .ts files. Claude's web UI restricts the number of individual file uploads per Project (currently around 5-10 files depending on the day).
The solution? Context Compilation. You must concatenate your entire codebase into a single, highly structured Markdown file.
The "Aha" Moment: XML Structuring
The breakthrough came when I realized Anthropic models are explicitly trained to parse XML tags. If you throw a 100,000-line text file at Claude without structure, it will suffer from massive "lost-in-the-middle" recall issues. If you wrap each file in <document> and <source_file> XML tags, Claude's attention mechanism locks onto the structure perfectly.
The Python Context Compiler Script
I use a custom Python script to walk my repository, filter out node_modules and build artifacts, and compile my codebase into a single `claude_context.txt` file formatted with XML tags. This is the exact script I run before uploading to a Claude Project.
import os
import pathspec
def get_gitignore_spec(root_dir):
"""Parses .gitignore to ensure we don't compile garbage into our prompt."""
gitignore_path = os.path.join(root_dir, '.gitignore')
lines = []
if os.path.isfile(gitignore_path):
with open(gitignore_path, 'r') as f:
lines = f.readlines()
# Always ignore these massive directories
lines.extend(['node_modules/', '.git/', '.next/', '__pycache__/', 'dist/'])
return pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, lines)
def compile_project_context(root_dir, output_file):
spec = get_gitignore_spec(root_dir)
allowed_extensions = {'.js', '.jsx', '.ts', '.tsx', '.py', '.json', '.prisma', '.md'}
with open(output_file, 'w', encoding='utf-8') as out:
out.write("<repository_context>\n")
out.write("This file contains the complete source code for the project.\n\n")
for dirpath, dirnames, filenames in os.walk(root_dir):
# Filter directories in-place based on gitignore
dirnames[:] = [d for d in dirnames if not spec.match_file(os.path.relpath(os.path.join(dirpath, d), root_dir))]
for f in filenames:
file_path = os.path.join(dirpath, f)
rel_path = os.path.relpath(file_path, root_dir)
ext = os.path.splitext(f)[1].lower()
if ext in allowed_extensions and not spec.match_file(rel_path):
try:
with open(file_path, 'r', encoding='utf-8') as infile:
content = infile.read()
# Wrap each file in XML tags that Claude's attention mechanism loves
out.write(f"\n<file path='{rel_path}'>\n")
out.write(content)
out.write(f"\n</file>\n")
except Exception as e:
print(f"Skipping {rel_path}: {e}")
out.write("</repository_context>")
print(f"Compilation complete. Output saved to {output_file}")
# Run the script on the current directory
if __name__ == "__main__":
compile_project_context(".", "claude_project_context.xml")When you upload claude_project_context.xml to your Project Knowledge base, Claude processes the entire structure. From that point on, you never have to paste code again. You simply ask: "Look at <file path='src/components/Header.jsx'> and refactor it to use the new exact CSS variables defined in my layout file." Focus on the high-level architecture; Claude already knows the implementation details.
The Most Important Document: Rules.md
While the compiled codebase provides the "What," you still need to provide the "How." Every single one of my Claude Projects includes a manually written Rules.md file uploaded alongside the codebase.
This document dictates the persona, the architectural constraints, and the formatting rules. Here is an exact excerpt from the Rules.md I use for Next.js App Router projects:
<project_rules>
<persona>
You are a Staff Frontend Engineer specializing in Next.js 14+ (App Router),
React Server Components, and Zod validation. You value accessibility (a11y),
semantic HTML, and robust error handling.
</persona>
<technical_constraints>
1. NEVER use the old pages/ router. Always use the app/ directory.
2. Default to React Server Components (RSC) unless interactivity is explicitly
required. If so, place 'use client' at the very top of the file.
3. Use Tailwind CSS for all styling. Never use inline styles.
4. For form handling, always use React Hook Form paired with Zod resolvers.
5. All data fetching must happen on the server side unless in a client component.
</technical_constraints>
<output_formatting>
1. Do not omit code for brevity. Never write "// ... existing code ...".
Always output the full, complete file so I can copy-paste it directly.
2. When proposing a new component, also propose the exact file path where it
should be saved based on the current repository structure.
</output_formatting>
</project_rules>The Limitations to Watch Out For
As much as I love Projects, they aren't flawless. The biggest issue is staleness and drift. When you update your code in your local VS Code environment, those changes aren't magically synced to the Claude Project.
If you spend three hours refactoring your database schema, and then ask Claude to write a new Prisma query, it will write a query based on the outdated schema currently sitting in its Project Knowledge base. You have to build a habit: every time you make a systemic change to your codebase, you must rerun the Python compiler script, delete the old `.xml` file from Claude, and upload the new one.
Eventually, tools like Cursor and GitHub Copilot Workspace (which natively sync your local files to the LLM context) will make the web UI obsolete. But until those tools perfectly integrate with the sheer intelligence of Claude 3.5 Sonnet, a manual Claude Project paired with my XML context compiler script remains the absolute most powerful way to build software today.