uv: one lockfile, one sync command, and a Python environment you can rebuild on the customer's machine
When a pipeline runs on your laptop but breaks on the customer's server, the cause is usually environment drift. uv is built to stop exactly that drift.

In brief
- uv.lock is a lockfile that works on every operating system and must be committed. .venv is not committed; each machine rebuilds it with uv sync.
- The .python-version file pins the project's Python version. If the customer's machine does not have it, uv downloads it by default.
- The published speed figures do not agree with each other. The reason for an FDE to use uv is reproducible environments; speed is a bonus.
uv locks down the Python side. Network access, system libraries and speed promises are still yours to manage.
Graphic: FDE Times
The uv README lists the tools it aims to replace: pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, “and more”. Any FDE who has built the same pipeline on their own Mac, one customer’s Linux server and another customer’s Windows machine will recognise that list. Every name on it is a place where environments can drift apart.
Astral, the company behind Ruff, describes uv as an “extremely fast” Python package and project manager written in Rust. Speed, however, is not the main reason for an FDE to learn it. The main reason is reproducibility: the code you deliver has to behave the same way on machines you do not control and may never have seen.
If you are moving into an FDE role, this skill is worth practising early, because the job is shipping code that runs on someone else’s infrastructure. A demo that works on your machine proves little. What counts is the first command that works on the customer’s.
The bug is usually not in the code
Picture a common scenario. You write a document classification service on a Mac, test it thoroughly and push it to Git. An engineer on the customer side clones the repo onto a Windows machine, installs the libraries with whatever Python version is already there, and hits an import error on the first line.
There are three usual causes. The customer’s Python version differs from yours; the libraries were resolved on a different day and pulled in different versions; or your lockfile is only valid for the operating system it was created on. uv has a specific answer to each.
Three files and one command
Whether a project currently uses requirements.txt or another tool, the target after moving to uv is a repo with these three items:
customer-project/
├── .python-version # commit
├── uv.lock # commit
└── .venv/ # do NOT commit
The .python-version file pins the project’s Python version. The uv.lock file records the packages to be installed, and the uv documentation stresses that it belongs in version control. The .venv directory is the actual environment on each machine; it is not committed (KHÔNG commit, in the comment above, means “do not commit”) because any machine can rebuild it from the lockfile.
On the customer’s machine, after cloning the repo, you only need to run:
uv sync
uv reads .python-version. If the machine lacks a suitable Python, uv downloads one by default, so the customer does not need the right version installed in advance. uv then builds .venv to match exactly what uv.lock records.
The key word is “universal”. uv creates the lockfile by resolving for all platforms, so the same uv.lock works across operating systems. Without that kind of resolution, a lockfile is only correct on the platform that produced it, which is precisely the Mac-to-Windows failure in the scenario above.
When the customer wants last month’s run, exactly
One request comes up constantly on customer sites: last month’s report produced one number, rerunning it now produces another, and the customer wants to know why.
If the lockfile was committed alongside the code, checking out the old commit solves most of the problem. When you have to resolve from scratch, uv offers the --exclude-newer option, which restricts resolution to distributions uploaded before a given date.
There is a smaller case too: a data-cleaning script you wrote in a hurry for an analyst on the customer’s team. uv lets you declare dependencies inside the script file itself, following PEP 723, and lock that single script. The uv documentation states that the purpose is to let the script rebuild the same environment when it is run again later.
Common mistakes
The first mistake is committing .venv. It is heavy, tied to the machine that created it, and entirely redundant when uv sync can rebuild the environment from the lockfile.
The opposite mistake is leaving uv.lock out of Git on the assumption that it is a generated file. Without a lockfile, the customer’s machine resolves again on a different day, and you are back at the import error above.
With standalone scripts, the usual mistake is declaring dependencies without locking them. The list of libraries is there, but the versions chosen depend on the day the script runs. Before sending one, lock it and run it yourself on a clean machine to make sure the customer gets everything they need.
What uv won’t do for you
Start with the speed figures. The official documentation says uv is 10 to 100 times faster than pip, while Charlie Marsh’s launch post of 15 February 2024 claims 8-10 times faster than pip and pip-tools without a cache.
The two figures do not match, so neither amounts to a promise for a customer’s infrastructure. If you want to talk about speed, measure it on their machines first.
Automatic Python downloads are convenient but require the customer’s machine to have outbound network access. In locked-down environments, ask about this at the kickoff meeting and prepare an alternative. Beyond that, uv.lock only records Python packages; drivers, system libraries and machine configuration sit outside the lockfile, so you still have to check them separately.
What to learn first
A sensible order: the lockfile and uv sync first, then .python-version, and only then --exclude-newer and scripts with inline dependencies. The best exercise is to break things and fix them yourself: delete .venv, move to a machine with a different operating system, and check whether one command is enough to get the project running again.
When reading FDE job descriptions, look for phrases such as “reproducible environments” or “deploy on customer infrastructure”. That is where this skill gets used. On your CV, don’t just write “knows uv”; describe how many steps you cut environment setup down to, and give a concrete instance of your project running on an unfamiliar machine.
Customers will not remember which tools you used. They will remember whether, the first time they cloned your repo, everything worked straight away or took an entire afternoon to fix.
Was this article useful?
Thanks for the feedback!