Best Practices

[AIR-3][AIS-3][AIT-3][RES-3][BPC-3]

Best practices for developing high-quality, secure, and maintainable extensions for the Anya Core platform.

Last updated: June 7, 2025

Code Quality Standards

Rust Best Practices

Error Handling

// ✅ Good: Use Result types with meaningful error messages
fn process_transaction(tx: &Transaction) -> Result<ProcessingResult, ExtensionError> {
    let validated = validate_transaction(tx)
        .map_err(|e| ExtensionError::Validation(format!("Invalid transaction: {}", e)))?;

    // Process the validated transaction
    Ok(ProcessingResult::Success(validated))
}

// ❌ Bad: Using unwrap() or expect() in production code
fn process_transaction_bad(tx: &Transaction) -> ProcessingResult {
    let validated = validate_transaction(tx).unwrap(); // Can panic!
    ProcessingResult::Success(validated)
}

Memory Management

// ✅ Good: Use Arc for shared ownership, minimize clones
use std::sync::Arc;

struct ExtensionState {
    config: Arc<ExtensionConfig>,
    cache: Arc<RwLock<HashMap<String, Value>>>,
}

// ✅ Good: Implement Drop for proper cleanup
impl Drop for MyExtension {
    fn drop(&mut self) {
        // Clean up resources
        self.close_connections();
        self.flush_caches();
    }
}

Concurrency

// ✅ Good: Use async/await for I/O operations
async fn fetch_bitcoin_data(&self, block_hash: &str) -> Result<Block, BitcoinError> {
    let response = self.client
        .get(&format!("/block/{}", block_hash))
        .send()
        .await?;

    let block: Block = response.json().await?;
    Ok(block)
}

// ✅ Good: Use proper synchronization primitives
use tokio::sync::RwLock;

struct CacheManager {
    cache: Arc<RwLock<HashMap<String, CachedValue>>>,
}

impl CacheManager {
    async fn get(&self, key: &str) -> Option<CachedValue> {
        let cache = self.cache.read().await;
        cache.get(key).cloned()
    }

    async fn set(&self, key: String, value: CachedValue) {
        let mut cache = self.cache.write().await;
        cache.insert(key, value);
    }
}

Architecture Patterns

Hexagonal Architecture

// ✅ Good: Separate domain logic from infrastructure
pub struct TransactionProcessor {
    // Domain logic - no external dependencies
    rules: Vec<ValidationRule>,
}

impl TransactionProcessor {
    pub fn process(&self, tx: &Transaction) -> Result<ProcessedTransaction, ProcessingError> {
        // Pure domain logic
        for rule in &self.rules {
            rule.validate(tx)?;
        }
        Ok(ProcessedTransaction::from(tx))
    }
}

// Infrastructure adapters
pub struct BitcoinNetworkAdapter {
    client: BitcoinClient,
}

impl NetworkPort for BitcoinNetworkAdapter {
    async fn broadcast_transaction(&self, tx: &Transaction) -> Result<(), NetworkError> {
        self.client.send_transaction(tx).await
    }
}

Dependency Injection

// ✅ Good: Use dependency injection for testability
pub struct MyExtension<N: NetworkPort, S: StoragePort> {
    network: N,
    storage: S,
    processor: TransactionProcessor,
}

impl<N: NetworkPort, S: StoragePort> MyExtension<N, S> {
    pub fn new(network: N, storage: S) -> Self {
        Self {
            network,
            storage,
            processor: TransactionProcessor::new(),
        }
    }
}

// Easy to test with mocks
#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_transaction_processing() {
        let mock_network = MockNetworkAdapter::new();
        let mock_storage = MockStorageAdapter::new();
        let extension = MyExtension::new(mock_network, mock_storage);

        // Test logic...
    }
}

Security Best Practices

Input Validation

// ✅ Good: Validate all inputs
fn process_user_input(input: &str) -> Result<ProcessedInput, ValidationError> {
    // Length validation
    if input.len() > MAX_INPUT_LENGTH {
        return Err(ValidationError::InputTooLong);
    }

    // Content validation
    if !input.chars().all(|c| c.is_alphanumeric() || c.is_whitespace()) {
        return Err(ValidationError::InvalidCharacters);
    }

    // Business logic validation
    let parsed = parse_input(input)?;
    validate_business_rules(&parsed)?;

    Ok(ProcessedInput::new(parsed))
}

