Code smells and heuristics skill

Comprehensive catalog of code smells organized by category, with identification criteria and targeted refactorings.

by wondelai·MIT license·★ 2,235 Stars on the repo·GitHub ↗

Use now

Files of Code smells and heuristics

wondelai/main1 file
code-smells.md
Show the full text495 lines

Code Smells and Heuristics

Comprehensive catalog of code smells organized by category, with identification criteria and targeted refactorings. Based on Robert C. Martin's Clean Code, Chapter 17.

Table of Contents

  1. What Is a Code Smell?
  2. Comment Smells
  3. Environment Smells
  4. Function Smells
  5. General Smells
  6. Naming Smells
  7. Test Smells
  8. Smell Detection Quick Reference

What Is a Code Smell?

A code smell is a surface indication that usually corresponds to a deeper problem in the system. Smells are not bugs -- the code works. But they suggest fragility, rigidity, or unnecessary complexity that will cause problems as the codebase evolves. Learn to recognize smells quickly and you'll know where to focus refactoring effort.


Comment Smells

Comments that indicate problems in the code:

C1: Inappropriate Information

Comments that hold information better kept in other systems.

Information type Where it belongs Not in comments
Change history Git log Not // Changed by John on 2024-01-15
Author attribution git blame Not // Author: [email protected]
Issue tracking Jira/GitHub Issues Not // Fixes bug #1234 (use commit message)
Build instructions README or Makefile Not // Run with -Xmx512m flag

Comments should only contain technical notes about the code itself.

C2: Obsolete Comments

Comments that have grown old and are no longer accurate. Code evolves; comments don't always follow.

// BAD: This comment is now a lie
// Returns the total price including 5% tax
public double getTotal() {
    return subtotal * 1.08; // Tax rate changed to 8%, comment not updated
}

Fix: Delete the comment. If the information is important, express it in the code:

private static final double TAX_RATE = 0.08;

public double getTotal() {
    return subtotal * (1 + TAX_RATE);
}
C3: Redundant Comments

Comments that describe something that adequately describes itself.

i++; // increment i

If the code is clear, the comment is noise. If the code is not clear, fix the code.

C4: Poorly Written Comments

If you're going to write a comment, take the time to write it well. Don't ramble. Don't state the obvious. Use correct grammar and punctuation. Be brief and precise.

C5: Commented-Out Code
# result = old_algorithm(data)
# if result > threshold:
#     notify_admin(result)
result = new_algorithm(data)

Commented-out code rots. Others are afraid to delete it ("maybe someone needs it?"). It accumulates. Delete it. Version control is your safety net.


Environment Smells

E1: Build Requires More Than One Step

You should be able to build the entire system with a single command.

# GOOD: One command
make build
# or
npm run build
# or
./gradlew build

If building requires checkout, install, configure, set env vars, download dependencies separately, the build is fragile and developers will avoid running it.

E2: Tests Require More Than One Step

You should be able to run all the tests with a single command.

# GOOD: One command
make test
# or
npm test
# or
pytest

If running tests requires starting databases, setting up fixtures manually, or running scripts in a specific order, developers won't run them.


Function Smells

F1: Too Many Arguments

More than three arguments is a smell. Arguments are hard to understand, hard to remember, and hard to test (every combination of arguments is a test case).

Arguments Assessment Action
0 Ideal Keep as is
1 Good Common and clear
2 Acceptable Ensure natural pairing
3 Questionable Can any be grouped into an object?
4+ Refactor Introduce parameter object or builder

Refactoring:

# BAD: Too many arguments
def create_user(first_name, last_name, email, phone, address, city, state, zip_code):
    ...

# GOOD: Parameter object
def create_user(personal_info: PersonalInfo, address: Address):
    ...
F2: Output Arguments

Arguments that are modified by the function are confusing. Is the argument input or output?

// BAD: Is report being read or modified?
appendFooter(report);

// GOOD: Method on the object being modified
report.appendFooter();

