7.1 KiB
7.1 KiB
When NOT to Use Single-File Scripts
This document helps determine when to use a single-file uv script vs. when to use a proper uv project.
Decision Tree
Is this a Python program?
├─ No → Use appropriate language/tool
└─ Yes
├─ Is it a one-time task or simple automation?
│ ├─ Yes → Consider single-file script
│ └─ No → Use proper uv project
└─ Does it meet ANY of these criteria?
├─ >500 lines of code → Use proper uv project
├─ Multiple Python files needed → Use proper uv project
├─ Web application or API → Use proper uv project
├─ Long-running service → Use proper uv project
├─ Complex configuration → Use proper uv project
├─ Shared library code → Use proper uv project
├─ Heavy ML/data dependencies → Use proper uv project
└─ None of the above → Single-file script is appropriate
Use Proper uv Project When
1. Code Complexity
Use project if:
- Script exceeds 500 lines
- Logic is spread across multiple functions/classes
- Code would benefit from splitting into modules
- Complex data models or class hierarchies
Example - Too Complex for Script:
# This needs a proper project structure:
# - 800 lines of code
# - Multiple classes (User, Database, API, Config)
# - Would be clearer as separate modules
# - Needs tests
2. Multiple Files Needed
Use project if:
- Shared utilities across multiple scripts
- Common data models used by different tools
- Reusable library code
- Multiple entry points
Example:
# This needs a project:
my-tool/
├── src/
│ ├── __init__.py
│ ├── database.py # Shared by multiple tools
│ ├── models.py # Common data structures
│ └── utils.py # Utility functions
├── scripts/
│ ├── import_data.py # Uses shared code
│ └── export_data.py # Uses shared code
└── pyproject.toml
3. Web Applications
Use project for:
- Flask/FastAPI/Django applications
- REST APIs
- Web services
- Applications with routes/controllers
Example - Needs Project:
# Don't use single-file script for web apps:
from flask import Flask, request, jsonify
app = Flask(__name__)
# 50+ routes
# Database models
# Authentication
# Background tasks
# Configuration management
# This should be a proper project structure
4. Long-Running Services
Use project for:
- Daemons
- Background workers
- Queue consumers
- Services that run continuously
Example:
# Don't use script for services:
# - Runs 24/7
# - Monitors message queue
# - Complex retry logic
# - Logging configuration
# - Health checks
# - Graceful shutdown
# Needs proper project structure with:
# - Proper logging setup
# - Configuration management
# - Process management (systemd/supervisor)
5. Complex Configuration
Use project if:
- Multiple environment configs (dev/staging/prod)
- YAML/JSON configuration files
- Feature flags
- Database connection pools
Example:
# This needs a project:
config/
├── dev.yaml
├── staging.yaml
└── production.yaml
# Single-file scripts should use simple env vars instead
6. Heavy Dependencies
Use project for:
- Machine learning frameworks (TensorFlow, PyTorch)
- Large data processing (PySpark)
- Complex scientific computing
- GUI frameworks
Example - Too Heavy:
# /// script
# dependencies = [
# "tensorflow>=2.15.0", # ❌ ~500MB download
# "torch>=2.1.0", # ❌ ~800MB download
# "transformers>=4.35.0", # ❌ Complex dependency tree
# ]
# ///
# Use proper project with managed virtual environment
7. Testing Requirements
Use project if:
- Comprehensive test suite needed
- Multiple test files
- Fixtures and mocking
- CI/CD integration
Example:
# This needs a project:
tests/
├── unit/
│ ├── test_models.py
│ ├── test_utils.py
│ └── test_api.py
├── integration/
│ └── test_database.py
└── conftest.py
# Can't reasonably organize this with single-file script
8. Team Collaboration
Use project if:
- Multiple developers working on code
- Code review processes
- Versioning and releases
- Documentation requirements
Single-File Scripts ARE Appropriate For
✅ Good Use Cases
One-time tasks:
# Convert CSV format
# Migrate data between systems
# Clean up old files
# Generate reports
Simple automation:
# Check server health
# Send notifications
# Backup files
# Parse logs
CLI utilities:
# Format files
# Validate data
# Query APIs
# Process input
Prototyping:
# Test API endpoints
# Experiment with libraries
# Quick data analysis
# Proof of concept
✅ Characteristics of Good Single-File Scripts
- <500 lines of code
- Self-contained logic
- Simple, clear purpose
- Minimal dependencies (1-5 packages)
- Standalone execution
- Quick to understand
Examples of Good Single-File Scripts
Example 1: Health Check
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "psutil>=5.9.0",
# "rich>=13.0.0",
# ]
# ///
"""Check system health and display metrics."""
import psutil
from rich import print
def main():
cpu = psutil.cpu_percent()
mem = psutil.virtual_memory().percent
disk = psutil.disk_usage('/').percent
print(f"CPU: {cpu}% | Memory: {mem}% | Disk: {disk}%")
if cpu > 80 or mem > 80 or disk > 80:
print("[red]⚠ High resource usage![/red]")
exit(1)
if __name__ == "__main__":
main()
Example 2: API Query
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0",
# ]
# ///
"""Query API and display results."""
import httpx
import sys
def main():
response = httpx.get("https://api.github.com/users/octocat")
response.raise_for_status()
data = response.json()
print(f"Name: {data['name']}")
print(f"Public repos: {data['public_repos']}")
if __name__ == "__main__":
main()
Migration Path
When a script outgrows single-file format:
-
Create proper uv project:
uv init my-tool cd my-tool -
Move script logic to
src/:mv script.py src/my_tool/main.py -
Add dependencies to
pyproject.toml:[project] dependencies = [ "httpx>=0.27.0", "rich>=13.0.0", ] -
Create entry point in
pyproject.toml:[project.scripts] my-tool = "my_tool.main:main"
Summary
Use single-file scripts for:
- Simple automation (<500 lines)
- One-off tasks
- CLI utilities
- Prototypes
- Standalone tools
Use proper uv projects for:
- Complex applications (>500 lines)
- Multiple files/modules
- Web applications
- Long-running services
- Heavy dependencies
- Team collaboration
- Comprehensive testing
When in doubt: Start with a script. If it grows too complex, migrate to a project.