Cryptographic Operations

// ✅ Good: Use secure random number generation
use rand::rngs::OsRng;

fn generate_nonce() -> [u8; 32] {
    let mut nonce = [0u8; 32];
    OsRng.fill_bytes(&mut nonce);
    nonce
}

// ✅ Good: Use constant-time comparisons for sensitive data
use subtle::ConstantTimeEq;

fn verify_signature(signature: &[u8], expected: &[u8]) -> bool {
    signature.ct_eq(expected).into()
}

// ✅ Good: Clear sensitive data from memory
use zeroize::Zeroize;

struct PrivateKey([u8; 32]);

impl Drop for PrivateKey {
    fn drop(&mut self) {
        self.0.zeroize();
    }
}

Permission Management

// ✅ Good: Implement principle of least privilege
impl ExtensionTrait for MyExtension {
    fn metadata(&self) -> ExtensionMetadata {
        ExtensionMetadata {
            permissions: vec![
                Permission::ReadBitcoinData, // Only request what's needed
                Permission::NetworkAccess(vec!["api.bitcoin.org".to_string()]),
            ],
            // Don't request unnecessary permissions
        }
    }
}

// ✅ Good: Check permissions before operations
fn access_bitcoin_data(&self) -> Result<BitcoinData, SecurityError> {
    if !self.has_permission(&Permission::ReadBitcoinData) {
        return Err(SecurityError::PermissionDenied);
    }

    // Proceed with operation
    self.fetch_bitcoin_data()
}

Performance Best Practices

Efficient Data Structures

// ✅ Good: Use appropriate data structures
use std::collections::HashMap;
use indexmap::IndexMap;

struct TransactionCache {
    // For fast lookups
    by_id: HashMap<TxId, Transaction>,
    // For maintaining insertion order
    ordered: IndexMap<TxId, Transaction>,
}

// ✅ Good: Implement efficient serialization
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct EfficientData {
    #[serde(with = "serde_bytes")]
    binary_data: Vec<u8>,

    #[serde(skip_serializing_if = "Option::is_none")]
    optional_field: Option<String>,
}

Caching Strategies

// ✅ Good: Implement LRU cache with TTL
use lru::LruCache;
use std::time::{Duration, Instant};

struct CachedValue<T> {
    value: T,
    expires_at: Instant,
}

struct TimedLruCache<T> {
    cache: LruCache<String, CachedValue<T>>,
    ttl: Duration,
}

impl<T> TimedLruCache<T> {
    fn get(&mut self, key: &str) -> Option<&T> {
        if let Some(cached) = self.cache.get(key) {
            if cached.expires_at > Instant::now() {
                return Some(&cached.value);
            } else {
                self.cache.pop(key);
            }
        }
        None
    }

    fn put(&mut self, key: String, value: T) {
        let cached = CachedValue {
            value,
            expires_at: Instant::now() + self.ttl,
        };
        self.cache.put(key, cached);
    }
}

Resource Management

// ✅ Good: Use connection pooling
use deadpool_postgres::{Config, Pool};

struct DatabaseManager {
    pool: Pool,
}

impl DatabaseManager {
    async fn execute_query(&self, query: &str) -> Result<Vec<Row>, DatabaseError> {
        let client = self.pool.get().await?;
        let rows = client.query(query, &[]).await?;
        Ok(rows)
    }
}

// ✅ Good: Implement backpressure for high-throughput scenarios
use tokio::sync::Semaphore;

struct RateLimitedProcessor {
    semaphore: Arc<Semaphore>,
    max_concurrent: usize,
}

impl RateLimitedProcessor {
    async fn process_transaction(&self, tx: Transaction) -> Result<(), ProcessingError> {
        let _permit = self.semaphore.acquire().await?;
        // Process transaction with rate limiting
        self.do_process(tx).await
    }
}

Testing Best Practices

Unit Testing

#[cfg(test)]
mod tests {
    use super::*;
    use mockall::mock;

