Code Maintenance & Programming Rules

 This guide outlines essential best practices spanning code style, architectural design, debugging, testing, performance, and portability—all aimed at reducing the long-term cognitive load of code maintenance.

🎨 1. Style

Code is written for humans to read, and only incidentally for computers to execute.

  • Variable Naming: Use descriptive names for global variables, and short names for local variables.

  • Precision and Consistency: Use active names for functions (e.g., calculateTotal). Above all, keep your coding style consistent throughout the project.

  • Structure & Expressions:

    • Use a consistent indentation and brace ({}) style to show program structure visually.

    • Use the natural form for expressions.

    • Use parentheses to make the semantics unambiguous. Break up overly complex expressions to keep them clear.

  • Side Effects & Macros: Beware of functions with side effects. Avoid function-like macros; if unavoidable, parenthesize the macro body and arguments carefully.

  • Magic Numbers: Give names to magic numbers by defining constants. Use character constants rather than integer constants, and use the language to calculate the size of an object (e.g., sizeof).

  • Smart Comments:

    • Comment functions and global data, but never let comments contradict the code.

    • Don’t waste effort on commenting the obvious.

    • Don’t comment bad code—rewrite it. Keep it clear and do not manufacture confusion.

🔌 2. Interface

A well-designed interface hides complexity and reduces component coupling.

  • Encapsulation: Hide implementation details. Choose a small, cohesive set of primitive operations that are just sufficient.

  • Transparency & Consistency: Never do things behind the user's back that they cannot perceive. Do the same thing the same way everywhere.

  • Resource & Error Handling:

    • Symmetry Principle: Free resources at the same level of abstraction and scope where they were allocated.

    • Layered Defense: Detect errors at a low level, but handle them at a high level.

    • Exceptions: Use exceptions only for truly exceptional circumstances.

🔍 3. Debugging

Debugging should be approached as a systematic science, not a guessing game.

  • Localization & Reproduction: Make bugs reproducible. Display output (such as log files) to localize your search.

  • Proven Strategies: Divide and conquer to isolate errors. Look for patterns you recognize, and study the numerology of failures.

  • Good Habits:

    • Debug immediately; do not put it off until later. Never make the same mistake twice.

    • Examine your most recent changes and obtain a stack trace.

    • Read the code carefully before you start changing it.

  • External Leverage: Explain your code to someone else (Rubber Duck Debugging). Write self-checking code, maintain a log file, draw a diagram, leverage modern debugging tools, and keep records of past bugs.

🧪 4. Testing

Testing provides the ultimate safety net for code refactoring and maintenance.

  • Boundaries & Conditions: Test code at its boundaries (edge cases). Always test pre-conditions and post-conditions.

  • Defensive Programming: Program defensively. Use assertions and rigorously check returned error codes.

  • Testing Strategies:

    • Test incrementally and progressively. Always test simple parts first.

    • Know exactly what output you expect before running a test case.

  • Automation & Verification: Verify conserved properties. Compare independent implementations to ensure correctness. Measure test coverage, automate regression testing, and build independent tests.

⚡ 5. Performance

Premature optimization is the root of all evil. Get it right first, then make it fast.

  • Scientific Measurement: Automate timing measurements and use profiling tools to focus your attention exclusively on the actual bottlenecks.

  • High-Level Optimization: Draw a diagram. Often, choosing a better algorithm or data structure yields massive gains compared to micro-tuning code. Turn on compiler optimization features.

  • Code Tuning (Details):

    • Do not optimize parts of the code that do not matter.

    • Eliminate or collect common subexpressions. Replace expensive operations with cheaper ones (e.g., bit shifting).

    • Unroll loops or eliminate them entirely.

  • Caching & Buffering: Cache frequently used values. Buffer input and output operations (Buffered I/O). Write a special-purpose memory allocator if needed.

  • Computation Tweaks: Handle special cases separately. Precompute results (Lookup Tables), use approximations, or rewrite critical paths in a lower-level language.

  • Space Optimization: Use the smallest possible data types to save memory space. Do not store things that you can easily recompute.

🌍 6. Portability

Ensure code runs consistently across different environments, architectures, and time.

  • Adhere to Standards: Follow international standards. Program in the mainstream and test your code with multiple compilers.

  • Libraries & Dependencies: Use standard libraries and rely only on features that are universally available across platforms.

  • Isolate System Dependencies: Avoid conditional compilation wherever possible. Separate system-dependent code into distinct files and hide them behind clean interfaces.

  • Data Exchange: Use text for data exchange. If binary data exchange is required, enforce a fixed byte order (endianness).

  • Compatibility & Localization:

    • If you change a specification, change its name. Maintain backward compatibility with existing software and data formats.

    • Never assume ASCII is universal, and never assume English is the default perspective for localization.

留言

這個網誌中的熱門文章