Dev Environment
Your tools shape your thinking. Set them up once, set them up right.
Prerequisites: None
Video coming soon
An AI-narrated walkthrough for this module is on the way. Everything you need is in the lesson below.
#Learning Objectives
- Set up Python 3.11+, Node.js 20+, and Rust toolchains from scratch
- Configure virtual environments and package managers for reproducible builds
- Verify GPU access with CUDA/MPS and run a test tensor operation
- Understand the four-layer stack: system, packages, runtimes, AI libraries
#The Problem
You're about to learn AI engineering across 200+ lessons using Python, TypeScript, Rust, and Julia. If your environment is broken, every single lesson becomes a fight against tooling instead of learning.
Most people skip environment setup. Then they spend hours debugging import errors, version conflicts, and missing CUDA drivers. We're going to do this once, properly.
#The Concept
An AI engineering environment has four layers:
graph TD
A["4. AI/ML Libraries\nPyTorch, JAX, transformers, etc."] --> B["3. Language Runtimes\nPython 3.11+, Node 20+, Rust, Julia"]
B --> C["2. Package Managers\nuv, pnpm, cargo, juliaup"]
C --> D["1. System Foundation\nOS, shell, git, editor, GPU drivers"]
We install bottom-up. Each layer depends on the one below it.
#Build It
#Step 1: System Foundation
Check your system and install the basics.
# macOS
xcode-select --install
brew install git curl wget
# Ubuntu/Debian
sudo apt update && sudo apt install -y build-essential git curl wget
# Windows (use WSL2)
wsl --install -d Ubuntu-24.04#Step 2: Python with uv
We use uv — it's 10-100x faster than pip and handles virtual environments automatically.
curl -LsSf https://astral.sh/uv/install.sh | sh
uv python install 3.12
uv venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
uv pip install numpy matplotlib jupyterVerify:
import sys
print(f"Python {sys.version}")
import numpy as np
print(f"NumPy {np.__version__}")
a = np.array([1, 2, 3])
print(f"Vector: {a}, dot product with itself: {np.dot(a, a)}")#Step 3: Node.js with pnpm
For TypeScript lessons (agents, MCP servers, web apps).
curl -fsSL https://fnm.vercel.app/install | bash
fnm install 22
fnm use 22
npm install -g pnpm
node -e "console.log('Node', process.version)"#Step 4: Rust
For performance-critical lessons (inference, systems).
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustc --version
cargo --version#Step 5: Julia (Optional)
For math-heavy lessons where Julia shines.
curl -fsSL https://install.julialang.org | sh
julia -e 'println("Julia ", VERSION)'#Step 6: GPU Setup (If You Have One)
# NVIDIA
nvidia-smi
# Install PyTorch with CUDA
uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124import torch
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f"GPU: {torch.cuda.get_device_name(0)}")No GPU? No problem. Most lessons work on CPU. For training-heavy lessons, use Google Colab or cloud GPUs.
#Step 7: Verify Everything
Run the verification script:
python phases/00-setup-and-tooling/01-dev-environment/code/verify.py#Use It
Your environment is now ready for every lesson in this course. Here's what you'll use where:
| Language | Used In | Package Manager |
|---|---|---|
| Python | Phases 1-12 (ML, DL, NLP, Vision, Audio, LLMs) | uv |
| TypeScript | Phases 13-17 (Tools, Agents, Swarms, Infra) | pnpm |
| Rust | Phases 12, 15-17 (Performance-critical systems) | cargo |
| Julia | Phase 1 (Math foundations) | Pkg |
#Ship It
This lesson produces a verification script that anyone can run to check their setup.
See outputs/prompt-env-check.md for a prompt that helps AI assistants diagnose environment issues.
#Exercises
- Run the verification script and fix any failures
- Create a Python virtual environment for this course and install PyTorch
- Write a "hello world" in all four languages and run each one
Run this lesson in the Code Lab
A real JupyterLab, with this lesson's code files (and a ready-to-run notebook). The Quick Lab opens instantly in your browser. The Cloud Lab adds a terminal, saved files and the full toolchain.
// Lesson: Dev Environment (phase 00 / lesson 01)
// Topic: verify that the four-layer toolchain (system, package managers, runtimes, libs)
// is reachable from a Rust binary. Spawns each tool with `--version`, captures stdout,
// reports PASS/FAIL plus the parsed version string. Stdlib only.
// Refs:
// https://doc.rust-lang.org/std/process/struct.Command.html
// https://doc.rust-lang.org/std/process/struct.Output.html
// https://doc.rust-lang.org/book/ch12-00-an-io-project.html
// Build: rustc --edition 2021 code/main.rs -o /tmp/lesson_dev_env && /tmp/lesson_dev_env
use std::process::{Command, ExitCode};
struct Check {
name: &'static str,
program: &'static str,
args: &'static [&'static str],
optional: bool,
}
const CHECKS: &[Check] = &[
Check { name: "Git", program: "git", args: &["--version"], optional: false },
Check { name: "Python 3.10+", program: "python3", args: &["--version"], optional: false },
Check { name: "Node.js", program: "node", args: &["--version"], optional: false },
Check { name: "Rust (rustc)", program: "rustc", args: &["--version"], optional: false },
Check { name: "Cargo", program: "cargo", args: &["--version"], optional: false },
Check { name: "uv (Python)", program: "uv", args: &["--version"], optional: true },
Check { name: "pnpm", program: "pnpm", args: &["--version"], optional: true },
Check { name: "Julia", program: "julia", args: &["--version"], optional: true },
];
fn run_check(check: &Check) -> Result<String, String> {
let output = Command::new(check.program)
.args(check.args)
.output()
.map_err(|e| format!("{}: {}", check.program, e))?;
if !output.status.success() {
return Err(format!("exit code {:?}", output.status.code()));
}
let combined = if !output.stdout.is_empty() {
&output.stdout
} else {
&output.stderr
};
let raw = String::from_utf8_lossy(combined);
let line = raw.lines().next().unwrap_or("").trim().to_string();
if line.is_empty() {
Err("empty version output".to_string())
} else {
Ok(line)
}
}
fn parse_minor_python(version_line: &str) -> Option<(u32, u32)> {
let trimmed = version_line.trim_start_matches("Python").trim();
let mut parts = trimmed.split('.');
let major: u32 = parts.next()?.parse().ok()?;
let minor: u32 = parts.next()?.parse().ok()?;
Some((major, minor))
}
fn print_header() {
println!();
println!("=== AI Engineering from Scratch — Environment Check (Rust) ===");
println!();
println!("Layer 1 (system) -> Layer 2 (package managers) -> Layer 3 (runtimes) -> Layer 4 (libs)");
println!();
}
fn main() -> ExitCode {
print_header();
let mut required_pass = 0u32;
let mut required_total = 0u32;
let mut optional_pass = 0u32;
let mut optional_total = 0u32;
let mut python_ok = true;
println!("Required tools:");
for check in CHECKS.iter().filter(|c| !c.optional) {
required_total += 1;
match run_check(check) {
Ok(version) => {
if check.name.starts_with("Python") {
match parse_minor_python(&version) {
Some((major, minor)) if (major, minor) >= (3, 10) => {}
_ => {
println!(" [FAIL] {:<14} {} (need parseable Python 3.10+)", check.name, version);
python_ok = false;
continue;
}
}
}
required_pass += 1;
println!(" [PASS] {:<14} {}", check.name, version);
}
Err(why) => {
println!(" [FAIL] {:<14} {}", check.name, why);
if check.name.starts_with("Python") {
python_ok = false;
}
}
}
}
println!();
println!("Optional tools:");
for check in CHECKS.iter().filter(|c| c.optional) {
optional_total += 1;
match run_check(check) {
Ok(version) => {
optional_pass += 1;
println!(" [PASS] {:<14} {}", check.name, version);
}
Err(_) => {
println!(" [skip] {:<14} not installed", check.name);
}
}
}
println!();
println!("Summary: {}/{} required, {}/{} optional",
required_pass, required_total, optional_pass, optional_total);
if required_pass == required_total && python_ok {
println!();
println!("Environment is ready. Start with Phase 1.");
ExitCode::SUCCESS
} else {
println!();
println!("Fix the failed checks above, then run this again.");
ExitCode::from(1)
}
}
import sys
import shutil
import subprocess
CHECKS = [
("Python 3.10+", lambda: sys.version_info >= (3, 10), f"Python {sys.version}"),
("NumPy", lambda: __import__("numpy"), None),
("Matplotlib", lambda: __import__("matplotlib"), None),
("Jupyter", lambda: __import__("jupyter"), None),
("Git", lambda: shutil.which("git") is not None, None),
("Node.js", lambda: shutil.which("node") is not None, None),
("Rust (cargo)", lambda: shutil.which("cargo") is not None, None),
]
GPU_CHECKS = [
("PyTorch", lambda: __import__("torch"), None),
(
"CUDA",
lambda: __import__("torch").cuda.is_available(),
lambda: __import__("torch").cuda.get_device_name(0) if __import__("torch").cuda.is_available() else "Not available",
),
]
def run_check(name, check_fn, detail_fn=None):
try:
result = check_fn()
if result is False:
raise Exception("Check returned False")
detail = ""
if detail_fn:
if callable(detail_fn):
detail = f" ({detail_fn()})"
else:
detail = f" ({detail_fn})"
print(f" [PASS] {name}{detail}")
return True
except Exception:
print(f" [FAIL] {name}")
return False
def main():
print("\n=== AI Engineering from Scratch — Environment Check ===\n")
print("Core:")
passed = sum(run_check(name, fn, detail) for name, fn, detail in CHECKS)
total = len(CHECKS)
print("\nGPU (optional):")
gpu_passed = sum(run_check(name, fn, detail) for name, fn, detail in GPU_CHECKS)
gpu_total = len(GPU_CHECKS)
print(f"\nResult: {passed}/{total} core checks passed", end="")
if gpu_passed > 0:
print(f", {gpu_passed}/{gpu_total} GPU checks passed")
else:
print(" (no GPU — that's fine, most lessons work on CPU)")
if passed == total:
print("\nYou're ready. Start with Phase 1.\n")
else:
print("\nFix the failed checks above, then run this script again.\n")
return 0 if passed == total else 1
if __name__ == "__main__":
sys.exit(main())
// Phase 0 · Lesson 01 — Dev Environment verifier (TypeScript port).
// Probes node version + presence of git, python3, cargo, deno; mirrors verify.py.
// Refs: https://nodejs.org/api/process.html https://nodejs.org/api/child_process.html
import { execFileSync } from "node:child_process";
import process from "node:process";
type ProbeFn = () => { ok: boolean; detail?: string };
type Probe = {
name: string;
required: boolean;
run: ProbeFn;
};
function whichVersion(cmd: string, args: string[] = ["--version"]): ReturnType<ProbeFn> {
// execFile (not exec) avoids a shell, so user PATH lookups can't be re-interpreted.
try {
const out = execFileSync(cmd, args, {
stdio: ["ignore", "pipe", "ignore"],
encoding: "utf8",
timeout: 4000,
});
return { ok: true, detail: out.trim().split("\n")[0] };
} catch {
return { ok: false };
}
}
const PROBES: Probe[] = [
{
name: "Node.js 20+",
required: true,
run: () => {
const major = Number.parseInt(process.versions.node.split(".")[0]!, 10);
return { ok: major >= 20, detail: `v${process.versions.node}` };
},
},
{
name: "TypeScript runner (tsx)",
required: false,
run: () => whichVersion("npx", ["-y", "tsx", "--version"]),
},
{
name: "Git",
required: true,
run: () => whichVersion("git"),
},
{
name: "Python 3.10+",
required: true,
run: () => {
const probe = whichVersion("python3");
if (!probe.ok || !probe.detail) return probe;
// Detail looks like "Python 3.11.7"; pull major.minor.
const match = probe.detail.match(/(\d+)\.(\d+)/);
if (!match) return { ok: false, detail: probe.detail };
const [major, minor] = [Number(match[1]), Number(match[2])];
const ok = major > 3 || (major === 3 && minor >= 10);
return { ok, detail: probe.detail };
},
},
{
name: "Rust (cargo)",
required: false,
run: () => whichVersion("cargo"),
},
{
name: "Deno",
required: false,
run: () => whichVersion("deno"),
},
];
function run(): number {
process.stdout.write("\n=== AI Engineering from Scratch — Environment Check ===\n\n");
let requiredPassed = 0;
let requiredTotal = 0;
for (const probe of PROBES) {
const result = probe.run();
const tag = result.ok ? "PASS" : "FAIL";
const detail = result.detail ? ` (${result.detail})` : "";
const flag = probe.required ? "" : " [optional]";
process.stdout.write(` [${tag}] ${probe.name}${detail}${flag}\n`);
if (probe.required) {
requiredTotal += 1;
if (result.ok) requiredPassed += 1;
}
}
process.stdout.write(`\nResult: ${requiredPassed}/${requiredTotal} required checks passed\n`);
if (requiredPassed === requiredTotal) {
process.stdout.write("\nYou're ready. Start with Phase 1.\n\n");
return 0;
}
process.stdout.write("\nFix the failed required checks above, then re-run.\n\n");
return 1;
}
process.exit(run());
5 questions on this lesson
Answer each one and see straight away whether you were right and why. Your first answer to a question counts. You are not signed in, so this is practice and nothing is saved.
Reusable outputs from this lesson. Copy them into your own work.
---
name: prompt-env-check
description: Diagnose and fix AI engineering environment setup issues
phase: 0
lesson: 1
---
You are an AI engineering environment diagnostician. The user is setting up their development environment for an AI/ML course that uses Python, TypeScript, Rust, and Julia.
When the user describes an issue:
1. Identify which layer is broken (system, package manager, runtime, or library)
2. Ask for the output of the relevant diagnostic command
3. Provide the exact fix — not a general guide, the specific commands to run
Common issues and fixes:
- **Python version too old**: Install with `uv python install 3.12`
- **CUDA not detected**: Check `nvidia-smi`, then reinstall PyTorch with the correct CUDA version
- **Node.js missing**: Install with `fnm install 22`
- **Import errors after install**: Check you're in the right virtual environment with `which python`
- **Permission errors**: Never use `sudo pip install`, use `uv` with a virtual environment instead
Always verify the fix worked by asking the user to run the verification script:
```bash
python phases/00-setup-and-tooling/01-dev-environment/code/verify.py
```
The text and code of this lesson are based on AI Engineering from Scratch by Rohit Ghumare, MIT licensed.