Files of Functions and methods
wondelai/
Show the full text374 lines
Functions and Methods
Comprehensive guide to writing small, focused functions that do one thing well. Based on Robert C. Martin's Clean Code, Chapters 3 and 4.
Table of Contents
- The First Rule of Functions
- Do One Thing
- Function Arguments
- Flag Arguments
- Command-Query Separation
- Side Effects
- Extract Till You Drop
- Structured Programming
- DRY: Don't Repeat Yourself
- Function Organization Within a Class
- Common Function Anti-Patterns
The First Rule of Functions
Functions should be small. The second rule of functions is that they should be smaller than that.
A well-written function:
- Fits on one screen (ideally 4-10 lines)
- Has a name that describes exactly what it does
- Takes few arguments (zero is best, three is the maximum)
- Has no side effects
- Operates at a single level of abstraction
Do One Thing
Functions should do one thing. They should do it well. They should do it only.
How to Know if a Function Does One Thing
If you can extract another function from it with a name that is not merely a restatement of its implementation, the function does more than one thing.
# BAD: Does three things
def process_payment(order):
# 1. Validate
if not order.items:
raise ValueError("Empty order")
if order.total <= 0:
raise ValueError("Invalid total")
# 2. Charge
payment_result = gateway.charge(order.customer.card, order.total)
if not payment_result.success:
raise PaymentError(payment_result.error)
# 3. Notify
email_service.send_receipt(order.customer.email, order)
analytics.track("payment_completed", order.id)
# GOOD: Does one thing, delegates details
def process_payment(order):
validate_order(order)
charge_customer(order)
send_notifications(order)
The Step-Down Rule
Code should read like a top-down narrative. Every function should be followed by the next level of abstraction.
To process a payment:
We validate the order.
We charge the customer.
We send notifications.
To validate the order:
We check it has items.
We check the total is positive.
To charge the customer:
We call the payment gateway.
We handle any payment failure.
To send notifications:
We email the receipt.
We track the analytics event.
This reads like a newspaper: the headline (top-level function) tells you the story, and each successive paragraph (sub-function) provides more detail.
Function Arguments
The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification.
Argument Count Guide
| Count | Name | When acceptable | Example |
|---|---|---|---|
| 0 | Niladic | Simple operations | getCurrentTime() |
| 1 | Monadic | Asking a question or transforming input | isValid(email), parse(json) |
| 2 | Dyadic | Natural pairings | Point(x, y), assertEquals(expected, actual) |
| 3 | Triadic | Rarely; consider object | Color(r, g, b) |
| 4+ | Polyadic | Almost never | Wrap in object: new Config(...) |
Common Monadic Forms
Three common reasons to pass a single argument:
- Asking a question:
boolean fileExists(path)-- returns true/false about the argument - Transforming it:
InputStream fileOpen(path)-- transforms the argument and returns the result - Event:
void passwordAttemptFailedNTimes(attempts)-- uses the argument to alter system state (make this clear from the name)
Why Many Arguments Are Problematic
// BAD: What does each argument mean? What order?
createReport(title, startDate, endDate, format, includeCharts, sendEmail, recipients);
// GOOD: Parameter object groups related arguments
ReportConfig config = new ReportConfig.Builder()
.title("Q4 Revenue")
.dateRange(startDate, endDate)
.format(PDF)
.includeCharts(true)
.build();
createReport(config);
Each argument increases the difficulty of understanding, testing, and calling the function. Arguments also create ordering dependencies that the reader must memorize.
Flag Arguments
Flag arguments are ugly. Passing a boolean into a function loudly declares that the function does two things -- one thing if the flag is true, another if false.
# BAD: Flag argument
def render(document, is_for_print):
if is_for_print:
# 20 lines of print rendering
...
else:
# 20 lines of screen rendering
...
# GOOD: Two clearly named functions
def render_for_print(document):
...
def render_for_screen(document):
...
If a function must behave differently based on a condition, split it into two named functions. If the behaviors share logic, extract the shared part into a private helper.
Command-Query Separation
Functions should either do something (command) or answer something (query), but not both.
// BAD: Does this set the attribute or check if it exists?
if (set("username", "unclebob")) { ... }
// GOOD: Separate query from command
if (attributeExists("username")) {
setAttribute("username", "unclebob");
}
Commands change the state of an object. They should return void. Queries return information about an object. They should not change state.
When a function both changes state and returns a value, the reader cannot tell from the call site what is happening.
Side Effects
A side effect is when a function promises to do one thing but also does other hidden things.
Common Hidden Side Effects
| Declared purpose | Hidden side effect | Danger |
|---|---|---|
checkPassword(user, password) |
Initializes a session | Calling "check" unexpectedly logs user in |
getUser(id) |
Creates user if not found | "Get" implies read-only; caller doesn't expect writes |
toString() |
Modifies internal state | Debugging with print statements changes behavior |
validate(input) |
Sends analytics event | Validation during testing triggers real events |
How to Fix Side Effects
- Make the side effect explicit in the name:
checkPasswordAndInitSession() - Separate the concerns:
checkPassword()+initSession()as two calls - Prefer option 2 -- separation makes testing easier and keeps functions honest
Extract Till You Drop
If you can extract a named function from a block of code, you should. The extracted function's name adds documentation value, even if the function is only called from one place.
When to Extract
| Signal | Action |
|---|---|
A block inside an if, else, for, or while |
Extract to named function |
| A comment explaining what the next lines do | Replace comment with named function |
| A function longer than 10 lines | Look for extraction opportunities |
| Nested indentation deeper than 2 levels | Extract inner blocks |
| Code that you'd need to re-read to understand | Name it so you don't have to |
Extraction Example
// BEFORE: Deeply nested, hard to follow
function processOrders(orders: Order[]) {
for (const order of orders) {
if (order.status === 'pending') {
let total = 0;
for (const item of order.items) {
if (item.inStock) {
total += item.price * item.quantity;
if (item.quantity > 10) {
total *= 0.9; // bulk discount
}
}
}
if (total > 0) {
order.total = total;
order.status = 'processed';
db.save(order);
emailService.sendConfirmation(order);
}
}
}
}
// AFTER: Each function does one thing
function processOrders(orders: Order[]) {
orders
.filter(isPending)
.forEach(processOrder);
}
function processOrder(order: Order) {
const total = calculateOrderTotal(order);
if (total > 0) {
finalizeOrder(order, total);
}
}
function calculateOrderTotal(order: Order): number {
return order.items
.filter(item => item.inStock)
.reduce((sum, item) => sum + calculateItemPrice(item), 0);
}
function calculateItemPrice(item: Item): number {
const basePrice = item.price * item.quantity;
return item.quantity > BULK_DISCOUNT_THRESHOLD
? basePrice * BULK_DISCOUNT_RATE
: basePrice;
}
function finalizeOrder(order: Order, total: number) {
order.total = total;
order.status = 'processed';
db.save(order);
emailService.sendConfirmation(order);
}
Structured Programming
Dijkstra's rule states that every function should have one entry and one exit: one return statement, no break or continue in loops, and never goto.
In practice, for small functions, multiple returns and early exits improve clarity:
# Guard clauses improve readability in small functions
def calculate_discount(customer):
if customer is None:
return 0
if not customer.is_active:
return 0
if customer.total_purchases < MINIMUM_FOR_DISCOUNT:
return 0
return customer.total_purchases * DISCOUNT_RATE
Guard clauses handle the error cases at the top of the function, leaving the happy path unindented and clear. This pattern is preferable to deeply nested if-else chains.
DRY: Don't Repeat Yourself
Duplication is the root of all evil in software. Every piece of knowledge should have a single, unambiguous, authoritative representation in the system.
Types of Duplication
| Type | Example | Fix |
|---|---|---|
| Exact duplication | Same code block in three places | Extract to shared function |
| Structural duplication | Same algorithm with different data | Template Method or Strategy pattern |
| Conceptual duplication | Same business rule expressed differently | Consolidate into single source of truth |
| Data duplication | Same value computed in multiple places | Compute once, pass result |
The Rule of Three
The first time you write something, just write it. The second time you see duplication, note it. The third time, refactor. This avoids premature abstraction while still catching genuine duplication.
Function Organization Within a Class
The Newspaper Metaphor
Organize functions the way a newspaper organizes articles:
- Headline at the top: Public methods (the API) appear first
- Synopsis next: High-level private methods called by the public methods
- Details last: Low-level helper functions at the bottom
The reader can stop reading at any depth once they have enough understanding.
Vertical Distance
Dependent functions should be close. If one function calls another, they should be vertically close in the source file, and the caller should be above the callee.
// GOOD: Caller above callee, close together
public void processOrder(Order order) {
validateOrder(order);
chargeCustomer(order);
shipOrder(order);
}
private void validateOrder(Order order) {
// validation logic
}
private void chargeCustomer(Order order) {
// payment logic
}
private void shipOrder(Order order) {
// shipping logic
}
Variables should be declared as close to their usage as possible. Local variables at the top of the function. Loop variables inside the loop statement. Instance variables at the top of the class (everyone needs to know they exist).
Common Function Anti-Patterns
| Anti-pattern | Problem | Refactoring |
|---|---|---|
| Output arguments | appendFooter(report) -- is report input or output? |
Make it a method: report.appendFooter() |
| Selector arguments | calculate(MONTHLY) enum switches behavior |
Split: calculateMonthly(), calculateAnnual() |
| Dead functions | Never called, just sitting there | Delete them. Version control remembers. |
| Switch statements | Long switches violate SRP and OCP | Replace with polymorphism or strategy pattern |
| Temporal coupling | Functions must be called in a specific order | Make ordering explicit through return values or builder |
| Leaky abstraction | Function exposes implementation details | Hide internals, return domain objects |
| 1 | # Functions and Methods |
| 2 | |
| 3 | Comprehensive guide to writing small, focused functions that do one thing well. Based on Robert C. Martin's *Clean Code*, Chapters 3 and 4. |
| 4 | |
| 5 | |
| 6 | ## Table of Contents |
| 7 | [The First Rule of Functions] |
| 8 | [Do One Thing] |
| 9 | [Function Arguments] |
| 10 | [Flag Arguments] |
| 11 | [Command-Query Separation] |
| 12 | [Side Effects] |
| 13 | [Extract Till You Drop] |
| 14 | [Structured Programming] |
| 15 | [DRY: Don't Repeat Yourself] |
| 16 | [Function Organization Within a Class] |
| 17 | [Common Function Anti-Patterns] |
| 18 | |
| 19 | |
| 20 | |
| 21 | ## The First Rule of Functions |
| 22 | |
| 23 | **Functions should be small.** The second rule of functions is that they should be smaller than that. |
| 24 | |
| 25 | A well-written function: |
| 26 | Fits on one screen (ideally 4-10 lines) |
| 27 | Has a name that describes exactly what it does |
| 28 | Takes few arguments (zero is best, three is the maximum) |
| 29 | Has no side effects |
| 30 | Operates at a single level of abstraction |
| 31 | |
| 32 | |
| 33 | |
| 34 | ## Do One Thing |
| 35 | |
| 36 | **Functions should do one thing. They should do it well. They should do it only.** |
| 37 | |
| 38 | ### How to Know if a Function Does One Thing |
| 39 | |
| 40 | If you can extract another function from it with a name that is not merely a restatement of its implementation, the function does more than one thing. |
| 41 | |
| 42 | |
| 43 | # BAD: Does three things |
| 44 | def process_payment(order): |
| 45 | # 1. Validate |
| 46 | if not order.items: |
| 47 | raise ValueError("Empty order") |
| 48 | if order.total <= 0: |
| 49 | raise ValueError("Invalid total") |
| 50 | |
| 51 | # 2. Charge |
| 52 | payment_result = gateway.charge(order.customer.card, order.total) |
| 53 | if not payment_result.success: |
| 54 | raise PaymentError(payment_result.error) |
| 55 | |
| 56 | # 3. Notify |
| 57 | email_service.send_receipt(order.customer.email, order) |
| 58 | analytics.track("payment_completed", order.id) |
| 59 | |
| 60 | # GOOD: Does one thing, delegates details |
| 61 | def process_payment(order): |
| 62 | validate_order(order) |
| 63 | charge_customer(order) |
| 64 | send_notifications(order) |
| 65 | |
| 66 | |
| 67 | ### The Step-Down Rule |
| 68 | |
| 69 | Code should read like a top-down narrative. Every function should be followed by the next level of abstraction. |
| 70 | |
| 71 | |
| 72 | To process a payment: |
| 73 | We validate the order. |
| 74 | We charge the customer. |
| 75 | We send notifications. |
| 76 | |
| 77 | To validate the order: |
| 78 | We check it has items. |
| 79 | We check the total is positive. |
| 80 | |
| 81 | To charge the customer: |
| 82 | We call the payment gateway. |
| 83 | We handle any payment failure. |
| 84 | |
| 85 | To send notifications: |
| 86 | We email the receipt. |
| 87 | We track the analytics event. |
| 88 | |
| 89 | |
| 90 | This reads like a newspaper: the headline (top-level function) tells you the story, and each successive paragraph (sub-function) provides more detail. |
| 91 | |
| 92 | |
| 93 | |
| 94 | ## Function Arguments |
| 95 | |
| 96 | The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification. |
| 97 | |
| 98 | ### Argument Count Guide |
| 99 | |
| 100 | | Count | Name | When acceptable | Example | |
| 101 | |-------|------|-----------------|---------| |
| 102 | | **0** | Niladic | Simple operations | `getCurrentTime()` | |
| 103 | | **1** | Monadic | Asking a question or transforming input | `isValid(email)`, `parse(json)` | |
| 104 | | **2** | Dyadic | Natural pairings | `Point(x, y)`, `assertEquals(expected, actual)` | |
| 105 | | **3** | Triadic | Rarely; consider object | `Color(r, g, b)` | |
| 106 | | **4+** | Polyadic | Almost never | Wrap in object: `new Config(...)` | |
| 107 | |
| 108 | ### Common Monadic Forms |
| 109 | |
| 110 | Three common reasons to pass a single argument: |
| 111 | |
| 112 | **Asking a question:** `boolean fileExists(path)` -- returns true/false about the argument |
| 113 | **Transforming it:** `InputStream fileOpen(path)` -- transforms the argument and returns the result |
| 114 | **Event:** `void passwordAttemptFailedNTimes(attempts)` -- uses the argument to alter system state (make this clear from the name) |
| 115 | |
| 116 | ### Why Many Arguments Are Problematic |
| 117 | |
| 118 | |
| 119 | // BAD: What does each argument mean? What order? |
| 120 | createReport(title, startDate, endDate, format, includeCharts, sendEmail, recipients); |
| 121 | |
| 122 | // GOOD: Parameter object groups related arguments |
| 123 | ReportConfig config = new ReportConfig.Builder() |
| 124 | .title("Q4 Revenue") |
| 125 | .dateRange(startDate, endDate) |
| 126 | .format(PDF) |
| 127 | .includeCharts(true) |
| 128 | .build(); |
| 129 | createReport(config); |
| 130 | |
| 131 | |
| 132 | Each argument increases the difficulty of understanding, testing, and calling the function. Arguments also create ordering dependencies that the reader must memorize. |
| 133 | |
| 134 | |
| 135 | |
| 136 | ## Flag Arguments |
| 137 | |
| 138 | **Flag arguments are ugly.** Passing a boolean into a function loudly declares that the function does two things -- one thing if the flag is true, another if false. |
| 139 | |
| 140 | |
| 141 | # BAD: Flag argument |
| 142 | def render(document, is_for_print): |
| 143 | if is_for_print: |
| 144 | # 20 lines of print rendering |
| 145 | ... |
| 146 | else: |
| 147 | # 20 lines of screen rendering |
| 148 | ... |
| 149 | |
| 150 | # GOOD: Two clearly named functions |
| 151 | def render_for_print(document): |
| 152 | ... |
| 153 | |
| 154 | def render_for_screen(document): |
| 155 | ... |
| 156 | |
| 157 | |
| 158 | If a function must behave differently based on a condition, split it into two named functions. If the behaviors share logic, extract the shared part into a private helper. |
| 159 | |
| 160 | |
| 161 | |
| 162 | ## Command-Query Separation |
| 163 | |
| 164 | Functions should either do something (command) or answer something (query), but not both. |
| 165 | |
| 166 | |
| 167 | // BAD: Does this set the attribute or check if it exists? |
| 168 | if (set("username", "unclebob")) { ... } |
| 169 | |
| 170 | // GOOD: Separate query from command |
| 171 | if (attributeExists("username")) { |
| 172 | setAttribute("username", "unclebob"); |
| 173 | } |
| 174 | |
| 175 | |
| 176 | **Commands** change the state of an object. They should return void. |
| 177 | **Queries** return information about an object. They should not change state. |
| 178 | |
| 179 | When a function both changes state and returns a value, the reader cannot tell from the call site what is happening. |
| 180 | |
| 181 | |
| 182 | |
| 183 | ## Side Effects |
| 184 | |
| 185 | A side effect is when a function promises to do one thing but also does other hidden things. |
| 186 | |
| 187 | ### Common Hidden Side Effects |
| 188 | |
| 189 | | Declared purpose | Hidden side effect | Danger | |
| 190 | |-----------------|-------------------|--------| |
| 191 | | `checkPassword(user, password)` | Initializes a session | Calling "check" unexpectedly logs user in | |
| 192 | | `getUser(id)` | Creates user if not found | "Get" implies read-only; caller doesn't expect writes | |
| 193 | | `toString()` | Modifies internal state | Debugging with print statements changes behavior | |
| 194 | | `validate(input)` | Sends analytics event | Validation during testing triggers real events | |
| 195 | |
| 196 | ### How to Fix Side Effects |
| 197 | |
| 198 | **Make the side effect explicit in the name:** `checkPasswordAndInitSession()` |
| 199 | **Separate the concerns:** `checkPassword()` + `initSession()` as two calls |
| 200 | **Prefer option 2** -- separation makes testing easier and keeps functions honest |
| 201 | |
| 202 | |
| 203 | |
| 204 | ## Extract Till You Drop |
| 205 | |
| 206 | If you can extract a named function from a block of code, you should. The extracted function's name adds documentation value, even if the function is only called from one place. |
| 207 | |
| 208 | ### When to Extract |
| 209 | |
| 210 | | Signal | Action | |
| 211 | |--------|--------| |
| 212 | | A block inside an `if`, `else`, `for`, or `while` | Extract to named function | |
| 213 | | A comment explaining what the next lines do | Replace comment with named function | |
| 214 | | A function longer than 10 lines | Look for extraction opportunities | |
| 215 | | Nested indentation deeper than 2 levels | Extract inner blocks | |
| 216 | | Code that you'd need to re-read to understand | Name it so you don't have to | |
| 217 | |
| 218 | ### Extraction Example |
| 219 | |
| 220 | |
| 221 | // BEFORE: Deeply nested, hard to follow |
| 222 | function processOrders(orders: Order[]) { |
| 223 | for (const order of orders) { |
| 224 | if (order.status === 'pending') { |
| 225 | let total = 0; |
| 226 | for (const item of order.items) { |
| 227 | if (item.inStock) { |
| 228 | total += item.price * item.quantity; |
| 229 | if (item.quantity > 10) { |
| 230 | total *= 0.9; // bulk discount |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | if (total > 0) { |
| 235 | order.total = total; |
| 236 | order.status = 'processed'; |
| 237 | db.save(order); |
| 238 | emailService.sendConfirmation(order); |
| 239 | } |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | // AFTER: Each function does one thing |
| 245 | function processOrders(orders: Order[]) { |
| 246 | orders |
| 247 | .filter(isPending) |
| 248 | .forEach(processOrder); |
| 249 | } |
| 250 | |
| 251 | function processOrder(order: Order) { |
| 252 | const total = calculateOrderTotal(order); |
| 253 | if (total > 0) { |
| 254 | finalizeOrder(order, total); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | function calculateOrderTotal(order: Order): number { |
| 259 | return order.items |
| 260 | .filter(item => item.inStock) |
| 261 | .reduce((sum, item) => sum + calculateItemPrice(item), 0); |
| 262 | } |
| 263 | |
| 264 | function calculateItemPrice(item: Item): number { |
| 265 | const basePrice = item.price * item.quantity; |
| 266 | return item.quantity > BULK_DISCOUNT_THRESHOLD |
| 267 | ? basePrice * BULK_DISCOUNT_RATE |
| 268 | : basePrice; |
| 269 | } |
| 270 | |
| 271 | function finalizeOrder(order: Order, total: number) { |
| 272 | order.total = total; |
| 273 | order.status = 'processed'; |
| 274 | db.save(order); |
| 275 | emailService.sendConfirmation(order); |
| 276 | } |
| 277 | |
| 278 | |
| 279 | |
| 280 | |
| 281 | ## Structured Programming |
| 282 | |
| 283 | Dijkstra's rule states that every function should have one entry and one exit: one `return` statement, no `break` or `continue` in loops, and never `goto`. |
| 284 | |
| 285 | **In practice, for small functions, multiple returns and early exits improve clarity:** |
| 286 | |
| 287 | |
| 288 | # Guard clauses improve readability in small functions |
| 289 | def calculate_discount(customer): |
| 290 | if customer is None: |
| 291 | return 0 |
| 292 | if not customer.is_active: |
| 293 | return 0 |
| 294 | if customer.total_purchases < MINIMUM_FOR_DISCOUNT: |
| 295 | return 0 |
| 296 | |
| 297 | return customer.total_purchases * DISCOUNT_RATE |
| 298 | |
| 299 | |
| 300 | Guard clauses handle the error cases at the top of the function, leaving the happy path unindented and clear. This pattern is preferable to deeply nested `if-else` chains. |
| 301 | |
| 302 | |
| 303 | |
| 304 | ## DRY: Don't Repeat Yourself |
| 305 | |
| 306 | Duplication is the root of all evil in software. Every piece of knowledge should have a single, unambiguous, authoritative representation in the system. |
| 307 | |
| 308 | ### Types of Duplication |
| 309 | |
| 310 | | Type | Example | Fix | |
| 311 | |------|---------|-----| |
| 312 | | **Exact duplication** | Same code block in three places | Extract to shared function | |
| 313 | | **Structural duplication** | Same algorithm with different data | Template Method or Strategy pattern | |
| 314 | | **Conceptual duplication** | Same business rule expressed differently | Consolidate into single source of truth | |
| 315 | | **Data duplication** | Same value computed in multiple places | Compute once, pass result | |
| 316 | |
| 317 | ### The Rule of Three |
| 318 | |
| 319 | The first time you write something, just write it. The second time you see duplication, note it. The third time, refactor. This avoids premature abstraction while still catching genuine duplication. |
| 320 | |
| 321 | |
| 322 | |
| 323 | ## Function Organization Within a Class |
| 324 | |
| 325 | ### The Newspaper Metaphor |
| 326 | |
| 327 | Organize functions the way a newspaper organizes articles: |
| 328 | |
| 329 | **Headline at the top:** Public methods (the API) appear first |
| 330 | **Synopsis next:** High-level private methods called by the public methods |
| 331 | **Details last:** Low-level helper functions at the bottom |
| 332 | |
| 333 | The reader can stop reading at any depth once they have enough understanding. |
| 334 | |
| 335 | ### Vertical Distance |
| 336 | |
| 337 | **Dependent functions should be close.** If one function calls another, they should be vertically close in the source file, and the caller should be above the callee. |
| 338 | |
| 339 | |
| 340 | // GOOD: Caller above callee, close together |
| 341 | public void processOrder(Order order) { |
| 342 | validateOrder(order); |
| 343 | chargeCustomer(order); |
| 344 | shipOrder(order); |
| 345 | } |
| 346 | |
| 347 | private void validateOrder(Order order) { |
| 348 | // validation logic |
| 349 | } |
| 350 | |
| 351 | private void chargeCustomer(Order order) { |
| 352 | // payment logic |
| 353 | } |
| 354 | |
| 355 | private void shipOrder(Order order) { |
| 356 | // shipping logic |
| 357 | } |
| 358 | |
| 359 | |
| 360 | **Variables should be declared as close to their usage as possible.** Local variables at the top of the function. Loop variables inside the loop statement. Instance variables at the top of the class (everyone needs to know they exist). |
| 361 | |
| 362 | |
| 363 | |
| 364 | ## Common Function Anti-Patterns |
| 365 | |
| 366 | | Anti-pattern | Problem | Refactoring | |
| 367 | |-------------|---------|-------------| |
| 368 | | **Output arguments** | `appendFooter(report)` -- is `report` input or output? | Make it a method: `report.appendFooter()` | |
| 369 | | **Selector arguments** | `calculate(MONTHLY)` enum switches behavior | Split: `calculateMonthly()`, `calculateAnnual()` | |
| 370 | | **Dead functions** | Never called, just sitting there | Delete them. Version control remembers. | |
| 371 | | **Switch statements** | Long switches violate SRP and OCP | Replace with polymorphism or strategy pattern | |
| 372 | | **Temporal coupling** | Functions must be called in a specific order | Make ordering explicit through return values or builder | |
| 373 | | **Leaky abstraction** | Function exposes implementation details | Hide internals, return domain objects | |
| 374 |
Discussion
Browse more free Claude skills.