Search Tech Journey

Find topics, journeys and posts

back to blog
systemsintermediate 32m read

R01 · Week 1 Recall & Drill

Week 1 revision: reproducible dev environments, Git's object model, shell pipelines, doc-hunting strategy, and Python's reference-based memory model.

🧰SetupRevision · Week 1· Session 001 of 130 90 min

🎯 Rebuild Week 1 from a blank page: environment as a specification, commits as snapshots, pipes as processes, docs as a search space, and names as pointers.

Weekly revision · Week 1 · Covers 5 sessions from Mon–Fri.

Sessions covered

By the end of this revision you can
  • Rebuild your whole dev environment from a script in version control, and name the three bugs that setup prevents: CRLF line endings, cross-filesystem I/O, and a missing venv.
  • State what a commit, a branch, and a remote actually are — snapshot, movable pointer, named URL — and recover lost work with git reflog.
  • Write a shell pipeline that answers a real question from a raw log file, and explain that each stage is a separate process reading stdin and writing stdout.
  • Pick the right doc type for the question you have (tutorial, how-to, reference, explanation) and fall through the doc-hunting funnel when the first source fails.
  • Draw the memory diagram for any Python assignment and predict whether a mutation is visible through a second name.
  • Spot the mutable-default-argument bug on sight and give two fixes.

90-min structure

BlockMinutesWhat you do
Warm-up recall5Close every tab. Name the 5 sessions and one sentence each.
Blank-page reconstruction30Work the per-session prompts below, on paper.
Hands-on drill30The single drill in this post, start to finish.
Quiz + misconception15Answer before revealing. Score yourself honestly.
Gap analysis + preview10Write down what you got wrong. Skim next week.

Blank-page reconstruction · 30 min

No tabs open. Paper or a scratch file only. Three prompts per session, then check the gotcha.

S001 · Dev Environment

  1. Write the skeleton of setup.sh from memory — what does it install, in what order, and what makes it safe to re-run twice in a row?
  2. Explain the difference between WSL 1 and WSL 2 in one sentence, and say which one you should be running.
  3. Name the three problems that environment tooling exists to solve, and give one tool that solves each.

Gotcha you probably forgot: keeping code in /mnt/c/... instead of ~/projects. The Windows filesystem is reached through a translation layer, so every file read, every hot-reload, and every git status pays a large I/O tax. Native Linux filesystem, always.

S002 · Git & GitHub

  1. Draw the three areas a file moves between — working tree, staging area, repository — and label the command that moves a change across each boundary.
  2. Define a commit, a branch, and a remote in one sentence each, with no analogy that breaks under pressure.
  3. You committed three times on the wrong branch. Write the exact command sequence that moves those commits to a new branch without losing them.

Gotcha you probably forgot: a commit is not a diff. It is a full snapshot of the tracked tree, addressed by the hash of its content, plus pointers to its parents. Diffs are computed on demand for display — the object database stores no patches. This is why branches are cheap: a branch is one file holding one hash.

S003 · The Command Line

  1. Draw what happens when you run cat access.log | grep 500 | wc -l. How many processes exist? Which file descriptors are wired to which?
  2. Write out set -euo pipefail and explain each of the four things it turns on, one line each.
  3. Answer this with a pipeline: which endpoint appeared most often in a log file? Assume the path is the seventh whitespace-separated field.

Gotcha you probably forgot: globs are expanded by the shell, not by the program. When you type ls *.log, ls never sees a * — it receives an already-expanded list of filenames. The same is true of ~, $VAR, brace sets like \{a, b\}, and command substitution. This is also why unquoted variables containing spaces silently break: the shell splits them into separate arguments before the program starts.

S004 · Reading Docs & Effective Googling

  1. Name the four Diátaxis documentation types and say which one you reach for when a tool is already installed and you have a specific task.
  2. List the sections of a man page in order and say which one you should read first.
  3. Write a search query that returns only official documentation for a specific flag of a specific tool — no blog spam.

Gotcha you probably forgot: when a tutorial and the source disagree, the source wins. And when an LLM hands you a method name you have never seen, the very next step is to verify it exists in the reference — not to run it and see. Search gives you one instance; the reference gives you the whole space of what is askable.

S005 · Python Variables & Types

  1. Draw the memory diagram for x = [1, 2, 3], then y = x, then y.append(4). What does x print?
  2. Sort these into mutable and immutable: int, str, tuple, list, dict, set, frozenset, bytes.
  3. Explain why a tuple can be a dict key but a list cannot.

Gotcha you probably forgot: def add(item, cart=[]) evaluates the default once, at function definition time. Every call that omits cart shares the same list, so the "empty" cart accumulates across calls. The fix is cart=None plus if cart is None: cart = [] — or a tuple default when you genuinely want immutability.


Hands-on drill · 30 min

