Refactoring 035 - Separate Exception Types
Distinguish your technical failures from business rules
TL;DR: Use separate exception hierarchies for business and technical errors.
Problems Addressed 😔
Confused contracts
Mixed responsibilities and error treatment
Difficult handling
Poor readability
Misleading signals
Exceptions for expected cases
Nested Exceptions
Mixed exception hierarchies
Improper error responses
Tangled architectural concerns
Mixed alarms
Related Code Smells 💨
Code Smell 73 — Exceptions for Expected Cases
Exceptions are handy Gotos and flags. Let’s abuse them.
Code Smell 80 — Nested Try/Catch
Exceptions are a great way of separating happy path versus trouble path. But we tend to over-complicate our solutions.
Code Smell 184 — Exception Arrow Code
Arrow code is a code smell. Exception polluting is another. This is a mortal combination.
Steps 👣
Identify business exceptions
Identify technical exceptions
Create two separate exception hierarchies
Update the code to throw the right one
Adjust handlers accordingly
Sample Code 💻
Before 🚨
public void Withdraw(int amount) {
if (amount > Balance) {
throw new Exception(”Insufficient funds”);
// You might want to show this error to end users
}
if (connection == null) {
throw new Exception(”Database not available”);
// Internal error, log and notify operators.
// Fail with a more generic error
}
Balance -= amount;
}
After 👉
// 1. Identify business exceptions
public class BusinessException : Exception {}
public class InsufficientFunds : BusinessException {}
// 2. Identify technical exceptions
public class TechnicalException : Exception {}
public class DatabaseUnavailable : TechnicalException {}
public void Withdraw(int amount) {
// 3. Use the correct hierarchy
if (amount > Balance) {
throw new InsufficientFunds();
}
if (connection == null) {
throw new DatabaseUnavailable();
}
// 4. Apply safe logic
Balance -= amount;
}
// 5. Adjust handlers in the calling code
Type 📝
[X] Manual
Safety 🛡️
This refactoring is safe if you apply it gradually and update your code with care.
You must ensure all thrown exceptions are caught at the proper architectural level.
Why is the Code Better? ✨
You make the code clearer and more predictable.
You express technical failures and business rules separately, taking corrective actions with different stakeholders.
You also reduce confusion for the caller and improve maintainability.
How Does it Improve the Bijection? 🗺️
This refactoring strengthens the mapping between real-world concepts and code representation.
In reality, business rule violations and technical failures are fundamentally different situations.
Business exceptions represent expected alternative flows in your domain model.
Technical exceptions represent unexpected system problems that break the execution environment.
By separating these concerns, your code more accurately reflects the real-world distinction between “business says no” and “system cannot proceed”.
Limitations ⚠️
You need discipline to maintain two hierarchies.
If you misuse them, the benefits are lost. You also need to communicate the contract clearly to the clients of your code.
You should also create your own integrity tests to enforce these rules.
Refactor with AI 🤖
Suggested Prompt: 1. Identify business exceptions 2. Identify technical exceptions 3. Create two separate hierarchies 4. Update code to throw the right one 5. Adjust handlers accordingly
Without Proper Instructions 📵
With Specific Instructions 👩🏫
Tags 🏷️
Exceptions
Level 🔋
[X] Intermediate
Related Refactorings 🔄
Refactoring 004 — Remove Unhandled Exceptions
Creating YAGNI exception classes pollutes our environment. Let's remove them.
Credits 🙏
This article is part of the Refactoring Series.
How to Improve your Code With Easy Refactorings
Refactorings are amazing to grow and improve our code