Monday, December 9, 2024

Software Anti-patterns

 Anti-patterns are common but counterproductive practices in software design and development that lead to poor performance, maintainability, or scalability. Here are examples of anti-patterns in different areas of software engineering:


1. Spaghetti Code

  • Description: Code with little structure or organization, making it hard to understand, debug, or extend.
  • Example:
    void process() {
        if (condition1) {
            // Some code
            if (condition2) {
                // Nested logic
                while (condition3) {
                    // Deeply nested loops
                }
            }
        }
    }
    
  • Why It's Bad: Hard to maintain and debug due to lack of modularity and excessive nesting.
  • Solution: Refactor into smaller functions or modules, use design patterns like Strategy or Command.

2. God Object

  • Description: A single class that handles too many responsibilities and grows excessively large.
  • Example:
    class GodClass {
        void manageUser() { /* User management */ }
        void manageInventory() { /* Inventory management */ }
        void processPayments() { /* Payment processing */ }
    }
    
  • Why It's Bad: Violates the Single Responsibility Principle (SRP) and becomes hard to maintain or test.
  • Solution: Break the class into smaller, more focused classes with distinct responsibilities.

3. Copy-Paste Programming

  • Description: Duplicating code instead of creating reusable components or functions.
  • Example:
    void calculateSalary() {
        // Repeated logic here
    }
    void calculateBonus() {
        // Same logic as above with slight variations
    }
    
  • Why It's Bad: Duplicates logic, increasing maintenance effort and risk of inconsistencies.
  • Solution: Refactor into reusable functions or use inheritance or composition where applicable.

4. Premature Optimization

  • Description: Spending time optimizing parts of the code without evidence of bottlenecks.
  • Example:
    // Writing complex, unreadable code to save a few microseconds
    int result = (x << 2) + (x << 1); // Instead of result = x * 5;
    
  • Why It's Bad: Leads to unreadable code and wasted effort on non-critical parts.
  • Solution: Optimize only after profiling and identifying bottlenecks.

5. Singleton Overuse

  • Description: Overusing the Singleton pattern to manage global state.
  • Example:
    class GlobalConfig {
        private static GlobalConfig instance;
        private GlobalConfig() {}
        public static GlobalConfig getInstance() {
            if (instance == null) instance = new GlobalConfig();
            return instance;
        }
    }
    
  • Why It's Bad: Encourages global state, making code harder to test and increasing coupling.
  • Solution: Use Dependency Injection or modular configuration instead.

6. Golden Hammer

  • Description: Over-relying on a single technology, tool, or design pattern for all problems.
  • Example: Always using a relational database when a NoSQL database would be more suitable.
  • Why It's Bad: Leads to suboptimal solutions for specific problems.
  • Solution: Understand the problem domain and choose tools or patterns that best fit.

7. Hardcoding

  • Description: Embedding values directly in the code instead of using configuration files or constants.
  • Example:
    String dbHost = "192.168.1.1";
    int dbPort = 3306;
    
  • Why It's Bad: Makes code inflexible and harder to adapt to different environments.
  • Solution: Use configuration files, environment variables, or constants.

8. Magic Numbers

  • Description: Using unexplained numeric or string literals in code.
  • Example:
    if (speed > 42) {
        // Do something
    }
    
  • Why It's Bad: Reduces readability and makes code harder to understand.
  • Solution: Replace magic numbers with named constants.
    const int MaxSpeed = 42;
    if (speed > MaxSpeed) {
        // Do something
    }
    

9. Sequential Coupling

  • Description: Requiring methods or functions to be called in a specific order.
  • Example:
    File f = new File("data.txt");
    f.open();
    f.read();
    f.close();
    
  • Why It's Bad: Easy to misuse the API if the order isn’t followed.
  • Solution: Encapsulate logic within the class to manage ordering.

10. Lava Flow

  • Description: Accumulating outdated, unused, or irrelevant code in the system.
  • Example: Old functions or commented-out code that no one dares to delete.
  • Why It's Bad: Clutters the codebase and increases technical debt.
  • Solution: Regularly clean up and remove unused code through refactoring.

11. Yo-Yo Problem

  • Description: Excessive jumping between different levels of abstraction.
  • Example:
    obj->getService()->getManager()->executeTask();
    
  • Why It's Bad: Reduces readability and makes debugging difficult.
  • Solution: Reduce chaining or create intermediate abstractions.

12. Not Invented Here Syndrome

  • Description: Avoiding external libraries or tools and reimplementing them in-house.
  • Example: Writing a custom logging library instead of using established ones like log4j or Boost.Log.
  • Why It's Bad: Wastes time and effort, and often results in inferior solutions.
  • Solution: Leverage existing libraries and tools when appropriate.

13. Object-Orgy

  • Description: Excessive sharing of mutable global objects among different parts of the code.
  • Example:
    extern Settings settings;
    // Global settings object used everywhere
    
  • Why It's Bad: Increases coupling and makes debugging harder.
  • Solution: Pass objects explicitly and use dependency injection where appropriate.

14. Big Ball of Mud

  • Description: An unstructured and chaotic system without clear modularity or architecture.
  • Why It's Bad: Makes it nearly impossible to maintain or extend.
  • Solution: Refactor incrementally and adopt a well-defined architecture like MVC or microservices.

