Copilot  

GitHub Copilot for JetBrains: Testing Memory with Large Enterprise Codebases

Introduction

AI coding assistants are becoming more useful as development tasks become larger and more connected.

A developer might start a conversation by asking an AI assistant to explain a class. Later, the same developer may ask it to refactor the class, add tests, update an API, or investigate a related issue.

The challenge is context.

In a small project, a developer can provide enough information in a single conversation. In a large enterprise codebase, important information may be spread across hundreds or thousands of files.

This is where AI memory becomes interesting.

Instead of repeatedly explaining the same project conventions, architecture decisions, and development preferences, an AI assistant can use previously established context when supported by the product and configuration.

For JetBrains developers, this creates an important engineering question:

Does AI memory actually reduce repeated explanation and rework when working with a large codebase?

The answer should be measured rather than assumed.

What AI Memory Means in Development

Traditional AI coding assistance often works around the current context.

For example:

Developer
   |
   v
Prompt
   |
   v
Current File
   |
   v
AI Response

Memory introduces another layer:

Developer
   |
   v
Current Task
   |
   +---- Current Code
   |
   +---- Previous Context
   |
   +---- Project Preferences
   |
   v
AI Response

The important distinction is that memory should not be confused with simply having a larger context window.

A context window contains information supplied to a particular interaction.

Memory is about retaining useful information beyond that immediate interaction.

Why This Matters in Enterprise Codebases

Consider a large .NET repository with:

src/
├── Api/
├── Application/
├── Domain/
├── Infrastructure/
├── Persistence/
├── Messaging/
└── Shared/

A developer may have established conventions such as:

Use MediatR for commands
Use EF Core for persistence
Use async APIs for I/O
Keep controllers thin
Use FluentValidation
Write unit tests for application services

If the assistant understands those conventions consistently, developers may spend less time repeating them.

But memory can also create problems if the stored information becomes outdated.

For example:

Old Architecture Rule
        |
        v
Repository Refactored
        |
        v
AI Still Uses Old Rule
        |
        v
Incorrect Suggestion

Therefore, memory must be evaluated for both retention and correctness.

Memory vs Repository Context

These are different mechanisms.

Repository Context

Repository context is information the assistant obtains from the current codebase.

For example:

OrderService.cs
OrderRepository.cs
Order.cs
OrderTests.cs

The assistant can inspect those files to understand current implementation details.

Memory

Memory represents information retained from previous interactions.

For example:

"The team prefers xUnit for unit tests."

The important question is whether the stored information remains useful when the developer starts a later task.

A Simple Example

Suppose a developer says:

"Use xUnit for tests and avoid mocking EF Core directly.
Use integration tests for database behavior."

Later, the developer asks:

"Add tests for OrderRepository."

Without retained context, the assistant may choose a generic testing strategy.

With appropriate memory, it may be able to follow the established preference.

The useful outcome is:

Repeated Instruction
        |
        v
Less Repetition
        |
        v
More Consistent Output

But this should be tested rather than assumed.

Designing a Memory Benchmark

A useful benchmark should compare tasks with and without retained context.

For example:

Test A
Memory disabled

Test B
Memory enabled

Use the same:

  • Repository

  • Developer

  • IDE

  • Task

  • Model

  • Tests

  • Evaluation criteria

Only change the memory condition.

This reduces the chance that other variables influence the result.

Define the Metrics First

Do not start by asking whether memory "feels better."

Define measurable outcomes.

Useful metrics include:

MetricWhat It Measures
Task completion timeHow quickly the task reaches an acceptable result
Repeated instructionsHow often context must be restated
Generated code changesSize of AI-generated modifications
Test pass rateWhether generated changes satisfy tests
Rework countNumber of corrections required
Review commentsHuman corrections required
Compilation failuresIncorrect assumptions in generated code
Architecture violationsWhether project conventions are followed
User correctionsHow often the developer must redirect the assistant

These metrics provide a stronger evaluation than subjective impressions alone.

Testing With a Large .NET Repository

A useful test repository could contain:

100+ Projects
10+ Shared Libraries
Multiple APIs
Multiple Databases
Background Workers
Messaging
Integration Tests
Unit Tests

The exact repository size is less important than its complexity.

The benchmark should contain tasks that require knowledge beyond one file.

For example:

Task 1
Add an API endpoint.

Task 2
Modify the domain service.

Task 3
Add validation.

Task 4
Add tests.

Task 5
Update persistence behavior.

Task 6
Refactor an existing workflow.

These tasks are more suitable for testing memory than simple code completion.

Test 1: Repeated Architecture Instructions

Start with a project convention:

Controllers should only handle HTTP concerns.
Business logic belongs in application services.

Ask the assistant to implement a feature.

Then start a new conversation and ask for another feature.

Measure:

Without Memory:
Did the developer need to repeat the rule?

With Memory:
Was the rule followed?

This measures practical retention.

Test 2: Testing Preferences

Suppose the team uses:

xUnit
FluentAssertions
Moq
Testcontainers

Tell the assistant about these conventions.

Later, request:

"Add tests for PaymentService."

Evaluate:

