Chapter 25: Key Takeaways — Design Patterns and Clean Code

Summary Card

  1. Design patterns are a communication protocol between you and your AI assistant. Using pattern names in prompts (e.g., "implement using the Observer pattern") produces more architecturally sound code than vague instructions about behavior.

  2. Python patterns look different from Java patterns. First-class functions replace Strategy class hierarchies; module-level instances replace Singleton boilerplate; Protocol replaces interface inheritance; dataclasses replace Builder ceremony. Always prefer idiomatic Python over literal GoF translations.

  3. Creational patterns manage object creation. Use Factory (dictionary dispatch) when you need to instantiate different classes based on runtime data. Use Builder when constructing objects with many optional parameters and validation rules. Avoid Singleton in Python—use module-level instances or dependency injection instead.

  4. Structural patterns manage composition. Use Adapter to bridge incompatible interfaces (especially third-party libraries). Use the Decorator pattern (both Python decorators and GoF composition) for cross-cutting concerns. Use Facade to provide a clean API over complex subsystems, particularly useful for wrapping AI-generated internals.

  5. Behavioral patterns manage communication. Use Observer (callback-based event emitters) for one-to-many notifications. Use Strategy (functions or callable objects) for interchangeable algorithms. Use Command (dataclass-based command objects) when you need undo/redo or operation queuing.

  6. Clean code principles are your quality guardrails. Meaningful names reveal intent. Small functions do one thing well. DRY eliminates duplication. KISS prevents unnecessary complexity. The Boy Scout Rule ensures continuous improvement. Apply these principles especially when reviewing AI-generated code.

  7. Refactor toward patterns incrementally. The safest approach: (1) extract branches into functions, (2) create a registry dictionary, (3) replace conditionals with dispatch, (4) add registration for extensibility. Each step is small, testable, and reversible.

  8. Recognize when patterns are overkill. If you have one implementation with no foreseeable need for more, if the pattern adds more code than it saves, or if you are building for imaginary requirements—simplify. Apply the Rule of Three: wait for three concrete instances before abstracting.

  9. AI-specific patterns address recurring AI integration challenges. Prompt Templates separate structure from content. Conversation Managers maintain multi-turn context. Output Parsers extract structured data from unstructured text. Retry with Fallback provides resilience against API failures and rate limits.

  10. Code smells in AI-generated code have distinct characteristics. Watch for over-commenting obvious operations, unnecessary class wrapping (Java-in-Python syndrome), reinventing standard library features, and inconsistent error handling. A systematic review checklist catches these issues efficiently.

  11. The 80/20 rule of AI code review: Approximately 80% of AI-generated code is correct and well-structured. Your skill as a vibe coder is efficiently identifying and fixing the 20% that contains wrong patterns, missed edge cases, or unnecessary complexity.

  12. Patterns compose to create powerful architectures. The plugin system case study demonstrates how Factory (creation), Observer (communication), Protocol (contracts), and Facade (simplification) work together. No single pattern solves complex architectural problems—combinations do.

  13. Use typing.Protocol for interfaces and frozen=True dataclasses for value objects. These two Python features replace a large portion of the boilerplate that patterns require in other languages. They provide type safety, immutability, and structural subtyping with minimal code.

  14. Dictionary dispatch is the Pythonic alternative to switch statements. Map keys to functions or classes in a dictionary, then look up and call. It is more extensible than if-elif chains—new entries can be added without modifying the dispatch logic—and it satisfies the Open/Closed Principle.

  15. The best pattern is the simplest solution that handles your actual requirements. Do not apply patterns prophylactically. Wait for evidence of need, then apply the pattern that most directly addresses the identified problem. Clean code that works is always better than over-patterned code that impresses.