A React Todo App as a Blueprint for Architecture-First Thinking
The project models a discipline that pays off at scale: pin down the shape of your data and the boundaries of your state before writing a single component. Skipping this step produces the same `todo` object scattered across five files with conflicting assumptions, a pattern that turns routine changes into archaeology.
A React + TypeScript Todo application demonstrates architecture-first development by separating concerns into types, a custom Hook for all business logic, and pure UI components. The project defines a `Todo` interface and a `FilterType` literal union before any UI code exists, then encapsulates all state management inside a single `useTodos` Hook. Every state mutation uses functional updates to respect React's reference-comparison rendering, and the Hook returns an object rather than an array so consumers can destructure by name.
The component layer is deliberately left as empty shells — `TodoInput`, `TodoList`, `TodoItem`, `TodoFilter` — which reveals the intended container-presentation split before implementation begins. Data flows unidirectionally: user action triggers a Hook method, `setState` updates the store, and React re-renders only the affected components. The article also flags a deliberate gap: the `filter` state exists but no filtered list is computed yet, a decision point between Hook cohesion and component flexibility.
A restaurant metaphor maps types to recipes, the Hook to a head chef, components to waiters, and `App.tsx` to the manager, reinforcing that each layer minds only its own concern.
Leaving the component files as empty shells is a deliberate pedagogical choice: it forces the reader to see the architecture before the implementation, which is the opposite of how most tutorials operate.
The commented-out `filteredTodos` function is more instructive than a finished feature would be. It surfaces a genuine design tension — compute the derived list inside the Hook for cohesion, or in the component for flexibility — without resolving it prematurely.
Using `Date.now().toString()` for IDs trades a tiny performance cost for guaranteed uniqueness without a dependency, a pragmatic choice that a more dogmatic codebase might over-engineer with UUID libraries.
The restaurant metaphor maps cleanly onto the layers but also reveals what's missing: there is no persistence layer (localStorage, API), which is the next natural boundary to extract.