Best Practices - ruvnet/ruv-FANN GitHub Wiki

Best Practices Guide

Overview

This document outlines best practices for developing, maintaining, and deploying the ruv-FANN (Rust Universal Vector - Fast Approximate Nearest Neighbors) project. These guidelines ensure code quality, performance, security, and maintainability across all components.

Code Organization Best Practices

Project Structure

ruv-FANN/
├── Semantic_Cartan_Matrix/           # Core implementation
│   ├── micro_cartan_attn/           # Attention mechanisms
│   ├── micro_core/                  # Core functionality
│   ├── micro_metrics/               # Performance metrics
│   ├── micro_routing/               # Routing algorithms
│   └── micro_swarm/                 # Swarm coordination
├── docs/                            # Documentation
├── examples/                        # Usage examples
├── tests/                           # Integration tests
└── wiki/                           # Project wiki

Rust Module Organization

1. Crate Structure

  • Single Responsibility: Each crate should have a focused purpose
  • Clear Dependencies: Minimize cross-crate dependencies
  • API Surface: Keep public APIs minimal and well-documented
// Good: Clear module structure
pub mod attention {
    pub mod cartan_attention;
    pub mod semantic_matrix;
    mod internal_utils; // Private implementation details
}

pub mod core {
    pub mod vector_operations;
    pub mod distance_metrics;
}

2. File Organization

  • Maximum 500 lines per file: Split large files into focused modules
  • Logical grouping: Group related functionality together
  • Clear naming: Use descriptive file and module names
// lib.rs - Main entry point
pub mod attention;
pub mod core;
pub mod metrics;
pub mod routing;
pub mod swarm;

// Re-export common types
pub use attention::CartanAttention;
pub use core::{Vector, DistanceMetric};

Error Handling Patterns

1. Custom Error Types

use thiserror::Error;

#[derive(Error, Debug)]
pub enum RuvFannError {
    #[error("Vector dimension mismatch: expected {expected}, got {actual}")]
    DimensionMismatch { expected: usize, actual: usize },
    
    #[error("Invalid distance metric: {metric}")]
    InvalidDistanceMetric { metric: String },
    
    #[error("Swarm coordination failed: {reason}")]
    SwarmError { reason: String },
    
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
}

pub type Result<T> = std::result::Result<T, RuvFannError>;

2. Error Propagation

// Use ? operator for error propagation
pub fn process_vectors(vectors: Vec<Vector>) -> Result<ProcessedVectors> {
    let normalized = normalize_vectors(vectors)?;
    let indexed = build_index(normalized)?;
    Ok(ProcessedVectors { indexed })
}

// Provide context with map_err
fn load_config(path: &Path) -> Result<Config> {
    std::fs::read_to_string(path)
        .map_err(|e| RuvFannError::Io(e))?
        .parse()
        .map_err(|_| RuvFannError::InvalidConfig)
}

Performance Optimization Guidelines

1. Memory Management

Vector Operations

// Prefer in-place operations when possible
impl Vector {
    pub fn normalize_mut(&mut self) {
        let norm = self.norm();
        if norm > f32::EPSILON {
            for component in &mut self.data {
                *component /= norm;
            }
        }
    }
    
    // Provide immutable version when needed
    pub fn normalize(&self) -> Self {
        let mut result = self.clone();
        result.normalize_mut();
        result
    }
}

Memory Pool Usage

use object_pool::Pool;

pub struct VectorPool {
    pool: Pool<Vec<f32>>,
}

impl VectorPool {
    pub fn get_vector(&self, capacity: usize) -> Vec<f32> {
        let mut vec = self.pool.try_pull().unwrap_or_default();
        vec.clear();
        vec.reserve(capacity);
        vec
    }
    
    pub fn return_vector(&self, vec: Vec<f32>) {
        self.pool.attach(vec);
    }
}

2. Algorithmic Optimizations

SIMD Operations

use std::arch::x86_64::*;

#[cfg(target_arch = "x86_64")]
unsafe fn dot_product_simd(a: &[f32], b: &[f32]) -> f32 {
    assert_eq!(a.len(), b.len());
    assert!(a.len() % 8 == 0);
    
    let mut sum = _mm256_setzero_ps();
    
    for i in (0..a.len()).step_by(8) {
        let va = _mm256_loadu_ps(a.as_ptr().add(i));
        let vb = _mm256_loadu_ps(b.as_ptr().add(i));
        sum = _mm256_fmadd_ps(va, vb, sum);
    }
    
    // Horizontal sum
    let mut result = [0f32; 8];
    _mm256_storeu_ps(result.as_mut_ptr(), sum);
    result.iter().sum()
}