In general, output arguments should be avoided. If a function must change the state of something, have it change the state of the object it is called on.

F3: Flag Arguments

Boolean arguments loudly declare that the function does two things.

# BAD: Flag argument
def create_account(user_data, is_admin):
    if is_admin:
        # 15 lines of admin setup
    else:
        # 15 lines of regular setup

# GOOD: Separate functions
def create_admin_account(user_data):
    ...

def create_user_account(user_data):
    ...
F4: Dead Functions

Functions that are never called should be deleted. Don't keep them around "just in case." Your version control system remembers them if you ever need them back.

How to find dead functions:

  • IDE "Find Usages" reports zero callers
  • Static analysis tools flag unreachable code
  • Code coverage reports show 0% coverage
  • Search for the function name across the codebase

General Smells

G1: Multiple Languages in One Source File

A single source file should contain one language. Mixing HTML, JavaScript, CSS, SQL, and server-side code in one file creates confusion.

Acceptable Problematic
JavaScript in a .js file SQL strings embedded in Java
CSS in a .css file HTML templates inline in Python
SQL in a .sql migration file CSS-in-JS with complex logic

Minimize the extent and number of extra languages in source files.

G2: Obvious Behavior Is Not Implemented

When the obvious behavior of a function is not implemented, readers lose trust in the author.

# SURPRISING: dayOfWeek("Monday") should obviously return Day.MONDAY
def day_of_week(name):
    days = {"Monday": 1, "Tuesday": 2, ...}  # Missing "MONDAY", "monday"
    return days[name]  # Crashes on case variants

Follow the Principle of Least Surprise. Users and callers should not be surprised by what a function does.

G3: Incorrect Behavior at the Boundaries

Don't rely on your intuition for boundary cases. Write tests for every boundary condition. Things that commonly fail at boundaries:

Boundary Common failure
Empty input NullPointerException, IndexOutOfBounds
Single element Off-by-one in loops
Maximum capacity Buffer overflow, performance cliff
Negative values Unexpected results in calculations
Unicode Encoding issues, wrong string length
Concurrent access Race conditions, deadlocks
G4: Overridden Safeties

Turning off warnings, suppressing exceptions, or disabling tests is dangerous.

# BAD: Ignoring the warning doesn't fix the problem
@SuppressWarnings("unchecked")  # Why is this unchecked?
warnings.filterwarnings("ignore")  # What are we hiding?
@pytest.mark.skip("Flaky")  # Fix it instead of skipping

Turning off compiler warnings or ignoring failing tests is like ignoring a check-engine light.

G5: Duplication (DRY Violations)

Duplication is the single most important smell. Every instance represents a missed opportunity for abstraction.

Duplication type How to find it Refactoring
Exact clones Copy-paste detection tools Extract shared function
Structural clones Same algorithm, different data Template Method or Strategy pattern
Conditional chains Repeated if/else or switch Polymorphism
Cross-module Same logic in multiple modules Extract shared library or module

The Rule of Three: First instance: just write it. Second instance: note the duplication. Third instance: refactor.

G6: Code at Wrong Level of Abstraction

Functions and classes should operate at a single level of abstraction. Mixing high-level business logic with low-level implementation details creates confusion.

# BAD: Mixed abstraction levels
def process_order(order):
    # High-level business logic
    validate_order(order)

    # Suddenly low-level database details
    connection = psycopg2.connect(host="db.example.com", port=5432)
    cursor = connection.cursor()
    cursor.execute("INSERT INTO orders ...")
    connection.commit()

    # Back to high-level
    send_confirmation(order)

# GOOD: Consistent abstraction level
def process_order(order):
    validate_order(order)
    order_repository.save(order)
    send_confirmation(order)
G7: Feature Envy

A method that uses more features of another class than its own class has "feature envy." It wants to be somewhere else.

