--- title: Coding Best Practices description: Practical patterns and anti-patterns from real-world Archon development sidebar_position: 15 --- # Coding Best Practices Essential patterns and pitfalls to avoid when developing Archon. ## Async/Await Patterns ### ❌ DON'T: Lambda Functions Returning Unawaited Coroutines ```python # This will break - the coroutine is never awaited progress_callback=lambda data: update_progress(progress_id, data) ``` ### ✅ DO: Use asyncio.create_task for Async Callbacks ```python # Properly schedule async function execution progress_callback=lambda data: asyncio.create_task(update_progress(progress_id, data)) ``` ### ❌ DON'T: Call Sync Functions from Async Context ```python # This causes "event loop already running" errors async def process_data(): embeddings = create_embeddings_batch(texts) # Sync function in async context ``` ### ✅ DO: Use Async Versions in Async Context ```python async def process_data(): embeddings = await create_embeddings_batch_async(texts) ``` ## Socket.IO Best Practices ### Room-Based Broadcasting Always broadcast to specific rooms, not all clients: ```python # ✅ Good - targeted broadcast await sio.emit('crawl_progress', data, room=progress_id) # ❌ Bad - broadcasts to everyone await sio.emit('crawl_progress', data) ``` ### Simple Event Handlers Keep Socket.IO handlers simple and direct: ```python # ✅ Good - simple, clear purpose @sio.event async def crawl_subscribe(sid, data): progress_id = data.get('progress_id') if progress_id: await sio.enter_room(sid, progress_id) await sio.emit('crawl_subscribe_ack', {'status': 'subscribed'}, to=sid) ``` ## Service Layer Patterns ### Progress Callbacks When services need progress callbacks, ensure proper async handling: ```python # ✅ Good - service accepts optional async callback async def process_with_progress(data, progress_callback=None): if progress_callback: await progress_callback({'status': 'processing', 'percentage': 50}) ``` ### Error Handling in Services Always provide fallbacks for external service failures: ```python # ✅ Good - graceful degradation try: embeddings = await create_embeddings_batch_async(texts) except Exception as e: logger.warning(f"Embedding creation failed: {e}") # Return zero embeddings as fallback return [[0.0] * 1536 for _ in texts] ``` ## Common Pitfalls ### Event Loop Issues **Problem**: "This event loop is already running" **Solution**: Check if you're in an async context before using sync functions: ```python try: loop = asyncio.get_running_loop() # Already in async context - use async version result = await async_function() except RuntimeError: # No event loop - safe to use sync version result = sync_function() ``` ### Socket.IO Progress Updates **Problem**: Progress updates not reaching the UI **Solution**: Ensure the client is in the correct room and progressId is included: ```python # Always include progressId in the data data['progressId'] = progress_id await sio.emit('crawl_progress', data, room=progress_id) ``` ### Embedding Service Optimization **Problem**: Synchronous embedding calls blocking async operations **Solution**: Always use async versions and batch operations: ```python # Process in batches with rate limiting async def create_embeddings_for_documents(documents): batch_size = 20 # OpenAI limit for i in range(0, len(documents), batch_size): batch = documents[i:i + batch_size] embeddings = await create_embeddings_batch_async(batch) await asyncio.sleep(0.5) # Rate limiting ``` ## Testing Async Code ### Mock Async Functions Properly ```python # ✅ Good - proper async mock mock_update = AsyncMock() with patch('module.update_progress', mock_update): await function_under_test() mock_update.assert_called_once() ``` ### Test Socket.IO Events ```python # ✅ Good - test room management @patch('src.server.socketio_app.sio') async def test_subscribe_joins_room(mock_sio): await crawl_subscribe('sid-123', {'progress_id': 'progress-456'}) mock_sio.enter_room.assert_called_with('sid-123', 'progress-456') ``` ## React/Frontend Best Practices ### Performance Anti-Patterns to Avoid #### ❌ DON'T: Update State on Every Keystroke Without Debouncing ```typescript // This causes excessive re-renders setFormData({ ...formData, title: e.target.value })} /> ``` #### ✅ DO: Use Debounced Inputs or Local State ```typescript // Use a debounced input component ``` #### ❌ DON'T: Create New Functions in Render ```typescript // Creates new function every render ``` #### ✅ DO: Use useCallback for Stable References ```typescript const handleClick = useCallback((id) => { handleAction(id); }, []); ``` ### Socket.IO Client-Side Patterns #### Room Subscription Pattern ```typescript useEffect(() => { const ws = createWebSocketService(); const connect = async () => { await ws.connect('/'); // Always default namespace // Join room ws.send({ type: 'join_project', data: { project_id: projectId } }); // Handle messages ws.addMessageHandler('task_updated', handleTaskUpdate); }; connect(); return () => ws.disconnect(); }, [projectId]); ``` ### State Management Patterns #### Batch Updates ```typescript // ✅ Good - Single render setState(prev => ({ ...prev, field1: value1, field2: value2, field3: value3 })); // ❌ Bad - Three renders setField1(value1); setField2(value2); setField3(value3); ``` #### Local State for Transient Values ```typescript // Keep form inputs local until save const [localTitle, setLocalTitle] = useState(title); const handleSave = () => { onSave({ title: localTitle }); }; ``` ## Key Takeaways 1. **Always await async functions** - Never let coroutines go unawaited 2. **Use rooms for Socket.IO** - Target specific audiences, not everyone 3. **Handle async boundaries** - Know when you're in an async context 4. **Fail gracefully** - Always have fallbacks for external services 5. **Test async code properly** - Use AsyncMock and proper async test patterns 6. **Optimize React renders** - Use memo, useCallback, and debouncing 7. **Batch state updates** - Minimize renders with single setState calls 8. **Use local state** - Keep transient values local to components