Before you write code, you need to read it. This optional module teaches you to recognize patterns, understand conventions, and parse code visually — skills that make everything else easier.
IDEs evolved from simple text editors to VS Code, Cursor, and AI-powered environments. Syntax highlighting, auto-complete, and linting help you read code faster.
Dark mode, ligature fonts, AI code completion, pair programming. How we read and write code keeps evolving, but the fundamental patterns remain.
Indentation shows structure. Naming conventions signal intent. Comments explain why. These reading skills transfer across all programming languages forever.
Part A
Core Concepts
01
Why Code Literacy Matters
As a designer who codes with AI, you'll spend more time reading code than writing it. Understanding what you're looking at is the foundation of effective collaboration.
80%
Time spent READING code
20%
Time spent WRITING code
Even professional developers spend most of their time reading existing code, debugging, and understanding systems before making changes.
Reading vs Writing Code
This ratio is even more extreme when working with AI. You prompt, AI generates, you read and evaluate. If you can't read code, you can't judge if AI's output is correct, secure, or maintainable.
Key insight: Code literacy is your quality filter for AI-generated code
◉
Visual Pattern Recognition
You already see structure, hierarchy, and relationships. Code has the same patterns.
◐
Consistency Sensitivity
You notice when things don't match. Inconsistent code stands out to trained eyes.
△
Systematic Thinking
Design systems = code architecture. Components, tokens, and variants map directly.
Why Designers Have an Advantage
Designers already think in systems, patterns, and visual hierarchy. Code is just another design system with its own visual language. Your existing skills transfer directly.
Advantage: Your design brain is already wired for code patterns
VISUAL STRUCTURE
Indentation, brackets, nesting — how code shows hierarchy
Reading stack traces, line numbers, debugging basics
CODE SMELLS
Recognizing bad patterns before they cause problems
This Module Covers
This module is optional but recommended. If you already read code comfortably, skip ahead. If code looks like hieroglyphics, start here.
Guidance: Skip if comfortable, essential if code feels foreign
Prerequisites
None. This is the starting point. You don't need to know anything about programming to begin here.
02
Visual Structure of Code
Code has a visual grammar. Indentation, brackets, and whitespace aren't random — they show you how code is organized, just like margins and hierarchy in design.
No Indentation (Unreadable)
functionprocessUser(user) { if (user.active) { user.items.forEach(item => { console.log(item); });}}
Rule:Each indent level = "this is inside that"Standard:2 spaces, 4 spaces, or 1 tab — teams pick one
Indentation = Hierarchy
Indentation is visual hierarchy for code. Just like nested bullet points or margin levels in a document, deeper indentation means "contained within." Most editors auto-indent for you.
Design parallel: Indentation in code = margins in design. Same concept.
{ }
Curly Braces — Code Blocks
Functions, if statements, loops. "Everything between these runs together."
[ ]
Square Brackets — Lists/Arrays
Collections of items. [1, 2, 3] or ['apple', 'banana']
( )
Parentheses — Function Calls & Grouping
doSomething() or (a + b) * c. "Call this" or "group these."
< >
Angle Brackets — HTML Tags & Generics
<div>, <p>, or Array<string> in TypeScript
Brackets Define Boundaries
Every opening bracket needs a closing bracket. When code breaks, it's often a missing bracket. Modern editors highlight matching pairs and show errors when they don't match.
Key rule: Brackets are containers. Every open needs a close.
Modern code editors draw vertical lines to help you track nesting levels. These "indent guides" connect opening and closing brackets visually. When something looks off, the lines help you find mismatched pairs.
Editor feature: Indent guides are training wheels — use them until patterns become automatic
Blank lines separate logical sections, like paragraphs. Spaces around operators (=, >, +) improve readability. Formatters like Prettier enforce these automatically.
Whitespace is Intentional
Whitespace in code serves the same purpose as whitespace in design — it creates breathing room, groups related items, and separates concepts. Dense code is hard to read, just like dense typography.
Design parallel: Whitespace in code = whitespace in design. Same readability principles.
Pro Tip
If code looks messy, it might just need formatting. In VS Code, press Shift+Alt+F (Windows) or Shift+Option+F (Mac) to auto-format. This fixes indentation and spacing instantly.
03
Naming Conventions
Names in code follow strict patterns. These conventions aren't arbitrary — they signal what language you're in, what type of thing something is, and how it should be used.
camelCaseuserNameJavaScript variables & functions
kebab-caseuser-nameCSS classes, URLs, HTML attributes
Naming conventions are semantic signals. When you see kebab-case, you know it's probably CSS. PascalCase means a class or component. These patterns help you navigate unfamiliar code instantly.
Key insight: Case style tells you the language AND the type of thing
THE PROBLEM WITH CAMELCASE IN HTML
<!-- HTML is case-INSENSITIVE --> <div class="userName"> <div class="username"> <div class="USERNAME">
<!-- All three are THE SAME! -->
HTML treats these identically, causing confusion when CSS expects exact matches.
Dashes can't be confused. No case-sensitivity issues. Industry standard since CSS1.
Why CSS Uses Dashes (Not camelCase)
CSS uses kebab-case because HTML attributes are case-insensitive. If you used camelCase, .userName and .username could both exist but HTML would treat them the same. Dashes eliminate this ambiguity.
Technical reason: kebab-case exists because of HTML's case-insensitivity
Block__Element--Modifier
Block
.cardThe component itself
Element
.card__titleA part inside the block (double underscore)
Modifier
.card--featuredA variation (double dash)
.card { } /* the block */ .card__title { } /* element inside */ .card__image { } /* another element */ .card--featured { } /* modified version */ .card--featured .card__title { } /* element in modified block */
BEM: A CSS Naming System
BEM (Block Element Modifier) is a naming convention that makes CSS scalable. When you see __ you know it's an element inside something. -- means a variation. Not all projects use BEM, but it's common enough to recognize.
State classes like is-active or has-error are toggled by JavaScript to trigger CSS changes. The naming makes it obvious: is- describes the element's current state, has- describes what it contains.
Pattern: is-* and has-* are bridges between JS behavior and CSS styling
Component files (React/Vue)
UserProfile.jsx NavigationBar.tsx Button.vue
PascalCase — matches the component name inside
Utility/config files
user-utils.js api-client.ts date-helpers.js
kebab-case — no ambiguity across operating systems
CSS/Style files
main.css user-profile.module.css _variables.scss
kebab-case — _ prefix means "partial" in Sass
File & Folder Naming Conventions
File naming varies by ecosystem. React components use PascalCase (matching the component name). Utility files use kebab-case. The convention helps you identify file purpose at a glance in file trees.
Convention: File naming predicts file contents
Quick Rule
When in doubt: camelCase for JS, kebab-case for CSS/HTML, PascalCase for components. Following conventions makes your code predictable to others.
04
JavaScript Vocabulary
These terms appear constantly in JavaScript. Understanding them transforms gibberish into readable code. Each one represents a core concept you'll encounter daily.
// The pattern: element.addEventListener(event, handler)
button.addEventListener('click', handleClick);
// Translation: "When button is clicked, run handleClick"
This is how JavaScript responds to user actions. The browser constantly watches for events (clicks, scrolls, key presses). addEventListener says "when this event happens on this element, run this function." It's the bridge between user interaction and code.
Core concept: addEventListener = "when X happens, do Y"
Before you can do anything to an element with JavaScript, you need to "find" it. querySelector uses the same CSS selectors you already know — .class, #id, element. Your CSS knowledge transfers directly.
forEach runs code for each item (like printing or saving). map transforms each item into something new and returns a new list. You'll see these everywhere when code deals with lists of things.
Key difference: forEach = do something. map = transform into something new.
// Fetching data takes time. await says "wait for this."
async"This function will need to wait for things"await"Pause here until this finishes"fetch"Go get data from a URL"
async/await — Waiting for Things
Some operations take time (loading data, waiting for user input). async/await makes asynchronous code readable. await pauses execution until the operation completes, then continues. Without it, code would try to use data before it arrived.
Time handling: await = "pause until this finishes, then continue"
Two ways to write the same function:
Traditional
functionadd(a, b) { returna + b; }
Arrow function
constadd = (a, b) => { returna + b; };
Even shorter for simple functions:
constadd = (a, b) => a + b; // implicit return constdouble = x => x * 2; // single param, no parens needed
=> Arrow Functions
Arrow functions (=>) are a shorter syntax for functions. They're used everywhere in modern JavaScript, especially in callbacks like forEach, map, and event handlers. The arrow "points to" what the function returns.
Syntax sugar: => is just a shorter way to write function
const
Constant — can't be reassigned
Use by default. "This value won't change."
constname = "Jo"; // ✓ name = "Bo"; // ✗ Error!
let
Variable — can be reassigned
Use when value will change. "This will be updated."
letcount = 0; count = count + 1; // ✓ OK
var
Legacy — avoid in new code
Old way. Has scoping issues. You'll see it in older code.
Variable Declarations
Modern JavaScript uses const by default (for things that don't change) and let when reassignment is needed. var is the old way — you'll see it in legacy code but shouldn't use it in new code.
Best practice: Default to const. Use let when you need to reassign.
The 80/20
These 6 patterns (addEventListener, querySelector, forEach/map, async/await, arrow functions, const/let) cover most JavaScript you'll encounter. Master these first.
05
Reading Error Messages
Errors aren't punishment — they're guidance. Learning to read them transforms debugging from frustrating guesswork into systematic problem-solving.
TypeError: Cannot read properties of undefined at getUserName (src/utils/user.js:23:15) at renderProfile (src/components/Profile.jsx:45:8)
TypeErrorThe category of error (what went wrong)
MessageHuman-readable description of the problem
File pathWhich file contains the error
:23:15Line 23, character 15 — exact location
Anatomy of an Error Message
Errors are precise. They tell you the type, the message, the file, the line, and the column. :23:15 means line 23, character 15. Click the file path in most editors to jump directly there.
Location format: file.js:23:15 = line 23, character 15
TypeError
"Cannot read properties of undefined" — you tried to use something that doesn't exist. Usually a typo or missing data.
ReferenceError
"X is not defined" — you used a variable that was never created. Check spelling and scope.
SyntaxError
"Unexpected token" — code structure is wrong. Missing bracket, extra comma, unclosed string.
NetworkError / 404 / 500
Data fetching failed. 404 = URL doesn't exist. 500 = server crashed. Check the URL and API status.
Most Common Error Types
Most errors fall into a few categories. TypeError means something is undefined or null. SyntaxError means you typed something wrong. ReferenceError means a variable doesn't exist.
Pattern: 90% of errors are these 4 types
The stack trace shows the path the code took to reach the error:
Error: User not found at getUser (api.js:10)← Error happened here at loadProfile (profile.js:25)← Which was called from here at handleClick (button.js:8)← Which was called from here
Read from top to bottom: The top line is where the error occurred. Each line below shows what called it. This helps you trace the problem's origin.
Reading the Stack Trace
The stack trace is a breadcrumb trail. Start at the top (where the error happened), then work down to find where the problem originated. Often the bug is in a caller, not where the error appeared.
Reading order: Top = where it crashed. Below = how it got there.
// Print values to see what's happening console.log('user:', user);
// Print multiple things console.log('before:', items.length); // ... code that modifies items ... console.log('after:', items.length);
// Table format for arrays/objects console.table(users);
.log()Print anything to console.error()Print as error (red).table()Print array/object as table.dir()Expandable object view
Using console.log for Debugging
console.log is the simplest debugging tool. Print values to see what they actually are. "I expected X, but it's Y" tells you exactly where your assumption was wrong.
Debug tip: When confused, console.log everything
Mindset
Errors are not failures — they're clues. Every error message is the computer trying to help you. Read it carefully before searching online.
06
Code Quality Concepts
What makes code 'good'? These concepts help you evaluate code quality — whether it's your own, AI-generated, or from a team member.
DRY means every piece of knowledge has one authoritative source. If you copy-paste code, you create multiple places that can become inconsistent. Extract repeated logic into functions or constants.
Refactoring improves code structure without changing behavior. Better names, smaller functions, clearer logic. The code does the same thing but is easier to understand and modify.
Definition: Refactoring = cleaning up without breaking anything
LOC
Lines of Code
Rough complexity measure. "This file is 500 lines." Shorter is often (not always) better.
KB
Bundle Size
"Bundle is 150KB." Smaller loads faster. Critical for web performance.
CC
Cyclomatic Complexity
Number of paths through code. High CC = hard to test, likely has bugs.
You don't need to master these metrics, but knowing the terminology helps in technical conversations. When someone says "the bundle is too big" or "this is O(n²)", you'll understand the concern.
Terminology: Vocabulary for discussing code performance
Comment types
// Single line comment /* Multi-line comment */ <!-- HTML comment -->
Special markers (tools detect these)
// TODO: implement error handling // FIXME: breaks on Safari // HACK: temporary workaround // NOTE: requires API key
Good vs bad comments
// Bad: i++; // increment i // Good: // Skip first item (it's the header row)
Reading & Writing Comments
Comments explain why, not what. Good code is self-explanatory about what it does. Comments add context: business rules, workarounds, warnings, or decisions that aren't obvious from the code itself.
Conventions: TODO = incomplete. FIXME = known bug. HACK = temporary.
"Code smells" are patterns that suggest deeper problems:
Long functions (100+ lines) — probably does too many things
Deep nesting (5+ levels) — hard to follow, extract into functions
Magic numbers (if (x > 86400)) — use named constants
Duplicated code — violates DRY, extract to function
Vague names (data, temp, x) — names should describe purpose
Recognizing Code Smells
Code smells aren't bugs — the code works. But they indicate maintenance problems ahead. Learning to spot these helps you give better feedback on AI-generated code or identify areas needing refactoring.
Warning signs: Smells aren't bugs, but they predict future bugs
AI Code Quality
AI often generates working-but-smelly code. It might use vague names, repeat patterns, or miss edge cases. Your code literacy helps you spot these issues.
Key Takeaways
Code has visual grammar
Indent = Depth
{ } = Block
[ ] = List
( ) = Call
Code structure is visible. Indentation shows nesting, brackets show boundaries. Once you see these patterns, code becomes scannable like any other visual design.
Case style reveals context
camelCase JavaScript
kebab-case CSS / HTML
PascalCase Components
The way something is named tells you what it is. camelCase = JavaScript, kebab-case = CSS/HTML, PascalCase = class or component. This context helps you navigate unfamiliar code instantly.
Six patterns cover 80% of JavaScript
addEventListener
querySelector
forEach / map
async / await
arrow =>
const / let
Master these six patterns and you'll understand most JavaScript you encounter. They're the vocabulary of modern web development — event handling, DOM selection, iteration, async, functions, and variables.
Errors are guides, not failures
TypeError at file.js:23:15
Every error tells you exactly what went wrong and where: error type, file path, line number, character position. Read errors carefully — they're trying to help you. file:23:15 means line 23, character 15.
Good code is readable, not clever
DRY Don't Repeat
Clear Names Self-Documenting
Small Units Easy to Test
Quality code isn't about being clever — it's about being clear. DRY (don't repeat), clear naming, small functions. When reviewing AI code, ask: "Can I understand this in 10 seconds?"
Code literacy is your AI superpower
⊛ + ◉ = ↑
AI generates • You evaluate • Together you build
You don't need to write code from scratch. You need to read and evaluate what AI generates. Code literacy is the filter between AI output and production code. This skill makes AI collaboration thousands of times more powerful.