Tagline: Commit small, commit often, write real messages.
Creating a hello-world app sounds easy — and it is. But doing it the right way, with proper git hygiene, isolated environments, and secrets handled safely from day one? That takes a bit more care. Here's what I actually did, step by step, and why.
Index
- Introduction
- Structure of this log
- Step 1: Project Foundations
- Step 2: Getting FastAPI Running Locally
-
Step 3: Secrets — the
.envFile - Step 4: Start This Build Log
- Key Habits to Keep
Introduction
I started learning backend dev properly this week, and today I got a hello-world FastAPI app running locally. Nothing fancy — one route, one JSON response — but I wanted to write down what actually happened along the way, because "install a package and run a command" always looks simpler in hindsight than it feels in the moment.
Structure of this log
- Create project folder → init git →
.gitignore→ virtual environment - Connect to a GitHub repo
- Build and run a FastAPI hello-world app, explore
/docs - Set up a
.envfile for secrets and confirm it's gitignored - Start this build log
Step 1: Project Foundations
Create the folder
mkdir my-project-1
cd my-project-1
Initialize git first
git init
Why first? So every file created from this point on is tracked from day one — no risk of forgetting to init later.
Create .gitignore before installing anything
cat > .gitignore << 'EOF'
venv/
__pycache__/
.env
.DS_Store
EOF
Why before the virtual environment? If you create the venv first, git could briefly see hundreds of library files before you've excluded them. Doing this first avoids ever accidentally staging things you don't want tracked.
How this command works:
-
cat— normally reads and displays file content, but here it's not reading a file — it's about to receive typed input instead. -
>— redirect operator. Instead of printing to the screen, the output is written into a file. Since.gitignoredoesn't exist yet, this creates it. -
<< 'EOF'— a heredoc. It tells bash: "don't wait for a real file — treat everything typed next, up until a line that says exactlyEOF, as the input text." -
EOF(closing) — signals "stop, that's the end of the input."
Note:
EOFisn't a special keyword — it's just a convention. You could useBANANAinstead, and it would work identically: bash treats everything as raw text until it sees a line containing exactly that word.EOF("End Of File") is used everywhere because it instantly signals intent to anyone reading the script.Note: This heredoc syntax is bash-specific. PowerShell uses a different syntax (
@" ... "@ | Out-File) — match the syntax to the shell you're actually in.
Create and activate the virtual environment
python -m venv venv
source venv/Scripts/activate
What this does:
-
python -m venv venv— runs Python's built-invenvmodule, creating a folder (venv/) containing an isolated copy of Python and its own package directory. -
source venv/Scripts/activate— activates it, redirecting your terminal'spython/pipto point insidevenv/instead of your system-wide install. You'll know it worked when(venv)appears in your prompt.
Note: The folder is named
Scripts/on Windows andbin/on Linux/macOS. Ifsource venv/bin/activatefails with "No such file or directory," check which one actually exists withls venv.
Install packages + freeze requirements
pip install requests
pip freeze > requirements.txt
Why this matters: every time you install something new, freeze it into requirements.txt. This makes the exact dependency set shareable — anyone (including future-you) can recreate your environment with pip install -r requirements.txt.
First commit
touch README.md main.py # or: New-Item README.md, main.py (PowerShell)
git add .
git commit -m "Initial project setup"
What makes a good commit message: it describes what changed and why — not just "update" or "fix stuff."
Connect to GitHub
Create a new, empty repository on GitHub first, then:
git remote add origin <your-repo-url>
git branch -M main
git push -u origin main
Breaking this down:
| Command | What it does |
|---|---|
git remote add origin <url> |
Registers a remote connection, nicknamed origin (convention), pointing at your GitHub repo's URL. No data moves yet — this just registers the address. |
git branch -M main |
Renames your current branch to main (forcing it even if a branch with that name exists), matching what GitHub expects by default. |
git push -u origin main |
Uploads your commits to GitHub for the first time. -u sets up tracking between local main and origin/main, so every push after this can just be git push. |
Run this sequence once per project — right at the start, connecting a fresh local repo to a fresh GitHub repo.
The repeatable loop (used for every session after this)
source venv/Scripts/activate
# ... work, install packages ...
pip freeze > requirements.txt
git add .
git commit -m "clear description of change"
git push
deactivate
The commit flow, in isolation
git status # check what changed
git add .gitignore # stage the change
git commit -m "clear description" # commit with a real message
git push # push to GitHub
git log --oneline -1 # sanity check
Step 2: Getting FastAPI Running Locally
Ensure the virtual environment is active
source venv/Scripts/activate
Install FastAPI and Uvicorn
pip install fastapi uvicorn
pip freeze > requirements.txt
- FastAPI — lets you define API routes/endpoints in Python. It doesn't serve requests on its own — it needs an ASGI (Asynchronous Server Gateway Interface) server to actually run.
- Uvicorn — a lightweight ASGI server that runs your app and listens for HTTP requests.
Create the app file
code . # opens the current folder in VS Code
Structure of main.py:
- Import
FastAPI - Create an application instance (
app = FastAPI()) for Uvicorn to run - Use a decorator (
@app.get("/")) so that a GET request to the root URL runs the function below it - Define the function to handle the request
- Return the response (FastAPI auto-converts a Python dict to JSON)
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, World!"}
Start the server
uvicorn main:app --reload
-
main:app— tells Uvicorn to look insidemain.pyfor an object namedapp. -
--reload— automatically restarts the server whenever you save a code change.
Visit http://127.0.0.1:8000 to see the JSON response.
Check the automatic docs
Visit http://127.0.0.1:8000/docs — FastAPI auto-generates a full interactive API explorer (Swagger UI / OpenAPI spec), built entirely from your route definitions. No docs code required.
A read-only alternative view is available at /redoc.
Step 3: Secrets — the .env File
Create .env
cat > .env << 'EOF'
APP_SECRET=super-secret-value-123
EOF
Confirm it's actually ignored — don't just assume
git status # .env should not appear anywhere in the output
git check-ignore -v .env # the definitive test
git check-ignore -v reports exactly which line in which file is causing the file to be ignored, e.g.:
.gitignore:7:.env .env
Important:
.gitignoreonly prevents git from tracking files it hasn't already started tracking. If a secret was committed before being added to.gitignore, it will still exist in your git history. Setting up the ignore rule before the firstgit add— which is what happened here — avoids this entirely.
Step 4: Start This Build Log
Keep two documents, for two different audiences:
-
BUILD_LOG.md(this file) — chronological, for future-me. What I did, what broke, what I learned. Terse is fine. -
README.md— for other people (or future-me pretending to be someone else). What the project is and how to run it — a snapshot of current state, not a diary.
The most useful log entries capture the why, not just the what — especially for snags:
## 2026-07-16
- Set up project folder, git repo, and Python virtual environment
- Installed FastAPI + Uvicorn, built a hello-world app with a single GET / route
- Confirmed it runs locally and explored the auto-generated /docs (Swagger UI)
- Set up a .env file for secrets and confirmed it's properly gitignored
(learned: .gitignore only blocks files git hasn't tracked yet — order matters)
Commit it the same way as everything else:
git add BUILD_LOG.md
git commit -m "Start build log with first entry"
git push
Key Habits to Keep
- Commit small, commit often, write real messages — a message should answer "what changed and why," not just "update."
-
.gitignorebeforegit add, always — order of operations is what actually protects secrets. - Freeze requirements on every install, not just at the end.
- Log the "why" and the snags, not just the "what" — that's the part future-you will actually forget.
Top comments (0)