**Wave D Phase 6 - Technical Debt Cleanup (Agent C6)** ## Changes - Identified deprecated code patterns across codebase - Analyzed mock repository usage (strategically retained per AGENT_M13) - Documented deprecation cleanup strategy - Prepared deprecation removal todos ## Analysis Results - Mock structs: RETAINED (strategic testing infrastructure) - Never-read fields: 2 instances in backtesting_service - Dead code warnings: 35 total across workspace - databento_old references: None found in active code ## Status - ✅ Deprecation analysis complete - ⏳ Cleanup execution pending user confirmation - 📊 Test impact assessment ready 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
Agent M9: Mock Method Necessity Analysis - COMPREHENSIVE REPORT
Analysis Date: 2025-10-18
Target: BacktestingRepositories::mock() method
Status: DEAD CODE - Recommended for Refactoring
Executive Summary
The mock() method on the BacktestingRepositories trait is LEGACY CODE with minimal actual usage. While it appears in 5 files, analysis shows:
- Actually Used: 3 call sites (1 production code, 2 test files)
- Usage Pattern: Only in non-critical wave comparison examples and test setup
- Problem: The trait method forces all implementors to provide mock implementations, violating the Rust idiom of optional trait methods
- Verdict: REFACTOR - Replace with direct mock struct instantiation (already available in codebase)
Evidence & Findings
1. All Call Sites (Grep Results)
File Line Usage
─────────────────────────────────────────────────────────────────
ml/src/ensemble/hot_swap.rs N/A CanaryMetrics::mock() [UNRELATED]
services/backtesting_service/tests/
ml_backtest_integration_test.rs 28 DefaultRepositories::mock()
services/backtesting_service/examples/
wave_comparison.rs 34 BacktestingRepositories::mock()
services/backtesting_service/src/
wave_comparison.rs 219 DefaultRepositories::mock()
wave_comparison.rs 272 DefaultRepositories::mock()
Finding: Only 5 actual calls to .mock(), and only 1 is in production code (wave_comparison.rs)
2. Trait Definition Issues
Current Implementation (repositories.rs, lines 138-153):
#[async_trait]
pub trait BacktestingRepositories: Send + Sync {
fn market_data(&self) -> &dyn MarketDataRepository;
fn trading(&self) -> &dyn TradingRepository;
fn news(&self) -> &dyn NewsRepository;
fn mock() -> Self // <-- PROBLEMATIC: Required trait method
where
Self: Sized;
}
Problem: This forces all implementors (DefaultRepositories, MockBacktestingRepositories) to implement this method. This is not idiomatic Rust.
3. Implementations
Two implementations exist:
DefaultRepositories::mock() (lines 179-185)
fn mock() -> Self {
Self {
market_data: Box::new(MockMarketDataRepository),
trading: Box::new(MockTradingRepository),
news: Box::new(MockNewsRepository),
}
}
MockBacktestingRepositories::mock() (tests/mock_repositories.rs, lines 311-317)
fn mock() -> Self {
Self::new(
Box::new(MockMarketDataRepository::new()),
Box::new(MockTradingRepository::new()),
Box::new(MockNewsRepository::new()),
)
}
Finding: Both implementations are identical in purpose - they just create default empty mock repositories.
4. Alternatives Already in Codebase
The codebase already has better testing patterns:
Pattern A: Direct Constructor (RECOMMENDED)
// From mock_repositories.rs (lines 283-295)
pub struct MockBacktestingRepositories {
market_data: Box<dyn MarketDataRepository>,
trading: Box<dyn TradingRepository>,
news: Box<dyn NewsRepository>,
}
impl MockBacktestingRepositories {
pub fn new(
market_data: Box<dyn MarketDataRepository>,
trading: Box<dyn TradingRepository>,
news: Box<dyn NewsRepository>,
) -> Self { ... }
}
This is BETTER because:
- Explicit and clear intention
- Allows easy customization
- No trait coupling
Pattern B: Builder Pattern (AVAILABLE)
// Could easily add this to MockBacktestingRepositories:
pub fn with_market_data(mut self, data: Box<dyn MarketDataRepository>) -> Self {
self.market_data = data;
self
}
Pattern C: Default Implementation (RUST STANDARD)
impl Default for DefaultRepositories {
fn default() -> Self {
Self {
market_data: Box::new(MockMarketDataRepository),
trading: Box::new(MockTradingRepository),
news: Box::new(MockNewsRepository),
}
}
}
// Usage: DefaultRepositories::default()
5. Usage Analysis
ml_backtest_integration_test.rs (Line 28)
let repositories: Arc<dyn BacktestingRepositories> =
Arc::new(DefaultRepositories::mock());
Context: Test helper for ML backtesting tests
Risk: Test-only, low priority
Refactor Path: Change to DefaultRepositories::default()
wave_comparison.rs (Lines 219, 272)
Arc::new(DefaultRepositories::mock()), // Called twice in wave comparison
Context: Wave comparison backtesting (production-ish code, but non-critical)
Risk: Example/demo code, not hot path
Refactor Path: Change to DefaultRepositories::default()
examples/wave_comparison.rs (Line 34)
let repositories = Arc::new(BacktestingRepositories::mock());
Context: Example program, NOT production code
Risk: None (example only)
Refactor Path: Change to Arc::new(DefaultRepositories::default())
6. Rust Testing Best Practices
This violates several Rust idioms:
| Pattern | Status | Alternative |
|---|---|---|
| Trait Mock Method | Anti-pattern | Use impl Default or direct constructors |
| Type-Based Test Mocking | OK when used correctly | Should use #[cfg(test)] imports |
| Dependency Injection | Good | Current approach is correct |
| Hard-coded Mocks | Anti-pattern if required | Should be optional, not trait-enforced |
Precedent: Rust standard library uses impl Default for test stubs, not trait methods.
Test Coverage Analysis
Current Mock Usage in Tests:
| Test File | Purpose | Mock Usage | Can Refactor? |
|---|---|---|---|
| ml_backtest_integration_test.rs | ML backtesting validation | .mock() |
YES → .default() |
| mock_repositories.rs | Mock implementations | Helper functions | N/A (is the mock) |
| Other 25+ test files | Various | Use real DBN data or direct constructors | Already optimal |
Finding: Most tests use real DBN data (see dbn_integration_tests.rs, dbn_loader_filtering_test.rs, etc.), not mocks. The .mock() method is underutilized.
Test Pattern Evolution:
- Early tests (mock_repositories.rs): Used
.mock()trait method - Wave A-C tests: Evolved to use real DBN data from
test_data/real/databento/ - Current best practice: DBN-based testing (0.70ms load time, deterministic data)
Finding: The codebase is moving away from generic mocks toward realistic data.
Performance Impact Analysis
| Aspect | Impact | Severity |
|---|---|---|
| Method Resolution | Trait vtable lookup (minimal) | Negligible |
| Instantiation | ~1 microsecond | Negligible |
| Memory | 3x RwLock<> boxes | <1KB per instance |
| Code Maintenance | Requires trait impl for each BacktestingRepositories type | MEDIUM |
Verdict: Performance is NOT a concern. Maintainability IS a concern.
Recommendation: REFACTOR
Action Plan (Priority Order)
Phase 1: Add Default Implementation (1 hour)
File: services/backtesting_service/src/repositories.rs
// Add impl Default for DefaultRepositories
impl Default for DefaultRepositories {
fn default() -> Self {
Self {
market_data: Box::new(MockMarketDataRepository),
trading: Box::new(MockTradingRepository),
news: Box::new(MockNewsRepository),
}
}
}
// Add impl Default for MockBacktestingRepositories
impl Default for MockBacktestingRepositories {
fn default() -> Self {
Self::new(
Box::new(MockMarketDataRepository::new()),
Box::new(MockTradingRepository::new()),
Box::new(MockNewsRepository::new()),
)
}
}
Phase 2: Update Call Sites (30 minutes)
Replace 5 call sites:
// BEFORE
let repos = Arc::new(DefaultRepositories::mock());
// AFTER
let repos = Arc::new(DefaultRepositories::default());
Files to update:
services/backtesting_service/tests/ml_backtest_integration_test.rs(line 28)services/backtesting_service/examples/wave_comparison.rs(line 34)services/backtesting_service/src/wave_comparison.rs(lines 219, 272)
Phase 3: Remove Trait Method (15 minutes)
File: services/backtesting_service/src/repositories.rs
Remove from trait (lines 149-152):
// DELETE THIS:
fn mock() -> Self
where
Self: Sized;
Remove from DefaultRepositories impl (lines 179-185):
// DELETE THIS entire function
fn mock() -> Self { ... }
Remove from MockBacktestingRepositories impl (lines 311-317):
// DELETE THIS entire function
fn mock() -> Self { ... }
Phase 4: Verification (15 minutes)
cargo test --package backtesting_service
cargo clippy --workspace -- -D warnings
Estimated Impact
| Metric | Before | After |
|---|---|---|
| Trait Bloat | 4 lines of trait method | 0 lines |
| Code Clarity | Generic "mock()" | Explicit "default()" |
| Type Safety | Optional impl risk | Standard Rust pattern |
| Maintainability | Medium | High |
| Breaking Changes | None (internal) | None (internal) |
Alternative Recommendations (Not Pursued)
Option A: Keep as-is
Pros: No changes
Cons: Non-idiomatic, requires all implementors to provide mock method
Verdict: REJECTED - Adds tech debt
Option B: Use #[cfg(test)] Conditional Trait Method
Pros: Only available in test builds
Cons: Rust doesn't support conditional trait methods
Verdict: REJECTED - Not possible
Option C: Create a TestableRepositories Trait
Pros: Separates test concerns
Cons: Adds trait complexity, unnecessary indirection
Verdict: REJECTED - Over-engineering
Option D: Use Builder Pattern (OPTIONAL ENHANCEMENT)
Pros: Enables flexible test fixture creation
Cons: Overkill for current simple use case
Verdict: OPTIONAL ENHANCEMENT for future if test complexity grows
Example:
pub struct DefaultRepositoriesBuilder {
market_data: Option<Box<dyn MarketDataRepository>>,
trading: Option<Box<dyn TradingRepository>>,
news: Option<Box<dyn NewsRepository>>,
}
impl DefaultRepositoriesBuilder {
pub fn with_market_data(mut self, repo: Box<dyn MarketDataRepository>) -> Self {
self.market_data = Some(repo);
self
}
pub fn build(self) -> DefaultRepositories {
DefaultRepositories {
market_data: self.market_data.unwrap_or_else(|| Box::new(MockMarketDataRepository)),
trading: self.trading.unwrap_or_else(|| Box::new(MockTradingRepository)),
news: self.news.unwrap_or_else(|| Box::new(MockNewsRepository)),
}
}
}
// Usage: DefaultRepositoriesBuilder::default()
// .with_market_data(Box::new(custom_repo))
// .build()
Conclusion
The mock() trait method is DEAD CODE MASQUERADING AS A TESTING UTILITY. It:
- ✗ Is NOT idiomatic Rust (should use impl Default)
- ✗ Only has 3-5 actual call sites (2 are examples)
- ✗ Adds cognitive overhead (why is mock() required in trait?)
- ✗ Forces all implementors to provide it
- ✓ Has direct alternatives already in codebase
- ✓ Can be replaced with 5-line changes
Recommendation: REFACTOR to use impl Default pattern (1-2 hours total work)
This aligns with:
- Rust testing best practices
- Standard library conventions
- Current codebase evolution toward real data testing
- Improved code clarity
Appendix: Call Site Details
ml/src/ensemble/hot_swap.rs
let metrics = CanaryMetrics::mock(); // Different struct, not BacktestingRepositories
Status: UNRELATED - Don't touch
services/backtesting_service/tests/ml_backtest_integration_test.rs:28
async fn create_test_backtesting_service() -> Result<BacktestingServiceImpl> {
use backtesting_service::repositories::BacktestingRepositories;
let repositories: Arc<dyn BacktestingRepositories> =
Arc::new(DefaultRepositories::mock());
BacktestingServiceImpl::new(repositories, None).await
}
Context: Test helper used by 4 RED phase tests
Refactor: Arc::new(DefaultRepositories::default())
services/backtesting_service/examples/wave_comparison.rs:34
let repositories = Arc::new(BacktestingRepositories::mock());
Context: Example program, not part of main codebase
Note: This is actually a compile error (trait method can't be called on trait object directly)
Refactor: Arc::new(DefaultRepositories::default())
services/backtesting_service/src/wave_comparison.rs:219, 272
Arc::new(DefaultRepositories::mock()), // Called in run_comparison()
Context: Production-ish code but for wave comparison demo
Refactor: Arc::new(DefaultRepositories::default())
Verification Checklist
- Add
impl Default for DefaultRepositories - Add
impl Default for MockBacktestingRepositories - Update 5 call sites to use
.default() - Remove trait method from BacktestingRepositories
- Remove impl from DefaultRepositories
- Remove impl from MockBacktestingRepositories
- Run:
cargo test --package backtesting_service(should pass all) - Run:
cargo clippy --workspace -- -D warnings(should have no new warnings) - Verify with:
cargo build --workspace