Skip to content

What Is Success-Driven Development?

Success-Driven Development is a paradigm that designs code around the success path by using explicit result types instead of exceptions.


Exception-Centric vs Success-Centric Development

Section titled “Exception-Centric vs Success-Centric Development”

Problems with Exception-Centric Development

Section titled “Problems with Exception-Centric Development”

In traditional development, exceptions are used to handle errors:

// ❌ Exception-centric development - problematic approach
public User CreateUser(string email, int age)
{
if (string.IsNullOrEmpty(email))
throw new ArgumentException("Email is required.");
if (!email.Contains("@"))
throw new ArgumentException("Email format is invalid.");
if (age < 0 || age > 150)
throw new ArgumentException("Age is not valid.");
return new User(email, age);
}
// The caller "must remember" to handle exceptions
try
{
var user = CreateUser("invalid", -5);
}
catch (ArgumentException ex)
{
// Exception handling... but what if you forget?
}

Problems:

ProblemDescription
Easy to forgetThe compiler does not enforce exception handling even if the caller omits it
Not visible in signatureYou cannot tell from the function signature which exceptions may be thrown
Performance costExceptions incur high performance costs such as stack trace generation
Violates pure functionsThrowing exceptions is a side effect that violates purity

Success-Driven Development solves these problems:

// ✅ Success-driven development - recommended approach
public Fin<User> CreateUser(string email, int age)
{
return
from validEmail in Email.Create(email)
from validAge in Age.Create(age)
select new User(validEmail, validAge);
}
// Result handling is "enforced" at the call site
var result = CreateUser("user@example.com", 25);
result.Match(
Succ: user => Console.WriteLine($"User created: {user.Email}"),
Fail: error => Console.WriteLine($"Failed: {error.Message}")
);

Advantages:

AdvantageDescription
Type system enforcesThe compiler enforces result handling
Failure possibility is explicitThe function signature explicitly states that failure is possible
Maintains pure functionsPurity is maintained without exceptions
Performance optimizedNo exception stack traces

This tutorial uses the LanguageExt library. LanguageExt is a powerful library that enables functional programming in C#.

Terminal window
dotnet add package LanguageExt.Core
using LanguageExt;
using LanguageExt.Common;
using static LanguageExt.Prelude;

Fin<T> is a type representing Success or Failure.

// Success cases
Fin<int> success = 42; // Implicit conversion
Fin<int> success2 = Fin<int>.Succ(42); // Explicit creation
// Failure cases
Fin<int> fail = Error.New("Value is not valid");
Fin<int> fail2 = Fin<int>.Fail(Error.New("Error"));
// Result handling
var result = Fin<int>.Succ(42);
var output = result.Match(
Succ: value => $"Success: {value}",
Fail: error => $"Failure: {error.Message}"
);

Validation<Error, T> - Validation Result (Error Accumulation)

Section titled “Validation<Error, T> - Validation Result (Error Accumulation)”

Validation<Error, T> is a type that can collect all validation errors.

// Single validation
Validation<Error, string> ValidateEmail(string email) =>
email.Contains("@")
? email
: Error.New("Email requires @");
// Parallel validation via Apply (collects all errors)
var result = (ValidateEmail(email), ValidateAge(age), ValidateName(name))
.Apply((e, a, n) => new User(e, a, n));
// On failure, all errors are collected as ManyErrors

  • User input errors (invalid email, negative age, etc.)
  • Business rule violations (division by zero, invalid date, etc.)
  • Domain constraints (exceeding maximum, below minimum, etc.)
  • System resource exhaustion (out of memory, out of disk space)
  • External system errors (network connection failure, database connection failure)
  • Unexpected system errors (file deletion, insufficient permissions)

Q1: Does Success-Driven Development mean never using exceptions at all?

Section titled “Q1: Does Success-Driven Development mean never using exceptions at all?”

A: No. Exceptions are still used for unpredictable system errors like network connection failures or out-of-memory conditions. Success-Driven Development handles only predictable failures such as user input errors or business rule violations with result types.

Q2: Does using Fin<T> degrade performance?

Section titled “Q2: Does using Fin<T> degrade performance?”

A: It actually performs better than exceptions. Exceptions have a high cost for generating stack traces, while Fin<T> is a simple value wrapper that only incurs allocation cost.

Q3: Can it be adopted incrementally in an existing codebase?

Section titled “Q3: Can it be adopted incrementally in an existing codebase?”

A: Yes. Start by making the Create methods of newly written value objects return Fin<T>. Since it can coexist with existing exception-based code, there is no need to change everything at once.


Now that you understand the concept of Success-Driven Development, let’s prepare the practice environment. In the next chapter, we proceed with .NET SDK installation and LanguageExt package setup.

0.3 Environment Setup