Contributing to ApexStore

First off, thank you for considering contributing to ApexStore! ๐ŸŽ‰

This document provides guidelines and instructions for contributing to the project. Following these guidelines helps maintain code quality and makes the review process smoother.

๐Ÿ“œ Table of Contents


๐Ÿค Code of Conduct

Our Pledge

We are committed to providing a welcoming and inspiring community for all. We pledge to:

  • Be respectful and inclusive
  • Accept constructive criticism gracefully
  • Focus on what is best for the community
  • Show empathy towards other community members

Expected Behavior

  • Use welcoming and inclusive language
  • Be respectful of differing viewpoints and experiences
  • Gracefully accept constructive criticism
  • Focus on what is best for the community

Unacceptable Behavior

  • Trolling, insulting/derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information without explicit permission
  • Other conduct which could reasonably be considered inappropriate

๐Ÿš€ Getting Started

Prerequisites

  1. Rust Toolchain (1.70 or later)

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    
  2. Git

    # Ubuntu/Debian
    sudo apt-get install git
    
    # macOS
    brew install git
    
  3. Code Editor (Recommended: VS Code with rust-analyzer)

Initial Setup

  1. Fork the Repository

  2. Clone Your Fork

    git clone https://github.com/YOUR_USERNAME/ApexStore.git
    cd ApexStore
    
  3. Add Upstream Remote

    git remote add upstream https://github.com/ElioNeto/ApexStore.git
    
  4. Install Dependencies

    cargo build
    
  5. Run Tests

    cargo test
    

For detailed setup instructions, see SETUP.md.


๐Ÿ”„ Development Workflow

Automated Workflow Overview

ApexStore uses GitHub Actions to automate the development workflow:

  • Feature/Fix branches โ†’ Auto-create PR to develop + run tests
  • Develop โ†’ Auto-create release PR to main
  • Main โ†’ Create release tag + close related issues

See WORKFLOWS.md for complete documentation.

1. Create a Feature Branch

# Update your fork
git checkout main
git pull upstream main

# Create a new branch from main
git checkout -b feature/your-feature-name

Branch Naming Conventions:

  • feature/ - New features (e.g., feature/compaction-strategy)
  • fix/ - Bug fixes (e.g., fix/wal-corruption)
  • docs/ - Documentation changes (e.g., docs/api-guide)
  • refactor/ - Code refactoring (e.g., refactor/codec-interface)
  • test/ - Test additions/improvements (e.g., test/integration-suite)
  • perf/ - Performance improvements (e.g., perf/bloom-filter-optimization)

2. Make Your Changes

# Make changes to the code
vim src/core/engine.rs

# Test your changes
cargo test --all-features

# Format code
cargo fmt

# Check for issues
cargo clippy --all-features -- -D warnings

3. Commit Your Changes

git add .
git commit -m "feat: add compaction strategy interface (#55)"

Important: Reference issues in commit messages using #issue_number for automatic tracking.

See Commit Messages for formatting guidelines.

4. Push to Your Fork

git push origin feature/your-feature-name

What Happens Next:

  • โœ… GitHub Actions automatically runs tests
  • โœ… Auto-creates PR to develop branch
  • โœ… Adds comment to referenced issues (if any)
  • โœ… Runs Clippy and format checks

5. Pull Request Review

Once the automated PR is created:

  1. Review the PR description
  2. Wait for CI checks to pass
  3. Address any reviewer feedback
  4. PR will be merged to develop once approved

Note: You don't need to manually create PRs - the workflow handles this automatically!


๐Ÿ“ Coding Standards

Rust Style Guide

We follow the Rust API Guidelines and Rust Style Guide.

Key Principles:

  1. Use cargo fmt - All code must be formatted

    cargo fmt --all
    
  2. Pass cargo clippy - Zero warnings policy

    cargo clippy --all-features -- -D warnings
    
  3. Write Documentation - Public APIs must have doc comments

    #![allow(unused)]
    fn main() {
    /// Retrieves a value from the store by key.
    ///
    /// # Arguments
    ///
    /// * `key` - The key to look up
    ///
    /// # Returns
    ///
    /// * `Ok(Some(value))` - Key found
    /// * `Ok(None)` - Key not found
    /// * `Err(e)` - Error occurred
    ///
    /// # Example
    ///
    /// ```
    /// let value = engine.get(b"user:123")?;
    /// ```
    pub fn get(&self, key: &[u8]) -> Result<Option<String>> {
        // Implementation
    }
    }

SOLID Principles

This project follows SOLID principles:

  • Single Responsibility: Each module/struct has one clear purpose
  • Open/Closed: Extend behavior through traits, not modification
  • Liskov Substitution: Implementations must be interchangeable
  • Interface Segregation: Small, focused traits
  • Dependency Inversion: Depend on abstractions, not concretions

