This skill aims to provide a set of general coding standards and best practices to improve code readability, maintainability, and robustness.
1. Exception Handling
- Avoid over-catching: During development, try not to catch exceptions; instead, let them be thrown and expose the problem so that potential bugs can be fixed.
- Catch precisely: Never catch top-level exceptions for convenience, such as
ExceptionorThrowable. Only catch a specific type of exception when you know exactly that a certain block of code will throw that specific exception under a specific edge case, and that exception can be handled reasonably.
2. Comments
- Self-explanatory code: The code itself should be the best documentation.
- Comment functionality: If comments are needed, method-level and class-level comments should be preferred over inline comments inside methods.
3. Functional Programming and Chaining
- Prefer chained calls: For scenarios such as collection operations and stream processing, prefer functional programming and chained calls.
- Example: Get all students whose average score across all classes exceeds 90:
classes.flatMap(class -> class.students) .filter(student -> student.score > 90) .toList()
- Example: Get all students whose average score across all classes exceeds 90:
- Avoid overuse: If a chained call has only one node and cannot be written on one line, do not use chaining; use a native loop instead.
- Example: Iterate over the student list, generate a report for each student, and send an email:
- Counterexample:
students.forEach(student -> { String report = generateReport(student); sendEmail(student.email, report); }); - Correct example:
for (Student student : students) { String report = generateReport(student); sendEmail(student.email, report); }
4. Guard Clauses and Control Flow
- Reduce nesting: Minimize
if-elsedeep nesting and keep the code flat. It is recommended that the nesting level not exceed 3 levels. - Use guard clauses: Prefer "guard clauses" to handle exceptions or edge cases early and return, avoiding wrapping subsequent core logic in an
elseblock.- Example:
if (input == null) { return; } // Core logic here...
- Example:
- Time-driven waiting: When implementing waiting or polling logic, prefer a "timeout + polling interval" mechanism over a simple "loop N times". The time semantics are clearer and easier to adjust and configure.
5. Immutability
- Prefer immutability: Prefer immutable objects and variables.
- Java: Use
final - Kotlin: Use
val - JS/TS: Use
const
- Java: Use
- Limit mutability: Use mutable types only when it is truly necessary to modify a variable's value, such as
varor non-finalvariables. - Pure functions: Avoid side effects as much as possible; a function's output should depend only on its input parameters and should not modify external state.
6. Logging
- Prohibit standard output: In production code, it is strictly forbidden to use
System.out.println、console.logorprintand other standard output statements. - Logging tools: Prioritize reusing the project's existing logging tool wrapper, or use a mature logging framework.
7. Method Design
- Single Responsibility Principle (SRP): A method should do only one thing and do it well. If a method takes on too many responsibilities, it should be split into multiple smaller methods.
- Control length: Try to keep methods short, preferably no more than 50 lines. Long methods are difficult to read and maintain.
- Limit the number of parameters: Try to keep the number of method parameters within 4. If there are too many parameters, consider introducing a Parameter Object or using the Builder pattern.
- Reduce cyclomatic complexity: Avoid overly deep nesting and overly complex logic branches. If the logic is complex, try extracting sub-methods or using the Strategy pattern.
- Eliminate duplication (DRY): Follow the "Don't Repeat Yourself" principle. If the same kind of logic appears in multiple places, it should be extracted into a unified helper method or class to reduce duplicate code and lower maintenance costs.
- Flatten calls: Avoid unnecessary intermediate layers of encapsulation. If an intermediate function is merely a simple pass-through, the target method should be called directly. Keeping the call chain short helps improve code readability and debugging efficiency.
8. Type Safety & Return Values
- Explicitly express "no result": When a method may not return a result, it should be expressed with an explicit type (such as C++
std::optional<T>, JavaOptional<T>, RustOption<T>) to force the caller to handle the empty value case, avoiding implicit null returns that cause null pointer exceptions.
9. Naming Conventions
- Unify behavior naming: For behaviors with the same semantics, a unified API name should be used (such as
Attach,Open), and different input sources should be distinguished through parameter overloading. Maintain interface consistency and reduce cognitive burden.
10. Unit Testing
- Coverage: The unit test coverage of core business logic should reach 80% or above.
- Independence: Unit tests must be independent and not depend on external environments (databases, networks, etc.).
- Naming conventions: Test method names should clearly describe the test scenario and expected result.
- Example:
shouldReturnTrueWhenInputIsValid
- Example:
- Clear assertions: Use an expressive assertion library.
- Counterexample:
assertTrue(result == expected) - Positive example:
assertThat(result).isEqualTo(expected)
- Counterexample:
11. Build & Verification
- Local build: After completing code writing, only when it is recognized that the build system supports a default build directory or shared cache (such as Gradle, Maven, npm), attempt to execute the build to verify the code; for systems that require manually specifying the build directory (such as CMake), automatic compilation should be skipped to avoid wasting resources on repeated builds.
- Compilation check: Ensure the code can compile successfully. It is strictly forbidden to submit code that cannot compile.