# BAD: This method envies the Order class
class ReportGenerator:
    def calculate_order_summary(self, order):
        subtotal = sum(item.price * item.quantity for item in order.items)
        tax = subtotal * order.tax_rate
        shipping = order.weight * order.shipping_rate
        return subtotal + tax + shipping

# GOOD: Move the method to where the data lives
class Order:
    def calculate_total(self):
        subtotal = sum(item.price * item.quantity for item in self.items)
        tax = subtotal * self.tax_rate
        shipping = self.weight * self.shipping_rate
        return subtotal + tax + shipping
G8: Selector Arguments

Arguments used to select behavior (not just booleans, but enums and strings too).

# BAD: Selector argument
def calculate(operation, a, b):
    if operation == "add": return a + b
    if operation == "subtract": return a - b
    if operation == "multiply": return a * b

# GOOD: Separate functions
def add(a, b): return a + b
def subtract(a, b): return a - b
def multiply(a, b): return a * b
G9: Obscured Intent

Code that is designed to be compact at the expense of clarity.

# BAD: What does this do?
def m(a):return sum(1 for c in a if c.s=='A'and c.d<dt.now()-td(30))

# GOOD: Clear intent
def count_recently_active_customers(customers):
    thirty_days_ago = datetime.now() - timedelta(days=30)
    return sum(
        1 for customer in customers
        if customer.status == 'ACTIVE'
        and customer.last_active_date < thirty_days_ago
    )
G10: Magic Numbers

Raw numeric literals scattered through the code.

# BAD: What do these numbers mean?
if len(password) < 8:
    raise ValueError("Too short")
time.sleep(86400)
price = amount * 0.08

# GOOD: Named constants explain intent
MIN_PASSWORD_LENGTH = 8
SECONDS_PER_DAY = 86400
SALES_TAX_RATE = 0.08

if len(password) < MIN_PASSWORD_LENGTH:
    raise ValueError(f"Password must be at least {MIN_PASSWORD_LENGTH} characters")
time.sleep(SECONDS_PER_DAY)
price = amount * SALES_TAX_RATE
G11: Dead Code

Code that is never executed: unreachable conditions, unused variables, functions with no callers, impossible catch blocks.

Types of dead code:

  • Conditions that can never be true
  • catch blocks for exceptions that are never thrown
  • Variables that are assigned but never read
  • Functions that are never called
  • Entire modules with no imports

Fix: Delete it. Every line of dead code is a line someone has to read, wonder about, and maintain. Version control is the safety net.


Naming Smells

N1: Choosing Descriptive Names

Names should be descriptive. Don't settle for the first name that comes to mind. Take time to choose a name that is as descriptive and unambiguous as possible.

N2: Choosing Names at the Appropriate Level of Abstraction

Don't choose names that communicate implementation. Choose names that reflect the level of abstraction of the class or function you are working in.

# BAD: Implementation-level name
class FTPFileDownloader:  # What if we switch to HTTP?

# GOOD: Abstraction-level name
class FileDownloader:  # The how is an implementation detail
N3: Using Standard Nomenclature Where Possible

Use names from well-known patterns and conventions:

  • Factory, Strategy, Visitor, Iterator for design patterns
  • Repository, Service, Controller for architectural layers
  • Domain terms from the business (Ubiquitous Language from DDD)
N4: Unambiguous Names

Choose names that make the function or variable's workings unambiguous.

# BAD: Ambiguous
def rename(old, new):  # Rename what? A file? A user? A variable?

# GOOD: Unambiguous
def rename_file(old_path, new_path)
N5: Use Long Names for Long Scopes

The length of a name should be proportional to the size of the scope that contains it.

Scope Name length Example
1-line lambda 1 char x in items.map(x => x.id)
5-line method Short i, sum, item
Class field Medium retryCount, lastUpdate
Module constant Long MAX_CONNECTION_POOL_SIZE
Global/public API Very long DEFAULT_SESSION_TIMEOUT_MINUTES
N6: Avoid Encodings

Don't use Hungarian notation, member prefixes, or interface prefixes.

