Thinking in Components
A large, undivided block of code is hard to understand, hard to change, and hard to test. The solution every working software team has converged on is decomposition: breaking a system into smaller, named parts called components. Learning to see a project as a collection of components — before writing any code — is what separates an architect from someone who just starts typing.
What a Component Is
A component is a distinct, named unit of a software system that encapsulates a specific responsibility and communicates with other components through a defined interface. Three ideas in that definition deserve attention. Encapsulation means a component hides its internal details. Other parts of the system interact with a component through its interface — the set of operations it exposes — without needing to know how those operations are implemented. A payment component might expose a single operation, chargeCard(amount, cardToken), while internally handling encryption, bank API calls, and error logging. Callers do not need to know any of that. Single responsibility means each component should have one clear reason to exist. The Single Responsibility Principle, a cornerstone of software design, states that a module should have one, and only one, reason to change. A component that handles user authentication, sends emails, and formats invoices will break in unpredictable ways whenever any of those three domains changes. Interface is the contract between a component and its callers: what inputs the component accepts, what outputs it produces, and what errors it might signal. A well-defined interface allows you to replace a component's implementation without changing anything else in the system — as long as the new implementation honors the same contract. Example: A recipe app might decompose into: a Search component (accepts a query string, returns a list of matching recipes), a Recipe Detail component (accepts a recipe ID, returns full recipe data), a User Account component (manages login, logout, and profile data), and a Saved Recipes component (stores and retrieves a user's bookmarks). Each has one job; none knows how the others work internally.
A component should have one, and only one, reason to change. If you can name two unrelated reasons why a component might need to be rewritten, it is doing too much and should be split.
How do you identify components in a new project? A reliable starting technique is noun extraction from your requirements. Read through your requirements list and circle every significant noun — users, recipes, payments, notifications, sessions, orders. Each noun is a candidate for a component or data entity. Then identify the verbs associated with each noun — create, search, update, delete, send. Those verbs become the operations on the component's interface. A second technique is drawing a system diagram. Place your core user interactions in the center and draw the components those interactions touch. Arrows between components represent dependencies — component A calls component B. If you end up with a diagram where everything connects to everything else, that is a sign your decomposition is too coarse or your responsibilities are poorly separated. Coherence and coupling are the two metrics that guide good decomposition. High coherence means the internals of a component are strongly related — everything inside the component serves the same purpose. Low coupling means components have minimal dependencies on each other's internals — they interact only through clean interfaces. Aim for high coherence and low coupling in every design.
Complete each statement with the correct term.
Components in Practice: Front End, Back End, and Data
In web and app development, a common high-level decomposition splits the system into three layers: the front end, the back end, and the data layer. The front end (also called the client or UI layer) is responsible for everything the user sees and interacts with. It renders information, captures user input, and sends requests to the back end. It should not contain business logic — the rules that govern how your application behaves. A front end that applies discount rules, calculates tax, and enforces permissions is too responsible. The back end (also called the server or API layer) enforces business logic, authenticates users, applies permissions, and orchestrates data access. It receives requests from the front end, processes them according to the rules of the application, and returns structured responses. A well-designed back end is completely independent of the front end technology — you could replace a web front end with a mobile app and the back end would not change. The data layer is responsible for persisting and retrieving information. In most applications this is a database. The data layer's interface exposes operations like create, read, update, and delete (collectively called CRUD), and the back end interacts with it through those operations. The data layer should not contain business logic; it stores and retrieves, and that is all. This three-layer model is not the only valid decomposition, but it is the dominant pattern in web development and a sound default starting point. Recognizing these layers — and keeping each one honest about its responsibility — prevents an enormous class of common architectural mistakes.
When using AI to help build, there is a temptation to describe the whole project and let the AI produce a single file. That produces tangled, unmaintainable code. Always design your components first, then use the AI to implement each component separately.
A student's authentication component also handles sending welcome emails and storing user profile photos. What design principle is being violated?
You are designing a bookstore app. Which decomposition best reflects good component boundaries?
Component Diagram
- Step 1: Use your project from the previous lessons (or a simple new idea: a task manager, a quiz app, a weather dashboard).
- Step 2: Extract all significant nouns from your requirements list.
- Step 3: Group related nouns into candidate components. Write one sentence naming the single responsibility of each component.
- Step 4: Draw a simple box diagram: each box is a component; arrows show which component calls which.
- Step 5: Review your diagram. Does any component have more than one distinct responsibility? If yes, split it. Does any component have more than three arrows pointing to it? If yes, consider whether it is doing too much. Write a revised diagram if needed.