This document outlines the standards and best practices for documenting code across all Bayat projects. Following these guidelines ensures that code is well-documented, maintainable, and understandable by all team members.
- Documentation Principles
- Inline Documentation
- Language-Specific Standards
- Class and Module Documentation
- Code Examples
- Documentation Tools
- Documentation Tests
- Documentation Review Process
- Versioning and Maintenance
All code documentation at Bayat should adhere to the following core principles:
- Clarity: Use clear, concise language that is easily understood by all team members
- Completeness: Cover all required aspects without unnecessary verbosity
- Consistency: Use consistent terminology, format, and style throughout
- Accuracy: Ensure documentation accurately reflects the current code behavior
- Necessity: Document what is needed for usage, not implementation details unless they are critical
- Proximity: Keep documentation as close to the code as possible
- Maintainability: Design documentation to be easily updated alongside code changes
Use inline comments to explain complex logic, important decisions, or non-obvious code.
Do:
- Comment on "why" rather than "what" when the code itself is clear
- Add comments for complex algorithms or business logic
- Document workarounds, temporary solutions, or known limitations
- Use TODO comments for future enhancements (with ticket numbers)
Don't:
- Add comments that repeat what the code obviously does
- Leave outdated or incorrect comments
- Add commented-out code into production codebase
Examples:
// Good comment
// Cache result to avoid expensive recalculation during animation loop
const cachedValue = expensiveCalculation(input);
// Bad comment - obvious from code
// Set value to 5
const value = 5;
// Good TODO with ticket reference
// TODO(BAY-1234): Implement pagination for this endpoint
Every public function, method, class, and module should have a docstring or header comment that explains:
- Purpose: What the function/class does
- Parameters: Description of each parameter and their requirements
- Return Value: What the function returns (if applicable)
- Exceptions/Errors: What errors can be raised or returned
- Examples: Usage examples where appropriate
Use JSDoc for documenting JavaScript and TypeScript code:
/**
* Calculates the total price including tax
*
* @param {Object[]} items - The items to calculate total for
* @param {number} items[].price - The price of each item
* @param {number} [items[].quantity=1] - The quantity of each item
* @param {number} taxRate - The tax rate as a decimal (e.g., 0.1 for 10%)
* @returns {number} The total price including tax
* @throws {Error} If any item has an invalid price
*
* @example
* // Returns 33
* calculateTotal([{price: 10}, {price: 20}], 0.1);
*/
function calculateTotal(items, taxRate) {
// Implementation
}
For TypeScript, leverage type definitions along with JSDoc:
/**
* User profile information
*/
interface UserProfile {
/** Unique identifier */
id: string;
/** User's full name */
name: string;
/** User's email address */
email: string;
/** User's profile picture URL */
pictureUrl?: string;
}
/**
* Fetches a user's profile from the API
*
* @param userId - The ID of the user to fetch
* @returns The user's profile data
* @throws {ApiError} If the user cannot be found or the request fails
*/
async function fetchUserProfile(userId: string): Promise<UserProfile> {
// Implementation
}
Use Google-style docstrings for Python code:
def process_user_data(user_id: int, data: dict) -> tuple[bool, Optional[str]]:
"""Process user data and update the user profile.
This function validates the provided data, updates the user profile
in the database, and triggers any necessary side effects.
Args:
user_id: The ID of the user to update
data: Dictionary containing user data fields to update
Supported fields: name, email, preferences
Returns:
A tuple where:
- The first element is a boolean indicating success
- The second element is an error message if successful is False,
otherwise None
Raises:
ValueError: If the data contains invalid values
UserNotFoundError: If the user_id does not exist
Example:
>>> success, error = process_user_data(123, {"name": "John Doe"})
>>> if success:
... print("User updated successfully")
... else:
... print(f"Failed to update user: {error}")
"""
# Implementation
Use XML documentation comments for C# code:
/// <summary>
/// Processes a payment transaction.
/// </summary>
/// <remarks>
/// This method handles the full payment flow including authorization,
/// capture, and receipt generation.
/// </remarks>
/// <param name="amount">The payment amount in cents.</param>
/// <param name="cardToken">The tokenized payment card.</param>
/// <param name="customerId">The ID of the customer making the payment.</param>
/// <returns>
/// A <see cref="PaymentResult"/> containing the transaction details and status.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="amount"/> is less than or equal to zero.
/// </exception>
/// <exception cref="PaymentDeclinedException">
/// Thrown when the payment is declined by the payment processor.
/// </exception>
public async Task<PaymentResult> ProcessPaymentAsync(
long amount,
string cardToken,
string customerId)
{
// Implementation
}
Use Doxygen-style comments for C++ code:
/**
* @brief Calculates the distance between two points
*
* @param p1 The first point
* @param p2 The second point
* @return double The Euclidean distance between p1 and p2
*
* @throws std::invalid_argument If either point has invalid coordinates
*
* @note This function uses double precision for all calculations
*
* @see Point2D for the point structure definition
*/
double calculateDistance(const Point2D& p1, const Point2D& p2)
{
// Implementation
}
Use Javadoc for Java code:
/**
* Processes an order and returns the order confirmation.
*
* <p>This method handles the entire order processing workflow, including
* inventory checks, payment processing, and notification dispatch.
*
* @param order The order to process, must not be null
* @param paymentMethod The payment method to use for this order
* @param options Additional options for order processing (can be null)
* @return An OrderConfirmation object with the details of the processed order
* @throws InsufficientInventoryException if any item in the order is out of stock
* @throws PaymentFailedException if payment processing fails
*
* @see OrderConfirmation
* @since 2.1.0
*/
public OrderConfirmation processOrder(
Order order,
PaymentMethod paymentMethod,
OrderOptions options) {
// Implementation
}
Each class or module should be documented with:
- Purpose: What the class/module is responsible for
- Usage: How to use the class/module (with examples)
- Dependencies: Any external dependencies
- Lifecycle: Any initialization or cleanup requirements
- Thread Safety: Thread safety guarantees (if applicable)
Example (Python):
"""User management module.
This module provides functionality for managing user accounts,
including creation, authentication, and profile management.
Usage:
from users import UserManager
# Create a user manager
user_manager = UserManager(db_connection)
# Create a new user
user_id = user_manager.create_user("[email protected]", "password123")
# Authenticate a user
user = user_manager.authenticate("[email protected]", "password123")
Dependencies:
- Database connection (postgres or mysql)
- Email service for sending verification emails
Thread Safety:
This module is thread-safe and can be used from multiple threads.
"""
Include code examples where appropriate:
- Provide examples for complex or non-obvious usage patterns
- Include examples for main use cases of public APIs
- Show both success and error handling cases
- Keep examples concise but complete
- Ensure examples are correct and actually work
- Update examples when the API changes
Example (JavaScript):
/**
* User authentication service
*
* @example
* // Basic login
* const authService = new AuthService();
* try {
* const user = await authService.login('user@example.com', 'password');
* console.log(`Logged in as ${user.name}`);
* } catch (error) {
* console.error('Login failed:', error.message);
* }
*
* @example
* // Login with two-factor authentication
* const authService = new AuthService();
* try {
* // First step: request verification code
* await authService.requestTwoFactorCode('user@example.com', 'password');
*
* // Second step: verify code
* const user = await authService.verifyTwoFactorCode('123456');
* console.log(`Logged in as ${user.name}`);
* } catch (error) {
* console.error('Two-factor authentication failed:', error.message);
* }
*/
class AuthService {
// Implementation
}
Use the following documentation generation tools based on the language:
- JavaScript/TypeScript: JSDoc, TypeDoc
- Python: Sphinx, pydoc
- C#: DocFX, Sandcastle
- C++: Doxygen
- Java: Javadoc
For all projects, ensure that:
- Documentation building is part of the CI/CD pipeline
- Generated documentation is published to a central location
- Documentation coverage is monitored
- Links to the documentation are included in the README
Test your documentation to ensure it remains accurate:
- Doctest: For languages that support it (Python, Rust), use doctest to verify examples
- Compilation Tests: Ensure code examples compile and execute correctly
- Link Validation: Verify that all links in documentation are valid
- Example Testing: Run examples in CI to ensure they work
Example Python doctest:
def add(a, b):
"""Add two numbers and return the result.
Args:
a: First number
b: Second number
Returns:
The sum of a and b
Examples:
>>> add(1, 2)
3
>>> add(-1, 1)
0
"""
return a + b
Documentation should be reviewed along with code:
- Accuracy: Does the documentation accurately describe the code?
- Completeness: Are all public APIs documented?
- Clarity: Is the documentation clear and understandable?
- Examples: Are examples correct and helpful?
- Grammar and Style: Does the documentation follow style guidelines?
Reviewers should treat documentation deficiencies as seriously as code issues.
Keep documentation in sync with code:
- Version Specific: Ensure documentation is specific to the version it describes
- Update with Code: Update documentation whenever the related code changes
- Deprecation Notices: Clearly mark deprecated features in documentation
- Change Log: Maintain a change log of API changes
- Documentation Review: Review documentation as part of regular maintenance
For API documentation, include:
- When features were introduced
- Compatibility notes
- Migration guides for breaking changes
Example deprecation notice:
/**
* Processes a payment
*
* @param {Object} paymentInfo Payment information
* @returns {Promise<PaymentResult>} The payment result
*
* @deprecated Since version 2.3.0. Use processPaymentV2() instead.
* This method will be removed in version 3.0.0.
*
* @example
* // Old way (deprecated)
* const result = await processPayment({ amount: 1000 });
*
* // New way
* const result = await processPaymentV2({ amount: 1000, currency: 'USD' });
*/
Version | Date | Description |
---|---|---|
1.0 | 2025-03-20 | Initial version |