Skip to content

Performance Guide

Overview

This guide provides best practices and recommendations for optimizing performance when using TinyResult in your applications.

Memory Usage

1. Value Types vs Reference Types

// Prefer value types for small data
public Result<int> GetUserId() { ... }

// Use reference types for larger data
public Result<User> GetUser() { ... }

2. Avoid Unnecessary Allocations

// Avoid: Creates new Error instance for each call
public Result<T> Validate(T value)
{
    return value != null 
        ? Result<T>.Success(value)
        : Result<T>.Failure(new Error(ErrorCode.ValidationError, "Value cannot be null"));
}

// Prefer: Reuse Error instances
private static readonly Error NullValueError = Error.Create(ErrorCode.ValidationError, "Value cannot be null");

public Result<T> Validate(T value)
{
    return value != null 
        ? Result<T>.Success(value)
        : Result<T>.Failure(NullValueError);
}

Async Operations

1. ConfigureAwait

public async Task<Result<T>> GetDataAsync()
{
    return await ResultPipeline<T>
        .Start(initialValue)
        .ThenAsync(async value => await ProcessAsync(value).ConfigureAwait(false))
        .BuildAsync();
}

2. Parallel Operations

public async Task<Result<List<T>>> ProcessInParallelAsync(List<int> ids)
{
    var tasks = ids.Select(id => ProcessAsync(id));
    var results = await Task.WhenAll(tasks);

    return Result.Combine(results);
}

Pipeline Optimization

1. Chain Length

// Avoid: Too many chained operations
var result = pipeline
    .Map(x => x + 1)
    .Map(x => x * 2)
    .Map(x => x.ToString())
    .Map(x => x.ToUpper())
    .Map(x => x.Length);

// Prefer: Combine operations
var result = pipeline
    .Map(x => (x + 1) * 2)
    .Map(x => x.ToString().ToUpper().Length);

2. Early Validation

// Avoid: Validating after expensive operations
var result = await pipeline
    .ThenAsync(async x => await ExpensiveOperationAsync(x))
    .Validate(x => x.IsValid, "Invalid result");

// Prefer: Validate early
var result = await pipeline
    .Validate(x => x.IsValid, "Invalid input")
    .ThenAsync(async x => await ExpensiveOperationAsync(x));

Error Handling

1. Error Creation

// Avoid: Creating detailed errors for every failure
public Result<T> Process(T value)
{
    try
    {
        // Operation
    }
    catch (Exception ex)
    {
        return Result<T>.Failure(new Error(
            ErrorCode.InternalError,
            ex.Message,
            new Dictionary<string, object>
            {
                { "StackTrace", ex.StackTrace },
                { "Source", ex.Source },
                { "Timestamp", DateTime.UtcNow }
            }
        ));
    }
}

// Prefer: Create detailed errors only when needed
public Result<T> Process(T value)
{
    try
    {
        // Operation
    }
    catch (Exception ex)
    {
        return Result<T>.Failure(ErrorCode.InternalError, "Operation failed");
    }
}

2. Error Recovery

// Avoid: Multiple recovery attempts
var result = await pipeline
    .Catch(error => Recovery1(error))
    .Catch(error => Recovery2(error))
    .Catch(error => Recovery3(error));

// Prefer: Single recovery with fallback
var result = await pipeline
    .Catch(error => 
        error.Code == ErrorCode.Timeout 
            ? Recovery1(error)
            : Recovery2(error)
    );

Validation

1. Batch Validation

// Avoid: Multiple validation calls
var result = value
    .Validate(x => x != null, "Value cannot be null")
    .Validate(x => x.Length > 0, "Value cannot be empty")
    .Validate(x => x.Length < 100, "Value too long");

// Prefer: Single validation with multiple rules
var result = value.Validate(x => new[]
{
    (x != null, "Value cannot be null"),
    (x.Length > 0, "Value cannot be empty"),
    (x.Length < 100, "Value too long")
});

2. Lazy Validation

// Avoid: Validating all fields immediately
public Result<User> ValidateUser(User user)
{
    var validationResult = ValidationResult.Create()
        .AddErrorIf(user.Name == null, "Name", "Name is required")
        .AddErrorIf(user.Email == null, "Email", "Email is required")
        .AddErrorIf(user.Password == null, "Password", "Password is required");

    return validationResult.IsValid
        ? Result<User>.Success(user)
        : Result<User>.Failure(validationResult.Errors);
}

// Prefer: Validate fields only when needed
public Result<User> ValidateUser(User user)
{
    return ResultPipeline<User>
        .Start(user)
        .Then(u => ValidateName(u))
        .Then(u => ValidateEmail(u))
        .Then(u => ValidatePassword(u))
        .Build();
}

Best Practices

  1. Use Value Types for Small Data
  2. Reduces memory allocations
  3. Improves cache locality

  4. Reuse Error Instances

  5. Cache common error messages
  6. Use static readonly fields

  7. Optimize Async Operations

  8. Use ConfigureAwait(false)
  9. Process in parallel when possible

  10. Minimize Pipeline Length

  11. Combine operations
  12. Validate early

  13. Optimize Error Handling

  14. Create detailed errors only when needed
  15. Use efficient recovery strategies

  16. Efficient Validation

  17. Use batch validation
  18. Implement lazy validation

  19. Memory Management

  20. Avoid unnecessary allocations
  21. Use object pooling for frequently created objects

  22. Exception Handling

  23. Catch specific exceptions
  24. Avoid catching general Exception

  25. Logging

  26. Log errors at appropriate levels
  27. Include relevant context

  28. Testing

    • Write performance tests
    • Monitor memory usage
    • Profile critical paths