Welcome to the Clean Code Summary repository! This guide is a distilled version of Robert C. Martin's seminal book, "Clean Code: A Handbook of Agile Software Craftsmanship." Whether you're a seasoned developer or just starting your coding journey, this summary will help you understand the principles of writing clean, maintainable, and efficient code. 💻🚀
- Business Impact: Bad code can lead to project delays, increased costs, and even company failure. 🚨
- Total Cost of Ownership: Messy code slows down development, making it harder to add new features or fix bugs. This can lead to the costly decision of a complete system rewrite. 💸
- Professional Responsibility: Developers should prioritize writing clean code, even when pressured by deadlines or management. Clean code is essential for long-term project success. 🛠️
"Clean code is not just about making the code work; it's about making the code easy to understand, maintain, and extend." ✨
- Art vs. Science: Writing clean code is like an art form, requiring both technical skill and a "code-sense" for cleanliness. 🎨
- Key Principles:
- Readability: Code should be easy to read and understand. 📖
- Simplicity: Strive for simplicity by avoiding unnecessary complexity. 🧩
- Expressiveness: Code should clearly express the intent of the programmer. 💡
- Single Responsibility: Functions and classes should have a single responsibility. 🎯
"Clean code is like a well-crafted painting; it should be beautiful, functional, and timeless." 🖼️
- Intention-Revealing: Names should clearly convey the purpose, functionality, and usage of variables, functions, and classes.
- Bad:
int d;
- Good:
int daysSinceCreation;
✅
- Bad:
- Avoid Disinformation: Avoid names that can be misleading or ambiguous.
- Meaningful Distinctions: Differentiate names clearly to avoid confusion.
- Pronounceable and Searchable: Use names that are easy to pronounce and search for in the codebase.
- Avoid Encodings and Prefixes: Modern languages and IDEs render these unnecessary.
- Use Solution and Problem Domain Names: Leverage computer science terms for technical concepts and problem domain terms for business concepts.
- Add Meaningful Context: Provide context when necessary, but avoid unnecessary verbosity.
- Small and Focused: Functions should be concise, ideally no longer than 20 lines.
- Bad: A function that spans multiple pages. 📄
- Good: A function that fits on a single screen. 👀
- Do One Thing: Each function should perform a single task and do it well.
- Descriptive Names: Use clear and descriptive names that convey the function's purpose.
- Avoid Flag Arguments: Flag arguments (e.g., booleans) can indicate that a function is doing more than one thing.
- Command-Query Separation: Functions should either perform an action or return a value, but not both.
- Prefer Exceptions to Error Codes: Use exceptions to handle errors, and keep try-catch blocks separate from business logic.
- Avoid Side Effects: Functions should not produce unexpected side effects that can lead to bugs.
- Use Sparingly: Comments should not be used to compensate for poorly written code.
- Good Comments:
- Legal comments (e.g., copyright) 📜
- Informative comments that clarify complex code 🧐
- Warnings of consequences
⚠️ - TODO comments ✅
- Amplification of importance 🔍
- Javadocs for public APIs 📚
- Bad Comments:
- Redundant or obvious comments 🙄
- Misleading or incorrect comments ❌
- Mandated comments (e.g., every function must have a comment) 🚫
- Journaling comments (use version control instead) 🗄️
- Noise comments that add no value 🔊
- Commented-out code ❌
- Vertical Formatting: Keep file sizes consistent, use line breaks to separate concepts, and maintain appropriate spacing between related code.
- Horizontal Formatting: Use spaces around operators, align code for readability, and use indentation to indicate hierarchy.
- Team Rules: Agree on formatting conventions as a team to ensure consistency.
- Data Abstraction: Use abstractions to hide implementation details and expose only the necessary data and functionality.
- Law of Demeter: Modules should only interact with their immediate collaborators, avoiding tight coupling.
- Objects vs. Data Structures: Objects encapsulate data and expose behavior, while data structures expose data and have minimal behavior.
- Use Exceptions: Leverage exception handling mechanisms to manage errors gracefully.
- Provide Context: Include sufficient information to identify the source and cause of errors.
- Define Exception Classes Based on Caller Needs: Design exceptions to be easily caught and handled by the calling code.
- Avoid Returning Null: Consider using exceptions or special case objects instead of returning null to prevent NullPointerExceptions.
- Wrapper Code: Use wrappers or adapters to interact with third-party code, providing a clean interface and isolating dependencies.
- Exploratory Testing: Write tests for third-party code to understand its behavior and ensure compatibility.
- Clean Boundaries: Maintain clear separation between your code and external dependencies to facilitate future changes.
- Three Laws of TDD:
- Write a failing test before writing production code. ✅
- Write only enough test code to fail.
- Write only enough production code to pass the failing test.
- Clean Tests: Tests should be readable, with a focus on clarity, simplicity, and expressiveness.
- Characteristics of Good Tests:
- Fast: Tests should run quickly to encourage frequent execution. 🏃♂️
- Independent: Tests should not depend on each other.
- Repeatable: Tests should produce the same results every time they are run.
- Self-Validating: Tests should have a boolean output (pass/fail).
- Timely: Tests should be written in a timely manner, ideally before the production code.
- Small and Focused: Classes should be concise, adhering to the Single Responsibility Principle.
- Encapsulation: Keep variables and utility functions private, exposing only necessary interfaces.
- Open-Closed Principle: Design classes to be open for extension but closed for modification.
- Isolate from Change: Use abstract classes and interfaces to decouple implementation details from the rest of the system.
- Separation of Concerns: Break down the system into distinct modules or components, each with a specific responsibility.
- Dependency Injection: Use dependency injection to manage dependencies and promote modularity.
- Test-Driven Architecture: Apply TDD principles to system architecture to ensure testability and flexibility.
- Domain-Specific Languages (DSLs): Use DSLs to write code that is more aligned with the problem domain.
- Challenges: Concurrency introduces complexity and potential for bugs, such as race conditions and deadlocks.
- Defense Principles:
- Single Responsibility: Keep concurrent code separate from other code.
- Limit Data Scope: Minimize the amount of shared data.
- Use Copies of Data: Use copies of data when possible to reduce contention.
- Thread Independence: Design threads to be as independent as possible.
- Know Your Library and Execution Model: Understand the thread-safety of libraries and the concurrency model of the platform.
- Beware of Synchronized Method Dependencies: Use techniques like client-based locking, server-based locking, or adapted server to manage dependencies between synchronized methods.
- Keep Synchronized Sections Small: Reduce the scope of synchronized blocks to minimize contention.
- Write Correct Shutdown Code: Ensure that threads terminate gracefully.
- Test for Threading Issues: Write tests that can expose concurrency bugs.
- Instrument Code: Use tools to detect and diagnose concurrency issues.
The Clean Code Summary emphasizes the importance of writing clean, maintainable, and efficient code. By applying the principles and practices outlined in this guide, you can improve the quality of your code and contribute to the long-term success of your projects. Remember, writing clean code is not just a technical skill; it's a mindset and a commitment to excellence. 💪
Happy Coding! 👩💻👨💻
-
add examples for every section in examples folder
-
add content in other languages, every language in separate folder