← Back to Course

Module -01

Code Literacy

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.

◉
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.

VISUAL STRUCTURE
Indentation, brackets, nesting — how code shows hierarchy
NAMING PATTERNS
camelCase, kebab-case, why they matter
COMMON TERMS
addEventListener, querySelector, async — demystified
CODE QUALITY
DRY, refactoring, what makes code "good"
ERROR MESSAGES
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.

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)
function processUser(user) {
if (user.active) {
user.items.forEach(item => {
console.log(item);
});}}
Proper Indentation (Clear)
function processUser(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.

{ }
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.

1
2
3
4
5
6
7
8
function validate(data) {
. if (data.valid) {
.   return true;
. }
. return false;
}
│Line numbers — navigate to errors│Indent guides — track nesting depth
Editor Guides & Visual Aids

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.

Dense (Hard to Scan)
const user={name:"Jo",age:25};
const items=[1,2,3];
if(user.age>18){doThing();}
Spaced (Easy to Scan)
const user = { name: "Jo", age: 25 };

const items = [1, 2, 3];

if (user.age > 18) {
  doThing();
}
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.

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
snake_caseuser_namePython, databases, file names
PascalCaseUserNameClasses, React components, types
SCREAMING_CASEMAX_SIZEConstants that never change
Different Cases for Different Contexts

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.

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.
KEBAB-CASE IS UNAMBIGUOUS
<!-- Always clear, always consistent -->
<div class="user-name">
<div class="card-header">
<div class="nav-item">
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.

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.

is-* Pattern (Current State)
.is-active.is-open.is-loading.is-disabled
has-* Pattern (Contains Something)
.has-error.has-icon.has-children.has-tooltip
JavaScript Toggles These
element.classList.add('is-active');
element.classList.remove('is-active');
element.classList.toggle('is-active');
Naming States & Conditions

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.

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.

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"
Common events you'll see:
'click''submit''mouseover''keydown''scroll''load''change'
addEventListener — Connecting Actions to Code

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.

// Uses CSS selectors to find elements

const button = document.querySelector('.btn');
const header = document.querySelector('#main-header');
const items = document.querySelectorAll('.nav-item');

// Same selectors you use in CSS!
querySelector
Returns first match (or null)
querySelectorAll
Returns ALL matches (as a list)
querySelector — Finding Elements

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 — Do something to each
const names = ['Jo', 'Bo', 'Mo'];

names.forEach(name => {
  console.log(name);
});

// Logs: Jo, Bo, Mo
map — Transform each into something new
const numbers = [1, 2, 3];

const doubled = numbers.map(n => {
  return n * 2;
});

// doubled = [2, 4, 6]
forEach & map — Doing Things to Lists

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.

// Fetching data takes time. await says "wait for this."

async function loadUsers() {
  const response = await fetch('/api/users');
  const users = await response.json();
  return users;
}
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.

Two ways to write the same function:
Traditional
function add(a, b) {
  return a + b;
}
Arrow function
const add = (a, b) => {
  return a + b;
};
Even shorter for simple functions:
const add = (a, b) => a + b; // implicit return
const double = 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.

const
Constant — can't be reassigned
Use by default. "This value won't change."
const name = "Jo"; // ✓
name = "Bo"; // ✗ Error!
let
Variable — can be reassigned
Use when value will change. "This will be updated."
let count = 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.

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.

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.

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.

// 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.

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.

WET (Repeated Code)
// Same logic in 3 places
const tax1 = price1 * 0.08;
const tax2 = price2 * 0.08;
const tax3 = price3 * 0.08;

// If tax rate changes...
// You must change ALL THREE
DRY (Single Source)
// Logic defined once
const TAX_RATE = 0.08;

function calcTax(price) {
  return price * TAX_RATE;
}

// Change once, works everywhere
DRY — Don't Repeat Yourself

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.

Before (Works but messy)
function process(d) {
  let r = [];
  for(let i=0;i<d.length;i++){
    if(d[i].a&&d[i].v>0){
      r.push(d[i]);
    }
  }
  return r;
}
After (Same behavior, readable)
function getActiveItems(items) {
  return items.filter(item => {
    const isActive = item.active;
    const hasValue = item.value > 0;
    return isActive && hasValue;
  });
}
Refactoring — Improving Without Changing

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.

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.
O(n)
Big O Notation
How code scales. O(n) = linear, O(n²) = exponential slowdown. Performance matters.
How Code is Measured

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.

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.

"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.

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.