Start Your First Python Project with uv init
The moment uv starts to make sense is when you stop thinking about it as a command catalog and use it to create a real project. In this post, we will start with an empty terminal, create a project with uv init, inspect the files uv creates, and run the starter code in the managed environment.
UV Series
Create a real uv project and understand every file that appears.
A uv project is centered on pyproject.toml, a managed environment, and a lockfile.
You will know what uv init makes and how to run the project without guessing.
Run uv init and Step Into the Folder
Start with a new directory name:
uv init my-first-uv-project
cd my-first-uv-project
By default, uv creates an application project. That is the right choice for most beginners, because it gives you a simple executable main.py and avoids package structure decisions on day one.
Right after uv init, your folder should look roughly like this:
my-first-uv-project/
├── .gitignore
├── .python-version
├── README.md
├── main.py
└── pyproject.toml
This is enough to begin writing Python immediately.
Run the Starter Program with uv run
Now run the file uv created:
uv run main.py
That single command does more than beginners usually realize. It checks whether the project is in sync, creates the environment if needed, and then runs the command inside the correct project environment.
That is why uv run is so central. It removes the usual “wait, which Python am I using?” problem.
What Each File Is For
pyproject.toml
This is the heart of the project. It stores the project name, version, Python requirement, and dependencies. When you later run uv add, this file changes.
.python-version
This is the version hint that tells uv which Python version the project expects. That is one reason uv can create the right environment without you micromanaging it.
main.py
This is just the starter application file. Think of it as a placeholder that proves the workflow works.
README.md
This is where you can document what the project is and how to run it. It matters more than beginners think once a project outlives its first day.
.gitignore
This keeps generated files like local environments and caches out of version control.
Why .venv and uv.lock Show Up After You Start Working
Two important project parts often appear after you run a project command like uv run or uv sync:
.venv/
uv.lock
.venv is the local virtual environment for the project. Your packages live there. Editors also use it to understand imports and completions.
uv.lock records the resolved package versions. That is what makes the project reproducible instead of “whatever happened to install on this machine.”
uv.lock to version control. It helps every machine resolve the same environment instead of drifting over time.
When Default uv init Is Enough and When --package Makes Sense
For scripts, small tools, and early learning, the default application template is enough. It gives you a main.py and keeps the file structure obvious.
Use uv init --package your-name when you are building something that should behave like an installable Python package, especially if you want a src/ layout or plan to publish it later.
For this series, the application template is perfect. We are learning the project workflow first, not packaging strategy.
Change the Starter File So the Workflow Feels Real
Open main.py and make a harmless change:
def main():
print("Hello from my-first-uv-project!")
if __name__ == "__main__":
main()
Now run it again:
uv run main.py
That feedback loop is the core beginner rhythm in uv: edit a file, run with uv run, then add dependencies when your project actually needs them.
Frequently Asked Questions
These are the practical questions beginners usually ask at this stage of the uv workflow.
What is the difference between uv init and creating a folder yourself?
You can create the folder yourself, but uv init gives you a recognized project structure, metadata, a starter file, and Python version pinning immediately.
Why does uv run matter if my script is only one file?
Because it runs the script in the project environment and keeps the project synchronized first. That becomes even more important once you add dependencies.
Should I delete main.py if I want different filenames?
No. You can rename or replace it once you are comfortable. The important part is learning the project workflow, not preserving the starter file forever.
Why does uv.lock appear later instead of immediately?
Because uv creates it when the project actually needs environment resolution, such as during uv run, uv sync, or uv lock.
Do I need to activate .venv right now?
No. You can keep using uv run throughout the series. Activation is optional, not the first thing you need to learn.
Conclusion
At this point, you have a real uv project, and the project layout should already feel less mysterious.
The next skill is the one that makes the project genuinely useful: adding dependencies, syncing the environment, and running code that depends on those packages.
Raell Dottin
Comments