What's changed: Initial version
1.3Code organization and design patterns
Covers organizing code with functions/methods, classes, and modules/packages, plus the MVC pattern that separates responsibilities and the Observer pattern that notifies subscribers of state changes, framed as the design judgment of "how do I split this monolithic script?" and "which pattern fits this notification requirement?"
Left alone, an automation script grows into a "monolith" where fetching, transforming, displaying, and notifying all blur into one long procedure. Then every fix in one place breaks another, and testing gets hard. This section covers organizing code into functions, classes, and modules for reuse and ease of change, plus MVC, which systematizes separation of responsibilities, and Observer, which achieves change notification with loose coupling—as the design judgment of "how should I structure this, and which pattern fits?" The goal is not memorizing names but smelling the code in front of you and choosing the right split.
1.3.1Functions, classes, and modules
- A function is a reusable unit of processing that takes input and returns output; it consolidates repeated steps in one place and expresses intent by name. A method is a function belonging to a class that acts on that object's state (attributes). "Repeated processing goes into a function" is the first step of organization.
- A class is a blueprint that bundles related data (attributes) and behavior (methods). For example, a
Deviceclass holdsname/ipattributes andconnect()/get_config()methods. When state and operations belong together, a class is easier to handle than scattered variables and functions. - A module/package groups functions and classes at the file/directory level and reuses them via
import. Splitting files by role (e.g., API communication, data shaping, output) localizes the blast radius of a change and lets you isolate what to test.
1.3.2Design patterns: MVC and Observer
- MVC separates responsibilities into Model (data and business logic), View (display), and Controller (receives input and mediates between Model and View). Splitting code where fetch logic and screen display are mixed along MVC makes it easy to swap only the display or test only the logic.
- Observer is a pattern where, when a subject's state changes, it automatically notifies registered subscribers (observers). It realizes one-to-many notification with loose coupling, so adding/removing subscribers does not affect the subject's code. It fits event-driven, webhook-like "tell me when something changes" requirements.
Most-tested: repeated processing goes into functions, state+operations into classes, split modules by role; MVC = Model (logic)/View (display)/Controller (mediation) for separation of responsibilities; Observer = loosely coupled one-to-many notification of state changes to subscribers. Questions arrive as judgment: "how do I split this monolith?" and "which pattern fits this notification/display-separation requirement?"
You inherit maintenance of a monolithic script that pulls interface data from devices and draws a table in the terminal. Inside one long main(), the HTTP call to the API, JSON shaping, colored table rendering, and file logging are all blended. Requests arrive: "show it on a web page instead of a table," "unit-test just the fetch logic." But in the current structure, changing the display touches the logic, and every test hits real gear. The right judgment is to separate responsibilities along MVC. Carve the part that fetches and holds/shapes data into the Model (make device dependencies functions so they can be mocked), the table/web presentation into the View, and the coordinator that receives a request, calls the Model, and hands off to the View into the Controller. Then swapping the View from table to web leaves the Model intact, and you can unit-test just the Model logic. Now suppose a requirement is added: "when an interface flips to down, notify three places at once—log, alert, dashboard." Adding an if inside the Model for each new destination is tight coupling and fragile—Observer is the fit here. Register subscribers (log/alert/dashboard) with the subject that raises the state change (down detected), and on change it notifies them all at once, while adding/removing subscribers does not affect the subject's code. Conversely, bringing a pattern into a simple one-off transform is over-engineering. The essence is reading the code smells (duplication, mixing, change ripple) and judging which of the function/class/module structure and the MVC/Observer patterns solves the current pain.
| Structure/pattern | Aim | When it fits |
|---|---|---|
| Function/method | Consolidate duplication, name intent | The same steps repeat |
| Class | Bundle state and operations | Data and behavior belong together |
| Module/package | Split by role, localize impact | Files bloat and mix roles |
| MVC | Separate logic/display/mediation | Swap display, test logic |
| Observer | Loosely notify subscribers of change | One change, many notified |
Trap: "The MVC Controller holds the business logic and data" is wrong—logic and data are the Model's responsibility, and the Controller merely mediates between Model and View after receiving input. Also wrong: "Observer creates new subscribers on each state change"—subscribers are registered in advance, and on change the subject only notifies the existing subscribers. Forcing a pattern onto simple processing is over-engineering.
1.3.3Section summary
- Put repeated processing into functions, state+operations into classes, and split by role into modules to localize change impact
- MVC separates Model (logic/data), View (display), Controller (mediation), making display swaps and logic testing easy
- Observer notifies pre-registered subscribers of state changes one-to-many with loose coupling; avoid over-engineering and choose the structure/pattern that matches the pain
Sign in to track progress — Log in.
Quick check
(just a quick review)Q1. In a script where the API call, data shaping, table rendering, and logging are all mixed into one long main(), requests arise to "switch the display from table to web" and "unit-test just the fetch logic." Which restructuring is most appropriate?
Q2. When an interface flips to down, you want to notify three places at once—log, alert, dashboard—and more destinations are expected, without editing the subject's code each time. Which design pattern fits best?
Q3. The same "10-line procedure to SSH into a device and fetch its config" is copy-pasted in five places in a script, and changing the connection method means editing all five. What is the most appropriate first step to improve maintainability?

