DEV Community

Max
Max

Posted on

Discuss: How should you handle errors in your library code?

I like to work on open source projects in my free time and thus have created a few JavaScript modules in the past. Most of the times, they're like really small libraries and only serve one purpose. As the main point about a library is being used somewhere else, it is quite likely that somewhere else will be somebody else's code, codebase, project, you name it. As such, it's of course very important to document your API (which I always try my best at, I swear! 😄). Still though, there's this one question I regularly have when creating a library API:

What if something goes wrong?

Let's think of a scenario here to illustrate my problem. Recently, I've been working on a little JS library that takes a string with a time and timezone, and gives back an object providing converted versions for UTC and local time. The usage looks like this:

import tizo from 'tizo';

tizo("19:30 cest").utc;
// → '[ 17, 30 ]'

As you can see, in the above example the timezone cest was provided. What I've been thinking of was, what if the timezone that is passed to the library does not exist? Be it because it actually doesn't exist, or tizo doesn't know about it.

The two ways I see here are:

  1. a "hard error", clearly interrupting execution, for example via Promise rejection, error throwing or process.exit(1)
  2. a "soft error", doing your best regardless, for example giving back a string stating the error, ignoring the parameter in question and work with default values, etc

What do you think? Lemme know!

Top comments (5)

Collapse
 
dmfay profile image
Dian Fay

Throw or reject, all the way. Libraries generally shouldn't control the process, but the other mechanisms you've identified for surfacing "hard errors" are the standard. It's on the consumer to ensure that they account for exceptional behavior where it's possible for it to occur, especially where that depends on input from the consumer.

Returning a string message is a non-standard and therefore inferior variation on proper exception raising. Ignoring or second-guessing invalid input is the stuff of which nightmares are made. Imagine somebody using your timezone code to manage flight schedules: a time and invalid timezone come in from, say, Chongqing, you've decided to ignore invalid timezones, the code returns a "UTC" time that's actually eight hours ahead, that makes it into a central database, and suddenly air traffic control's day is a lot more interesting.

Collapse
 
maxrimue profile image
Max

Thanks for your input! I think that makes sense what you say. To explain: the original motivation behind tizo was to be as easy to use for it's use case as possible, which is why I expect a string formatted in various possible ways instead of expecting the user to do the interpreting, but that puts me into this situation where i/o becomes hard to predict.

Collapse
 
dmfay profile image
Dian Fay

That's understood! The important principle here is that a library shouldn't do anything surprising. Something going wrong or being irresolvable may be a surprise in one sense, but raising an error so the consumer can decide what to do about it is the expected course of action for library code in that circumstance: you're not adding another surprise on top. Conversely, hiding errors or anticipating the user may afford a pleasant surprise sometimes, but you can't guarantee that it'll always be that way.

Collapse
 
nexdrew profile image
Andrew Goode

Generally I agree with Dian, but I think it depends on the API you want to use. Using moment or luxon as an example, when a string can't be parsed into a valid value, they return a special object that is "invalid", which you can check with .isValid - I can see arguments for both sides of this coin, but it at least allows the consumer to write a branch of logic that does not require catching an error (or handling a Promise rejection).

For your libraries, you could have 2 API methods - one that throws and one that doesn't - as long as it's clearly documented, you can let the consumer decide which they prefer.

Collapse
 
maxrimue profile image
Max

That sounds like a great idea, I will try that! This distinction in API would probably also help make clear the operation itself is rather "guesswork". Also, I like offering a "strict" API with "strict" (or as I called them, hard) errors, and the opposite with the opposite type of error handling.

Maybe the conclusion to this discussion here is, that error handling should be just as strict as the API itself? Make a "magic infer" function that guesses in the worst case, and an argument based one that throws when clearly defined requirements are not met.