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.
留言