Task: build a one-command log triage tool, and put it under version control with a reproducible environment. This drill touches all five sessions in a single artefact.

Step 1 — environment (5 min)

mkdir -p ~/projects/w1-drill && cd ~/projects/w1-drill
uv venv .venv --python 3.12
source .venv/bin/activate
git init
printf '.venv/\n__pycache__/\n*.log\n' > .gitignore

Step 2 — generate a log file to work against (5 min)

python - <<'PY'
import random, datetime
paths = ["/api/users", "/api/orders", "/api/search", "/health", "/api/orders/export"]
codes = [200, 200, 200, 200, 301, 404, 500, 503]
now = datetime.datetime(2024, 1, 1, 12, 0, 0)
with open("access.log", "w") as f:
    for i in range(5000):
        ts = (now + datetime.timedelta(seconds=i)).strftime("%d/%b/%Y:%H:%M:%S +0000")
        ip = f"10.0.0.{random.randint(1, 25)}"
        path = random.choice(paths)
        code = random.choice(codes)
        size = random.randint(200, 90000)
        f.write(f'{ip} - - [{ts}] "GET {path} HTTP/1.1" {code} {size}\n')
PY
wc -l access.log

Expected outcome: 5000 access.log.

Step 3 — answer three questions with shell only (10 min)

Write these as a script, triage.sh, not as ad-hoc history:

#!/usr/bin/env bash
set -euo pipefail
 
LOG="${1:-access.log}"
 
echo "== status code distribution =="
awk '{print $9}' "$LOG" | sort | uniq -c | sort -rn
 
echo
echo "== top 5 paths by request count =="
awk '{print $7}' "$LOG" | sort | uniq -c | sort -rn | head -5
 
echo
echo "== top 5 IPs by total bytes served =="
awk '{bytes[$1] += $10} END {for (ip in bytes) printf "%-15s %d\n", ip, bytes[ip]}' "$LOG" \
  | sort -k2 -rn | head -5
chmod +x triage.sh
./triage.sh access.log

Expected outcome: three sections print. Roughly half the status lines are 200 (it appears four times in the sample pool of eight), and every one of the five paths shows up in the path ranking. If uniq -c gives you scattered duplicate rows, you forgot that uniq only collapses adjacent lines — the sort before it is mandatory.

Step 4 — the Python half (7 min)

Prove the reference model to yourself rather than trusting the note:

# identity.py
def add(item, cart=[]):          # the bug
    cart.append(item)
    return cart
 
def add_fixed(item, cart=None):  # the fix
    if cart is None:
        cart = []
    cart.append(item)
    return cart
 
print(add("a"), add("b"))              # both calls share one list
print(add_fixed("a"), add_fixed("b"))  # independent lists
 
x = [1, 2, 3]
y = x
y.append(4)
print(x, x is y, x == list(x), x is list(x))

Expected outcome: the buggy pair prints two lists that both contain a and b; the fixed pair prints two single-item lists. The last line shows x as [1, 2, 3, 4], is y True, equality True, and identity against a fresh copy False — equality and identity are different questions.

Step 5 — commit it (3 min)

git add .gitignore triage.sh identity.py
git commit -m "drill: week 1 log triage + python identity demo"
git log --oneline --graph --all

Expected outcome: one commit, one branch pointer, and access.log absent from the commit because .gitignore excluded it. Confirm with git show --stat HEAD.


Common misconception
✗ What most people think

"My environment is set up and my repo has commits, so the state is safe. Setup was a one-time chore and Git is storing my changes as a list of patches I could always replay."

✓ What is actually true

Neither of those is a state you reach — both are specifications. An environment only counts if you can reproduce it on a machine you have never touched, from a file in version control, without you present. And Git stores snapshots addressed by content hash, not patches: git log computes diffs on demand for display. Once you internalise the snapshot model, reflog, reset, and cheap branching all stop being scary.


Week 1 recall · click to reveal
★ = stretch question

Gap analysis + next week preview · 10 min

Write answers to these three, in a file you keep:

  • Which of the five blank-page reconstructions was thinnest? That is the session to re-read, not the one that felt hardest emotionally.
  • Did the drill fail anywhere? A failed set -euo pipefail script that exits silently usually means a pipeline stage returned non-zero — find which one before moving on.
  • Can you now explain the snapshot model of Git to someone else without saying "it stores the changes"? If not, that is the single highest-leverage thing to fix this week.

Next week (S006–S010) moves from environment into Python proper: control flow and comprehensions, functions with arguments, scope, and closures, then the data structure decision table — list versus tuple versus dict versus set — followed by classes and objects, and finally inheritance, composition, and polymorphism. The reference model you drilled in S005 is the foundation for all of it: closures capture names, and mutable default arguments are just one instance of a much more general trap.


Part of the 6-month evergreen learning plan.