Test framework
Assertion style
Mocking approach
Integration testing strategy
Naming convention

If memory helps consistently reproduce these preferences, it provides measurable value.

Test 3: Naming Conventions

Large organizations often have specific naming conventions.

For example:

IOrderService
OrderService
CreateOrderCommand
CreateOrderHandler
OrderCreatedEvent

The benchmark can measure whether the assistant continues using the established convention in later tasks.

This is especially useful because naming decisions are easy to evaluate automatically.

Test 4: Architecture Rules

Suppose the repository follows:

API
 |
 v
Application
 |
 v
Domain
 |
 v
Infrastructure

and the team explicitly avoids:

API
 |
 X
Database

Now ask the assistant to implement a new endpoint.

Measure whether it respects the architecture.

This is more valuable than checking whether the generated code merely compiles.

Test 5: Existing Technical Decisions

Consider a project that uses:

SQL Server
EF Core
Redis
Message Queue

The team has decided that a specific operation must use asynchronous messaging rather than a synchronous database call.

Later, ask the assistant to implement a related feature.

The benchmark should determine whether it remembers the architectural decision.

Test 6: Stale Memory

This is one of the most important tests.

Suppose the original convention is:

Repository Pattern

Later, the architecture changes to:

Direct EF Core through Application Services

Now ask the assistant to implement a new feature.

A good system should not blindly follow obsolete information.

The test therefore becomes:

Old Memory
    |
    v
Architecture Changes
    |
    v
New Task
    |
    v
Does AI Follow Current Code?

This measures memory reliability rather than just retention.

Memory Accuracy Is More Important Than Memory Size

It is tempting to think:

More Memory = Better AI

That is not necessarily true.

Imagine the assistant remembers 50 project preferences but 10 are outdated.

That could create more problems than remembering 10 accurate preferences.

A better model is:

Useful Memory
=
Relevant
+
Accurate
+
Current

A memory that is technically retained but no longer applicable is not useful memory.

Measuring Rework

One practical metric is the number of corrections a developer must make.

For example:

Task Started
     |
     v
AI Generates Code
     |
     v
Developer Corrects Architecture
     |
     v
AI Generates Again
     |
     v
Developer Fixes Tests
     |
     v
Final Code

Count:

Architecture corrections
API corrections
Test corrections
Naming corrections
Configuration corrections

Then compare memory-enabled and memory-disabled workflows.

Measuring Developer Effort

Developer effort can be estimated through observable actions.

For example:

Prompt Count
Correction Count
Manual Edits
Review Comments
Task Duration

Do not treat these as perfect productivity measurements.

A developer may make fewer prompts but spend more time manually editing generated code.

Therefore, use multiple metrics together.

Example Benchmark Table

A benchmark can produce a table like this:

MetricWithout MemoryWith Memory
Task completion timeMeasureMeasure
Repeated instructionsMeasureMeasure
Manual correctionsMeasureMeasure
Test failuresMeasureMeasure
Architecture correctionsMeasureMeasure
Review commentsMeasureMeasure
Final acceptanceMeasureMeasure

The actual numbers should come from controlled experiments.

Do not fabricate performance improvements.

Testing Large Repositories

Large repositories introduce another challenge: context retrieval.

Suppose a feature depends on:

OrderController
OrderService
OrderRepository
CustomerService
PricingService
DatabaseContext

The assistant must reason across multiple files.

Memory may help with general project conventions, but repository retrieval remains important for current implementation details.

This gives us two separate questions:

Memory:
"Does the assistant remember the rule?"

Repository Context:
"Does the assistant find the current implementation?"

Both are necessary for reliable code generation.

Memory Should Not Replace Code Search

Consider a project convention stored in memory:

"Orders use OrderService."

But the repository has since been refactored.

The current code might now use:

OrderCommandHandler

The assistant should inspect the repository rather than blindly trusting memory.

A healthy priority is:

Current Source Code
        |
        v
Current Configuration
        |
        v
Current Tests
        |
        v
Historical Preferences

Memory should complement current repository evidence.

Testing Across Multiple Developers

Enterprise evaluation should not use only one developer.

Different developers may phrase the same task differently.

For example:

Developer A:
"Add pagination."

Developer B:
"Implement page-based order retrieval."

Developer C:
"Add skip/take support to Orders API."

The underlying requirement is similar.

A stronger benchmark uses several developers and several task styles.

Cross-Project Memory Testing

If an organization has multiple repositories, test whether memory remains appropriately scoped.

For example:

Project A
Uses xUnit

Project B
Uses NUnit

A dangerous outcome would be:

Project A Preference
      |
      v
Project B
      |
      v
Wrong Test Framework

Memory must not cause one project's conventions to leak into another project's workflow.

This is especially important for developers working across many repositories.

Security Considerations

Memory introduces another type of engineering consideration: what information should be retained?

Developers may discuss:

Internal architecture
Repository structure
Development preferences
Technical decisions

But they should avoid treating AI memory as a secure secret store.

Never intentionally store:

Passwords
API Keys
Access Tokens
Private Keys
Database Credentials
Sensitive Customer Data

A simple rule is:

