Theory

What you built — twice

Congratulations — you've built a real, production-like app. And most of all: you built it twice. Before extending it, look at the whole arc.

Part 1 — the platform, no tooling (lessons 1–8). You started with files and a browser, no build step. The DOM by hand, events, fetch, rendering, states. And lesson 8 — the pain: ten loose variables, three views, and "forget one re-render and the UI silently lies".

Part 2 — real website features (lessons 9–14). Lesson 9's pub/sub solved that pain: toggleFavorite collapsed from three re-renders to a single notify(). Then — localStorage, tables, forms with validation, modals with a focus trap, a router by hand. A full, working vanilla app.

Part 3 — TypeScript (lessons 15–18). You pointed the compiler at that working app — and it rejected real bugs: pic.hdurl on a video, pic.copyright that can be undefined. The tooling (Node, npm, Vite) arrived here, because types need compilation. You described the Apod discriminated union, validated at the boundary with type guards, turned on strict mode.

Part 4 — shipping (lessons 19–20). A Vite build, environment variables, deploy — and the truth about keys in the bundle.

Why twice? Because the migration is the argument. Had we started with TypeScript, the types would have looked like bureaucracy — extra syntax with no clear payoff. But by building the app first without them, and then pointing the compiler at your own working code, you saw it find real, already-present bugs. Types stopped being "rules" and became a tool that catches exactly the mistakes you yourself wrote.