    mock! {
        NetworkAdapter {}

        #[async_trait]
        impl NetworkPort for NetworkAdapter {
            async fn send_transaction(&self, tx: &Transaction) -> Result<(), NetworkError>;
        }
    }

    #[tokio::test]
    async fn test_transaction_processing() {
        // ✅ Good: Use mocks for external dependencies
        let mut mock_network = MockNetworkAdapter::new();
        mock_network
            .expect_send_transaction()
            .times(1)
            .returning(|_| Ok(()));

        let processor = TransactionProcessor::new(mock_network);
        let tx = create_test_transaction();

        let result = processor.process(tx).await;
        assert!(result.is_ok());
    }

    #[test]
    fn test_validation_rules() {
        // ✅ Good: Test edge cases and error conditions
        let validator = TransactionValidator::new();

        // Test valid transaction
        let valid_tx = create_valid_transaction();
        assert!(validator.validate(&valid_tx).is_ok());

        // Test invalid transactions
        let invalid_tx = create_invalid_transaction();
        assert!(validator.validate(&invalid_tx).is_err());

        // Test edge cases
        let zero_value_tx = create_zero_value_transaction();
        assert!(validator.validate(&zero_value_tx).is_err());
    }
}

Integration Testing

#[cfg(test)]
mod integration_tests {
    use super::*;
    use testcontainers::*;

    #[tokio::test]
    async fn test_bitcoin_integration() {
        // ✅ Good: Use testcontainers for integration tests
        let docker = clients::Cli::default();
        let bitcoin_node = docker.run(images::bitcoin::Bitcoin::default());

        let config = BitcoinConfig {
            rpc_url: format!("http://localhost:{}", bitcoin_node.get_host_port(18443)),
            // ... other config
        };

        let client = BitcoinClient::new(config);

        // Test real Bitcoin integration
        let block_count = client.get_block_count().await?;
        assert!(block_count >= 0);
    }
}

Documentation Best Practices

Code Documentation

/// Processes Bitcoin transactions according to BIP-341 Taproot specifications.
/// 
/// # Arguments
/// 
/// * `transaction` - The Bitcoin transaction to process
/// * `context` - Processing context including network parameters
/// 
/// # Returns
/// 
/// Returns `Ok(ProcessingResult)` on successful processing, or an error if:
/// - Transaction validation fails
/// - Network communication errors occur
/// - Insufficient permissions
/// 
/// # Examples
/// 
/// ```rust
/// use anya_core::bitcoin::Transaction;
/// 
/// let processor = TransactionProcessor::new();
/// let tx = Transaction::from_hex("01000000...")?;
/// let context = ProcessingContext::mainnet();
/// 
/// match processor.process_transaction(&tx, &context).await {
///     Ok(result) => println!("Processed: {:?}", result),
///     Err(e) => eprintln!("Error: {}", e),
/// }
/// ```
/// 
/// # Compliance
/// 
/// This function implements:
/// - BIP-341: Taproot validation rules
/// - BIP-342: Tapscript execution
/// - BIP-174: PSBT compatibility
pub async fn process_transaction(
    &self,
    transaction: &Transaction,
    context: &ProcessingContext,
) -> Result<ProcessingResult, ProcessingError> {
    // Implementation...
}

Error Messages

// ✅ Good: Provide helpful error messages with context
#[derive(Debug, Error)]
pub enum ValidationError {
    #[error("Transaction input {input_index} references unknown UTXO {txid}:{vout}")]
    UnknownUtxo {
        input_index: usize,
        txid: String,
        vout: u32,
    },

    #[error("Insufficient funds: required {required} satoshis, available {available} satoshis")]
    InsufficientFunds {
        required: u64,
        available: u64,
    },

    #[error("Invalid signature for input {input_index}: {reason}")]
    InvalidSignature {
        input_index: usize,
        reason: String,
    },
}

Configuration Management

Environment-Specific Settings

// ✅ Good: Use configuration files with environment overrides
#[derive(Debug, Deserialize)]
pub struct ExtensionConfig {
    #[serde(default)]
    pub network: NetworkConfig,

    #[serde(default)]
    pub security: SecurityConfig,

    #[serde(default)]
    pub performance: PerformanceConfig,
}

