Details

The Ultimate Result Pattern

Create an Error class to represent errors

public class Error
{
   public string Code { get; }
   public string Message { get; }
   
   public Error(string code, string message)
   {
       Code = code;
       Message = message;
   }
   
   public override string ToString() => $"{Code}: {Message}";
}

Create a basic Result class

public class Result
{
   public bool IsSuccess { get; }
   
   [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
   public Error? Error { get; }
   
   protected Result(bool isSuccess, Error? error)
   {
       if (isSuccess && error is not null ||
           isSuccess is false && error is null)
       {
           throw new ArgumentException("Invalid error", nameof(error));
       }
       IsSuccess = isSuccess;
       Error = error;
   }
   
   public static Result Success() => new Result(true, null);
   public static Result Failure(Error error) => new Result(false, error);
}

If you wish to return data, then add a generic Result<T> class

public class Result<T> : Result
{
   public T? Value { get; }
   
   private Result(T? value, bool isSuccess, Error? error) : base(isSuccess, error)
   {
       Value = value;
   }
   
   public static Result<T> Success(T value) => new Result<T>(value, true, null);
}

I think this will cover all your result needs.