Add ai-code-project-template repo files.
This commit is contained in:
258
ai_context/IMPLEMENTATION_PHILOSOPHY.md
Normal file
258
ai_context/IMPLEMENTATION_PHILOSOPHY.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# Implementation Philosophy
|
||||
|
||||
This document outlines the core implementation philosophy and guidelines for software development projects. It serves as a central reference for decision-making and development approach throughout the project.
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
Embodies a Zen-like minimalism that values simplicity and clarity above all. This approach reflects:
|
||||
|
||||
- **Wabi-sabi philosophy**: Embracing simplicity and the essential. Each line serves a clear purpose without unnecessary embellishment.
|
||||
- **Occam's Razor thinking**: The solution should be as simple as possible, but no simpler.
|
||||
- **Trust in emergence**: Complex systems work best when built from simple, well-defined components that do one thing well.
|
||||
- **Present-moment focus**: The code handles what's needed now rather than anticipating every possible future scenario.
|
||||
- **Pragmatic trust**: The developer trusts external systems enough to interact with them directly, handling failures as they occur rather than assuming they'll happen.
|
||||
|
||||
This development philosophy values clear documentation, readable code, and belief that good architecture emerges from simplicity rather than being imposed through complexity.
|
||||
|
||||
## Core Design Principles
|
||||
|
||||
### 1. Ruthless Simplicity
|
||||
|
||||
- **KISS principle taken to heart**: Keep everything as simple as possible, but no simpler
|
||||
- **Minimize abstractions**: Every layer of abstraction must justify its existence
|
||||
- **Start minimal, grow as needed**: Begin with the simplest implementation that meets current needs
|
||||
- **Avoid future-proofing**: Don't build for hypothetical future requirements
|
||||
- **Question everything**: Regularly challenge complexity in the codebase
|
||||
|
||||
### 2. Architectural Integrity with Minimal Implementation
|
||||
|
||||
- **Preserve key architectural patterns**: MCP for service communication, SSE for events, separate I/O channels, etc.
|
||||
- **Simplify implementations**: Maintain pattern benefits with dramatically simpler code
|
||||
- **Scrappy but structured**: Lightweight implementations of solid architectural foundations
|
||||
- **End-to-end thinking**: Focus on complete flows rather than perfect components
|
||||
|
||||
### 3. Library Usage Philosophy
|
||||
|
||||
- **Use libraries as intended**: Minimal wrappers around external libraries
|
||||
- **Direct integration**: Avoid unnecessary adapter layers
|
||||
- **Selective dependency**: Add dependencies only when they provide substantial value
|
||||
- **Understand what you import**: No black-box dependencies
|
||||
|
||||
## Technical Implementation Guidelines
|
||||
|
||||
### API Layer
|
||||
|
||||
- Implement only essential endpoints
|
||||
- Minimal middleware with focused validation
|
||||
- Clear error responses with useful messages
|
||||
- Consistent patterns across endpoints
|
||||
|
||||
### Database & Storage
|
||||
|
||||
- Simple schema focused on current needs
|
||||
- Use TEXT/JSON fields to avoid excessive normalization early
|
||||
- Add indexes only when needed for performance
|
||||
- Delay complex database features until required
|
||||
|
||||
### MCP Implementation
|
||||
|
||||
- Streamlined MCP client with minimal error handling
|
||||
- Utilize FastMCP when possible, falling back to lower-level only when necessary
|
||||
- Focus on core functionality without elaborate state management
|
||||
- Simplified connection lifecycle with basic error recovery
|
||||
- Implement only essential health checks
|
||||
|
||||
### SSE & Real-time Updates
|
||||
|
||||
- Basic SSE connection management
|
||||
- Simple resource-based subscriptions
|
||||
- Direct event delivery without complex routing
|
||||
- Minimal state tracking for connections
|
||||
|
||||
### Event System
|
||||
|
||||
- Simple topic-based publisher/subscriber
|
||||
- Direct event delivery without complex pattern matching
|
||||
- Clear, minimal event payloads
|
||||
- Basic error handling for subscribers
|
||||
|
||||
### LLM Integration
|
||||
|
||||
- Direct integration with PydanticAI
|
||||
- Minimal transformation of responses
|
||||
- Handle common error cases only
|
||||
- Skip elaborate caching initially
|
||||
|
||||
### Message Routing
|
||||
|
||||
- Simplified queue-based processing
|
||||
- Direct, focused routing logic
|
||||
- Basic routing decisions without excessive action types
|
||||
- Simple integration with other components
|
||||
|
||||
## Development Approach
|
||||
|
||||
### Vertical Slices
|
||||
|
||||
- Implement complete end-to-end functionality slices
|
||||
- Start with core user journeys
|
||||
- Get data flowing through all layers early
|
||||
- Add features horizontally only after core flows work
|
||||
|
||||
### Iterative Implementation
|
||||
|
||||
- 80/20 principle: Focus on high-value, low-effort features first
|
||||
- One working feature > multiple partial features
|
||||
- Validate with real usage before enhancing
|
||||
- Be willing to refactor early work as patterns emerge
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
- Emphasis on integration and end-to-end tests
|
||||
- Manual testability as a design goal
|
||||
- Focus on critical path testing initially
|
||||
- Add unit tests for complex logic and edge cases
|
||||
- Testing pyramid: 60% unit, 30% integration, 10% end-to-end
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Handle common errors robustly
|
||||
- Log detailed information for debugging
|
||||
- Provide clear error messages to users
|
||||
- Fail fast and visibly during development
|
||||
|
||||
## Decision-Making Framework
|
||||
|
||||
When faced with implementation decisions, ask these questions:
|
||||
|
||||
1. **Necessity**: "Do we actually need this right now?"
|
||||
2. **Simplicity**: "What's the simplest way to solve this problem?"
|
||||
3. **Directness**: "Can we solve this more directly?"
|
||||
4. **Value**: "Does the complexity add proportional value?"
|
||||
5. **Maintenance**: "How easy will this be to understand and change later?"
|
||||
|
||||
## Areas to Embrace Complexity
|
||||
|
||||
Some areas justify additional complexity:
|
||||
|
||||
1. **Security**: Never compromise on security fundamentals
|
||||
2. **Data integrity**: Ensure data consistency and reliability
|
||||
3. **Core user experience**: Make the primary user flows smooth and reliable
|
||||
4. **Error visibility**: Make problems obvious and diagnosable
|
||||
|
||||
## Areas to Aggressively Simplify
|
||||
|
||||
Push for extreme simplicity in these areas:
|
||||
|
||||
1. **Internal abstractions**: Minimize layers between components
|
||||
2. **Generic "future-proof" code**: Resist solving non-existent problems
|
||||
3. **Edge case handling**: Handle the common cases well first
|
||||
4. **Framework usage**: Use only what you need from frameworks
|
||||
5. **State management**: Keep state simple and explicit
|
||||
|
||||
## Practical Examples
|
||||
|
||||
### Good Example: Direct SSE Implementation
|
||||
|
||||
```python
|
||||
# Simple, focused SSE manager that does exactly what's needed
|
||||
class SseManager:
|
||||
def __init__(self):
|
||||
self.connections = {} # Simple dictionary tracking
|
||||
|
||||
async def add_connection(self, resource_id, user_id):
|
||||
"""Add a new SSE connection"""
|
||||
connection_id = str(uuid.uuid4())
|
||||
queue = asyncio.Queue()
|
||||
self.connections[connection_id] = {
|
||||
"resource_id": resource_id,
|
||||
"user_id": user_id,
|
||||
"queue": queue
|
||||
}
|
||||
return queue, connection_id
|
||||
|
||||
async def send_event(self, resource_id, event_type, data):
|
||||
"""Send an event to all connections for a resource"""
|
||||
# Direct delivery to relevant connections only
|
||||
for conn_id, conn in self.connections.items():
|
||||
if conn["resource_id"] == resource_id:
|
||||
await conn["queue"].put({
|
||||
"event": event_type,
|
||||
"data": data
|
||||
})
|
||||
```
|
||||
|
||||
### Bad Example: Over-engineered SSE Implementation
|
||||
|
||||
```python
|
||||
# Overly complex with unnecessary abstractions and state tracking
|
||||
class ConnectionRegistry:
|
||||
def __init__(self, metrics_collector, cleanup_interval=60):
|
||||
self.connections_by_id = {}
|
||||
self.connections_by_resource = defaultdict(list)
|
||||
self.connections_by_user = defaultdict(list)
|
||||
self.metrics_collector = metrics_collector
|
||||
self.cleanup_task = asyncio.create_task(self._cleanup_loop(cleanup_interval))
|
||||
|
||||
# [50+ more lines of complex indexing and state management]
|
||||
```
|
||||
|
||||
### Good Example: Simple MCP Client
|
||||
|
||||
```python
|
||||
# Focused MCP client with clean error handling
|
||||
class McpClient:
|
||||
def __init__(self, endpoint: str, service_name: str):
|
||||
self.endpoint = endpoint
|
||||
self.service_name = service_name
|
||||
self.client = None
|
||||
|
||||
async def connect(self):
|
||||
"""Connect to MCP server"""
|
||||
if self.client is not None:
|
||||
return # Already connected
|
||||
|
||||
try:
|
||||
# Create SSE client context
|
||||
async with sse_client(self.endpoint) as (read_stream, write_stream):
|
||||
# Create client session
|
||||
self.client = ClientSession(read_stream, write_stream)
|
||||
# Initialize the client
|
||||
await self.client.initialize()
|
||||
except Exception as e:
|
||||
self.client = None
|
||||
raise RuntimeError(f"Failed to connect to {self.service_name}: {str(e)}")
|
||||
|
||||
async def call_tool(self, name: str, arguments: dict):
|
||||
"""Call a tool on the MCP server"""
|
||||
if not self.client:
|
||||
await self.connect()
|
||||
|
||||
return await self.client.call_tool(name=name, arguments=arguments)
|
||||
```
|
||||
|
||||
### Bad Example: Over-engineered MCP Client
|
||||
|
||||
```python
|
||||
# Complex MCP client with excessive state management and error handling
|
||||
class EnhancedMcpClient:
|
||||
def __init__(self, endpoint, service_name, retry_strategy, health_check_interval):
|
||||
self.endpoint = endpoint
|
||||
self.service_name = service_name
|
||||
self.state = ConnectionState.DISCONNECTED
|
||||
self.retry_strategy = retry_strategy
|
||||
self.connection_attempts = 0
|
||||
self.last_error = None
|
||||
self.health_check_interval = health_check_interval
|
||||
self.health_check_task = None
|
||||
# [50+ more lines of complex state tracking and retry logic]
|
||||
```
|
||||
|
||||
## Remember
|
||||
|
||||
- It's easier to add complexity later than to remove it
|
||||
- Code you don't write has no bugs
|
||||
- Favor clarity over cleverness
|
||||
- The best code is often the simplest
|
||||
|
||||
This philosophy document serves as the foundational guide for all implementation decisions in the project.
|
Reference in New Issue
Block a user