Skip to content

1.1. Standards: Principles from Code Complete

Ulrond edited this page Jul 11, 2024 · 1 revision

Standards: Principles from Code Complete (C/C++)

This coding standard inspired by the principles in Steve McConnell's "Code Complete," aiming for clarity, maintainability, and robustness:

  • "Effective software construction practices are crucial for maintaining code quality."

1. Layout and Formatting

  • Indentation: Use consistent indentation (spaces, not tabs) to clearly show code structure.
  • Line Length: Keep lines to a reasonable length (around 80 characters).
  • Whitespace: Use blank lines to separate logical sections.
  • Braces: Always use braces for blocks, even for single-statement blocks. Place each brace on a new line.
// GOOD
if (x > 0)
{
    std::cout << "x is positive\n";
}
else
{
    std::cout << "x is not positive\n";
}

// BAD (no braces for single-statement block)
if (x > 0)
    std::cout << "x is positive\n";

2. Naming Conventions

Meaningful Names:

int employee_count;    
double total_revenue;   

Pronounceable Names:

// GOOD
bool is_valid_input(int input);

// BAD
bool chk_inp(int in); 

Consistent Style:

Stick to a consistent naming convention, e.g., snake_case for variables and PascalCase for classes.

double interest_rate;
class BankAccount { /* ... */ };

3. Comments and Documentation

Clear Comments:

// Calculate average temperature to assess if heating is needed (why)
float avg_temperature = calculate_average(temperatures, num_readings);

Doxygen Comments (C++):

/**
 * @brief Calculates the area of a circle.
 * @param radius The radius of the circle.
 * @return The area of the circle.
 */
double calculate_circle_area(double radius) 
{
    return M_PI * radius * radius;
}

4. Functions and Modules

Cohesion:

// GOOD (Single Responsibility)
double calculate_tax(double price, double tax_rate) 
{
    // ... implementation
}

// BAD (Multiple Responsibilities)
void process_order(Order* order) 
{
    calculate_price(order);
    apply_discount(order);
    send_email_confirmation(order);
}

Positive-Negative Coding Path (C++):

int divide(int numerator, int denominator) 
{
    // Negative Check (Guard Clause)
    if (denominator == 0) 
    {
        throw std::runtime_error("Error: Division by zero");
    }

    // Positive Flow (Main Logic)
    return numerator / denominator;
}

5. Error Handling (C++):

try 
{
    int result = divide(10, 0); // Potential for division by zero
} 
catch (const std::runtime_error& e) 
{
    std::cerr << "Error: " << e.what() << std::endl; // Clear error message
}

6. Classes and Objects

Encapsulation:

Encapsulation bundles data (attributes) and the operations that manipulate that data (methods) into a single unit, the class. This hides implementation details, making the code easier to understand and maintain.

// Example (C++)
class BankAccount 
{
private: // Private members (encapsulated)
    double balance;

public:
    void deposit(double amount) 
    {
        balance += amount;
    }
    
    double getBalance() const 
    {
        return balance;
    }
};

In this example, the balance attribute is private, so it can only be modified or accessed through the public deposit and getBalance methods. This protects the internal state of the object.

Inheritance:

Inheritance allows you to create new classes (derived classes) that are based on existing classes (base classes). Derived classes inherit the properties and behaviours of the base class, promoting code reuse and establishing hierarchical relationships.

// Example (C++)
class Animal // Base class
{
public:
    virtual void makeSound() const = 0; // Pure virtual function 
};

class Dog : public Animal // Derived class (inherits from Animal)
{
public:
    void makeSound() const override 
    {
        std::cout << "Woof!\n";
    }
};

class Cat : public Animal // Derived class (inherits from Animal)
{
public:
    void makeSound() const override 
    {
        std::cout << "Meow!\n";
    }
};

Polymorphism:

Polymorphism enables you to use objects of different classes through a common interface. This flexibility is achieved through virtual functions (C++) or function pointers (C), allowing the same function call to behave differently based on the object's type.

// Using polymorphism (C++)
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->makeSound(); // Output: Woof!
animal2->makeSound(); // Output: Meow!

7. Testing and Quality Assurance

Unit Tests:

Unit tests are small, isolated tests that verify the correctness of individual units of code (e.g., functions, methods).

// Example (using Google Test framework in C++)
TEST(MathTest, Addition) 
{
    EXPECT_EQ(add(2, 3), 5);
    EXPECT_EQ(add(-1, 1), 0);
}

Code Reviews:

Code reviews involve having other developers examine your code to catch errors, improve code quality, and share knowledge.

Automated Testing:

Tools like linters (e.g., Clang-Tidy) and formatters (e.g., ClangFormat) can help you automatically enforce coding standards and detect potential issues.

8. Collaboration and Communication

Clear Commit Messages (Example):

Added error handling for invalid file input in process_data() function. 

See also Commit Message: The 50/72 Rule: A Simple Guide

Pair Programming:

Involves two developers working on the same code simultaneously, sharing knowledge, catching errors, and improving design.

Documentation:

- Use comments within the code to explain complex logic or important decisions.
- Create external documentation (README files, wikis, etc.) to provide an overview of the project, instructions for use, and API references.

Additional Considerations

  • Language-Specific Conventions: Follow the conventions and best practices specific to the programming language you're using.
  • Performance: Consider performance implications, but prioritize readability and maintainability over premature optimization.
  • Security: Write secure code by following best practices to prevent vulnerabilities (e.g., input validation, sanitization).

References

Clone this wiki locally