Parallel Processing

use rayon::prelude::*;

impl SemanticCartanMatrix {
    pub fn batch_transform(&self, vectors: &[Vector]) -> Vec<Vector> {
        vectors
            .par_iter()
            .map(|v| self.transform(v))
            .collect()
    }
    
    pub fn parallel_search(&self, query: &Vector, k: usize) -> Vec<SearchResult> {
        self.index
            .par_chunks(1000)
            .flat_map(|chunk| {
                chunk.iter()
                    .map(|item| SearchResult {
                        distance: self.distance(query, &item.vector),
                        item: item.clone(),
                    })
            })
            .collect::<Vec<_>>()
            .into_iter()
            .k_closest(k)
    }
}

3. Async Best Practices

Non-blocking Operations

use tokio::sync::{RwLock, Semaphore};
use std::sync::Arc;

pub struct AsyncVectorIndex {
    data: Arc<RwLock<VectorData>>,
    semaphore: Arc<Semaphore>,
}

impl AsyncVectorIndex {
    pub async fn search(&self, query: Vector, k: usize) -> Result<Vec<SearchResult>> {
        let _permit = self.semaphore.acquire().await?;
        let data = self.data.read().await;
        
        tokio::task::spawn_blocking(move || {
            data.search_blocking(query, k)
        }).await?
    }
    
    pub async fn insert(&self, vector: Vector) -> Result<()> {
        let mut data = self.data.write().await;
        data.insert(vector).await
    }
}

Security Best Practices

1. Input Validation

Vector Dimension Validation

pub fn validate_vector_dimensions(vectors: &[Vector], expected_dim: usize) -> Result<()> {
    for (i, vector) in vectors.iter().enumerate() {
        if vector.len() != expected_dim {
            return Err(RuvFannError::DimensionMismatch {
                expected: expected_dim,
                actual: vector.len(),
            }.with_context(format!("Vector at index {}", i)));
        }
    }
    Ok(())
}

Numeric Stability

pub fn safe_normalize(vector: &mut [f32]) -> Result<()> {
    let norm_sq: f32 = vector.iter().map(|x| x * x).sum();
    
    if norm_sq.is_nan() || norm_sq.is_infinite() {
        return Err(RuvFannError::InvalidVector("NaN or infinite values detected"));
    }
    
    if norm_sq < f32::EPSILON {
        return Err(RuvFannError::InvalidVector("Zero vector cannot be normalized"));
    }
    
    let norm = norm_sq.sqrt();
    for component in vector {
        *component /= norm;
    }
    
    Ok(())
}

2. Memory Safety

Bounds Checking

pub struct SafeVectorAccess<'a> {
    data: &'a [f32],
}

impl<'a> SafeVectorAccess<'a> {
    pub fn new(data: &'a [f32]) -> Self {
        Self { data }
    }
    
    pub fn get(&self, index: usize) -> Option<f32> {
        self.data.get(index).copied()
    }
    
    pub fn get_range(&self, range: Range<usize>) -> Option<&[f32]> {
        self.data.get(range)
    }
}

3. Secure Configuration

Environment-based Configuration

use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
pub struct SecurityConfig {
    pub max_vector_dimension: usize,
    pub max_batch_size: usize,
    pub rate_limit_per_second: u32,
    pub enable_metrics: bool,
}

impl SecurityConfig {
    pub fn from_env() -> Result<Self> {
        let config = Self {
            max_vector_dimension: env::var("RUVAN_MAX_VECTOR_DIM")
                .unwrap_or_else(|_| "1024".to_string())
                .parse()?,
            max_batch_size: env::var("RUVAN_MAX_BATCH_SIZE")
                .unwrap_or_else(|_| "1000".to_string())
                .parse()?,
            rate_limit_per_second: env::var("RUVAN_RATE_LIMIT")
                .unwrap_or_else(|_| "100".to_string())
                .parse()?,
            enable_metrics: env::var("RUVAN_ENABLE_METRICS")
                .unwrap_or_else(|_| "true".to_string())
                .parse()?,
        };
        
        config.validate()?;
        Ok(config)
    }
    
    fn validate(&self) -> Result<()> {
        if self.max_vector_dimension == 0 || self.max_vector_dimension > 10_000 {
            return Err(RuvFannError::InvalidConfig("Invalid max_vector_dimension"));
        }
        if self.max_batch_size == 0 || self.max_batch_size > 100_000 {
            return Err(RuvFannError::InvalidConfig("Invalid max_batch_size"));
        }
        Ok(())
    }
}