Example:

#![allow(unused)]
fn main() {
// โœ… Good - depends on trait
pub struct LsmEngine<W: WriteAheadLog> {
    wal: W,
}

// โŒ Bad - depends on concrete type
pub struct LsmEngine {
    wal: FileBasedWal,
}
}

Error Handling

  1. Use Result<T, LsmError> for fallible operations

    #![allow(unused)]
    fn main() {
    pub fn put(&mut self, key: &[u8], value: &str) -> Result<()> {
        self.wal.append(key, value)?;
        self.memtable.insert(key, value);
        Ok(())
    }
    }
  2. Provide Context with error types

    #![allow(unused)]
    fn main() {
    use thiserror::Error;
    
    #[derive(Error, Debug)]
    pub enum LsmError {
        #[error("WAL corruption at offset {0}")]
        WalCorruption(u64),
        
        #[error("Key too large: {size} bytes (max: {max})")]
        KeyTooLarge { size: usize, max: usize },
    }
    }
  3. Don't Panic in library code (use Result instead)

Performance Considerations

  1. Minimize Allocations

    #![allow(unused)]
    fn main() {
    // โœ… Good - reuse buffer
    let mut buffer = Vec::with_capacity(1024);
    for item in items {
        buffer.clear();
        serialize_into(&mut buffer, item)?;
    }
    
    // โŒ Bad - allocate each iteration
    for item in items {
        let buffer = serialize(item)?;
    }
    }
  2. Use Appropriate Data Structures

    • BTreeMap for sorted data
    • HashMap for fast lookups
    • Vec for sequential access
  3. Benchmark Changes

    cargo bench
    

๐Ÿงช Testing Guidelines

Test Types

  1. Unit Tests - Test individual functions/modules

    #![allow(unused)]
    fn main() {
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_memtable_insert() {
            let mut memtable = MemTable::new();
            memtable.insert(b"key", "value");
            assert_eq!(memtable.get(b"key"), Some("value".to_string()));
        }
    }
    }
  2. Integration Tests - Test component interactions

    #![allow(unused)]
    fn main() {
    // tests/integration_test.rs
    #[test]
    fn test_engine_recovery() {
        let config = LsmConfig::default();
        let mut engine = LsmEngine::new(config).unwrap();
        
        engine.put(b"key", "value").unwrap();
        drop(engine);
        
        let engine = LsmEngine::new(config).unwrap();
        assert_eq!(engine.get(b"key").unwrap(), Some("value".to_string()));
    }
    }
  3. Property Tests - Test invariants (optional, using proptest)

Test Requirements

  • All new code must have tests
  • Tests must pass on all platforms
  • Test coverage should increase, not decrease
  • Use descriptive test names
    #![allow(unused)]
    fn main() {
    #[test]
    fn test_get_returns_none_for_nonexistent_key() { /* ... */ }
    }

Running Tests

# Run all tests
cargo test --all-features

# Run specific test
cargo test test_memtable_insert

# Run with output
cargo test -- --nocapture

# Run integration tests only
cargo test --test '*'

# Run with coverage (requires tarpaulin)
cargo tarpaulin --out Html

๐Ÿ“ Commit Messages

We follow the Conventional Commits specification.

Format

