Skip to main content

Team Collaboration and Reproducible Environments with AI

The "Works on My Machine" Problem

You've created a fantastic Python project. Your code runs perfectly on your computer. You invite a teammate to work on it and... their environment doesn't match yours. They get errors. Dependencies are different versions. The Python version is different. Nothing works.

This is the "works on my machine" problem, and it's plagued software teams for decades.

The solution? Reproducible environments — using UV to ensure that every developer on your team has the exact same library versions, the exact same Python version, and the exact same environment setup. No surprises. No debugging environment differences.

This is the capstone lesson of the UV chapter: we're moving from individual development to team workflows.

Understanding: What Makes Environments Reproducible?

The Two Files Your Project Needs

Your UV project contains two critical files that work together:

File 1: pyproject.toml (Constraints)

This file says "what I want, roughly":

[project]
dependencies = [
"requests>=2.31.0",
"flask>=3.0",
]

[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
]

Notice the >= signs. This means "I want requests, version 2.31.0 or higher." It's flexible—if a newer version appears, that's fine.

File 2: uv.lock (Pinned Versions)

This file says "here's exactly what I have":

requests==2.32.1
flask==3.0.2
pytest==7.4.0
black==23.12.0

Notice the == signs. This means "I tested with this exact version." It's strict—everyone gets this exact version.

Why Both Files?

Think of it like a recipe versus a specific meal:

  • pyproject.toml = Recipe ("Use flour, roughly 2-3 cups")
  • uv.lock = Exact meal ("2.5 cups of King Arthur flour, batch #2024-10")

When you're developing alone, pyproject.toml is flexible enough. You might use requests 2.31 or 2.32; both work fine.

But when you're on a team, you need the exact meal. If developer A used requests 2.31 and developer B used requests 2.32, and they have different behavior, you'll spend hours debugging an environment difference (not a code bug).

That's why UV automatically creates and updates uv.lock every time you run uv add or uv sync.

Real-World Scenario 1: Preparing Your Project for a Teammate

You've built a web scraper with UV. Now you want your teammate Maria to work on it.

Step 1: Ensure Both Files Exist

Verify your project has both pyproject.toml and uv.lock. If uv.lock doesn't exist:

uv lock

Step 2: Commit Both Files to Git

# These files go to Git:
git add pyproject.toml uv.lock .python-version

# These do NOT go to Git (already in .gitignore):
# .venv/ <- Virtual environment
# __pycache__/ <- Python cache
# *.pyc <- Compiled bytecode

Why not commit .venv? It's 50+ MB of dependencies. Everyone can recreate it in seconds using uv.lock.

Real-World Scenario 2: Teammate Clone and Setup

Maria just cloned your project. She has:

web-scraper/
├── pyproject.toml # Constraints
├── uv.lock # Exact versions
└── src/
└── scraper.py # Your code

She does not have .venv/ yet—she'll create it.

She Runs uv sync

uv sync

Output:

Resolved 15 packages in 0.25s
Downloaded 15 packages in 0.18s
Installed 15 packages in 0.08s

What happened:

  1. UV read uv.lock
  2. Downloaded exact versions (requests 2.32.1, beautifulsoup4 4.12.0, etc.)
  3. Created .venv/ with those exact packages

Maria's environment is now byte-for-byte identical to yours.

She Runs Your Code

uv run python src/scraper.py

Output:

Scraping https://example.com...
Found 42 items
Saved to results.json

Perfect. No "works on my machine" problems.


Real-World Scenario 3: Adding a New Dependency Mid-Project

Two weeks later, Maria needs pandas for data analysis.

Maria Adds the Dependency

uv add pandas

Output:

Resolved 52 packages in 0.40s
Downloaded 52 packages in 0.31s
Installed 52 packages in 0.12s

UV updates both pyproject.toml and uv.lock with pandas and its 50+ dependencies.

Maria Commits

git add pyproject.toml uv.lock
git commit -m "Add pandas for data analysis"

You Pull and Sync

git pull
uv sync

Now you have pandas too, with the exact same version Maria has.


Real-World Scenario 4: Production Deployment

When deploying to production, you don't need dev tools (pytest, black, etc.). Use:

uv sync --no-dev

This installs only production dependencies (requests, beautifulsoup4, pandas) and skips dev tools. Keeps your production environment lean, fast, and secure.


