Code Explanation
Explain unfamiliar programs in plain language. Read minds through their code.
The skill: Collaborative posture. Code is communication between minds. Reading it well means hearing what another mind was thinking.
Time: 5-6 hours total
Estimated time remaining: 5-6 hours
The Format
- 1. Read the code cold (no explanation given)
- 2. Answer structured questions: WHAT, HOW, WHY
- 3. Explain to a collaborator — as if another mind asked "what does this do?"
- 4. Compare to model explanation
- 5. Reflect — what did you miss? What did you see that the model didn't?
The explanation is the artifact. If you can explain it, you understand it.
Exercise 3.1: Simple Functions
Functions have a purpose. Identify it from the code, not from the name.
The Counter
1function mystery(arr) {2 let count = 0;3 for (let i = 0; i < arr.length; i++) {4 if (arr[i] > 0) {5 count++;6 }7 }8 return count;9}Read the code, then explain it. If you can explain it, you understand it.
The Transformer
1function process(text) {2 let result = "";3 for (let i = 0; i < text.length; i++) {4 let char = text[i];5 if (char === char.toUpperCase()) {6 result += char.toLowerCase();7 } else {8 result += char.toUpperCase();9 }10 }11 return result;12}Read the code, then explain it. If you can explain it, you understand it.
The Finder
1function locate(haystack, needle) {2 for (let i = 0; i < haystack.length; i++) {3 if (haystack[i] === needle) {4 return i;5 }6 }7 return -1;8}Read the code, then explain it. If you can explain it, you understand it.
Why -1? Returning -1 for "not found" is convention, not necessity. Some languages return null, others throw exceptions. Each choice has tradeoffs: -1 requires callers to check, null can propagate silently, exceptions force handling. JavaScript chose -1.
Exercise 3.2: Array Manipulation
Arrays are everywhere. Recognize common patterns: filter, transform, reduce, search.
The Filter
1function extract(items, threshold) {2 let result = [];3 for (let item of items) {4 if (item >= threshold) {5 result.push(item);6 }7 }8 return result;9}Read the code, then explain it. If you can explain it, you understand it.
The Reverser
1function flip(arr) {2 let result = [];3 for (let i = arr.length - 1; i >= 0; i--) {4 result.push(arr[i]);5 }6 return result;7}Read the code, then explain it. If you can explain it, you understand it.
The Deduplicator
1function unique(arr) {2 let seen = [];3 let result = [];4 5 for (let item of arr) {6 if (!seen.includes(item)) {7 seen.push(item);8 result.push(item);9 }10 }11 12 return result;13}Read the code, then explain it. If you can explain it, you understand it.
Two arrays, two purposes: 'seen' is working memory — the machine's scratchpad for tracking state. 'result' is output — what the caller asked for. Separating internal state from external interface is a core principle: callers don't need to know how you computed the answer.
Exercise 3.3: Object Handling
Objects store related data together. Keys are names, values are data.
The Lookup
1function getValue(obj, key, fallback) {2 if (obj[key] !== undefined) {3 return obj[key];4 }5 return fallback;6}Read the code, then explain it. If you can explain it, you understand it.
The Counter Object
1function tally(words) {2 let counts = {};3 4 for (let word of words) {5 if (counts[word] === undefined) {6 counts[word] = 0;7 }8 counts[word]++;9 }10 11 return counts;12}Read the code, then explain it. If you can explain it, you understand it.
The Merger
1function combine(obj1, obj2) {2 let result = {};3 4 for (let key in obj1) {5 result[key] = obj1[key];6 }7 8 for (let key in obj2) {9 result[key] = obj2[key];10 }11 12 return result;13}Read the code, then explain it. If you can explain it, you understand it.
Priority through order: The second object wins conflicts because it's processed second. This isn't magic — it's just assignment. If you write result['x'] = 1 then result['x'] = 2, you get 2. Order is meaning.
Exercise 3.4: Combined Patterns
Real code combines multiple concepts. Recognize compound patterns.
The Summarizer
1function summarize(transactions) {2 let total = 0;3 let count = 0;4 5 for (let t of transactions) {6 if (t.amount > 0) {7 total += t.amount;8 count++;9 }10 }11 12 return {13 total: total,14 count: count,15 average: count > 0 ? total / count : 016 };17}Read the code, then explain it. If you can explain it, you understand it.
The Grouper
1function groupBy(items, key) {2 let groups = {};3 4 for (let item of items) {5 let groupKey = item[key];6 7 if (groups[groupKey] === undefined) {8 groups[groupKey] = [];9 }10 11 groups[groupKey].push(item);12 }13 14 return groups;15}Read the code, then explain it. If you can explain it, you understand it.
The Validator
1function validate(user) {2 let errors = [];3 4 if (!user.email || !user.email.includes("@")) {5 errors.push("Invalid email");6 }7 8 if (!user.password || user.password.length < 8) {9 errors.push("Password must be at least 8 characters");10 }11 12 if (!user.age || user.age < 18) {13 errors.push("Must be 18 or older");14 }15 16 return {17 valid: errors.length === 0,18 errors: errors19 };20}Read the code, then explain it. If you can explain it, you understand it.
The validation gap: includes("@") accepts "@" as a valid email. Length >= 8 accepts "aaaaaaaa" as a password. Real validation uses regex patterns, checks against known bad passwords, and often involves external services. This code shows the structure, not production rules.
Exercise 3.5: Real-World-ish Programs
Longer code requires patience. Break it into parts. Trace the flow.
The Todo Filter
New syntax: arr.sort((a, b) => a - b) — sorts an array using a comparator function. Return negative to put a first, positive for b first, zero for equal.
1function getActiveTodos(todos) {2 let active = [];3 4 for (let todo of todos) {5 if (!todo.completed) {6 active.push({7 id: todo.id,8 text: todo.text,9 priority: todo.priority || "normal"10 });11 }12 }13 14 active.sort(function(a, b) {15 let priorityOrder = { high: 0, normal: 1, low: 2 };16 return priorityOrder[a.priority] - priorityOrder[b.priority];17 });18 19 return active;20}Read the code, then explain it. If you can explain it, you understand it.
The Price Calculator
1function calculateTotal(cart, discountCode) {2 let subtotal = 0;3 4 for (let item of cart) {5 subtotal += item.price * item.quantity;6 }7 8 let discount = 0;9 if (discountCode === "SAVE10") {10 discount = subtotal * 0.10;11 } else if (discountCode === "SAVE20" && subtotal >= 100) {12 discount = subtotal * 0.20;13 }14 15 let total = subtotal - discount;16 let tax = total * 0.08;17 18 return {19 subtotal: subtotal,20 discount: discount,21 tax: tax,22 total: total + tax23 };24}Read the code, then explain it. If you can explain it, you understand it.
The Search Ranker
New syntax: arr.map(fn) — transforms each element using fn, returns a new array. Like a loop that builds a new array of transformed values.
1function search(items, query) {2 let results = [];3 4 let queryLower = query.toLowerCase();5 6 for (let item of items) {7 let titleLower = item.title.toLowerCase();8 let score = 0;9 10 if (titleLower === queryLower) {11 score = 100;12 } else if (titleLower.startsWith(queryLower)) {13 score = 75;14 } else if (titleLower.includes(queryLower)) {15 score = 50;16 }17 18 if (score > 0) {19 results.push({20 item: item,21 score: score22 });23 }24 }25 26 results.sort(function(a, b) {27 return b.score - a.score;28 });29 30 return results.map(function(r) {31 return r.item;32 });33}Read the code, then explain it. If you can explain it, you understand it.
Scores encode assumptions: Why 100/75/50? Those specific numbers don't matter — only their order does. You could use 3/2/1. The designer assumed exact > prefix > substring. Different domains might weight differently: autocomplete favors prefixes, document search might favor frequency.