<type>(<scope>): <subject> (#issue)

<body>

<footer>

Types

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation changes
  • style - Code style changes (formatting, etc.)
  • refactor - Code refactoring
  • perf - Performance improvements
  • test - Test additions/modifications
  • chore - Build process, dependencies, tooling
  • ci - CI/CD changes

Examples

Simple commit:

feat: add bloom filter to SSTable reader

With scope and issue:

fix(wal): prevent corruption on unclean shutdown (#42)

With body:

feat(compaction): implement leveled compaction strategy (#47)

Adds a new LeveledCompaction struct that implements the Compaction
trait. This strategy reduces read amplification by maintaining
sorted levels with exponentially increasing sizes.

Closes #47

Breaking change:

feat(api)!: change SSTable format to V2

BREAKING CHANGE: SSTable V2 is incompatible with V1.
Migration tool will be provided in v1.4.

Referencing Issues:

  • #123 - Reference issue
  • fixes #123, closes #123 - Will auto-close issue when PR merges
  • resolves #123 - Alternative close syntax

๐Ÿ” Pull Request Process

Before Pushing

  • Code compiles without errors
  • All tests pass (cargo test --all-features)
  • No clippy warnings (cargo clippy --all-features -- -D warnings)
  • Code is formatted (cargo fmt)
  • Documentation is updated (if applicable)
  • Tests are added for new functionality

Automated PR Creation

When you push to a feature/* or fix/* branch:

  1. โœ… Tests run automatically (CI/CD)
  2. โœ… PR is auto-created to develop branch
  3. โœ… Issues are commented (if referenced in commits)
  4. โœ… Checks must pass before merge

Manual Steps (If Needed)

If you need to create a PR manually:

  1. Go to your fork on GitHub
  2. Click "New Pull Request"
  3. Select develop as the base branch
  4. Fill out the PR template
  5. Submit the PR

PR Template

When creating a PR manually, use this template:

## Description

Brief description of changes.

## Type of Change

- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Related Issues

Closes #123
Related to #456

## Testing

Describe how you tested your changes:
- [ ] Unit tests added
- [ ] Integration tests added
- [ ] Manual testing performed

## Checklist

- [ ] Code compiles
- [ ] Tests pass
- [ ] Clippy checks pass
- [ ] Code is formatted
- [ ] Documentation updated

## Screenshots (if applicable)

## Additional Notes

Review Process

  1. Automated Checks - CI runs tests and linters
  2. Code Review - Maintainer reviews code
  3. Feedback - Address review comments
  4. Approval - Maintainer approves PR
  5. Merge - Squash and merge to develop

Review Timeline

  • Simple PRs: 1-3 days
  • Complex PRs: 3-7 days
  • Breaking Changes: 7-14 days

๐Ÿ“ Project Structure

ApexStore/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ core/              # Core domain logic
โ”‚   โ”‚   โ”œโ”€โ”€ engine.rs      # LSM engine orchestration
โ”‚   โ”‚   โ”œโ”€โ”€ memtable.rs    # In-memory storage
โ”‚   โ”‚   โ””โ”€โ”€ log_record.rs  # Data model
โ”‚   โ”œโ”€โ”€ storage/           # Persistence layer
โ”‚   โ”‚   โ”œโ”€โ”€ wal.rs         # Write-ahead log
โ”‚   โ”‚   โ”œโ”€โ”€ sstable.rs     # SSTable reader
โ”‚   โ”‚   โ””โ”€โ”€ builder.rs     # SSTable writer
โ”‚   โ”œโ”€โ”€ infra/             # Infrastructure
โ”‚   โ”‚   โ”œโ”€โ”€ codec.rs       # Serialization
โ”‚   โ”‚   โ”œโ”€โ”€ error.rs       # Error types
โ”‚   โ”‚   โ””โ”€โ”€ config.rs      # Configuration
โ”‚   โ”œโ”€โ”€ api/               # HTTP API (feature-gated)
โ”‚   โ”œโ”€โ”€ cli/               # CLI interface
โ”‚   โ””โ”€โ”€ features/          # Feature flags
โ”œโ”€โ”€ tests/                 # Integration tests
โ”œโ”€โ”€ benches/               # Benchmarks
โ””โ”€โ”€ docs/                  # Documentation

Module Guidelines

  • core/ - Domain logic, no external dependencies
  • storage/ - File I/O, persistence
  • infra/ - Cross-cutting concerns
  • api/ - External interfaces (feature-gated)

๐ŸŽฏ Areas for Contribution

High Priority

  1. CI/CD Testing Pipeline (#55)

    • Difficulty: Easy
    • Impact: High
    • Skills: GitHub Actions, YAML
  2. Compaction Implementation (#47)

    • Difficulty: Hard
    • Impact: High
    • Skills: Rust, algorithms, file I/O
  3. Efficient Iterators (#21, #22, #23)

    • Difficulty: Medium
    • Impact: High
    • Skills: Rust, data structures

Medium Priority

  1. Benchmarking Suite (#48)

    • Difficulty: Easy
    • Impact: Medium
    • Skills: Rust, criterion
  2. CLI Command Equalization (#65)

    • Difficulty: Medium
    • Impact: Medium
    • Skills: Rust, CLI design
  3. Checksums & Integrity (#25)

    • Difficulty: Easy
    • Impact: High
    • Skills: Rust, CRC32

Good First Issues

  1. Add More Tests

    • Difficulty: Easy
    • Impact: Medium
    • Skills: Rust, testing
  2. Binary Search Optimization (#37)

    • Difficulty: Easy
    • Impact: Medium
    • Skills: Rust, algorithms
  3. Documentation Improvements

    • Difficulty: Easy
    • Impact: Medium
    • Skills: Technical writing

Advanced Topics

  1. Replication Support

    • Difficulty: Very Hard
    • Impact: Very High
    • Skills: Distributed systems, Raft
  2. Snapshot Isolation

    • Difficulty: Hard
    • Impact: High
    • Skills: Concurrency, MVCC

โ“ Questions?


๐Ÿš€ Ready to Contribute?

  1. Find an issue or create one
  2. Comment that you're working on it
  3. Fork the repo and create a branch from main
  4. Make your changes with tests
  5. Push to your fork (automated PR will be created)
  6. Wait for review and address feedback

Thank you for contributing! ๐ŸŽ‰


Last updated: March 2026