impl Default for ExtensionConfig {
    fn default() -> Self {
        Self {
            network: NetworkConfig::mainnet(),
            security: SecurityConfig::production(),
            performance: PerformanceConfig::default(),
        }
    }
}

// Load configuration with environment overrides
let config = ConfigBuilder::new()
    .add_source(config::File::with_name("config/default"))
    .add_source(config::File::with_name(&format!("config/{}", env)).required(false))
    .add_source(config::Environment::with_prefix("ANYA").separator("_"))
    .build()?
    .try_deserialize::<ExtensionConfig>()?;

Secrets Management

// ✅ Good: Use proper secrets management
use anya_core::security::SecretManager;

async fn load_api_key() -> Result<String, SecurityError> {
    let secret_manager = SecretManager::new();

    // Try environment variable first
    if let Ok(key) = std::env::var("API_KEY") {
        return Ok(key);
    }

    // Fall back to secure storage
    secret_manager.get_secret("bitcoin_api_key").await
}

Monitoring and Observability

Metrics and Logging

use tracing::{info, warn, error, instrument};
use anya_core::metrics::Counter;

static TRANSACTION_COUNTER: Counter = Counter::new("extension_transactions_processed_total");

#[instrument(skip(self, transaction))]
pub async fn process_transaction(&self, transaction: &Transaction) -> Result<(), ProcessingError> {
    info!(
        tx_id = %transaction.txid(),
        input_count = transaction.inputs().len(),
        output_count = transaction.outputs().len(),
        "Processing transaction"
    );

    match self.validate_transaction(transaction).await {
        Ok(_) => {
            TRANSACTION_COUNTER.increment();
            info!("Transaction processed successfully");
            Ok(())
        }
        Err(e) => {
            warn!(error = %e, "Transaction validation failed");
            Err(e)
        }
    }
}

Health Checks

impl ExtensionTrait for MyExtension {
    fn health_check(&self) -> HealthStatus {
        let mut checks = Vec::new();

        // Check database connectivity
        if let Err(e) = self.database.ping() {
            checks.push(HealthCheck::unhealthy("database", e.to_string()));
        } else {
            checks.push(HealthCheck::healthy("database"));
        }

        // Check external API connectivity
        if let Err(e) = self.api_client.health_check() {
            checks.push(HealthCheck::unhealthy("external_api", e.to_string()));
        } else {
            checks.push(HealthCheck::healthy("external_api"));
        }

        HealthStatus::from_checks(checks)
    }
}

Deployment Best Practices

Container Configuration

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

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

RUN cargo build --release

FROM debian:bookworm-slim

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

COPY --from=builder /app/target/release/my-extension /usr/local/bin/

# ✅ Good: Run as non-root user
RUN useradd -r -s /bin/false anya
USER anya

EXPOSE 8080
CMD ["my-extension"]

Resource Limits

# ✅ Good: Set appropriate resource limits
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: my-extension
    image: my-extension:latest
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 30
      periodSeconds: 10
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5

Common Anti-Patterns to Avoid

❌ Bad Practices

// Don't use global mutable state
static mut GLOBAL_CONFIG: Option<Config> = None;

// Don't ignore errors
let result = risky_operation(); // Missing error handling

// Don't use blocking operations in async code
async fn bad_async_function() {
    std::thread::sleep(Duration::from_secs(1)); // Blocks executor!
}

// Don't hardcode configuration values
const API_URL: &str = "https://api.bitcoin.org"; // Should be configurable

// Don't use unwrap() in production code
let value = might_fail().unwrap(); // Can panic!

✅ Good Alternatives

// Use dependency injection for configuration
struct MyExtension {
    config: Arc<Config>,
}

// Always handle errors appropriately
let result = risky_operation()
    .map_err(|e| ProcessingError::from(e))?;

// Use async sleep in async code
async fn good_async_function() {
    tokio::time::sleep(Duration::from_secs(1)).await;
}

// Make configuration flexible
let api_url = config.api_url.as_deref()
    .unwrap_or("https://api.bitcoin.org");

// Use proper error handling
let value = might_fail()
    .map_err(|e| format!("Operation failed: {}", e))?;