General C# development standards and functional programming patterns for Zafiro projects.
This skill encompasses the general C# coding standards and functional programming patterns used across all Zafiro projects.
Async suffix in method names, even if they return Task._ prefix for private fields.Zafiro relies heavily on CSharpFunctionalExtensions for clean, predictable flow control.
Favor functional operators over explicit if (result.HasValue) or if (result.IsSuccess) checks for cleaner and more declarative code.
Map: Use when you want to transform the value.Tap: Use for side effects (e.g., logging) when you want to keep the flow.Bind: Use when the transformation itself returns a Result or Maybe.Match: Use when you need to handle both cases (Success/Failure or Some/None) and return a value or Task (branching).Result<T> Directly When Caller Expects ResultWhen a method returns Task<Result> and the SDK/service returns Task<Result<T>>, prefer returning the chained result directly instead of manually converting with Match/if.
Tap(...).result.Match(_ => Result.Success(), Result.Failure) unless branching logic is actually needed.private async Task<Result> ApproveInvestment(...)
{
return await service
.ApproveInvestment(request)
.Tap(_ => Load.Execute(null));
}
if (result.HasValue)
{
await notificationService.Show($"Success: {result.Value}", "Done");
}
// Use Tap for side effects
result.Tap(x => notificationService.Show($"Success: {x}", "Done"));
[!NOTE] Use the imperative
ifcheck only if using the functional approach significantly impacts readability.
dotnet build and verifying that there are no errors.