Encoding Example Better
Hungarian strName, iCount name, count
Member prefix m_name, _name name (context is the class)
Interface prefix IUserService UserService (implementations get suffix: UserServiceImpl)
Type suffix nameString name

Test Smells

T1: Insufficient Tests

A test suite should test everything that could possibly break. Test every condition, every boundary, every edge case.

T2: Using a Coverage Tool

Code coverage tools report which lines are not tested. Use them as a guide, not a goal. 100% line coverage does not mean 100% correctness, but untested lines definitely contain potential bugs.

T3: Don't Skip Trivial Tests

Trivial tests are easy to write and their documentary value is higher than the cost of writing them.

T4: An Ignored Test Is a Question About an Ambiguity

If requirements are unclear, write the test with @skip and a note about what's uncertain. A skipped test is a question waiting to be answered.

T5: Test Boundary Conditions

Boundaries are where bugs cluster. Test all edges: empty input, one element, max capacity, off-by-one, overflow.

T6: Exhaustively Test Near Bugs

When you find a bug in a function, don't just fix it. Test the function exhaustively. Bugs tend to congregate -- if there's one, there are likely others nearby.

T7: Patterns of Failure Are Revealing

If tests fail in a pattern (all tests with dates fail, all tests with unicode fail), the pattern reveals the nature of the bug. Use this diagnostic information.

T8: Test Coverage Patterns Can Be Revealing

Look at which code is not covered. If a complex conditional has untested branches, those branches likely contain bugs.

T9: Tests Should Be Fast

Slow tests don't get run. A test suite that takes 30 minutes will be run once a day at best. A suite that takes 10 seconds will be run after every change.


Smell Detection Quick Reference

Category Key Smells First Action
Comments Obsolete, redundant, commented-out code Delete the comment; improve the code
Environment Multi-step build or test Script to single command
Functions Too many args, flag args, dead functions Extract object, split function, delete
General Duplication, wrong abstraction level, feature envy Extract, move, consolidate
Names Ambiguous, wrong level, encoded Rename to reveal intent
Tests Insufficient, slow, skipped, no boundaries Add tests, mock dependencies, fix or delete

Remember: Not every smell requires immediate action. Use professional judgment. A smell in frequently-changed code demands attention. A smell in stable code that never changes may not be worth the risk of refactoring.

