Skip to content

Instantly share code, notes, and snippets.

@paulbrodner
Last active June 25, 2025 11:58
Show Gist options
  • Select an option

  • Save paulbrodner/b7e8e095b702c6ba1fb9ddfa4fcfda33 to your computer and use it in GitHub Desktop.

Select an option

Save paulbrodner/b7e8e095b702c6ba1fb9ddfa4fcfda33 to your computer and use it in GitHub Desktop.
# 🚀 Refined "Delete First, Code Last" Rules - Practical Edition
> **Core Philosophy: Question every line of code. Prefer simplicity over cleverness, but don't sacrifice clarity for brevity.**
## 🎯 **The Golden Question**
Before writing any code: **"What's the simplest way to solve this that I can still understand in 6 months?"**
---
## 🏆 **6 Practical Principles** (Guidelines, Not Laws)
### 1. **🔍 Question Abstractions - But Don't Avoid Them**
**Guideline:** Create abstractions when they genuinely simplify understanding or prevent errors.
```python
# ❌ Pointless wrapper
class StringHelper:
@staticmethod
def get_length(s):
return len(s)
# ❌ Missing useful abstraction
def process_order(order_data):
# 50 lines of validation, calculation, and formatting
# Should be broken down into conceptual chunks
# ✅ Meaningful abstraction
def process_order(order_data):
validated_order = validate_order_data(order_data)
calculated_totals = calculate_order_totals(validated_order)
return format_order_response(calculated_totals)
```
**Test:** Does this abstraction represent a meaningful concept that could be explained to a domain expert?
---
### 2. **🧹 Eliminate True Duplication**
**Guideline:** Consolidate identical logic, but similar-looking code isn't always duplication.
```python
# ❌ True duplication - same logic, same purpose
def validate_user_email(email):
if not email or "@" not in email:
raise ValueError("Invalid email")
return email.lower().strip()
def validate_admin_email(email):
if not email or "@" not in email:
raise ValueError("Invalid email")
return email.lower().strip()
# ✅ Consolidated
def validate_email(email):
if not email or "@" not in email:
raise ValueError("Invalid email")
return email.lower().strip()
# ✅ Similar but NOT duplication - different purposes
def format_display_name(name):
return name.title().strip()
def format_database_key(name):
return name.lower().strip()
```
**Test:** Is the logic identical AND serving the same purpose? Only then consolidate.
---
### 3. **📁 Functions vs Classes - Context Matters**
**Guideline:** Use functions for transformations, classes for stateful behavior and related operations.
```python
# ❌ Class for simple data holding
class UserSettings:
def __init__(self, theme, language):
self.theme = theme
self.language = language
# ✅ Simple data structure
@dataclass(frozen=True)
class UserSettings:
theme: str
language: str
# ✅ Class justified - manages state and related operations
class DatabaseConnection:
def __init__(self, connection_string):
self._connection = None
self._connection_string = connection_string
def connect(self):
if not self._connection:
self._connection = create_connection(self._connection_string)
def execute(self, query):
self.connect()
return self._connection.execute(query)
def close(self):
if self._connection:
self._connection.close()
```
**Test:** Does this need to maintain state between operations? If yes, consider a class. If no, use functions.
---
### 4. **⚡ Fail Fast - But Handle Gracefully**
**Guideline:** Let exceptions bubble up for programmer errors, handle expected failures gracefully.
```python
# ✅ Let programming errors bubble up
def calculate_average(numbers):
return sum(numbers) / len(numbers) # ZeroDivisionError is appropriate
# ✅ Handle expected business failures
def process_payment(amount, card_token):
try:
return payment_service.charge(amount, card_token)
except PaymentDeclinedError as e:
return PaymentResult(success=False, error=str(e))
# Let unexpected errors (network, auth) bubble up
```
**Test:** Is this an expected business scenario or a programming error? Handle business scenarios, let programming errors fail fast.
---
### 5. **🎯 Prefer Pure Functions - When Practical**
**Guideline:** Use pure functions where possible, but don't contort your code to avoid necessary state.
```python
# ✅ Pure function - easy to test and reason about
def calculate_tax(amount, rate):
return amount * rate
# ✅ Necessary state - don't force this into pure functions
class OrderProcessor:
def __init__(self, tax_calculator, payment_service):
self.tax_calculator = tax_calculator
self.payment_service = payment_service
def process_order(self, order):
# Coordinates multiple services - state is justified
tax = self.tax_calculator.calculate(order.amount)
payment_result = self.payment_service.charge(order.amount + tax)
return OrderResult(order, tax, payment_result)
```
**Test:** Can this logic work without external dependencies or state? If yes, make it pure.
---
### 6. **🗑️ The Contextual Delete Test**
**Guideline:** Delete code that doesn't serve a clear purpose, but consider the full context.
**Safe to Delete:**
- Dead code (unreachable)
- Obvious comments (`x = x + 1 # increment x`)
- Trivial wrappers that don't add meaning
- Unused imports (verified by tools)
**Consider Context Before Deleting:**
- Single-use functions (may represent important concepts)
- Seemingly redundant checks (may prevent edge case bugs)
- Comments explaining non-obvious business logic
- Error handling that seems excessive
**Delete Process:**
1. **Understand why it exists** - check git history, ask teammates
2. **Verify it's truly unused** - search codebase, check tests
3. **Comment it out first** - don't delete immediately
4. **Run full test suite** - including integration tests
5. **Monitor in production** - some code prevents rare edge cases
---
## 🎯 **Practical Implementation**
### **The 30-Second Rule:**
Before writing code, spend 30 seconds considering:
1. **Does this already exist?** (standard library, existing function)
2. **Is this the simplest approach?** (not necessarily shortest)
3. **Will this be clear to future maintainers?** (including yourself)
4. **What could go wrong?** (edge cases, failure modes)
### **Balanced Metrics:**
- ✅ **Cyclomatic complexity <15** per function (10 is ideal, but context matters)
- ✅ **File length <500 lines** (split if much larger, but related code can stay together)
- ✅ **Function parameters <7** (use objects for more, but don't force awkward groupings)
- ✅ **Clear naming** (slightly longer names are fine if they're clearer)
- ✅ **Good test coverage** (focus on critical paths, not 100% line coverage)
### **Code Smells (Investigate, Don't Auto-Delete):**
- 🔍 Functions >50 lines - may need breakdown
- 🔍 Classes with >10 methods - may have too many responsibilities
- 🔍 Files importing >15 modules - may be doing too much
- 🔍 Functions with >5 parameters - consider grouping
- 🔍 Deeply nested conditions - consider early returns or extraction
---
## 🌟 **Practical Mantras**
1. **"Clear is better than clever"**
2. **"Simple is better than easy"**
3. **"Explicit is better than implicit"**
4. **"Readable code is maintainable code"**
5. **"Optimize for the reader, not the writer"**
6. **"Perfect is the enemy of good"**
7. **"Delete with purpose, not prejudice"**
---
## 🧪 **Weekly Code Review**
**Every week, pick one area and ask:**
1. **What's confusing?** - simplify or document
2. **What's duplicated?** - consider consolidation
3. **What's unused?** - delete safely
4. **What's overly complex?** - break down or simplify
5. **What's missing?** - add needed abstractions
6. **What's fragile?** - add error handling or tests
---
## 🔄 **Balanced Before/After Template**
```
📊 CHANGE ANALYSIS:
❌ BEFORE: [brief description of issue]
✅ AFTER: [brief description of solution]
📈 METRICS:
- Lines: X → Y (Z% change)
- Complexity: A → B
- Test coverage: C → D
🎯 TRADE-OFFS:
- Gained: [specific benefits]
- Lost: [what was sacrificed, if anything]
- Risk: [potential issues to monitor]
🔧 TECHNIQUE: [specific method used]
```
---
## 🎪 **The Context Game**
When tempted to apply a rule rigidly, ask:
1. **What's the goal?** (performance, maintainability, clarity)
2. **Who's the audience?** (junior devs, domain experts, future you)
3. **What's the risk?** (production failure, maintenance burden, confusion)
4. **What's the timeline?** (prototype, long-term system, migration)
5. **What are the constraints?** (team size, expertise, deadlines)
**Remember: Rules are tools, not laws. Use judgment, not dogma.**
---
## 🎯 **The Ultimate Test**
**Would you rather:**
- Maintain 100 lines of crystal-clear code?
- Maintain 50 lines of clever, terse code?
- Maintain 150 lines of obvious, redundant code?
**The answer depends on your team, timeline, and domain. Choose consciously.**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment