Skip to content

Latest commit

 

History

History
238 lines (147 loc) · 11.7 KB

Lily's Program Writing Sets.md

File metadata and controls

238 lines (147 loc) · 11.7 KB

Lily's Program Writing Sets

A comprehensive guide to crafting elegant, maintainable, and efficient code, inspired by the pursuit of software craftsmanship.

Introduction

This document outlines "Lily's Program Writing Sets" — a collection of guidelines, principles, and best practices designed to elevate code quality and promote good programming habits. Whether you're a beginner or an experienced developer, these guidelines can serve as a valuable reference to enhance the clarity, correctness, maintainability, and performance of your code.

Guiding Principles

At the heart of "Lily's Program Writing Sets" lie four fundamental principles:

  1. Clarity: Code should be easy to read, understand, and reason about, even for those who didn't write it. Clear code reduces cognitive load and makes collaboration more effective.
  2. Correctness: Code should function as intended, be free of errors, and handle unexpected situations gracefully. Robust error handling and thorough testing are essential.
  3. Maintainability: Code should be structured in a way that makes it easy to modify, extend, and debug over time. This includes adhering to modular design principles and avoiding unnecessary complexity.
  4. Efficiency: Code should perform well, utilizing resources (CPU, memory, etc.) effectively without premature optimization. Efficiency should be balanced with readability and maintainability.

The Guidelines

These principles manifest in the following specific guidelines, categorized for ease of reference:

I. Data and Variables

  1. Limit Pointer Use:

    • Use pointers judiciously, only when they provide a clear advantage (e.g., performance optimization or direct memory manipulation).
    • Always perform robust error checking to avoid null pointer dereferencing and memory leaks.
    • Consider using smart pointers (e.g., std::unique_ptr in C++) to manage memory automatically.
  2. Declare Variables at the Smallest Scope Possible:

    • Keep variables close to where they are used to minimize side effects and enhance clarity.
    • Avoid global variables unless absolutely necessary, as they can lead to unintended dependencies and make debugging difficult.
  3. Use Strong Typing:

    • Prefer strong typing over weak typing to catch errors at compile time rather than runtime.
    • Use type aliases or typedefs to improve code readability and maintainability.
  4. Initialize Variables Upon Declaration:

    • Always initialize variables when they are declared to avoid undefined behavior.
    • Use default initialization or constructors to ensure variables are in a valid state.

II. Functions

  1. Restrict Function Complexity:

    • Aim for a Cyclomatic Complexity of 15 or less. Use tools like SonarQube or Lizard to measure this objectively.
    • Break down complex functions into smaller, more manageable units. Each function should ideally perform a single task (Single Responsibility Principle).
  2. Favor a Manageable Number of Function Parameters:

    • Minimize the number of parameters passed to a function for better readability and maintainability.
    • Consider using data structures (e.g., structs, classes) or objects to group related inputs.
    • Use default arguments where appropriate to reduce the number of parameters.
  3. Use Assertions Strategically:

    • Employ assertions to validate critical assumptions within your code and catch potential errors early.
    • Place assertions thoughtfully; their number should reflect the complexity and criticality of the code.
    • Avoid using assertions for input validation in production code; use proper error handling instead.
  4. Avoid Side Effects:

    • Functions should have minimal side effects. They should not modify global state or input parameters unless explicitly intended.
    • Prefer pure functions where possible, as they are easier to test and reason about.

III. Control Flow

  1. Use Simple Control Flow Structures:

    • Avoid complex, hard-to-follow structures like goto statements or excessive nesting.
    • Favor clear and straightforward control flow mechanisms (e.g., if, else, for, while).
    • Use early returns or guard clauses to reduce nesting and improve readability.
  2. Prefer Switch Statements Over Multiple If-Else Chains:

    • When dealing with multiple conditions, prefer switch statements over long if-else chains for better readability and performance.
    • Ensure that all cases are handled, including a default case.
  3. Avoid Deep Nesting:

    • Deeply nested code is harder to read and maintain. Aim to keep nesting levels to a minimum.
    • Use inversion of control (e.g., early returns) to flatten nested structures.

IV. Code Organization

  1. Code Structure and Organization:

    • Minimize Nesting:
      • Use Inversion: Invert conditions to reduce nesting depth (e.g., use if (!condition) return;).
      • Merge Related Conditions: Combine if conditions that perform similar checks.
      • Extract Logic: Move nested code into separate functions to improve readability.
    • Promote Reusability and Clarity:
      • Avoid Code Duplication: Extract common code blocks into reusable functions or modules.
      • Use Shared Logic: Centralize common algorithms or calculations to reduce redundancy.
    • Follow the DRY Principle: Don't Repeat Yourself. Reuse code whenever possible to avoid duplication.
  2. Modularize Your Code:

    • Break your code into modules or components that encapsulate related functionality.
    • Use namespaces or packages to organize code logically and avoid naming conflicts.
  3. Use Design Patterns Appropriately:

    • Familiarize yourself with common design patterns (e.g., Singleton, Factory, Observer) and use them where appropriate.
    • Avoid over-engineering; use patterns only when they provide clear benefits.

V. Naming and Conventions

  1. Apply a Clear and Consistent Naming Convention:

    • Universality: Apply the same convention to all code elements (variables, functions, classes, etc.).
    • Language Awareness: Adhere to established conventions of your chosen programming language (e.g., camelCase in Java, snake_case in Python).
    • Clarity is Key: Choose descriptive and meaningful names that clearly convey purpose and functionality.
    • Avoid Abbreviations: Use full words instead of abbreviations unless they are widely understood (e.g., max for maximum).
  2. Use Meaningful Names for Variables and Functions:

    • Variable names should describe their purpose (e.g., userCount instead of cnt).
    • Function names should describe what they do (e.g., calculateTotal() instead of calc()).
  3. Follow Naming Conventions for Constants:

    • Use uppercase with underscores for constants (e.g., MAX_USERS).
    • Clearly distinguish constants from variables to avoid accidental modification.

VI. Tools and Practices

  1. Use a Version Control System:

    • Track changes, collaborate effectively, and safeguard your codebase using a version control system like Git.
    • Commit often with meaningful commit messages that describe the changes made.
  2. Write Meaningful Comments:

    • Explain the "why" behind your code decisions, not just the "what," to improve understanding and maintainability.
    • Avoid redundant comments that simply restate the code.
    • Use comments to document complex algorithms, edge cases, or non-obvious behavior.
  3. Adopt Code Reviews:

    • Regularly conduct code reviews to catch potential issues early and share knowledge among team members.
    • Use tools like GitHub Pull Requests or Gerrit to facilitate the review process.
  4. Write Unit Tests:

    • Write unit tests for your code to ensure correctness and catch regressions.
    • Use testing frameworks (e.g., JUnit, pytest) to automate testing.
    • Aim for high test coverage, but prioritize testing critical and complex parts of the code.
  5. Profile and Optimize Code:

    • Use profiling tools (e.g., Valgrind, gprof) to identify performance bottlenecks.
    • Optimize code only after identifying actual performance issues; avoid premature optimization.
  6. Document Your Code:

    • Provide high-level documentation for your codebase, including architecture diagrams, API documentation, and usage examples.
    • Use tools like Doxygen, Sphinx, or Javadoc to generate documentation automatically.

VII. Error Handling and Resilience

  1. Handle Errors Gracefully:

    • Always check for error conditions and handle them appropriately.
    • Use exceptions for exceptional cases, but avoid using them for control flow.
    • Provide meaningful error messages to help diagnose issues.
  2. Validate Inputs:

    • Validate all inputs to your functions, especially those from external sources (e.g., user input, API responses).
    • Use defensive programming techniques to ensure your code behaves correctly even with invalid inputs.
  3. Logging and Monitoring:

    • Use logging to track the flow of your program and capture important events.
    • Avoid excessive logging; focus on critical information that would be useful for debugging.
    • Integrate monitoring tools to track the health and performance of your application in production.

VIII. Security Considerations

  1. Follow Security Best Practices:

    • Avoid common security vulnerabilities such as SQL injection, cross-site scripting (XSS), and buffer overflows.
    • Use secure libraries and frameworks, and keep them up to date.
    • Sanitize all inputs and outputs to prevent injection attacks.
  2. Use Encryption for Sensitive Data:

    • Encrypt sensitive data both in transit (e.g., using HTTPS) and at rest (e.g., using AES encryption).
    • Avoid hardcoding sensitive information like API keys or passwords in your code.
  3. Implement Access Control:

    • Ensure that only authorized users can access sensitive parts of your application.
    • Use role-based access control (RBAC) to manage permissions effectively.

IX. Performance Optimization

  1. Optimize Algorithms and Data Structures:

    • Choose the right algorithms and data structures for your problem domain.
    • Understand the time and space complexity of your code (e.g., Big-O notation) and optimize accordingly.
  2. Avoid Premature Optimization:

    • Focus on writing clear and maintainable code first, then optimize only when necessary.
    • Use profiling tools to identify actual performance bottlenecks before optimizing.
  3. Leverage Caching:

    • Use caching to reduce redundant computations or database queries.
    • Be mindful of cache invalidation and ensure that cached data remains consistent.

X. Continuous Learning and Improvement

  1. Stay Updated with Industry Trends:

    • Keep up with the latest developments in programming languages, frameworks, and tools.
    • Follow blogs, attend conferences, and participate in online communities to stay informed.
  2. Refactor Regularly:

    • Continuously refactor your code to improve its design, readability, and maintainability.
    • Use refactoring tools (e.g., ReSharper, Eclipse) to automate repetitive tasks.
  3. Seek Feedback:

    • Regularly seek feedback from peers and mentors to improve your coding skills.
    • Participate in code reviews and pair programming sessions to learn from others.

Conclusion

"Lily's Program Writing Sets" is a living document that evolves with the ever-changing landscape of software development. By adhering to these guidelines, you can write code that is not only functional but also elegant, maintainable, and efficient. Remember, the goal is not just to write code that works, but to write code that stands the test of time and fosters collaboration among developers.

Happy coding!