Testing Strategies

1. Unit Testing

Test Organization

#[cfg(test)]
mod tests {
    use super::*;
    use proptest::prelude::*;
    
    #[test]
    fn test_vector_normalization() {
        let mut vector = vec![3.0, 4.0];
        safe_normalize(&mut vector).unwrap();
        
        let norm: f32 = vector.iter().map(|x| x * x).sum::<f32>().sqrt();
        assert!((norm - 1.0).abs() < f32::EPSILON);
    }
    
    #[test]
    fn test_dimension_mismatch_error() {
        let vectors = vec![
            Vector::new(vec![1.0, 2.0]),
            Vector::new(vec![1.0, 2.0, 3.0]), // Wrong dimension
        ];
        
        let result = validate_vector_dimensions(&vectors, 2);
        assert!(matches!(result, Err(RuvFannError::DimensionMismatch { .. })));
    }
}

Property-based Testing

proptest! {
    #[test]
    fn test_distance_metric_properties(
        v1 in prop::collection::vec(any::<f32>(), 1..100),
        v2 in prop::collection::vec(any::<f32>(), 1..100)
    ) {
        if v1.len() == v2.len() {
            let dist = euclidean_distance(&v1, &v2);
            
            // Distance is non-negative
            prop_assert!(dist >= 0.0);
            
            // Distance is symmetric
            let dist_rev = euclidean_distance(&v2, &v1);
            prop_assert!((dist - dist_rev).abs() < f32::EPSILON);
            
            // Distance to self is zero
            let self_dist = euclidean_distance(&v1, &v1);
            prop_assert!(self_dist < f32::EPSILON);
        }
    }
}

2. Integration Testing

End-to-End Workflows

#[tokio::test]
async fn test_complete_search_workflow() {
    let config = TestConfig::default();
    let mut index = SemanticCartanMatrix::new(config).await.unwrap();
    
    // Insert test vectors
    let test_vectors = generate_test_vectors(1000, 128);
    for vector in test_vectors.iter() {
        index.insert(vector.clone()).await.unwrap();
    }
    
    // Build index
    index.build().await.unwrap();
    
    // Perform search
    let query = test_vectors[0].clone();
    let results = index.search(&query, 10).await.unwrap();
    
    assert_eq!(results.len(), 10);
    assert_eq!(results[0].item.id, query.id); // Self should be closest
    
    // Verify distances are sorted
    for i in 1..results.len() {
        assert!(results[i-1].distance <= results[i].distance);
    }
}

3. Benchmarking

Performance Tests

use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn benchmark_vector_operations(c: &mut Criterion) {
    let vectors = generate_random_vectors(1000, 128);
    
    c.bench_function("dot_product_naive", |b| {
        b.iter(|| {
            for i in 0..vectors.len()-1 {
                black_box(dot_product_naive(&vectors[i], &vectors[i+1]));
            }
        })
    });
    
    c.bench_function("dot_product_simd", |b| {
        b.iter(|| {
            for i in 0..vectors.len()-1 {
                black_box(unsafe { dot_product_simd(&vectors[i], &vectors[i+1]) });
            }
        })
    });
}

criterion_group!(benches, benchmark_vector_operations);
criterion_main!(benches);

4. Test Data Management

Fixtures and Utilities

pub struct TestDataBuilder {
    dimension: usize,
    count: usize,
    distribution: Distribution,
}

impl TestDataBuilder {
    pub fn new() -> Self {
        Self {
            dimension: 128,
            count: 1000,
            distribution: Distribution::Normal,
        }
    }
    
    pub fn with_dimension(mut self, dim: usize) -> Self {
        self.dimension = dim;
        self
    }
    
    pub fn with_count(mut self, count: usize) -> Self {
        self.count = count;
        self
    }
    
    pub fn build(self) -> Vec<Vector> {
        match self.distribution {
            Distribution::Normal => generate_normal_vectors(self.count, self.dimension),
            Distribution::Uniform => generate_uniform_vectors(self.count, self.dimension),
        }
    }
}

Documentation Standards

1. Code Documentation

Public API Documentation

