Contributing to Felix¶
Thank you for your interest in contributing to Felix! This guide will help you get started.
Code of Conduct¶
Felix is an open and welcoming project. We expect all contributors to:
- Be respectful and considerate
- Focus on constructive feedback
- Help create a positive community
- Report unacceptable behavior to project maintainers
Ways to Contribute¶
Reporting Bugs¶
Found a bug? Please open an issue with:
- Clear title: Summarize the issue
- Description: What happened vs what you expected
- Reproduction steps: Minimal steps to reproduce
- Environment: OS, Rust version, Felix version
- Logs/errors: Relevant error messages or stack traces
Template:
**Bug Description**
A clear description of the bug.
**To Reproduce**
1. Start broker with config X
2. Run client command Y
3. Observe error Z
**Expected Behavior**
What should have happened.
**Environment**
- OS: Ubuntu 22.04
- Rust: 1.92.0
- Felix: main branch, commit abc123
**Logs**
Suggesting Features¶
Have an idea? Open an issue with:
- Problem statement: What problem does this solve?
- Proposed solution: How would it work?
- Alternatives considered: Other approaches you've thought about
- Additional context: Use cases, examples
Template:
**Problem**
Describe the problem or limitation.
**Proposed Solution**
How this feature would work.
**Alternatives**
Other solutions considered and why they're less ideal.
**Use Case**
Real-world scenario where this would help.
Improving Documentation¶
Documentation is always welcome! You can:
- Fix typos or clarify existing docs
- Add examples or tutorials
- Improve API documentation
- Write guides for common scenarios
See Building the Docs below.
Contributing Code¶
See Development Workflow for details.
Getting Started¶
Prerequisites¶
- Rust 1.92.0+: Install via rustup
- Git: For version control
- Task (optional): Install from taskfile.dev
- Development tools:
cargo-fmt,cargo-clippy
Fork and Clone¶
# Fork repository on GitHub
# Then clone your fork
git clone https://github.com/YOUR_USERNAME/felix.git
cd felix
# Add upstream remote
git remote add upstream https://github.com/gabloe/felix.git
Build and Test¶
# Build everything
cargo build --workspace
# Run tests
cargo test --workspace
# Or use Task
task build
task test
Development Workflow¶
1. Create a Branch¶
# Sync with upstream
git fetch upstream
git checkout main
git merge upstream/main
# Create feature branch
git checkout -b feature/my-feature
# Or bugfix branch
git checkout -b fix/issue-123
Branch naming:
- feature/description: New features
- fix/description: Bug fixes
- docs/description: Documentation changes
- refactor/description: Code refactoring
- test/description: Test additions/improvements
2. Make Changes¶
Code style:
Felix follows standard Rust style guidelines:
Run linter:
# Check formatting
cargo fmt -- --check
# Run clippy
cargo clippy --workspace --all-targets --all-features -- -D warnings
# Or with Task
task lint
Writing tests:
All new code should include tests:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_my_feature() {
// Arrange
let input = setup_test_data();
// Act
let result = my_function(input);
// Assert
assert_eq!(result, expected_value);
}
}
Test async code:
#[tokio::test]
async fn test_async_feature() {
let result = my_async_function().await;
assert!(result.is_ok());
}
3. Commit Changes¶
Commit message format:
Types:
- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (formatting, etc.)
- refactor: Code refactoring
- test: Adding or modifying tests
- chore: Build process or tooling changes
Example:
git add .
git commit -m "feat(broker): add configurable batch timeout
Adds FELIX_EVENT_BATCH_MAX_DELAY_US configuration to control
the maximum delay before flushing event batches. This allows
users to tune the latency vs throughput trade-off.
Closes #123"
Guidelines:
- Use present tense ("add" not "added")
- Keep subject line under 72 characters
- Reference issues in footer (Closes #123, Fixes #456)
- Add breaking changes in footer: BREAKING CHANGE: description
4. Push and Create PR¶
Pull request template:
## Description
Brief description of changes.
## Motivation
Why is this change needed?
## Changes
- List of changes made
## Testing
How was this tested?
## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] Ran `task lint`
- [ ] Ran `task test`
- [ ] No breaking changes (or documented in commit)
5. Code Review¶
- Respond to feedback promptly
- Make requested changes in new commits
- Push updates to the same branch
- Request re-review when ready
6. Merge¶
Once approved: - Maintainers will merge your PR - Delete your feature branch after merge
Code Style Guidelines¶
Rust Style¶
Follow standard conventions:
// Use descriptive names
fn calculate_batch_size(events: &[Event]) -> usize { ... }
// Document public APIs
/// Publishes an event to the specified stream.
///
/// # Arguments
/// * `stream` - The target stream name
/// * `payload` - The event payload
///
/// # Returns
/// Result indicating success or error
pub async fn publish(&self, stream: &str, payload: &[u8]) -> Result<()> { ... }
// Use Result for errors
fn parse_config(path: &str) -> Result<Config> { ... }
// Prefer ? operator over unwrap()
let config = parse_config(path)?;
// Use meaningful error messages
return Err(anyhow!("Failed to bind to {}: {}", addr, err));
Formatting¶
Linting¶
# Run clippy
cargo clippy --workspace --all-targets --all-features -- -D warnings
# Fix automatically where possible
cargo clippy --fix
Comments¶
When to comment: - Complex algorithms - Non-obvious design decisions - Performance-critical sections - Workarounds for external issues
When not to comment: - Obvious code - What the code does (code should be self-documenting)
// Good: Explains why
// We batch events to amortize framing overhead across multiple messages.
// Empirical testing shows 64 events per batch optimizes for 1-4KB payloads.
const DEFAULT_BATCH_SIZE: usize = 64;
// Bad: Restates the obvious
// Set the batch size to 64
const DEFAULT_BATCH_SIZE: usize = 64;
Error Handling¶
// Use anyhow for application code
use anyhow::{Context, Result};
fn load_config(path: &str) -> Result<Config> {
let contents = fs::read_to_string(path)
.with_context(|| format!("Failed to read config from {}", path))?;
let config: Config = serde_yaml_ng::from_str(&contents)
.context("Failed to parse config YAML")?;
Ok(config)
}
// Use custom error types for libraries
#[derive(Debug, thiserror::Error)]
pub enum BrokerError {
#[error("Connection closed")]
ConnectionClosed,
#[error("Invalid frame: {0}")]
InvalidFrame(String),
}
Testing Guidelines¶
Test Coverage¶
- All public APIs must have tests
- Bug fixes must include regression tests
- Aim for >80% code coverage
Test Organization¶
#[cfg(test)]
mod tests {
use super::*;
// Unit tests for internal functions
#[test]
fn test_parse_frame() { ... }
// Integration tests for APIs
#[tokio::test]
async fn test_publish_subscribe_flow() { ... }
}
Running Tests¶
# All tests
cargo test --workspace
# Specific test
cargo test test_name
# With output
cargo test -- --nocapture
# With coverage
task coverage
Test Fixtures¶
// Create reusable test helpers
fn create_test_broker() -> Broker {
BrokerBuilder::new()
.with_config(test_config())
.build()
.unwrap()
}
#[tokio::test]
async fn test_feature() {
let broker = create_test_broker();
// Test code
}
Documentation¶
Code Documentation¶
/// A brief one-line summary.
///
/// More detailed description if needed. Can span multiple paragraphs.
///
/// # Arguments
/// * `arg1` - Description of arg1
/// * `arg2` - Description of arg2
///
/// # Returns
/// Description of return value
///
/// # Errors
/// Description of error conditions
///
/// # Examples
/// ```
/// use felix_broker::Broker;
///
/// let broker = Broker::new();
/// broker.start().await?;
/// ```
pub async fn my_function(arg1: Type1, arg2: Type2) -> Result<Type3> {
// Implementation
}
Building Documentation¶
API docs:
User documentation:
# Install mkdocs
pip install mkdocs mkdocs-material
# Serve locally
cd docs-site
mkdocs serve
# Open http://127.0.0.1:8000
# Build static site
mkdocs build
Performance Considerations¶
Benchmarking¶
# Run latency benchmarks
cargo run --release -p broker --bin latency-demo -- \
--binary --fanout 10 --batch 64 --payload 4096
# Run cache benchmarks
cargo run --release -p broker --bin cache-demo
Profiling¶
CPU profiling:
# Linux perf
sudo perf record -g cargo run --release -p broker
sudo perf report
# Flamegraph
cargo install flamegraph
cargo flamegraph -p broker
Memory profiling:
# Valgrind (debug build)
valgrind --leak-check=full ./target/debug/broker
# Heaptrack (Linux)
heaptrack cargo run --release -p broker
Release Process¶
(For maintainers)
- Update version in
Cargo.toml - Update
CHANGELOG.md - Create git tag:
git tag -a v0.1.0 -m "Release v0.1.0" - Push tag:
git push origin v0.1.0 - GitHub Actions builds and publishes release
Getting Help¶
Stuck?
- GitHub Discussions: Ask questions
- GitHub Issues: Bug reports and feature requests
- Documentation: docs-site/
Before asking:
- Check existing issues/discussions
- Review documentation
- Try to create a minimal reproduction
Recognition¶
Contributors are recognized in:
CONTRIBUTORS.md(added on first merged PR)- Release notes
- GitHub contributors graph
Thank you for contributing to Felix! 🎉
Next Steps¶
- Build system: Building & Testing Guide
- Project structure: Project Structure
- Architecture: System Design