| name | codebase-consolidation-pattern |
| description | Reorganize scattered modules into subpackages while maintaining backwards compatibility via shims. Trigger when: (1) too many root-level files, (2) need to group related functionality, (3) cleaning up codebase structure. |
| author | Claude Code |
| date | Tue Dec 31 2024 00:00:00 GMT+0000 (Coordinated Universal Time) |
Codebase Consolidation Pattern
Experiment Overview
| Item | Details |
|---|---|
| Date | 2024-12-31 |
| Goal | Reorganize root-level modules into logical subpackages without breaking existing imports |
| Environment | alpaca_trading/ package with 24 root-level .py files |
| Status | Success |
Context
Problem: Codebase grew organically with many root-level modules (24 .py files). Related functionality scattered:
- Trading: broker.py, executor.py, pdt.py, market_hours.py, profit_tracker.py, notify.py
- Training: model_version.py, online_bandit.py, online_learning.py, rl_context_gpu.py
- Risk: risk_monitor.py floating at root instead of in risk/
Solution: Move related modules to subpackages, create compatibility shims at original locations.
Verified Workflow
1. Identify Module Groups
Analyze imports and functionality to group related modules:
# Trading execution group
trading/
- broker.py # Alpaca API wrapper
- executor.py # Order execution
- pdt.py # Pattern Day Trader rules
- market_hours.py # Market session detection
- profit_tracker.py # P&L tracking
- notify.py # Notifications
# Training system group
training/
- model_version.py # Version protocol
- online_bandit.py # Thompson sampling
- online_learning.py # Real-time updates
- rl_context_gpu.py # GPU context building
# Existing packages to extend
risk/
- risk_monitor.py # Real-time monitoring (move from root)
2. Move Files with Git (Preserve History)
# Create new package directory
mkdir alpaca_trading/trading
# Move files preserving git history
git mv alpaca_trading/broker.py alpaca_trading/trading/
git mv alpaca_trading/executor.py alpaca_trading/trading/
git mv alpaca_trading/pdt.py alpaca_trading/trading/
git mv alpaca_trading/market_hours.py alpaca_trading/trading/
git mv alpaca_trading/profit_tracker.py alpaca_trading/trading/
git mv alpaca_trading/notify.py alpaca_trading/trading/
3. Update Internal Imports
After moving, update relative imports in moved files:
# BEFORE (in executor.py at root):
from .broker import Broker, BrokerConfig
from .utils import setup_logger
from .pdt import DayTradeGuard
# AFTER (in trading/executor.py):
from .broker import Broker, BrokerConfig # Same package, no change
from ..utils import setup_logger # Parent package, add ..
from .pdt import DayTradeGuard # Same package, no change
4. Create Package init.py
# alpaca_trading/trading/__init__.py
"""Trading execution and broker integration."""
from .broker import Broker, BrokerConfig
from .executor import Executor
from .pdt import DayTradeGuard
from .market_hours import (
is_trading_hours,
get_next_trading_window,
detect_asset_type,
AssetType,
MarketSession,
)
from .profit_tracker import ProfitTracker
from .notify import (
EmailNotifier,
TelegramNotifier,
from_env_email,
from_env_telegram,
)
__all__ = [
'Broker', 'BrokerConfig', 'Executor', 'DayTradeGuard',
'is_trading_hours', 'get_next_trading_window', 'detect_asset_type',
'AssetType', 'MarketSession', 'ProfitTracker',
'EmailNotifier', 'TelegramNotifier', 'from_env_email', 'from_env_telegram',
]
5. Create Backwards Compatibility Shims
At original locations, create thin wrapper files:
# alpaca_trading/broker.py (shim)
"""Compatibility shim - broker moved to trading/broker.py"""
from .trading.broker import * # noqa: F401,F403
# alpaca_trading/executor.py (shim)
"""Compatibility shim - executor moved to trading/executor.py"""
from .trading.executor import * # noqa: F401,F403
6. Handle Private Functions
from module import * doesn't export private functions (starting with _). If tests use them:
# alpaca_trading/mdp.py (shim with private function)
"""Compatibility shim - mdp archived to _archive/mdp.py"""
from ._archive.mdp import * # noqa: F401,F403
from ._archive.mdp import _tanh_clip # noqa: F401 - private but used in tests
7. Update Extended Package init.py
When adding to existing package (e.g., risk/):
# alpaca_trading/risk/__init__.py
"""Risk management module."""
from .portfolio_risk import AdvancedRiskManager, RiskLimits, PositionRisk
from .garch import GARCHRiskManager, fit_garch_model
from .capital_manager import CapitalManager, CapitalAllocation
# NEW: Add moved module
from .risk_monitor import (
RealTimeRiskMonitor,
RiskAlert,
CircuitBreakerConfig,
create_risk_monitoring_system,
)
__all__ = [
# ... existing exports ...
# NEW exports
'RealTimeRiskMonitor', 'RiskAlert', 'CircuitBreakerConfig',
'create_risk_monitoring_system',
]
Import Patterns Summary
| Original Location | New Location | Import Change |
|---|---|---|
from alpaca_trading.broker import Broker |
alpaca_trading/trading/broker.py |
No change (shim handles it) |
from alpaca_trading import broker |
alpaca_trading/trading/broker.py |
No change (shim handles it) |
Internal: from .utils import |
Now in subpackage | from ..utils import |
Internal: from .other_module import |
Now in same subpackage | from .other_module import |
Failed Attempts
| Attempt | Why it Failed | Lesson Learned |
|---|---|---|
| Move without shims | Breaks all existing imports immediately | Always create backwards-compat shims |
| Use deprecation warnings in shims | Noisy for users, not actionable | Only warn in truly deprecated modules |
from module import * for private functions |
Private _func not exported by * |
Explicitly import private functions if needed |
| Forget to update init.py | New package not importable | Always create/update package init.py |
| Update imports before moving | Files don't exist yet | Move first, then update imports |
Checklist
- Identify logical module groups
- Create target package directories
- Use
git mvto preserve history - Update internal imports (
.→..for parent) - Create package
__init__.pywith exports - Create compatibility shims at original locations
- Handle private functions explicitly if needed
- Run tests to verify nothing broken
- Commit with descriptive message
Final Structure
alpaca_trading/
├── trading/ # NEW: Trading execution
│ ├── __init__.py
│ ├── broker.py
│ ├── executor.py
│ ├── pdt.py
│ ├── market_hours.py
│ ├── profit_tracker.py
│ └── notify.py
├── training/ # EXTENDED: Training system
│ ├── __init__.py
│ ├── archive.py # existing
│ ├── gating.py # existing
│ ├── model_version.py # moved
│ ├── online_bandit.py # moved
│ ├── online_learning.py # moved
│ └── rl_context_gpu.py # moved
├── risk/ # EXTENDED: Risk management
│ ├── __init__.py
│ ├── portfolio_risk.py # existing
│ ├── garch.py # existing
│ └── risk_monitor.py # moved from root
│
├── broker.py # SHIM → trading/broker.py
├── executor.py # SHIM → trading/executor.py
├── pdt.py # SHIM → trading/pdt.py
├── market_hours.py # SHIM → trading/market_hours.py
├── profit_tracker.py # SHIM → trading/profit_tracker.py
├── notify.py # SHIM → trading/notify.py
├── model_version.py # SHIM → training/model_version.py
├── online_bandit.py # SHIM → training/online_bandit.py
├── online_learning.py # SHIM → training/online_learning.py
├── rl_context_gpu.py # SHIM → training/rl_context_gpu.py
└── risk_monitor.py # SHIM → risk/risk_monitor.py
Key Insights
- Shims are essential: Without them, all existing code breaks instantly
- Git history preserved: Use
git mv, notmv+git add - Test after each group: Move one group, test, commit, then next group
- Relative imports change: Moving deeper requires
..prefix for parent imports - Private functions need explicit import:
*doesn't export_prefixedfunctions
References
- Commit
d0f12b5: Full consolidation implementation alpaca_trading/trading/__init__.py: Example package exportsalpaca_trading/broker.py: Example shim file