/// Semantic Cartan Matrix for high-dimensional vector operations.
/// 
/// This structure implements a Cartan-based attention mechanism for
/// efficient similarity search in high-dimensional spaces.
/// 
/// # Examples
/// 
/// ```rust
/// use ruv_fann::{SemanticCartanMatrix, Vector};
/// 
/// let config = CartanConfig::default();
/// let mut matrix = SemanticCartanMatrix::new(config)?;
/// 
/// // Insert vectors
/// let vector = Vector::new(vec![1.0, 2.0, 3.0]);
/// matrix.insert(vector).await?;
/// 
/// // Build index
/// matrix.build().await?;
/// 
/// // Search
/// let query = Vector::new(vec![1.1, 2.1, 3.1]);
/// let results = matrix.search(&query, 10).await?;
/// ```
/// 
/// # Performance
/// 
/// The search operation has O(log n) complexity for indexed data,
/// with SIMD optimizations providing 2-4x speedup on supported hardware.
/// 
/// # Thread Safety
/// 
/// This structure is thread-safe for concurrent reads, but writes
/// require exclusive access. Use `Arc<RwLock<>>` for shared access.
#[derive(Debug)]
pub struct SemanticCartanMatrix {
    // Implementation details...
}

Internal Documentation

/// Internal helper for computing Cartan metric distances.
/// 
/// This function uses the Cartan connection to compute distances
/// in the semantic manifold. The implementation follows the
/// mathematical formulation from [Paper Reference].
/// 
/// # Arguments
/// 
/// * `point_a` - First point in the manifold
/// * `point_b` - Second point in the manifold
/// * `connection` - Cartan connection coefficients
/// 
/// # Returns
/// 
/// The geodesic distance between the two points.
/// 
/// # Panics
/// 
/// Panics if the points have different dimensions or if the
/// connection matrix is not properly initialized.
fn compute_cartan_distance(
    point_a: &[f32],
    point_b: &[f32],
    connection: &CartanConnection,
) -> f32 {
    // Implementation...
}

2. README Structure

# ruv-FANN: Rust Universal Vector - Fast Approximate Nearest Neighbors

## Quick Start

```rust
use ruv_fann::prelude::*;

let vectors = load_vectors("data.bin")?;
let index = build_index(vectors)?;
let results = index.search(&query, 10)?;

Features

  • 🚀 High Performance: SIMD-optimized vector operations
  • 🧠 Semantic Understanding: Cartan-based attention mechanisms
  • 🔧 Flexible: Multiple distance metrics and index types
  • 🦀 Safe: Memory-safe Rust implementation
  • Async: Non-blocking operations with Tokio

Installation

Add to your Cargo.toml:

[dependencies]
ruv-fann = "0.2.0"

Architecture

[Include architectural diagrams and explanations]

Performance Benchmarks

[Include benchmark results and comparisons]

Contributing

See CONTRIBUTING.md for development setup and guidelines.


### 3. API Documentation

#### OpenAPI Specification (for HTTP APIs)
```yaml
openapi: 3.0.0
info:
  title: ruv-FANN API
  description: Fast Approximate Nearest Neighbors service
  version: 0.2.0

paths:
  /search:
    post:
      summary: Search for similar vectors
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                query:
                  type: array
                  items:
                    type: number
                  example: [1.0, 2.0, 3.0]
                k:
                  type: integer
                  minimum: 1
                  maximum: 1000
                  example: 10
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        distance:
                          type: number
                        vector:
                          type: array
                          items:
                            type: number

Deployment Recommendations

1. Container Configuration

Dockerfile Best Practices

# Multi-stage build for smaller production images
FROM rust:1.70 as builder

WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src

# Build with optimizations
RUN cargo build --release

FROM debian:bullseye-slim

# Install runtime dependencies
RUN apt-get update && apt-get install -y \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Create non-root user
RUN useradd -r -s /bin/false appuser

COPY --from=builder /app/target/release/ruv-fann /usr/local/bin/
COPY --chown=appuser:appuser config/ /app/config/

USER appuser
WORKDIR /app

EXPOSE 8080

CMD ["ruv-fann", "--config", "/app/config/production.toml"]

Docker Compose for Development

version: '3.8'

services:
  ruv-fann:
    build: .
    ports:
      - "8080:8080"
    environment:
      - RUST_LOG=info
      - RUVAN_CONFIG_PATH=/app/config/development.toml
    volumes:
      - ./config:/app/config:ro
      - ./data:/app/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  redis_data:

2. Production Configuration

Performance Tuning

# production.toml
[server]
host = "0.0.0.0"
port = 8080
workers = 4  # Number of CPU cores

[vector_index]
dimensions = 512
index_type = "hnsw"
ef_construction = 200
max_connections = 16
use_simd = true

[memory]
vector_pool_size = 10000
batch_size = 1000
max_memory_mb = 4096

[performance]
enable_metrics = true
metrics_interval_seconds = 60
log_slow_queries = true
slow_query_threshold_ms = 100

