Code Standards

12003

Importance of Code Standards

Code standards are a set of established coding rules and guidelines, which are particularly important for junior software engineers. Code standards help team members write consistent code, improving readability, maintainability, and scalability. Standardized code can reduce errors, lower the difficulty of debugging and maintenance, and facilitate team collaboration, making it easier for different individuals' code to be understood and integrated.

To keep the code clean and tidy, naming functions, variables, and modules is crucial; names should be meaningful, readable, and accurate.

How to Improve Code Readability

  • Good Naming Conventions: Names for variables, functions, classes, etc., should be descriptive and adhere to the team's or project's naming conventions. Names should be clear, concise, and meaningful, allowing others to easily understand their meaning and purpose.
  • Clear Code Structure: Code should have a good structure, including proper indentation, appropriate spacing, and clear code blocks and logical flows. A well-structured codebase can make the code clearer, easier to understand, and maintain.
  • Comments and Documentation: Code should include sufficient comments and documentation so that others can understand the design, functionality, and usage of the code. Comments should be clear, concise, and consistent with the code, helping others understand the logic and implementation.
  • Avoid Overly Complex Logic: Complex logical structures can reduce code readability, so overly complex conditional statements, nesting, and loops should be avoided. Consider using functions, classes, etc., to extract and encapsulate complex logic, making the code more concise and easier to understand.
  • Consistent Code Style: Teams or projects should adhere to a consistent code style and standards, including indentation, naming, comments, etc. A consistent code style can improve readability and reduce misunderstandings among team members.

Rules

Variables

  • Variable Names:
    • The most critical aspect of variable names is readability.
    • Use process variables (complex judgments, regex logic, etc.)
    • Use full names and readable abbreviations.

Functions

  • Function Names:

    • (Adverb) + Verb + Noun
    • Voice (avoid tense) and adjectives.
    • CRUD operations should follow database keywords, with the only difference being replacing "select" with "get."
  • Functions:

    • A function should do one thing.
    • Encourage the use of "ctx & middleware" patterns.
    • Encourage functional programming for data operations (array functions, lodash).
    • Use Async/Await.
    • Use objects as function parameters.
    • Use Object.assign to set default objects.
    • Use ternary expressions to avoid directly returning variables from logical expressions.

Modules

  • Modules:
    • Prefer composition over inheritance.
    • Group related data and functions together => Object => Encapsulation, Inheritance, Polymorphism.
    • Use ES6 classes.
    • Do one thing at a time.
    • Easy to extend, hard to modify.
      • Private properties, once encapsulated, will not get mixed up when used together.
      • Provide default constructors.
      • Execute additional operations on object properties, using getters and setters.
      • Serialization, converting between objects and files.
    • Subclass objects should be able to replace their superclass objects when used.
    • Inversion of Control: Hand over control of objects to the IoC container.
      • Creating a car every time you need one is costly.
      • Dependency injection, annotations, adding some code.
    • Interface Segregation.
      • Resource-related settings.

Examples

For instance, when handling large amounts of data, you can choose to use Set or Map data structures instead of arrays to improve lookup and deletion efficiency. Below is an example code demonstrating the optimization of array operations using Set:

// Unoptimized array operation
function arrayOperation(arr, target) {
    if (arr.includes(target)) {
        arr.splice(arr.indexOf(target), 1);
    }
    return arr;
}

// Optimized operation using Set
function setOperation(arr, target) {
    const set = new Set(arr);
    set.delete(target);
    return Array.from(set);
}
  1. Reduce Unnecessary Computation and Memory Usage

    For example, when handling large amounts of data, you can use asynchronous operations and streams to reduce memory usage and improve performance. Below is an example code demonstrating the optimization of handling large files using streams:

    // Unoptimized one-time read of a large file
    const fs = require('fs');
    
    function readLargeFile(file) {
        const data = fs.readFileSync(file, 'utf8');
        // Process data
        return processedData;
    }
    
    // Optimized using streams
    const fs = require('fs');
    const readline = require('readline');
    
    function readLargeFileWithStream(file) {
        const rl = readline.createInterface({
            input: fs.createReadStream(file),
            output: process.stdout,
            terminal: false
        });
    
        rl.on('line', (line) => {
            // Process each line of data
        });
    
        rl.on('close', () => {
            // Callback after processing all data
        });
    }
  2. Simplify Complex Logic

    For example, when dealing with deeply nested callback functions, consider using Promises or async/await to simplify the logic and improve code readability. Below is an example code demonstrating the optimization of complex logic using async/await:

    // Unoptimized complex callback function
    function complexCallback(cb1) {
        asyncFunc1((result1) => {
            asyncFunc2(result1, (result2) => {
                asyncFunc3(result2, (result3) => {
                    // Process result3
                    cb1();
                });
            });
        });
    }
    
    // Optimized using async/await
    async function simplifiedAsyncFunc() {
        const result1 = await asyncFunc1();
        const result2 = await asyncFunc2(result1);
        const result3 = await asyncFunc3(result2);
        // Process result3
    }

These are just simple examples of optimizing Node.js code; the actual methods and approaches for optimization will vary based on specific projects and requirements. When optimizing code, one should analyze and choose based on the actual situation to ensure that the optimized code improves performance, memory usage, and readability.