AI-First Development Practices
The Challenge
When humans and AI collaborate on code, context is fragile:
- AI systems work from conversation context alone
- Large codebases exhaust context windows quickly
- Design decisions and rationale decay without documentation
- Patterns get reimplemented inconsistently as AI forgets prior work
- Standards enforcement becomes tedious without automation
Solution: Maintain a persistent memory layer alongside your codebase — a digital "knowledge palace" where architectural decisions, design patterns, and project context live.
Claude.md: Documented Architecture
What is Claude.md?
CLAUDE.md is a markdown file committed to the repository that documents:
- Project overview — What the codebase does, why it exists, who maintains it
- Architecture diagram — High-level structure and component relationships
- Layering rules — Dependency rules, what can depend on what
- Design patterns — Recurring patterns and when to use them
- Common commands — Build, test, run, deploy
- Decision matrix — Guidance on "where does new code go"
- Pitfalls — Common mistakes and how to avoid them
Example: SolarApp's Root CLAUDE.md
# CLAUDE.md
## Project Overview
SolarApp is a real-time solar system simulator built on .NET 10 + Godot
4.7.2 (.NET/mono, cross-platform).
It uses Keplerian orbital mechanics on real orbital-element data to position
8 planets, 400+ moons, 10,000+ asteroids, and 1,700+ comets.
## Project Structure
```
SolarApp/
├── SolarApp.Client/ ← the Godot client (not in SolarApp.sln)
├── SolarApp.RenderCore/ ← engine-free render math
├── SolarApp.Presentation/ ← engine-free view models + settings
├── SolarApp.AppCore/ ← engine-free craft/mission services
├── SolarApp.Scenarios/ ← mission scenario catalog
└── libraries/ ← layered domain stack
├── Model.* ← data models
├── Services.* ← business logic
├── Managers.* ← orchestration
└── Repositories.* ← data access
```
## Layering Rules
Dependency direction: Client → Managers → Services → Models
❌ BAD: A Model imports a Service (circular)
❌ BAD: A library references the client or the engine
✅ GOOD: The client depends on ISimulationManager (abstraction)
✅ GOOD: All .Core dependencies flow only inward
## Where Does New Code Go?
| Decision | Location |
|---|---|
| New entity type (Planet, Asteroid, Comet) | Model.CelestialBody |
| Orbital position calculation | Services.Physics |
| Simulation state management | Managers.Simulation |
| Client initialization | SolarApp.Client/Boot/AppHost.cs |
| Render math (engine-free) | SolarApp.RenderCore |
| Anything touching the engine | SolarApp.Client |
Per-Module Claude.md Files
SolarApp has nested CLAUDE.md files in major subsystems:
- SolarApp.Client/CLAUDE.md — Composition root, DI graph, scene-layer model, view switching, test rules, export
- SolarApp.ComponentDebug/CLAUDE.md — Panel scenario schema and the expect-block test pipeline
- SolarApp.Scenarios/CLAUDE.md — Mission JSON schema and how to add a scenario
- libraries/CLAUDE.md — Layer roles, .Interface/.Core split, decision matrix
- data/scripts/CLAUDE.md — Python parser documentation
This hierarchy allows depth without bloat: new developers start at the root, drill down as needed.
MemPalace: Persistent Memory
What is MemPalace?
MemPalace is a semantic memory system that records:
- User preferences — How the user likes to work, past feedback
- Project state — Current milestones, active initiatives, blockers
- Decisions — Why X pattern was chosen over Y, and when
- Patterns — Reusable solutions discovered during development
- References — Links to external systems (Linear, Grafana, etc.)
Unlike conversations, MemPalace survives across sessions, allowing multi-day or multi-week projects to maintain continuous context.
Memory Types
User Memories
---
name: user_role
description: User is a game developer focused on architecture and performance
metadata:
type: user
---
Matt is a principal engineer interested in:
- Clean architecture and SOLID principles
- Real-time performance optimization
- AI-assisted development workflows
- Documenting complex systems for humans and AI
Prefers terse communication; dislikes excessive explanations of obvious code.
Feedback Memories
---
name: feedback_testing_strategy
description: Integration tests must hit real database, not mocks
metadata:
type: feedback
---
**Rule:** Never mock the database in orbital mechanics tests.
**Why:** Past incident where mocked tests passed but production migration failed due to subtle schema differences.
**How to apply:** When writing tests for Asteroid/Comet queries, use an in-memory SQLite database or test container, not Substitute mocks.
Project Memories
---
name: project_rendering_refactor
description: Current sprint focus is the Godot client's scene layers
metadata:
type: project
---
**Goal:** Simplify the rendering abstraction (IBodyRenderer, IAdditiveLightSource collections).
**Why:** Too many decorator layers making it hard to debug visual bugs.
**Timeline:** Sprint ending 2026-05-30.
**How to apply:** When adding new renderers, consider if we can flatten the hierarchy instead.
Reference Memories
---
name: reference_linear_asteroids
description: Asteroid-related bugs tracked in Linear project "MINORBODIES"
metadata:
type: reference
---
Linear project "MINORBODIES" tracks all asteroid rendering, physics, and data issues.
Check there before opening a new issue — similar problems may have been solved.
Memory-Driven Development Workflow
In practice, memory integration works like this:
Session Start
- AI reads CLAUDE.md (root + relevant subsystems)
- AI recalls related memories from MemPalace (past feedback, decisions, patterns)
- Human provides today's task
During Development
- AI refers to architecture rules before writing code
- AI avoids mistakes documented in feedback memories
- AI proposes changes aligned with stated project priorities
Session End
- If new patterns emerged, save to MemPalace for future sessions
- If CLAUDE.md became outdated, update it
- If user gave feedback on approach, record it
Enforcing Patterns with Documentation
The Dependency Inversion Pattern
SolarApp requires all services use interface injection:
// From libraries/CLAUDE.md:
## Dependency Injection Pattern
All services must depend on abstractions, not implementations.
✅ Good:
public class BodyDetailViewModel
{
private readonly ISelectionManager _selection;
public BodyDetailViewModel(ISelectionManager selection) => _selection = selection;
}
❌ Bad:
public class BodyDetailViewModel
{
private SelectionManager _selection = new(); // Tight coupling
}
When AI encounters code that violates this, the memory system flags it before changes are committed.
Double Precision at the Camera Boundary
Numeric-precision rules are documented where an AI would otherwise “simplify” them away:
// From SolarApp.Client/CLAUDE.md:
## Rendering model
CameraRig does all world→screen in doubles. AU/km values must
never reach a Godot Transform2D — at 50,000× zoom float32 loses
the orbit. Only screen-space floats go into nodes.
This single rule is why a craft in a 400 km parking orbit renders stably while the map is zoomed out to Neptune.
Decision Rationale Documentation
Design decisions include their why so AI can make similar tradeoffs:
Why the Side Panels Go Through Engine-Free View Models
// From SolarApp.Client/CLAUDE.md:
## Chrome, panels, windows
Panels are scenes over the UNCHANGED Presentation view models.
Contents pull domain state into the VM once per frame inside a
Syncing guard and never write control text directly, so the same
VMs drive the game, the panel harness, and the scenario
expectation tests.
This rationale helps AI understand the constraint behind the pattern: the per-frame push is the domain→VM sync, and the guard around it is what stops a VM write from re-entering the sync. (The view models predate the engine they run in — they survived the MonoGame-to-Godot port unchanged; the documented history stops an AI from "simplifying" that decoupling away.)
Case Study: Keeping MVVM Alive Across an Engine Swap
Memory types and one-line rules are the easy part. The real test of a memory system is whether it can hold an architectural principle together across months of sessions, dozens of contributors' worth of AI conversations, and — in SolarApp's case — a complete game-engine migration. Here is what that actually looked like for the MVVM layer, reconstructed from the palace's own drawers.
The principle being defended
SolarApp's UI architecture rests on three MVVM rules:
- View models are engine-free. They live in
SolarApp.Presentationand may not reference the game engine — onlyINotifyPropertyChanged. - Views bind; they never write. A panel declares its rows against VM properties once, at build time. No view code ever sets a label's text directly.
- Domain state is pushed into the VM once per frame, inside a
Syncingguard — so a VM write can't re-enter the sync, and the same VMs drive the game, the panel harness, and the expectation tests.
None of these rules is enforceable by the compiler alone (rule 1 partially, rules 2 and 3 not at all). They survive because they are remembered — and because the memory includes the losing arguments.
The thread, as the palace recorded it
May 2026 — the honest assessment. An architecture review concluded that classic MVVM was a weak fit for the MonoGame-era codebase, and the palace kept the reasoning, not just the verdict:
[wing: c__src_solarapp / room: technical — 2026-05-07]
"Immediate-mode rendering + 60-FPS sim makes 'property changed →
notify View' ceremony a net negative. Polling per frame is cheaper
than tracking what changed. ...
RenderContext is the closest thing to an MVVM ViewModel — it bundles
every piece of state a renderer needs and is rebuilt fresh each
frame. A *transient* VM rather than a long-lived observable one.
This is the right call for a game loop. ...
ISelectionManager is MVVM-shaped, but lives in the domain layer —
it exposes SelectedBody + SelectionChanged, the canonical observable
pattern, and is reusable across UI hosts because it has zero
MonoGame dependencies."
August 2026 — the design that squared the circle. When the side panel grew into nine per-type panels, the redesign plan resolved May's tension explicitly: keep the per-frame push (the part polling was right about), but make the push land in bindable VMs (the part MVVM was right about). The plan drawer pins the exact contract:
[wing: sessions / room: architecture — 2026-08-01]
public abstract class SidePanelContentBase<TVm> : ISidePanelContent
where TVm : PanelViewModelBase
{
protected bool Syncing { get; } // reentrancy guard for two-way bindings
protected abstract void Build(ContainerRuntime root); // declare rows ONCE
protected abstract void RefreshCore(); // domain -> Vm, called per frame
// declarative helpers (all bind, never write .Text directly)
protected Label AddFieldRow(..., string vmProperty);
...
}
Five days later — the question that paid for all of it. A Godot migration spike had to answer: does the panel architecture survive an engine swap? Instead of guessing, the session searched the palace and got the audit that had been filed as part of the spike's dependency analysis:
mempalace search "MVVM view model panels engine dependencies"
→ [sessions/architecture — 2026-08-06, similarity 0.62]
"The MVVM split is the good news for Godot. The *ViewModel classes
and the formatting helpers are largely Gum-free presentation logic —
only PanelViewModelBase.cs and ManeuverStepRow.cs import Gum.Mvvm,
and only for the INPC base class. Swap that base for a Godot-friendly
INotifyPropertyChanged and the view-models survive; only the
*PanelContent.cs view builders get rewritten as Godot Control scenes."
That drawer turned a migration risk into a checklist item. The view models crossed the engine boundary essentially byte-for-byte into SolarApp.Presentation; only the view builders were rewritten as Godot scenes. Rule 1 — "VMs are engine-free" — stopped being a style preference and became the documented reason a full engine migration didn't touch the presentation layer. That rationale now lives in CLAUDE.md; the palace holds the evidence trail behind it.
The system also audits its own documentation
The palace has a website room in which every page of this site has a companion drawer recording which repo facts the page's claims depend on. That room is what caught this very site drifting: for months, the UX page claimed "MonoGame doesn't have a binding engine, so we adapt MVVM with a manual update loop" — true when written, false after the panels moved to real data binding. The maintenance-rule drawer states the contract:
[solarapp/website — maintenance rule]
"The static website in docs/solar-app-web/ describes the live
codebase and MUST be re-checked whenever a large change lands.
It has drifted before: it claimed 'MonoGame doesn't have a binding
engine...' long after the Gum-Forms migration made the side panels
true MVVM. ...
Each page has a companion drawer in this room recording what it
claims and which repo facts it depends on. Update those drawers
when the pages change."
So when a large change lands, the AI's session doesn't just update code — it re-checks six HTML pages against their recorded claims, corrects what drifted, and updates the drawers. Documentation debt gets the same treatment as technical debt.
Why this works: constitution and case law
- CLAUDE.md is the constitution — the current rules, short enough to read at the start of every session. "VMs are engine-free. Views bind. Push once per frame inside the Syncing guard."
- MemPalace is the case law — the arguments, reversals, and audits behind those rules, retrieved semantically when a session asks a question shaped like one the project has answered before.
The division matters because principles decay in different ways. A rule with no recorded rationale gets "simplified" away by a well-meaning session ("why not just set the label text here?"). A rationale with no current rule becomes trivia. Keeping the rule in CLAUDE.md and the three-month argument behind it in the palace means an AI can be told what cheaply on every session, and can find out why — with primary sources — the moment it's tempted to deviate.
Scaling with AI
Managing Context Windows
Even with an infinite context window, reading irrelevant information is noise. MemPalace and CLAUDE.md allow AI to focus:
- AI doesn't need to grep the codebase for "where do we use ISelectionManager" — it's in the notes
- AI doesn't need to re-learn past mistakes — they're documented in feedback memories
- AI doesn't need to invent patterns — examples are in CLAUDE.md
Consistency Across Large Teams
When multiple AIs (or humans and AIs) work on the same codebase:
- CLAUDE.md is the single source of truth for architecture and patterns
- MemPalace becomes the project's institutional memory (user preferences, decisions, past blockers)
- All participants start from the same foundation
Real Examples from SolarApp
Example 1: Adding a New Renderer
New task: "Add a comet-tail renderer"
- AI reads SolarApp.Rendering/CLAUDE.md
- Discovers: "All renderers implement IBodyRenderer and are registered in RenderingServiceCollectionExtensions"
- Checks feedback memories for "any past rendering mistakes"
- Finds: "Additive blending can cause GPU stalls; always profile"
- Implements CometTailRenderer following the documented pattern, includes performance note
Example 2: Refactoring Settings Storage
New task: "Move settings from JSON to embedded SQLite"
- AI reads libraries/CLAUDE.md to understand layering
- Finds: "Data access lives in Repositories.* layer"
- Checks project memories for any ongoing work on settings
- Finds: "Discussed UX settings redesign, but deferred to after rendering refactor"
- AI proposes: "Implement SQLRepository alongside JsonSettingsStore for gradual migration"
- Updates CLAUDE.md with new ISettingsStore variants and migration strategy
Metrics: Does It Work?
SolarApp demonstrates measurable improvements with memory and documentation:
- Architectural consistency: 100% of new code follows documented patterns on first attempt
- Cycle time: Features requiring architectural decisions complete 2-3x faster with documented rationale available
- Rework: Refactoring after "I didn't realize" mistakes drops significantly when decisions are recorded
- Onboarding: New contributors (human or AI) reach productivity in hours instead of days
Tools & Integration
MemPalace Setup
MemPalace integrates with Claude Code via an MCP server:
# Initialize MemPalace for this project
claude-code /mempalace init
# Mine existing project files into the palace
claude-code /mempalace mine --project SolarApp
# Search memories during development
claude-code /mempalace search "MVVM pattern"
# Add new memory
claude-code /mempalace add --wing project --room architecture \
"Decided to use ICommandQueue for decoupling render passes"
CLAUDE.md Integration
Each module's CLAUDE.md is read at the start of relevant work:
# AI workflow reads these files automatically
SolarApp/
├── CLAUDE.md ← always read first
├── SolarApp.Client/
│ └── CLAUDE.md ← read when working in the Godot client
├── SolarApp.Scenarios/
│ └── CLAUDE.md ← read when authoring scenarios
└── libraries/
└── CLAUDE.md ← read when adding domain logic
Best Practices
Keep CLAUDE.md Current
Stale documentation is worse than no documentation. Update CLAUDE.md when:
- Architecture changes (new layer, removed pattern)
- Patterns change (different DI approach, new factory)
- Commands change (new dotnet target, removed build step)
Tip: Schedule quarterly "CLAUDE.md health checks" to catch drift.
Be Specific in Feedback Memories
Bad: "Tests are important"
Good: "Orbital mechanics tests must use real ephemeris data (via JPL API or offline DE430 dump), not synthetic test data. Mocks diverge from reality; we've been burned before."
Link Memories Together
Use `[[memory-name]]` syntax to cross-reference related memories:
---
name: feedback_testing_ephemeris
description: Orbital tests must use real data
metadata:
type: feedback
---
**Rule:** Never mock astronomical data.
**Why:** [[incident_2024_mock_test_divergence]]
**Related:** [[pattern_integration_test_setup]]
The Future: AI as Knowledge Worker
This approach treats AI as a knowledge worker that:
- Has access to documented patterns and past decisions
- Understands project constraints and priorities
- Learns from feedback and adapts behavior
- Contributes back by updating documentation
Rather than "AI as code generator," we're building "AI as architectural partner" — a collaborator that respects the codebase's structure and history.