Lockfile Updates: When and Why

Scenario 1: Adding a New Dependency

When you run uv add new-package, UV automatically updates uv.lock:

uv add requests
# uv.lock is updated automatically
git add pyproject.toml uv.lock
git commit -m "Add requests"

Scenario 2: Updating Existing Packages

After months, you want to update dependencies:

# Check what's available
uv pip compile --upgrade pyproject.toml

# Actually update (test thoroughly before committing!)
uv lock --upgrade

This re-resolves dependencies with latest versions (respecting constraints in pyproject.toml) and updates uv.lock.


Onboarding a New Team Member

New developer Alex joins the team:

# Step 1: Clone
git clone https://github.com/yourorg/web-scraper.git
cd web-scraper

# Step 2: Sync (recreates exact environment)
uv sync

# Step 3: Verify
uv run pytest

In 30 seconds, Alex has your exact environment. If tests pass, they're ready to develop.


Handling Lockfile Conflicts (Advanced)

Scenario: You added package-a on your branch. Maria added package-b on hers. Now uv.lock has a Git conflict.

Fix:

# Don't manually edit uv.lock. Instead:
git merge --abort # Undo merge
git pull # Get latest
uv lock # Regenerate lockfile with all changes
git add uv.lock
git commit -m "Merge dependencies from both branches"

Why: uv lock re-resolves all dependencies and creates a consistent lockfile automatically. Never manually edit uv.lock.


Git Integration: What to Commit, What to Ignore

Here's the complete picture of what goes to Git and what doesn't:

File/DirectoryCommit?Why
pyproject.tomlYESProject configuration; needed for dependency resolution
uv.lockYESExact versions; needed for reproducibility
.python-versionYESPython version specification; needed for consistency
src/YESYour code
.venv/NOEnvironment directory; 50+ MB, recreated by uv sync
__pycache__/NOPython cache; recreated when code runs
*.pycNOCompiled bytecode; recreated automatically
.envNO (with care)Secrets; use .env.example instead

UV creates .gitignore automatically when you run uv init, so most of this is handled for you.


The Collaboration Workflow Summary

Here's the complete team workflow with UV:

Developer A (You):
1. Create project with uv init
2. Add dependencies with uv add
3. Code and test
4. Commit pyproject.toml + uv.lock to Git

Developer B (Teammate):
1. Clone repository (gets pyproject.toml + uv.lock)
2. Run uv sync (recreates exact environment)
3. Code and test in identical environment
4. Add new dependency with uv add
5. Commit updated pyproject.toml + uv.lock

Developer C (New Team Member):
1. Clone repository
2. Run uv sync (3-second setup)
3. Run uv run pytest (verify everything works)
4. Start developing

No environment mismatches. No "works on my machine" problems. Every developer, every branch, every deployment—identical environment.


Try With AI

Why do we need both pyproject.toml and uv.lock, and how do teams stay synchronized?

🔍 Explore Lockfile Differences:

"I added requests to my UV project. Now pyproject.toml shows requests>=2.31.0 but uv.lock shows requests==2.32.1. Why does pyproject.toml use >= while uv.lock uses ==? What happens if my teammate clones the project without the lockfile? Which files should Git track and why?"

🧪 Test Team Conflict Scenarios:

"My team hit these problems: Scenario A - I added pandas on my branch, teammate added numpy on theirs, we merged and now uv.lock has Git conflicts. Scenario B - Maria has requests 2.31.0, I have 2.32.1, we need matching versions. Scenario C - Alex cloned the repo but forgot to run uv sync and ran code directly. For each scenario, show me the fix commands, explain why they work, and what breaks if I manually edit uv.lock."

🎯 Practice Environment Recreation:

"Walk me through the complete workflow: I'm preparing a UV project for my team to clone. What files do I commit? How do I verify the lockfile is current? Then from my teammate's perspective: exact commands to clone, recreate the environment, and verify it worked. What happens if sync fails?"

🚀 Build Team Onboarding Guide:

"Create a team onboarding checklist for UV projects covering: project maintainer responsibilities (file commits, lockfile verification, README documentation) and new developer steps (cloning, environment setup, verification tests). Add a troubleshooting section with the top 3 mistakes new developers make, showing error messages and fixes for each."