AI Skill Report Card
Applying Software Principles
Quick Start
Python# Before: Violates multiple principles class UserManager: def create_user(self, name, email): # Validation (should be separate) if not email or "@" not in email: print("Invalid email") # Breaks CQS return None # Database logic (should be separate) conn = sqlite3.connect("users.db") cursor = conn.cursor() cursor.execute("INSERT INTO users VALUES (?, ?)", (name, email)) conn.commit() conn.close() # Email logic (should be separate) smtp = smtplib.SMTP('localhost') smtp.send_message(f"Welcome {name}") smtp.quit() return True # After: Follows principles class UserValidator: def validate(self, email: str) -> bool: return email and "@" in email class UserRepository: def save(self, user: User) -> None: # Database implementation class EmailService: def send_welcome(self, user: User) -> None: # Email implementation class UserService: def __init__(self, validator: UserValidator, repo: UserRepository, email: EmailService): self._validator = validator self._repo = repo self._email = email def create_user(self, name: str, email: str) -> User: if not self._validator.validate(email): raise ValueError("Invalid email") user = User(name, email) self._repo.save(user) self._email.send_welcome(user) return user
Workflow
Progress:
- Identify Violations: Scan for principle violations (long methods, mixed concerns, tight coupling)
- Extract Responsibilities: Apply SRP - one reason to change per class
- Define Interfaces: Use DIP - depend on abstractions, not concretions
- Minimize Coupling: Apply Law of Demeter - avoid chaining calls
- Separate Commands/Queries: CQS - methods either change state OR return data
- Validate Substitution: Ensure LSP - derived classes work transparently
- Refactor Incrementally: Boy Scout Rule - leave code cleaner
Examples
Example 1: DRY Violation Input:
Pythondef calculate_tax_individual(income): return income * 0.2 if income > 50000 else income * 0.1 def calculate_tax_business(revenue): return revenue * 0.2 if revenue > 50000 else revenue * 0.1
Output:
Pythondef calculate_tax(amount: float, threshold: float = 50000, high_rate: float = 0.2, low_rate: float = 0.1) -> float: return amount * high_rate if amount > threshold else amount * low_rate def calculate_tax_individual(income: float) -> float: return calculate_tax(income) def calculate_tax_business(revenue: float) -> float: return calculate_tax(revenue)
Example 2: SOLID Violation Input:
Pythonclass Report: def generate(self): data = self.fetch_data() # SRP violation formatted = self.format_data(data) # SRP violation self.save_to_file(formatted) # SRP violation self.email_report(formatted) # SRP violation
Output:
Pythonclass DataFetcher: def fetch(self) -> List[Dict]: pass class ReportFormatter: def format(self, data: List[Dict]) -> str: pass class FileStorage: def save(self, content: str, path: str) -> None: pass class EmailService: def send(self, content: str, recipient: str) -> None: pass class ReportGenerator: def __init__(self, fetcher: DataFetcher, formatter: ReportFormatter, storage: FileStorage, email: EmailService): self._fetcher = fetcher self._formatter = formatter self._storage = storage self._email = email def generate(self, save_path: str, email_to: str) -> None: data = self._fetcher.fetch() formatted = self._formatter.format(data) self._storage.save(formatted, save_path) self._email.send(formatted, email_to)
Best Practices
Design:
- Start with interfaces/protocols before implementations
- Favor composition over inheritance
- Use dependency injection for testability
- Keep public APIs minimal and stable
Implementation:
- Methods should be 5-20 lines max
- Classes should fit on one screen
- Avoid deep nesting (max 3 levels)
- Use meaningful names that explain intent
Refactoring:
- Extract methods when you see duplication
- Extract classes when methods don't belong together
- Introduce interfaces when you see concrete dependencies
- Use factory patterns for complex object creation
Common Pitfalls
Over-engineering:
- Don't create abstractions until you need them (YAGNI)
- Avoid "Enterprise FizzBuzz" - excessive patterns for simple problems
Coupling Issues:
- Avoid God objects that know too much
- Don't chain method calls (
obj.getA().getB().getC()) - Resist the urge to make everything public
Premature Optimization:
- Profile before optimizing
- Optimize algorithms, not micro-optimizations
- Remember: "Make it work, make it right, make it fast"
Testing Anti-patterns:
- Don't test implementation details
- Avoid mocking what you don't own
- Test behavior, not state