- Deleted unused API test files, RTSP diagnostic scripts, and development utility scripts to reduce clutter. - Removed outdated database schema and modularization proposal documents to maintain focus on current architecture. - Cleaned up configuration files and logging scripts that are no longer in use, enhancing project maintainability.
14 KiB
Code Quality Refactoring Summary
Date: November 2025
Branch: Modularization branch
Status: ✅ Completed and Verified
Overview
This document summarizes the modularization and code quality refactoring work performed on the USDA Vision system. The work was done in two phases:
- Frontend Modularization (React Dashboard) - Extracted features into microfrontends using Module Federation
- Backend Refactoring (Camera Management API) - Improved code organization within the monolithic architecture
This document focuses primarily on Phase 2 (API refactoring), but provides context about the overall modularization strategy.
Project Context: Two-Phase Modularization
Phase 1: Frontend Modularization (React Dashboard)
Status: ✅ Completed
Before working on the API, we first modularized the React dashboard application into a microfrontend architecture:
- Approach: Used Vite Module Federation to create independently deployable frontend modules
- First Module Extracted:
video-remote- The video library feature was extracted into its own microfrontend - Architecture:
- Main dashboard acts as a "shell" application
- Remotely loads
video-remotemodule when enabled via feature flags - Supports gradual migration (local fallback components remain available)
- Infrastructure Changes:
- Created separate
media-apicontainer for video processing (thumbnails, transcoding) - Added
mediamtxcontainer for RTSP/WebRTC streaming video-remotecontainer runs independently and can be updated separately
- Created separate
- Benefits Achieved:
- Independent deployment of video library feature
- Better separation of concerns (media handling separate from main dashboard)
- Foundation for extracting more features (camera management, experiments, etc.)
Phase 2: Backend Refactoring (Camera Management API)
Status: ✅ Completed
After successfully modularizing the frontend, we focused on improving the backend code quality. Important: We chose NOT to split the API into microservices, but rather to improve the organization within the existing monolithic architecture.
- Approach: Simple, low-risk refactorings within the monolithic structure
- Philosophy: "Simplest least destructive code refactorings that can significantly make the code more readable and manageable and editable"
- Decision: Keep monolithic architecture (no microservices) but improve internal organization
- Why Monolithic:
- Camera SDK and hardware interactions require tight coupling
- System is stable and working well
- Full microservices would be overkill and add complexity
- Focus on code quality over architectural changes
Motivation
The API refactoring was requested to improve code quality and manageability within the existing monolithic architecture. The goal was to improve organization without resorting to full microservices architecture or breaking changes, following the successful pattern we established with the frontend modularization.
Refactoring Tasks Completed
1. Extract Duplicate Code (suppress_camera_errors)
Problem: The suppress_camera_errors() context manager was duplicated in three files:
camera/recorder.pycamera/streamer.pycamera/monitor.py
Solution:
- Created
camera/utils.pywith the centralizedsuppress_camera_errors()function - Updated all three files to import from
utilsinstead of defining locally
Files Changed:
- ✅ Created:
camera-management-api/usda_vision_system/camera/utils.py - ✅ Updated:
camera/recorder.py- removed local definition, added import - ✅ Updated:
camera/streamer.py- removed local definition, added import - ✅ Updated:
camera/monitor.py- removed local definition, added import
Benefits:
- Single source of truth for error suppression logic
- Easier maintenance (fix bugs in one place)
- Consistent behavior across all camera modules
2. Extract Magic Numbers into Constants
Problem: Magic numbers scattered throughout camera code made it hard to understand intent and adjust settings:
- Queue sizes (5, 10, 30)
- Frame rates (10.0, 15.0, 30.0)
- Timeouts (200, 1000, 500 milliseconds)
- JPEG quality (70)
- Sleep intervals (0.1 seconds)
Solution:
- Created
camera/constants.pywith well-named constants - Replaced all magic numbers with constant references
Constants Defined:
# Queue sizes
MJPEG_QUEUE_MAXSIZE = 5
RTSP_QUEUE_MAXSIZE = 10
RECORDING_QUEUE_MAXSIZE = 30
# Frame rates
PREVIEW_FPS = 10.0
RTSP_FPS = 15.0
DEFAULT_VIDEO_FPS = 30.0
# JPEG quality
PREVIEW_JPEG_QUALITY = 70
# Timeouts (milliseconds)
CAMERA_GET_BUFFER_TIMEOUT = 200
CAMERA_INIT_TIMEOUT = 1000
CAMERA_TEST_CAPTURE_TIMEOUT = 500
# Sleep intervals (seconds)
STREAMING_LOOP_SLEEP = 0.1
BRIEF_PAUSE_SLEEP = 0.1
Files Changed:
- ✅ Created:
camera-management-api/usda_vision_system/camera/constants.py - ✅ Updated:
camera/recorder.py- replaced magic numbers with constants - ✅ Updated:
camera/streamer.py- replaced magic numbers with constants - ✅ Updated:
camera/manager.py- replaced magic numbers with constants - ✅ Updated:
camera/monitor.py- added import forCAMERA_TEST_CAPTURE_TIMEOUT
Benefits:
- Self-documenting code (constants explain what values represent)
- Easy to adjust performance settings (change in one place)
- Reduced risk of inconsistent values across modules
- Better code readability
3. Split Monolithic API Routes into Domain Modules
Problem: api/server.py was 868 lines with all routes defined in a single _setup_routes() method, making it:
- Hard to navigate and find specific endpoints
- Difficult to maintain (one large file)
- Prone to merge conflicts
- Not following separation of concerns
Solution:
- Created
api/routes/directory with domain-specific route modules - Each module exports a
register_*_routes()function - Updated
server.pyto import and call these registration functions
New File Structure:
api/routes/
├── __init__.py # Exports all register functions
├── system_routes.py # /, /health, /system/status, /system/video-module
├── camera_routes.py # /cameras, /cameras/{name}/*, RTSP endpoints
├── recording_routes.py # /cameras/{name}/start-recording, stop-recording
├── mqtt_routes.py # /mqtt/status, /mqtt/events
├── storage_routes.py # /storage/stats, /storage/files, /storage/cleanup
├── auto_recording_routes.py # /cameras/{name}/auto-recording/*
└── recordings_routes.py # /recordings
Files Changed:
- ✅ Created:
api/routes/__init__.py - ✅ Created:
api/routes/system_routes.py- 7 routes - ✅ Created:
api/routes/camera_routes.py- 14 routes - ✅ Created:
api/routes/recording_routes.py- 2 routes - ✅ Created:
api/routes/mqtt_routes.py- 2 routes - ✅ Created:
api/routes/storage_routes.py- 3 routes - ✅ Created:
api/routes/auto_recording_routes.py- 3 routes - ✅ Created:
api/routes/recordings_routes.py- 1 route - ✅ Updated:
api/server.py- reduced from 868 lines to 315 lines (63% reduction)
Remaining in server.py:
- WebSocket endpoint (
/ws) - kept here as it's core to the server - Debug endpoint (
/debug/camera-manager) - utility endpoint - Video module route integration - dynamic route inclusion
Import Path Corrections Made:
- Fixed all route modules to use correct relative imports:
from ...core.config(three levels up fromapi/routes/)from ..models(one level up toapi/models)
- Fixed
AutoRecordingManagerimport path (wasauto_recording.manager, corrected torecording.auto_manager) - Added proper type hints to all registration functions
Benefits:
- 63% reduction in
server.pysize (868 → 315 lines) - Routes organized by domain (easy to find specific endpoints)
- Easier maintenance (smaller, focused files)
- Reduced merge conflicts (different developers work on different route modules)
- Better code organization following separation of concerns
- Easier to test (can test route modules independently)
Key Design Decisions
Why Keep WebSocket in server.py?
The WebSocket endpoint (/ws) was kept in server.py because:
- It's tightly coupled with the
WebSocketManagerclass defined inserver.py - It's core functionality, not a domain-specific feature
- Moving it would require refactoring the manager class as well
Why Use register_*_routes() Functions?
Each route module exports a function that takes dependencies (app, managers, logger) and registers routes. This pattern:
- Keeps route modules testable (can pass mock dependencies)
- Allows
server.pyto control dependency injection - Makes it clear what dependencies each route module needs
Why Not Move Debug Endpoint?
The /debug/camera-manager endpoint could be moved to camera_routes.py, but it was kept in server.py as a utility endpoint for debugging the server's internal state. This is a reasonable design choice for debug utilities.
Verification
All refactoring changes were verified to work correctly:
✅ API Starts Successfully
- No import errors
- No syntax errors
- All route modules load correctly
✅ Endpoints Function Correctly
/health- Returns healthy status/system/status- Returns system status with cameras, machines, recordings/cameras- Returns camera status (both cameras now show correct status)- All other endpoints maintain functionality
✅ No Regressions
- Camera monitoring works correctly (camera1 shows "available" status)
- Constants are properly imported and used
- Utility functions work as expected
Migration Notes for Future Developers
Adding New Routes
-
Identify the domain: Which route module does your endpoint belong to?
- System/health →
system_routes.py - Camera operations →
camera_routes.py - Recording →
recording_routes.py - Storage →
storage_routes.py - MQTT →
mqtt_routes.py - Auto-recording →
auto_recording_routes.py - Recording sessions →
recordings_routes.py
- System/health →
-
Add route to appropriate module: Use the existing pattern:
@app.get("/your/endpoint") async def your_endpoint(): # Implementation -
If creating a new domain:
- Create new file:
api/routes/your_domain_routes.py - Export
register_your_domain_routes()function - Add import to
api/routes/__init__.py - Register in
server.py's_setup_routes()method
- Create new file:
Using Constants
When you need camera-related constants:
from ...camera.constants import CAMERA_GET_BUFFER_TIMEOUT, PREVIEW_FPS
Using Utility Functions
When you need camera error suppression:
from ...camera.utils import suppress_camera_errors
with suppress_camera_errors():
# Camera operations
Related Documentation
CODE_QUALITY_IMPROVEMENTS.md- Original analysis and suggestionsREFACTORING_PLAN.md- Step-by-step implementation guide
Lessons Learned
-
Start Small: The refactoring started with the simplest tasks (extracting duplicates and constants) before tackling the larger route split.
-
Verify as You Go: Each task was verified before moving to the next, preventing cascading errors.
-
Fix Imports Systematically: When splitting routes, import paths needed careful correction. Using relative imports requires counting directory levels carefully.
-
Maintain Type Safety: Added type hints to all route registration functions for better IDE support and error detection.
-
Test Endpoints: Always test actual API endpoints after refactoring to ensure no functionality was broken.
Future Improvement Opportunities
While not included in this refactoring, potential future improvements:
- Move Debug Endpoint: Consider moving
/debug/camera-managertocamera_routes.pyfor better organization - Extract WebSocket Manager: Could move
WebSocketManagerto a separate module if it grows - Route Unit Tests: Add unit tests for route modules to prevent regressions
- API Documentation: Consider adding OpenAPI/Swagger tags to organize routes in API docs
- More Constants: Consider extracting more magic numbers as the codebase evolves
Overall Project Status
Frontend (React Dashboard)
✅ Microfrontend architecture implemented
video-remotemodule extracted and working- Module Federation configured and tested
- Feature flags system in place for gradual rollout
- Foundation ready for extracting additional modules
Backend (Camera Management API)
✅ Monolithic refactoring completed
- Code organization significantly improved
- Routes split into domain modules
- Constants and utilities extracted
- 63% reduction in main server file size
- Decision: Maintained monolithic architecture (not split into microservices)
- All functionality preserved and verified
Summary
Frontend Modularization
✅ Microfrontend architecture established
✅ Video library extracted as first module
✅ Independent deployment pipeline ready
✅ Scalable pattern for future feature extraction
Backend Refactoring
✅ 3 refactoring tasks completed successfully
✅ Code quality significantly improved
✅ No functionality broken
✅ 63% reduction in main server file size
✅ Better code organization and maintainability
✅ Maintained monolithic architecture (intentional decision)
Overall
✅ Frontend: Microfrontends (independent modules, Module Federation)
✅ Backend: Improved Monolith (better organization, maintainability, no microservices)
The system now has a modular frontend with improved backend code quality, all while maintaining full backward compatibility and system stability.