[security]
max_request_size = "10MB"
rate_limit_per_second = 1000
enable_cors = false

Logging Configuration

use tracing_subscriber::{fmt, EnvFilter, prelude::*};
use tracing_appender::rolling::{RollingFileAppender, Rotation};

pub fn init_logging() -> Result<()> {
    let file_appender = RollingFileAppender::new(
        Rotation::daily(),
        "/var/log/ruv-fann",
        "app.log"
    );
    
    let (file_writer, _guard) = tracing_appender::non_blocking(file_appender);
    
    tracing_subscriber::registry()
        .with(
            fmt::layer()
                .with_target(false)
                .with_thread_ids(true)
                .with_file(true)
                .with_line_number(true)
        )
        .with(
            fmt::layer()
                .with_writer(file_writer)
                .json()
        )
        .with(
            EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| EnvFilter::new("info"))
        )
        .init();
    
    Ok(())
}

3. Monitoring and Observability

Metrics Collection

use prometheus::{Counter, Histogram, Registry, Encoder, TextEncoder};
use std::sync::Arc;

#[derive(Debug, Clone)]
pub struct Metrics {
    pub search_requests: Counter,
    pub search_duration: Histogram,
    pub index_size: prometheus::IntGauge,
    pub memory_usage: prometheus::IntGauge,
}

impl Metrics {
    pub fn new() -> Result<Self> {
        let search_requests = Counter::new(
            "search_requests_total",
            "Total number of search requests"
        )?;
        
        let search_duration = Histogram::with_opts(
            prometheus::HistogramOpts::new(
                "search_duration_seconds",
                "Search request duration in seconds"
            ).buckets(vec![0.001, 0.01, 0.1, 1.0, 10.0])
        )?;
        
        let index_size = prometheus::IntGauge::new(
            "index_vectors_total",
            "Total number of vectors in index"
        )?;
        
        let memory_usage = prometheus::IntGauge::new(
            "memory_usage_bytes",
            "Current memory usage in bytes"
        )?;
        
        Ok(Self {
            search_requests,
            search_duration,
            index_size,
            memory_usage,
        })
    }
}

Health Checks

use axum::{Json, response::Json as ResponseJson};
use serde_json::{json, Value};

pub async fn health_check() -> ResponseJson<Value> {
    let health_status = json!({
        "status": "healthy",
        "timestamp": chrono::Utc::now().to_rfc3339(),
        "version": env!("CARGO_PKG_VERSION"),
        "checks": {
            "database": check_database_connection().await,
            "memory": check_memory_usage().await,
            "index": check_index_health().await,
        }
    });
    
    ResponseJson(health_status)
}

async fn check_database_connection() -> Value {
    // Implementation
    json!({"status": "ok", "latency_ms": 5})
}

4. Scalability Patterns

Horizontal Scaling

use consul::Client as ConsulClient;
use std::collections::HashMap;

pub struct ServiceDiscovery {
    consul: ConsulClient,
    service_name: String,
}

impl ServiceDiscovery {
    pub async fn register_instance(&self, instance_id: String, address: String, port: u16) -> Result<()> {
        let service = consul::catalog::Service {
            id: Some(instance_id),
            service: self.service_name.clone(),
            address: Some(address),
            port: Some(port),
            tags: vec!["ruv-fann".to_string(), "vector-search".to_string()],
            meta: HashMap::from([
                ("version".to_string(), env!("CARGO_PKG_VERSION").to_string()),
                ("capabilities".to_string(), "search,insert".to_string()),
            ]),
            ..Default::default()
        };
        
        self.consul.agent().service_register(&service).await?;
        Ok(())
    }
    
    pub async fn discover_peers(&self) -> Result<Vec<ServiceInstance>> {
        let services = self.consul.health()
            .service(&self.service_name, None, true)
            .await?;
            
        Ok(services.into_iter()
            .map(|s| ServiceInstance {
                id: s.service.id.unwrap_or_default(),
                address: s.service.address.unwrap_or_default(),
                port: s.service.port.unwrap_or(8080),
            })
            .collect())
    }
}

Conclusion

Following these best practices ensures:

  • Code Quality: Maintainable, readable, and robust code
  • Performance: Optimal resource utilization and response times
  • Security: Safe operations and proper input validation
  • Reliability: Comprehensive testing and error handling
  • Scalability: Patterns that support growth and distribution
  • Observability: Proper monitoring and debugging capabilities

Regular review and updates of these practices help maintain project health as requirements evolve.

References

⚠️ **GitHub.com Fallback** ⚠️