1# Code Smells and Heuristics
2 
3Comprehensive catalog of code smells organized by category, with identification criteria and targeted refactorings. Based on Robert C. Martin's *Clean Code*, Chapter 17.
4 
5 
6## Table of Contents
71. [What Is a Code Smell?](#what-is-a-code-smell)
82. [Comment Smells](#comment-smells)
93. [Environment Smells](#environment-smells)
104. [Function Smells](#function-smells)
115. [General Smells](#general-smells)
126. [Naming Smells](#naming-smells)
137. [Test Smells](#test-smells)
148. [Smell Detection Quick Reference](#smell-detection-quick-reference)
15 
16---
17 
18## What Is a Code Smell?
19 
20A code smell is a surface indication that usually corresponds to a deeper problem in the system. Smells are not bugs -- the code works. But they suggest fragility, rigidity, or unnecessary complexity that will cause problems as the codebase evolves. Learn to recognize smells quickly and you'll know where to focus refactoring effort.
21 
22---
23 
24## Comment Smells
25 
26Comments that indicate problems in the code:
27 
28### C1: Inappropriate Information
29 
30Comments that hold information better kept in other systems.
31 
32| Information type | Where it belongs | Not in comments |
33|-----------------|-----------------|-----------------|
34| Change history | Git log | Not `// Changed by John on 2024-01-15` |
35| Author attribution | `git blame` | Not `// Author: [email protected]` |
36| Issue tracking | Jira/GitHub Issues | Not `// Fixes bug #1234` (use commit message) |
37| Build instructions | README or Makefile | Not `// Run with -Xmx512m flag` |
38 
39**Comments should only contain technical notes about the code itself.**
40 
41### C2: Obsolete Comments
42 
43Comments that have grown old and are no longer accurate. Code evolves; comments don't always follow.
44 
45```java
46// BAD: This comment is now a lie
47// Returns the total price including 5% tax
48public double getTotal() {
49 return subtotal * 1.08; // Tax rate changed to 8%, comment not updated
50}
51```
52 
53**Fix:** Delete the comment. If the information is important, express it in the code:
54 
55```java
56private static final double TAX_RATE = 0.08;
57 
58public double getTotal() {
59 return subtotal * (1 + TAX_RATE);
60}
61```
62 
63### C3: Redundant Comments
64 
65Comments that describe something that adequately describes itself.
66 
67```java
68i++; // increment i
69```
70 
71If the code is clear, the comment is noise. If the code is not clear, fix the code.
72 
73### C4: Poorly Written Comments
74 
75If you're going to write a comment, take the time to write it well. Don't ramble. Don't state the obvious. Use correct grammar and punctuation. Be brief and precise.
76 
77### C5: Commented-Out Code
78 
79```python
80# result = old_algorithm(data)
81# if result > threshold:
82# notify_admin(result)
83result = new_algorithm(data)
84```
85 
86Commented-out code rots. Others are afraid to delete it ("maybe someone needs it?"). It accumulates. **Delete it.** Version control is your safety net.
87 
88---
89 
90## Environment Smells
91 
92### E1: Build Requires More Than One Step
93 
94You should be able to build the entire system with a single command.
95 
96```bash
97# GOOD: One command
98make build
99# or
100npm run build
101# or
102./gradlew build
103```
104 
105If building requires checkout, install, configure, set env vars, download dependencies separately, the build is fragile and developers will avoid running it.
106 
107### E2: Tests Require More Than One Step
108 
109You should be able to run all the tests with a single command.
110 
111```bash
112# GOOD: One command
113make test
114# or
115npm test
116# or
117pytest
118```
119 
120If running tests requires starting databases, setting up fixtures manually, or running scripts in a specific order, developers won't run them.
121 
122---
123 
124## Function Smells
125 
126### F1: Too Many Arguments
127 
128More than three arguments is a smell. Arguments are hard to understand, hard to remember, and hard to test (every combination of arguments is a test case).
129 
130| Arguments | Assessment | Action |
131|-----------|------------|--------|
132| 0 | Ideal | Keep as is |
133| 1 | Good | Common and clear |
134| 2 | Acceptable | Ensure natural pairing |
135| 3 | Questionable | Can any be grouped into an object? |
136| 4+ | Refactor | Introduce parameter object or builder |
137 
138**Refactoring:**
139```python
140# BAD: Too many arguments
141def create_user(first_name, last_name, email, phone, address, city, state, zip_code):
142 ...
143 
144# GOOD: Parameter object
145def create_user(personal_info: PersonalInfo, address: Address):
146 ...
147```
148 
149### F2: Output Arguments
150 
151Arguments that are modified by the function are confusing. Is the argument input or output?
152 
153```java
154// BAD: Is report being read or modified?
155appendFooter(report);
156 
157// GOOD: Method on the object being modified
158report.appendFooter();
159```
160 
161In general, output arguments should be avoided. If a function must change the state of something, have it change the state of the object it is called on.
162 
163### F3: Flag Arguments
164 
165Boolean arguments loudly declare that the function does two things.
166 
167```python
168# BAD: Flag argument
169def create_account(user_data, is_admin):
170 if is_admin:
171 # 15 lines of admin setup
172 else:
173 # 15 lines of regular setup
174 
175# GOOD: Separate functions
176def create_admin_account(user_data):
177 ...
178 
179def create_user_account(user_data):
180 ...
181```
182 
183### F4: Dead Functions
184 
185Functions that are never called should be deleted. Don't keep them around "just in case." Your version control system remembers them if you ever need them back.
186 
187**How to find dead functions:**
188- IDE "Find Usages" reports zero callers
189- Static analysis tools flag unreachable code
190- Code coverage reports show 0% coverage
191- Search for the function name across the codebase
192 
193---
194 
195## General Smells
196 
197### G1: Multiple Languages in One Source File
198 
199A single source file should contain one language. Mixing HTML, JavaScript, CSS, SQL, and server-side code in one file creates confusion.
200 
201| Acceptable | Problematic |
202|------------|-------------|
203| JavaScript in a `.js` file | SQL strings embedded in Java |
204| CSS in a `.css` file | HTML templates inline in Python |
205| SQL in a `.sql` migration file | CSS-in-JS with complex logic |
206 
207**Minimize** the extent and number of extra languages in source files.
208 
209### G2: Obvious Behavior Is Not Implemented
210 
211When the obvious behavior of a function is not implemented, readers lose trust in the author.
212 
213```python
214# SURPRISING: dayOfWeek("Monday") should obviously return Day.MONDAY
215def day_of_week(name):
216 days = {"Monday": 1, "Tuesday": 2, ...} # Missing "MONDAY", "monday"
217 return days[name] # Crashes on case variants
218```
219 
220Follow the Principle of Least Surprise. Users and callers should not be surprised by what a function does.
221 
222### G3: Incorrect Behavior at the Boundaries
223 
224Don't rely on your intuition for boundary cases. **Write tests for every boundary condition.** Things that commonly fail at boundaries:
225 
226| Boundary | Common failure |
227|----------|---------------|
228| Empty input | NullPointerException, IndexOutOfBounds |
229| Single element | Off-by-one in loops |
230| Maximum capacity | Buffer overflow, performance cliff |
231| Negative values | Unexpected results in calculations |
232| Unicode | Encoding issues, wrong string length |
233| Concurrent access | Race conditions, deadlocks |
234 
235### G4: Overridden Safeties
236 
237Turning off warnings, suppressing exceptions, or disabling tests is dangerous.
238 
239```python
240# BAD: Ignoring the warning doesn't fix the problem
241@SuppressWarnings("unchecked") # Why is this unchecked?
242warnings.filterwarnings("ignore") # What are we hiding?
243@pytest.mark.skip("Flaky") # Fix it instead of skipping
244```
245 
246Turning off compiler warnings or ignoring failing tests is like ignoring a check-engine light.
247 
248### G5: Duplication (DRY Violations)
249 
250Duplication is the single most important smell. Every instance represents a missed opportunity for abstraction.
251 
252| Duplication type | How to find it | Refactoring |
253|-----------------|----------------|-------------|
254| **Exact clones** | Copy-paste detection tools | Extract shared function |
255| **Structural clones** | Same algorithm, different data | Template Method or Strategy pattern |
256| **Conditional chains** | Repeated `if/else` or `switch` | Polymorphism |
257| **Cross-module** | Same logic in multiple modules | Extract shared library or module |
258 
259**The Rule of Three:** First instance: just write it. Second instance: note the duplication. Third instance: refactor.
260 
261### G6: Code at Wrong Level of Abstraction
262 
263Functions and classes should operate at a single level of abstraction. Mixing high-level business logic with low-level implementation details creates confusion.
264 
265```python
266# BAD: Mixed abstraction levels
267def process_order(order):
268 # High-level business logic
269 validate_order(order)
270 
271 # Suddenly low-level database details
272 connection = psycopg2.connect(host="db.example.com", port=5432)
273 cursor = connection.cursor()
274 cursor.execute("INSERT INTO orders ...")
275 connection.commit()
276 
277 # Back to high-level
278 send_confirmation(order)
279 
280# GOOD: Consistent abstraction level
281def process_order(order):
282 validate_order(order)
283 order_repository.save(order)
284 send_confirmation(order)
285```
286 
287### G7: Feature Envy
288 
289A method that uses more features of another class than its own class has "feature envy." It wants to be somewhere else.
290 
291```python
292# BAD: This method envies the Order class
293class ReportGenerator:
294 def calculate_order_summary(self, order):
295 subtotal = sum(item.price * item.quantity for item in order.items)
296 tax = subtotal * order.tax_rate
297 shipping = order.weight * order.shipping_rate
298 return subtotal + tax + shipping
299 
300# GOOD: Move the method to where the data lives
301class Order:
302 def calculate_total(self):
303 subtotal = sum(item.price * item.quantity for item in self.items)
304 tax = subtotal * self.tax_rate
305 shipping = self.weight * self.shipping_rate
306 return subtotal + tax + shipping
307```
308 
309### G8: Selector Arguments
310 
311Arguments used to select behavior (not just booleans, but enums and strings too).
312 
313```python
314# BAD: Selector argument
315def calculate(operation, a, b):
316 if operation == "add": return a + b
317 if operation == "subtract": return a - b
318 if operation == "multiply": return a * b
319 
320# GOOD: Separate functions
321def add(a, b): return a + b
322def subtract(a, b): return a - b
323def multiply(a, b): return a * b
324```
325 
326### G9: Obscured Intent
327 
328Code that is designed to be compact at the expense of clarity.
329 
330```python
331# BAD: What does this do?
332def m(a):return sum(1 for c in a if c.s=='A'and c.d<dt.now()-td(30))
333 
334# GOOD: Clear intent
335def count_recently_active_customers(customers):
336 thirty_days_ago = datetime.now() - timedelta(days=30)
337 return sum(
338 1 for customer in customers
339 if customer.status == 'ACTIVE'
340 and customer.last_active_date < thirty_days_ago
341 )
342```
343 
344### G10: Magic Numbers
345 
346Raw numeric literals scattered through the code.
347 
348```python
349# BAD: What do these numbers mean?
350if len(password) < 8:
351 raise ValueError("Too short")
352time.sleep(86400)
353price = amount * 0.08
354 
355# GOOD: Named constants explain intent
356MIN_PASSWORD_LENGTH = 8
357SECONDS_PER_DAY = 86400
358SALES_TAX_RATE = 0.08
359 
360if len(password) < MIN_PASSWORD_LENGTH:
361 raise ValueError(f"Password must be at least {MIN_PASSWORD_LENGTH} characters")
362time.sleep(SECONDS_PER_DAY)
363price = amount * SALES_TAX_RATE
364```
365 
366### G11: Dead Code
367 
368Code that is never executed: unreachable conditions, unused variables, functions with no callers, impossible `catch` blocks.
369 
370**Types of dead code:**
371- Conditions that can never be true
372- `catch` blocks for exceptions that are never thrown
373- Variables that are assigned but never read
374- Functions that are never called
375- Entire modules with no imports
376 
377**Fix:** Delete it. Every line of dead code is a line someone has to read, wonder about, and maintain. Version control is the safety net.
378 
379---
380 
381## Naming Smells
382 
383### N1: Choosing Descriptive Names
384 
385Names should be descriptive. Don't settle for the first name that comes to mind. Take time to choose a name that is as descriptive and unambiguous as possible.
386 
387### N2: Choosing Names at the Appropriate Level of Abstraction
388 
389Don't choose names that communicate implementation. Choose names that reflect the level of abstraction of the class or function you are working in.
390 
391```python
392# BAD: Implementation-level name
393class FTPFileDownloader: # What if we switch to HTTP?
394 
395# GOOD: Abstraction-level name
396class FileDownloader: # The how is an implementation detail
397```
398 
399### N3: Using Standard Nomenclature Where Possible
400 
401Use names from well-known patterns and conventions:
402- `Factory`, `Strategy`, `Visitor`, `Iterator` for design patterns
403- `Repository`, `Service`, `Controller` for architectural layers
404- Domain terms from the business (Ubiquitous Language from DDD)
405 
406### N4: Unambiguous Names
407 
408Choose names that make the function or variable's workings unambiguous.
409 
410```python
411# BAD: Ambiguous
412def rename(old, new): # Rename what? A file? A user? A variable?
413 
414# GOOD: Unambiguous
415def rename_file(old_path, new_path)
416```
417 
418### N5: Use Long Names for Long Scopes
419 
420The length of a name should be proportional to the size of the scope that contains it.
421 
422| Scope | Name length | Example |
423|-------|-------------|---------|
424| 1-line lambda | 1 char | `x` in `items.map(x => x.id)` |
425| 5-line method | Short | `i`, `sum`, `item` |
426| Class field | Medium | `retryCount`, `lastUpdate` |
427| Module constant | Long | `MAX_CONNECTION_POOL_SIZE` |
428| Global/public API | Very long | `DEFAULT_SESSION_TIMEOUT_MINUTES` |
429 
430### N6: Avoid Encodings
431 
432Don't use Hungarian notation, member prefixes, or interface prefixes.
433 
434| Encoding | Example | Better |
435|----------|---------|--------|
436| Hungarian | `strName`, `iCount` | `name`, `count` |
437| Member prefix | `m_name`, `_name` | `name` (context is the class) |
438| Interface prefix | `IUserService` | `UserService` (implementations get suffix: `UserServiceImpl`) |
439| Type suffix | `nameString` | `name` |
440 
441---
442 
443## Test Smells
444 
445### T1: Insufficient Tests
446 
447A test suite should test everything that could possibly break. Test every condition, every boundary, every edge case.
448 
449### T2: Using a Coverage Tool
450 
451Code coverage tools report which lines are not tested. Use them as a guide, not a goal. 100% line coverage does not mean 100% correctness, but untested lines definitely contain potential bugs.
452 
453### T3: Don't Skip Trivial Tests
454 
455Trivial tests are easy to write and their documentary value is higher than the cost of writing them.
456 
457### T4: An Ignored Test Is a Question About an Ambiguity
458 
459If requirements are unclear, write the test with `@skip` and a note about what's uncertain. A skipped test is a question waiting to be answered.
460 
461### T5: Test Boundary Conditions
462 
463Boundaries are where bugs cluster. Test all edges: empty input, one element, max capacity, off-by-one, overflow.
464 
465### T6: Exhaustively Test Near Bugs
466 
467When you find a bug in a function, don't just fix it. Test the function exhaustively. Bugs tend to congregate -- if there's one, there are likely others nearby.
468 
469### T7: Patterns of Failure Are Revealing
470 
471If tests fail in a pattern (all tests with dates fail, all tests with unicode fail), the pattern reveals the nature of the bug. Use this diagnostic information.
472 
473### T8: Test Coverage Patterns Can Be Revealing
474 
475Look at which code is not covered. If a complex conditional has untested branches, those branches likely contain bugs.
476 
477### T9: Tests Should Be Fast
478 
479Slow tests don't get run. A test suite that takes 30 minutes will be run once a day at best. A suite that takes 10 seconds will be run after every change.
480 
481---
482 
483## Smell Detection Quick Reference
484 
485| Category | Key Smells | First Action |
486|----------|-----------|--------------|
487| **Comments** | Obsolete, redundant, commented-out code | Delete the comment; improve the code |
488| **Environment** | Multi-step build or test | Script to single command |
489| **Functions** | Too many args, flag args, dead functions | Extract object, split function, delete |
490| **General** | Duplication, wrong abstraction level, feature envy | Extract, move, consolidate |
491| **Names** | Ambiguous, wrong level, encoded | Rename to reveal intent |
492| **Tests** | Insufficient, slow, skipped, no boundaries | Add tests, mock dependencies, fix or delete |
493 
494**Remember:** Not every smell requires immediate action. Use professional judgment. A smell in frequently-changed code demands attention. A smell in stable code that never changes may not be worth the risk of refactoring.
495 

Discussion