uv Standards
by Convext
uv package management for Python (preferred)
Rules (25)
Config
Follow 12-factor app principles for configuration: - Store config in environment variables, not in code - Use .env files for local development (never commit .env) - Commit .env.example with dummy values to document required variables - Never hardcode secrets, API keys, or connection strings This enables the same codebase to run in any environment.
Database
Encrypt or hash sensitive data: - Passwords: Use bcrypt (has_secure_password in Rails) - API keys: Encrypt with application-level encryption - PII: Consider field-level encryption - Never log sensitive data ```ruby class User < ApplicationRecord has_secure_password # Handles bcrypt hashing encrypts :ssn # Rails 7+ encryption end ```
Dependencies
When adding or updating dependencies, always use the latest stable version: - Check the official registry before adding (rubygems.org, npmjs.com, pypi.org) - Security vulnerabilities are fixed in newer versions - Performance improvements accumulate over time Do NOT use older versions. If the latest version has breaking changes, fix the code to work with the new API. Never assume migration is "too hard" without actually attempting the upgrade first.
Git
Make small, focused commits that do one thing well. Use Conventional Commits: - feat: new feature - fix: bug fix - refactor: code restructuring without behavior change - test: adding or updating tests - docs: documentation only - chore: maintenance tasks Example: `feat: add user authentication endpoint` Each commit should be independently deployable and revertible.
Llm Behavior
You are the only coder. Every line was written by you (this or previous sessions). There are no "pre-existing issues" - only issues you created and haven't fixed. Do not attribute problems to external sources or wait for someone else.
You do not define success. The user and the test suite do. Do not redefine scope, declare victory on your terms, or summarize partial progress as completion. Done means: tests pass, linter passes, formatter run.
Prohibited excuses: "pre-existing", "unrelated errors", "would require refactoring", "tedious", "for now", "at this point", "tired", "frustrating", "time-consuming". These indicate you're making excuses. Recognize them and continue working.
Python
Use `uv` as the default Python package manager - it's the preferred choice: - 10-100x faster than pip/poetry - Built-in virtual environment management - Lockfile support (uv.lock) for reproducible builds - Compatible with pyproject.toml and requirements.txt - Rust-based, single binary, no Python dependency ```bash # Install uv curl -LsSf https://astral.sh/uv/install.sh | sh # Create new project uv init myproject cd myproject # Add dependencies uv add fastapi sqlalchemy pydantic # Add dev dependencies uv add --dev pytest ruff mypy # Run commands uv run python main.py uv run pytest ``` NEVER use pip, poetry, or pipenv for new projects - always use uv.
Standard uv project structure: ``` myproject/ ├── pyproject.toml # Project config and dependencies ├── uv.lock # Locked dependencies (commit this!) ├── .python-version # Python version (e.g., "3.12") ├── src/ │ └── myproject/ │ ├── __init__.py │ └── main.py └── tests/ └── test_main.py ``` Key files: - `pyproject.toml`: Define dependencies here, not requirements.txt - `uv.lock`: Always commit - ensures reproducible builds - `.python-version`: uv auto-installs the correct Python version
Use uv workspaces for multi-package Python projects: ```toml # Root pyproject.toml [tool.uv.workspace] members = ["packages/*"] [tool.uv.sources] mylib = { workspace = true } ``` Structure: ``` monorepo/ ├── pyproject.toml # Workspace root ├── uv.lock # Single lockfile for all packages └── packages/ ├── mylib/ │ ├── pyproject.toml │ └── src/mylib/ └── myapp/ ├── pyproject.toml # depends on mylib └── src/myapp/ ``` Benefits: - Single lockfile across all packages - Local package editable installs - Atomic dependency updates
Define scripts in pyproject.toml for common commands: ```toml [project.scripts] myapp = "myproject.main:main" [tool.uv.scripts] dev = "fastapi dev src/myproject/main.py" test = "pytest tests/ -v" lint = "ruff check src/" format = "ruff format src/" typecheck = "mypy src/" ``` Run with: `uv run dev`, `uv run test`, etc. For CI/production: ```bash # Install dependencies only (no dev) uv sync --no-dev # Export to requirements.txt if needed uv export > requirements.txt ```
Rails
Use Rails built-in authentication (has_secure_password, authenticate_by) for all authentication logic. Never use Devise, Sorcery, Clearance, or similar authentication gems. Rails 8 provides everything needed: - has_secure_password for password hashing - authenticate_by for secure credential lookup - generates_token_for for password resets and email verification ```ruby class User < ApplicationRecord has_secure_password generates_token_for :password_reset, expires_in: 15.minutes end ```
Always use Strong Parameters to whitelist attributes: - Define permitted params in a private method - Never use `.permit!` which allows all attributes - Be explicit about nested attributes - Use `require` for the root key ```ruby private def user_params params.require(:user).permit(:name, :email, address_attributes: [:street, :city]) end ```
Security
Separate authentication (who are you?) from authorization (what can you do?): Authentication: - Use secure password hashing (bcrypt) - Implement rate limiting on login - Use secure session management - Consider MFA for sensitive applications Authorization: - Check permissions on every request - Use policy objects or authorization gems (Pundit, CanCanCan) - Never rely on client-side checks alone ```ruby # Pundit example def update @post = Post.find(params[:id]) authorize @post # Raises unless user can update @post.update!(post_params) end ```
Force HTTPS for all traffic: - Configure SSL/TLS in production - Redirect HTTP to HTTPS - Use secure cookies (Secure, HttpOnly, SameSite) - Set HSTS headers ```ruby # Rails config/environments/production.rb config.force_ssl = true config.ssl_options = { hsts: { expires: 1.year, subdomains: true } } ```
Never trust user input - validate and sanitize everything: - Use allowlists, not denylists - Validate type, length, format, and range - Sanitize HTML to prevent XSS - Use parameterized queries to prevent SQL injection ```ruby # Rails automatically escapes in views, but be explicit with user HTML: sanitize(user_content, tags: %w[p br strong em]) # Always use parameterized queries (ActiveRecord does this by default): User.where(email: params[:email]) # Safe User.where("email = '#{params[:email]}'") # DANGEROUS - SQL injection ```
Testing
When writing unit tests, never mock the class you are testing. You should test the real instance of the class to ensure it behaves correctly. Mocking the class under test defeats the purpose of the test and can hide bugs.
If you encounter a test failure, do not ignore it or mark it as skipped. - Investigate the root cause immediately - If it's a real bug, fix it before proceeding - If it's a flaky test, fix the flakiness - Never wave off failures as "pre-existing" - own the codebase state A test suite with skipped or ignored tests is a test suite you can't trust.
Avoid mocking internal code or private methods. Mocks should be used only at system boundaries: - External HTTP APIs (use WebMock, VCR, or similar) - Time-dependent code (freeze time with travel_to) - File system operations that would persist outside tmp/ or create side effects - Third-party services with rate limits or costs Internal services, database operations, and business logic should use real implementations to ensure proper integration.
Write tests that verify behavior, not internal implementation: - Test public interfaces, not private methods - Focus on inputs and outputs - Tests should survive refactoring - If implementation changes but behavior doesn't, tests should pass ```ruby # Good: Test behavior test "order total includes tax" do order = Order.new(items: [Item.new(price: 100)]) assert_equal 108, order.total # 8% tax end # Bad: Test implementation test "order calls calculate_tax method" do order = Order.new(items: [Item.new(price: 100)]) assert order.instance_variable_get(:@tax_calculated) end ```
Follow Test-Driven Development for new features and bug fixes: 1. RED: Write a failing test that defines the expected behavior 2. GREEN: Write the minimum code to make the test pass 3. REFACTOR: Clean up the code while keeping tests green This ensures every line of code has a reason to exist and is tested.
Each test should verify one logical concept: - Multiple assertions are fine if they test the same thing - Separate tests for separate behaviors - Clear test names that describe the behavior ```ruby # Good: One concept test "user is invalid without email" do user = User.new(name: "Test") assert_not user.valid? assert_includes user.errors[:email], "can't be blank" end # Bad: Multiple concepts test "user validation" do user = User.new assert_not user.valid? # Missing everything user.email = "[email protected]" assert_not user.valid? # Missing name user.name = "Test" assert user.valid? # Now valid end ```
Use fixtures for consistent test data. Fixtures are: - Fast: loaded at database level, not through ActiveRecord - Simple: YAML files, no DSL to learn - Stable: good for reference data that rarely changes ```ruby # test/fixtures/users.yml admin: name: Admin User email: [email protected] role: admin regular: name: Regular User email: [email protected] role: member ``` Reference in tests with `users(:admin)`.
Workflow
Ensure your changes work on your local machine before pushing: - Run the application and manually verify changes - Run the full test suite - Check for console errors or warnings CI is for catching environment-specific issues, not basic functionality.
Before every commit, run the full quality pipeline: 1. Format code (prettier, black, rubocop --autocorrect, etc.) 2. Run linter (eslint, pylint, rubocop, etc.) 3. Run full test suite Never rely on CI to catch issues you could have caught locally. Configure pre-commit hooks to enforce this automatically.
Language Standards (2)
Use this Ruleset
Sign in to adopt or fork this ruleset
Sign in with GitHubStatistics
- Rules
- 25
- Standards
- 2
- Projects using
- 0
- Created
- Nov 28, 2025