15. Overengineering

  • Description: Creating overly complex solutions for simple problems.
  • Example:
    • Implementing a complex factory pattern for a simple object instantiation.
  • Why It's Bad: Leads to unnecessary complexity and increased maintenance effort.
  • Solution: Follow the YAGNI (You Aren’t Gonna Need It) principle and keep things simple.

16. Boat Anchor

  • Description: Keeping unused, irrelevant, or deprecated components in a system with the idea that they "might be needed someday."
  • Example: Adding a third-party library to a project but never using it.
  • Why It's Bad: Increases system complexity and maintenance overhead.
  • Solution: Regularly review and clean up unused dependencies or code.

17. Stovepipe System

  • Description: Systems that are built in isolation with little regard for integration or standardization with other systems.
  • Why It's Bad: Leads to redundant functionality, poor maintainability, and data silos.
  • Solution: Design with integration and scalability in mind.

18. Death by Documentation

  • Description: Excessive focus on documentation, to the point where it slows down actual development or becomes outdated.
  • Example: Writing exhaustive diagrams or manuals that no one reads or maintains.
  • Why It's Bad: Wastes time and can mislead developers if the documentation isn’t accurate.
  • Solution: Use concise, up-to-date, and relevant documentation. Focus on living documentation like comments or README files.

19. Vendor Lock-In

  • Description: Over-reliance on a specific vendor's tools, technologies, or platforms, limiting future flexibility.
  • Example: Using proprietary cloud services without portability in mind.
  • Why It's Bad: Makes it expensive and difficult to switch vendors or adapt to new technologies.
  • Solution: Adopt open standards and design for portability.

20. Reinventing the Wheel

  • Description: Reimplementing existing solutions or algorithms instead of reusing established ones.
  • Example: Writing a custom encryption algorithm instead of using a trusted library.
  • Why It's Bad: Increases development time and risk of errors.
  • Solution: Leverage existing frameworks, libraries, or tools when possible.

21. Cargo Cult Programming

  • Description: Blindly copying code or practices without understanding their purpose or context.
  • Example:
    // Copy-pasted without understanding its need:
    int x = someObject.doSomething();
    
  • Why It's Bad: Leads to errors or inefficient solutions.
  • Solution: Always understand the logic and purpose of the code you use.

22. Poltergeist

  • Description: Classes that serve no real purpose other than passing data between other classes.
  • Example:
    class TempHelper {
        void forwardRequest(Service service) {
            service.execute();
        }
    }
    
  • Why It's Bad: Adds unnecessary complexity and indirection.
  • Solution: Remove unnecessary layers or consolidate logic into relevant classes.

23. Design by Committee

  • Description: Overdesigning a system due to too many conflicting inputs from multiple stakeholders.
  • Why It's Bad: Leads to bloated, inconsistent designs that don’t satisfy anyone fully.
  • Solution: Streamline decision-making and prioritize user needs over multiple opinions.

24. Shotgun Surgery

  • Description: A small change requires modifying code in many places across the system.
  • Example: Changing a calculation formula affects dozens of files.
  • Why It's Bad: Indicates poor separation of concerns and high coupling.
  • Solution: Centralize the logic into reusable modules or functions.

25. Dead Code

  • Description: Code that is no longer used or executed but remains in the codebase.
  • Why It's Bad: Increases complexity and makes the codebase harder to navigate.
  • Solution: Identify and remove unused code during refactoring.

26. Mushroom Management

  • Description: Developers are kept in the dark and only brought in at the last minute with minimal information.
  • Why It's Bad: Leads to frustration, poor solutions, and low morale.
  • Solution: Foster open communication and involve the team throughout the project lifecycle.

27. Monolithic Architecture in a Scalable System

  • Description: Using a monolithic architecture for systems that need to scale rapidly.
  • Why It's Bad: Creates bottlenecks and makes scaling or updates challenging.
  • Solution: Use microservices or modular designs for better scalability.

28. Iceberg Class

  • Description: A class that exposes only a small amount of functionality but hides a massive amount of undocumented or unclear complexity.
  • Why It's Bad: Makes debugging and extending the class difficult.
  • Solution: Simplify and document the class properly. Refactor into smaller, focused components.

29. Object Cesspool

  • Description: Sharing objects too freely across the application, leading to unintended dependencies and side effects.
  • Why It's Bad: Creates high coupling and makes debugging hard.
  • Solution: Use immutability or encapsulation to protect shared objects.

30. Overloading Constructors

  • Description: Having multiple constructors with slightly different arguments, leading to confusion.
  • Example:
    public class User {
        public User(String name) {}
        public User(String name, int age) {}
        public User(String name, int age, boolean isActive) {}
    }
    
  • Why It's Bad: Reduces readability and maintainability.
  • Solution: Use a builder pattern or factory methods.

Conclusion

While the examples listed here represent common anti-patterns, many others might arise in specific domains like databases, DevOps, or UX design. Avoiding these anti-patterns requires constant vigilance, regular refactoring, and adopting best practices suited to your project and team.

No comments:

Post a Comment

LeetCode C++ Cheat Sheet June

🎯 Core Patterns & Representative Questions 1. Arrays & Hashing Two Sum – hash map → O(n) Contains Duplicate , Product of A...