Memory = Context

Secret Store = Credentials

They should not be interchangeable.

Common Mistakes

Mistake 1: Assuming Memory Is Always Correct

Stored context can become outdated.

Mistake 2: Testing Only Simple Tasks

Single-file code completion does not meaningfully test memory.

Mistake 3: Measuring Only Speed

A faster task that produces incorrect architecture is not necessarily better.

Mistake 4: Ignoring Current Repository Code

Current source code should remain authoritative for implementation details.

Mistake 5: Mixing Project Contexts

Different repositories may have completely different conventions.

Mistake 6: Storing Sensitive Information

Memory should not be used as a credential store.

Mistake 7: Using Subjective Evaluation Only

Use repeatable tests and measurable criteria.

Mistake 8: Ignoring Stale Memory

A memory benchmark should test what happens when project conventions change.

Troubleshooting

ProblemWhat to Check
AI repeats old conventionsCompare memory with current repository architecture
Wrong framework selectedCheck whether project-specific context is being applied
Different repositories get mixed conventionsTest memory scoping
Generated code ignores architectureVerify repository context and project instructions
AI follows obsolete decisionsReview and update retained context
Results vary between developersRepeat tests with multiple participants
Tasks improve only slightlyUse more context-dependent tasks
AI generates correct code but poor architectureAdd architecture compliance to evaluation

Best Practices

Keep Memory Relevant

Only retain information that is likely to remain useful.

Prefer Stable Project Conventions

Long-lived engineering practices are better memory candidates than temporary implementation details.

Verify Against Current Code

Memory should support repository analysis rather than replace it.

Test for Staleness

When architecture changes, verify that old assumptions no longer dominate responses.

Keep Projects Isolated

Project-specific preferences should not accidentally influence unrelated repositories.

Never Store Secrets

Use proper secret-management systems for credentials.

Measure Rework

Count corrections, failed tests, and review changes rather than relying only on completion time.

Use Real Development Tasks

Architecture changes, refactoring, testing, and cross-file features are better memory tests than basic autocomplete.

Advantages

Less Repetition

Developers may not need to repeatedly explain stable project conventions.

More Consistent Assistance

The assistant can potentially follow established development preferences across conversations.

Useful for Large Codebases

Memory can provide continuity when developers work across complex projects.

Better Developer Experience

Repeated tasks can become less conversationally repetitive.

Potentially Lower Rework

If remembered context is accurate, generated code may better align with project conventions.

Disadvantages and Limitations

Stale Information

Old context can become harmful when architecture changes.

Context Confusion

Developers working across many repositories need strong separation between project contexts.

Memory Does Not Understand Everything

Remembering a preference does not mean the assistant understands the complete architecture.

Evaluation Is Difficult

Productivity improvements can be difficult to isolate from other AI capabilities.

Security Considerations

Organizations need clear rules about what information should and should not be retained.

A Practical Benchmark for Enterprise Teams

A useful evaluation can be organized into four phases.

Phase 1: Baseline

Run tasks without relying on memory.

Measure:

Task time
Prompts
Corrections
Test failures
Review comments

Phase 2: Memory

Run comparable tasks with memory enabled.

Measure the same metrics.

Phase 3: Staleness

Change project architecture and repeat the tasks.

Measure whether outdated information affects the results.

Phase 4: Cross-Project

Use two repositories with different conventions.

Measure whether project-specific context remains correctly scoped.

This provides a much stronger evaluation than simply asking developers whether they like the feature.

Example Enterprise Evaluation

Imagine a team has 20 developers working on five .NET repositories.

The team could select:

5 repositories
10 tasks per repository
2 memory conditions

Then evaluate:

Task Completion
Code Correctness
Architecture Compliance
Test Success
Manual Rework
Developer Corrections

The goal is not to produce a marketing-style percentage.

The goal is to understand:

Where does memory help?
Where does it fail?
When does it become stale?
Which tasks benefit most?

Those answers are more useful for deciding whether memory should be part of the team's development workflow.

A Simple Decision Framework

Before enabling memory broadly, ask:

Is the information stable?
        |
        +---- No ----> Do not rely on memory
        |
       Yes
        |
        v
Is it project-specific?
        |
        +---- Yes ---> Scope carefully
        |
        v
Could it become sensitive?
        |
        +---- Yes ---> Do not retain
        |
        v
Can we verify it against source code?
        |
        +---- Yes ---> Good candidate

This keeps memory focused on useful development context.

Conclusion

AI memory can be useful for developers working on large enterprise codebases, but its value should be measured by the quality of the development workflow rather than by how much information the assistant can remember. The most useful memory is relevant, accurate, stable, and specific to the project. At the same time, current source code, tests, and configuration should remain the primary source of truth because architectures change and old decisions become stale. For JetBrains-based .NET development, a practical evaluation should measure repeated instructions, task completion time, manual corrections, test failures, architecture compliance, and review effort with and without memory. It should also test stale information and multiple repositories with different conventions. When used carefully, memory can reduce repetitive explanations and improve consistency, but it should remain a supporting layer rather than a replacement for repository analysis, engineering review, and established development practices.