Skip to content

Instantly share code, notes, and snippets.

@duongphuhiep
Last active May 31, 2024 00:21
Show Gist options
  • Save duongphuhiep/d67c8d9a216b438937ddc204b7d0b8fd to your computer and use it in GitHub Desktop.
Save duongphuhiep/d67c8d9a216b438937ddc204b7d0b8fd to your computer and use it in GitHub Desktop.
Technical Error vs Business Error

Technical Errors vs Business Errors

When designing or learning about APIs or microservices, it's crucial to consider error handling. API's designer often categorize errors into "Technical" and "Functional" (or "Business") errors. However, this distinction may not make sense:

  • An API/micro-service should alway return ONLY "Functional" (or "Business") errors.
  • The API/micro-service migh crash while processing a consumer's request and so the consumer will naturally get a technical error. But the API/micro-service should not deliberately return a "Technical" error for consumer.

We will explore the distinction between "real technical errors" and "fake technical errors," which are initially technical but later manifest as business errors, thus becoming "fake technical errors."

  • HTTP 404 Business error (or Fake technical error)

image

  • Http 404 "Real" Technical error

image

  • HTTP 500: Fake technical error (or more precisely it was a Technical error which is transformed to a Business error)

image

  • HTTP 500: Real Technical error

image

We will look at the problem from a C# / Java programer perspective. Check out the following (BAD) codes:

try 
{
    ...
}
catch (Exception ex)
{
    throw new TechnicalErrorException("Issue case 1234, please contact support.", ex);
}

You might not write the codes exactly like this but indirectly (via some middleware for example) to achieve the same effect.

Many developers created classes such as TechnicalErrorException, or BusinessErrorException then make the application response as following:

HTTP 500
{
  "error": {
      "category": "TechnicalError",
      "message": "Issue case 1234, please contact support."
  }
}

or in case of Business Error:

HTTP 500
{
  "error": {
      "category": "BusinessError",
      "message": "Product out of stock"
  }
}

The motives are usually

  • to help users distinguish between "Technical Errors" and "Business Errors".
  • return a "beautiful" response for users when unexpected situations happen.

This practice is BAD

Throwing a TechnicalErrorException is a mistake

  1. the TechnicalErrorException class has little of help for our users.
  2. in fact, both the "beautiful" HTTP 500 response above are "Business Error" in the users eyes, evens for the first one which claimed to be a "TechnicalError" in the category.
  3. when you return a beautiful "Technical error" response, you should expect that the users might parse your response and do something with it. You will have to maintain the backward-compatibility of your "beautiful" responses.

I believe that by removing the class TechnicalErrorException along with the try / catch will help to make a clear distinction between "real Technical Error" vs "Business Error".

  • Business Error == Normal Response generated by the Application. An expected behaviour of the application returning error for the situation.
  • Technical Error = every other errors (unepxected situations)
    • (1) Most technical errors are impossible to be captured by your application: Load balancer problem, DNS, Memory overflow.. anything which prevent the consumer to get back the response from your application or make it crash.
    • (2) However there are some Technical Errors which your application is able to capture, thus do something with it: For eg: Business data inconsistency, or strange situation which shouldn't happens, third-partie services crashed...

Developers can't do anything about (1) in their application. But (2) is a gray zone. When the unexpected situation occurs and the application is not crashed (yet) then developers have choices:

  • (choice 1): They can transform this "Technical Error" to a "beautiful" response similar to the above example or the transformation may be as simple as returning a blank HTTP 500 without any other information. image
  • (choice 2): Close the eyes and let the application naturally crashed as ugly as any other Technical Errors. image

In the first choice, when you capture the "Technical error" then transform it to something else (either a nice response HTTP 200, or just a blank HTTP 500) then the transformation itself can be considered as a Business Logic. By doing that, you actually transformed the real "Technical Error" to a "Business Error" then "gracefully" returning it as any other normal business error. The framework .NET or your OS (Windows, Linux) would not know that there was a "Technical Error", because this error was captured and handled by you. It won't try to do things like sending crashed report, or restart the Application Pool when too much crash occurs..

My recommendation is the second choice: When something crashed unexpectedly lets them bubble up naturally as any other Technical errors (Load balancer problem, DNS, Memory overflow..). Don't try to handle them (directly or indirectly via some middlewares), don't try to transform them to any other kind of error (directly or indirectly via middleware).

You should go for the first choice only if it was a Business Requirement defined by the Product Owner. In this case you should understand that there is no longer Technical Error. The real technical error was captured and transformed to a "nice" Business error for your users, and the transformation rule is defined by the Product Owner.

Business Error handling in the Application

When developping application, it is better to mind your own business:

No need to anticipate that unexpected things might happens and try to do something with it Except if it was a Business requirement asking you to anticipate some unexpected errors and do something with them. Otherwise, let nature take its course when unexpected things happened.

In a C# application, I usually avoid to use the generic class Exception: either throw new Exception() or try catch (Exception). This class is representative for "Technical Error" / "Unexpected situation" in the gray zone, and should not be handled by any means directly or indirectly via middleware (again Except that your Business requirement asks for it with a reasonable justification)

I usually create a class called BusinesErrorException, and only play with it:

  • throw the BusinesErrorException and give as much information as possible to explains why did it is threw (raised), and what should we/support would do when they see this error?
  • catch the BusinesErrorException and transform it to a nice response (HTTP 500 for eg.)

Evens better, if we can handle BusinessError as any other normal response: no throw, no try/catch, just returning error as value. This practice is the best for performance but it might not well align with your Programing language philosophy or eco-system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment