1Development Environment Setup
▶
Before a single line of project code is written, every member of a team needs a working, verified development environment. An environment is more than "having an editor installed" — it is the combination of a code editor, version control tooling, authentication credentials, a shared folder structure, and a verified communication path to the remote repository on GitHub. When these pieces are configured consistently across a team, collaboration becomes almost invisible: people clone, branch, commit, and push without thinking about it. When they are configured inconsistently, teams lose entire weeks to "it works on my machine," broken file paths, rejected pushes, and commits attributed to the wrong person. This topic walks through each layer of the setup, explains why each configuration choice matters, and ends with a concrete end-to-end verification procedure that every individual must complete independently.
Choosing a Code Editor
A code editor is the primary interface to the project, so the choice has a real effect on productivity and on how easily teammates can help each other. The practical recommendation for most teams — and the default assumption for the rest of this module — is Visual Studio Code (VS Code). It is free, it runs on Windows, macOS, and Linux with a near-identical interface, and it is overwhelmingly common in both industry and education. That ubiquity matters: when a teammate says "open the Command Palette and run Git: Clone," everyone knows exactly what that means, and the same keyboard shortcuts and screenshots work for the whole group.
It is worth distinguishing an editor from a full IDE. A traditional IDE such as IntelliJ IDEA or Visual Studio ships with a compiler toolchain, debugger, profiler, and project model bundled in. VS Code starts as a lightweight editor and becomes an IDE through extensions — you add only the language tooling you actually need. Other reasonable choices exist, and the table below summarizes the trade-offs, but a team should pick one primary editor and document it.
| Editor | Cost | Platforms | Best suited to | Main trade-off |
|---|---|---|---|---|
| VS Code | Free, open source core | Windows, macOS, Linux, browser | Web, Python, JavaScript, general-purpose, student teams | Needs extensions for deep language support |
| JetBrains IDEs (IntelliJ, PyCharm, WebStorm) | Paid (free student licences, free Community editions) | Windows, macOS, Linux | Large Java/Kotlin/Python codebases, heavy refactoring | Heavier, slower startup, licence management |
| Visual Studio | Free Community edition | Windows (limited macOS) | C#/.NET, C++ on Windows | Platform-limited; large install |
| Neovim / Vim | Free | All, including over SSH | Remote servers, keyboard-driven workflows | Steep learning curve; hard to standardize across a team |
Extensions. Extensions are what turn a generic editor into a project-specific workbench. They fall into a handful of recognizable categories:
- Language support — syntax highlighting, IntelliSense/autocomplete, go-to-definition, and inline type errors. Examples: the official
Pythonextension (which bundles Pylance),ESLintfor JavaScript/TypeScript,C/C++from Microsoft,Extension Pack for Java. - Linters — tools that flag suspicious or non-conforming code before it runs: unused variables, undefined names, shadowed parameters. ESLint, Pylint, Ruff, and Flake8 are typical.
- Formatters — tools that rewrite code into a canonical style automatically: Prettier for web languages, Black for Python,
clang-formatfor C/C++. Formatters are the single highest-value team extension because they eliminate whitespace-only diffs and style arguments in code review. - Version control helpers — GitLens (blame annotations and history), GitHub Pull Requests and Issues (review PRs without leaving the editor).
- AI assistance — GitHub Copilot, Copilot Chat, or similar tools that suggest completions and explain code. These are productivity aids, not authorities: suggestions must still be read, tested, and understood, and many courses or employers have explicit policies about their use.
- Collaboration and environment — Live Share for pair programming, Dev Containers and Remote–SSH for working inside containers or on remote machines.
Sharing a recommended extensions list. Rather than telling teammates in chat which extensions to install, commit the list to the repository. VS Code reads a file at .vscode/extensions.json and, when someone opens the folder, prompts them to install anything missing. This makes the toolchain part of the project itself, versioned alongside the code.
// .vscode/extensions.json
{
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"charliermarsh.ruff",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"github.vscode-pull-request-github"
],
"unwantedRecommendations": []
}
Pair it with a committed .vscode/settings.json so that formatting behaviour is identical for everyone — otherwise one person's editor reformats a whole file on save and the pull request becomes unreviewable:
// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.tabSize": 4
}
}
A complementary .editorconfig file at the repository root does the same job for teammates on other editors, since EditorConfig is supported natively or via plugin almost everywhere.
Installing and Configuring Git Locally
Git is the version control system; GitHub is a hosting service for Git repositories. Installing Git gives you the command-line program git, which is what actually performs clones, commits, merges, and pushes — the editor's buttons are just a friendly front end for those same commands.
- Windows: install Git for Windows from git-scm.com, which also provides Git Bash (a Unix-like shell) and the Git Credential Manager. Alternatively
winget install Git.Git. - macOS:
brew install git, or trigger Apple's Command Line Tools withxcode-select --install. The system Git is often an old version; Homebrew's is current. - Linux:
sudo apt install git(Debian/Ubuntu) orsudo dnf install git(Fedora).
Confirm the install before going further:
$ git --version
git version 2.45.1
Identity configuration. Every Git commit permanently records an author name and email in its metadata. If you skip this step, Git either refuses to commit or guesses from your machine's hostname, producing commits attributed to student@LAPTOP-7F3K2. Those commits will not link to your GitHub profile, will not appear in contribution graphs, and — in a graded group project — may not be credited to you at all. Set the identity once, globally:
$ git config --global user.name "Amara Okafor"
$ git config --global user.email "amara.okafor@university.edu"
$ git config --global init.defaultBranch main
$ git config --global pull.rebase false
$ git config --global core.autocrlf input # on macOS/Linux; use 'true' on Windows
$ git config --list --show-origin # verify, and see which file each value came from
The email must be one that is registered on your GitHub account (Settings → Emails), otherwise GitHub cannot associate the commit with your profile. If you prefer not to publish a personal address, enable email privacy on GitHub and use the supplied 12345678+username@users.noreply.github.com address instead. For a repository that needs a different identity (e.g. a work account), omit --global inside that repository to set a local override.
SSH key authentication. There are two ways to talk to GitHub: HTTPS, which requires a personal access token typed or cached as a password, and SSH, which uses a public/private key pair. SSH is preferred because the private key never leaves your machine, nothing password-like is transmitted, the key can be protected by a passphrase held in an agent, and you can revoke a single device's key without changing anything else. HTTPS tokens, by contrast, are bearer secrets that can be accidentally pasted into a chat, committed to a file, or captured by a shoulder-surfer; classic GitHub account passwords have not been accepted for Git operations since 2021 at all.
$ ssh-keygen -t ed25519 -C "amara.okafor@university.edu"
# Press Enter to accept ~/.ssh/id_ed25519, then set a passphrase
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519
$ cat ~/.ssh/id_ed25519.pub # copy this PUBLIC key only
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... amara.okafor@university.edu
Paste the public key (the .pub file) into GitHub → Settings → SSH and GPG keys → New SSH key. Never share or commit the private key, which has no extension. Then test the connection:
$ ssh -T git@github.com
Hi amara-okafor! You've successfully authenticated, but GitHub does not provide shell access.
That message is the success case — the "does not provide shell access" part is expected and not an error.
| Aspect | HTTPS + personal access token | SSH key pair |
|---|---|---|
| What is sent | A reusable secret token | A cryptographic signature; the key itself never leaves the device |
| Setup effort | Low — works immediately, token generated on GitHub | Moderate — generate key, add to agent, upload public half |
| Revocation | Revoke token (may affect several machines) | Delete one key; other devices unaffected |
| Firewall friendliness | Works on port 443 almost everywhere | Port 22 sometimes blocked; can fall back to ssh.github.com:443 |
| Expiry | Tokens typically expire and must be regenerated | Keys do not expire |
Cloning. git clone downloads the full repository — every file, branch, and commit in its history — and sets up a remote named origin pointing back at GitHub. This is how each team member gets their own complete working copy:
$ cd ~/projects
$ git clone git@github.com:team-aurora/inventory-tracker.git
Cloning into 'inventory-tracker'...
remote: Enumerating objects: 248, done.
Receiving objects: 100% (248/248), 1.21 MiB | 4.02 MiB/s, done.
$ cd inventory-tracker
$ git remote -v
origin git@github.com:team-aurora/inventory-tracker.git (fetch)
origin git@github.com:team-aurora/inventory-tracker.git (push)
$ git log --oneline -3
If you already cloned over HTTPS and want to switch, you do not need to re-clone: git remote set-url origin git@github.com:team-aurora/inventory-tracker.git.
Cloud Development with GitHub Codespaces
GitHub Codespaces provides a development environment that runs on a GitHub-hosted virtual machine and is accessed either through the browser or by connecting your desktop VS Code to it. The entire toolchain — compilers, runtimes, package managers, Git itself — lives on that remote machine. This changes several things that routinely derail team setups:
- The local operating system stops mattering. A teammate on a locked-down school Windows laptop, another on an M-series Mac, and a third on a Chromebook all get the same Linux container. Platform-specific breakages — PowerShell versus Bash commands, missing build tools,
CRLFline endings, a Python version that is two releases behind — simply do not arise. - No local install burden. Nothing has to be installed with administrator rights, which is decisive on managed lab or corporate machines.
- Built-in GitHub integration. A codespace is created from a repository, so the clone is already done, authentication is already in place via a scoped token, and
git pushworks immediately with no SSH key setup. Forwarded ports let you preview a running web server in the browser, and the port can be shared privately with teammates. - Disposability. If an environment gets into a broken state, delete the codespace and recreate it in a couple of minutes.
The trade-offs are real too: codespaces consume a monthly quota of core-hours and storage (generous on free and education plans, but finite), they require a reliable internet connection, and they stop automatically after a period of inactivity, so uncommitted work should not be left sitting indefinitely.
The devcontainer configuration. The mechanism that makes a codespace reproducible is a file at .devcontainer/devcontainer.json, committed to the repository. It declares the base image, additional tooling ("features"), VS Code extensions and settings, forwarded ports, and commands to run after creation. Because it is version-controlled, every teammate — and every future contributor — gets a byte-identical environment, and changes to it are reviewed like any other code change. The same file also works locally with the Dev Containers extension plus Docker, so people can run the identical container on their own machine.
// .devcontainer/devcontainer.json
{
"name": "Inventory Tracker",
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "20" },
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"charliermarsh.ruff",
"esbenp.prettier-vscode",
"eamodio.gitlens"
],
"settings": {
"editor.formatOnSave": true,
"python.defaultInterpreterPath": "/usr/local/bin/python"
}
}
},
"forwardPorts": [8000],
"postCreateCommand": "pip install -r requirements.txt && npm ci",
"remoteUser": "vscode"
}
With this in place, a new team member's onboarding is: open the repository on GitHub → Code → Codespaces → Create codespace on main. Two minutes later they have Python 3.12, Node 20, all dependencies installed, the agreed extensions loaded, and port 8000 forwarded — with no instructions to follow and nothing to get wrong.
Project Folder Structure and Conventions
A repository's layout is a form of documentation. If a newcomer can guess where things live, they can contribute; if files are scattered at the root with names like final_v2_REAL.py, every change becomes archaeology. Agree on a structure in week one, write it into the README.md, and create the directories immediately — even if some are nearly empty — so that the first commits land in the right place.
inventory-tracker/
├── README.md # what it is, how to set up, how to run, folder map
├── LICENSE
├── .gitignore
├── .editorconfig
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── .devcontainer/
│ └── devcontainer.json
├── .github/
│ └── workflows/ci.yml # automated tests on every push
├── src/ # application source code only
│ ├── models/
│ ├── services/
│ └── main.py
├── tests/ # mirrors src/ structure
│ ├── test_models.py
│ └── test_services.py
├── assets/ # images, fonts, sample data, icons
├── docs/ # design notes, meeting minutes, API reference
└── scripts/ # one-off or utility scripts (seeding, deploys)
Why separate source, assets, docs, and tests? Each directory has a different audience and lifecycle. Separating them means you can run the whole test suite with one path (pytest tests/), package or deploy only src/, let non-developers edit docs/ without touching code, keep large binary assets out of the import path, and configure tooling cleanly — linters scan src/ and tests/, the bundler reads assets/. It also makes code review diffs legible: a pull request touching only docs/ obviously carries no runtime risk.
Document it in the README from day one. The README is the first thing anyone reads. It should contain, at minimum: a one-paragraph description of the project, prerequisites, setup commands, how to run the app and the tests, and a short folder map explaining what belongs in each directory. A folder structure that exists only in one person's head is not a convention — it is a bottleneck.
File naming conventions and case sensitivity. This is the single most common source of baffling cross-platform bugs. Linux file systems are case-sensitive: UserCard.js and usercard.js are two different files. Windows and the default macOS file system are case-insensitive but case-preserving: they treat those names as the same file. So a developer on macOS can write import Card from './components/userCard' while the file is actually named UserCard.js, commit it, and everything works locally — then the Linux CI build or the Codespaces container fails with "module not found." Worse, renaming usercard.js to UserCard.js on a case-insensitive system may not even register as a change in Git unless you use git mv or enable core.ignorecase=false.
Practical rules that avoid the whole class of problem:
- Pick one convention per file type and record it in the README:
kebab-casefor web assets and URLs (user-card.css),snake_casefor Python modules (user_card.py),PascalCasefor class/component files where the framework expects it (UserCard.tsx). - Never rely on case alone to distinguish two files.
- Avoid spaces and special characters in filenames — they break shell commands and URLs. Use hyphens or underscores.
- Keep paths reasonably short; Windows historically limits total path length to 260 characters.
- Make imports exactly match the on-disk spelling, every time.
- Use a
.gitignorefrom the start so thatnode_modules/,__pycache__/,.venv/,.env,dist/, and.DS_Storenever enter the history. Committed secrets and build artefacts are painful to remove later.
Verifying the Complete Setup
Installing things is not the same as proving they work together. The purpose of verification is an end-to-end test: a tiny change that travels from the editor, through local Git, over the authenticated connection, into the GitHub repository, and back down to a teammate. Only a round trip proves that all four links in the chain — editor, Git, credentials, repository permissions — are sound.
A workable verification script for each person:
- Confirm the tools exist:
git --versionand, if relevant,python --version/node --version. - Confirm identity:
git config user.nameandgit config user.emailreturn your real details. - Confirm authentication:
ssh -T git@github.comgreets you by your GitHub username. - Clone the team repository and open it in the editor; accept the recommended extensions prompt.
- Create a branch, make a trivial but real change, commit, and push.
- Open a pull request, have a teammate review and merge it, then pull the merged result back down.
$ git switch -c setup/amara-verify
$ echo "- Amara Okafor — environment verified $(date +%F)" >> docs/team-roster.md
$ git add docs/team-roster.md
$ git commit -m "chore: verify local environment for Amara"
$ git push -u origin setup/amara-verify
$ gh pr create --fill # or open the PR in the browser
# after the PR is merged
$ git switch main
$ git pull
$ git log --oneline -1 # your commit should be at the tip
A useful team artefact is a docs/team-roster.md or a checklist issue in which each person appends their own line as part of the exercise. The file itself then becomes the evidence that the verification happened: if a name is missing, that person's setup is unproven.
Catch authentication and permission errors in week one. Setup failures are cheap to fix on day three and expensive to fix the night before a deadline, because they block the delivery of work that is already finished. The most common failures and their usual causes are predictable enough to triage quickly:
| Symptom | Likely cause | Fix |
|---|---|---|
Permission denied (publickey) | SSH key not generated, not added to the agent, or public key not uploaded to GitHub | Re-run ssh-add ~/.ssh/id_ed25519; confirm the key is listed in GitHub settings; test with ssh -T git@github.com |
remote: Permission to org/repo.git denied to user | Not a collaborator on the repository, or authenticated as the wrong account | Repository owner adds the user as a collaborator; check cached credentials in the OS credential manager |
Support for password authentication was removed | Using an account password over HTTPS | Switch the remote to SSH, or use a personal access token |
| Commits show the wrong author or no avatar | user.email not set, or not registered on the GitHub account | git config --global user.email with a verified or noreply GitHub address |
! [rejected] main -> main (fetch first) | Remote has commits you do not have locally | git pull (resolve any conflict), then push again |
| CI passes locally but fails in the cloud with "module not found" | Filename case mismatch between the import and the file on disk | git mv the file to the correct casing and fix the import |
Everyone verifies independently. It is tempting for the most confident member of a team to set things up, push the first commit, and declare the environment "working." That proves exactly one person's configuration. Authentication, identity, permissions, installed runtimes, and operating-system quirks are all per-machine and per-account, so a single successful push says nothing about the other four people. Make individual verification an explicit deliverable: each member pushes their own branch from their own machine under their own GitHub account, and the team confirms that every name appears in the commit history before any feature work begins. The cost is fifteen minutes each; the alternative is discovering in week six that one teammate has been emailing zip files because their push has never once worked.