Errors in software

When it comes to software development, errors are more common than developers would like them to be. That’s why there are a lot of best practices and tools out there that help developers to write error-free software.

Sometimes automated testing, linters and compile tools are not enough, and some sneaky errors get inside our deployments. The larger the project is, the more true this becomes.

Error services

For managing this kind of case we need error services: tools that report unexpected exceptions and notify us when an error occurs. Notifications usually include the current backtrace, the server of origin, the number of occurrences and more context that allows us to identify and fix the problem. This is much more efficient than having to dig through log files searching for errors and other debugging techniques.

But not all errors will produce an exception, sometimes we would need to report an anomaly where throwing an exception would be inappropriate and could degrade user experience. This is when sending custom reports to the error service could work better than filling our application with code rescuing logic. Using this technique you’ll be able to leverage the error service to identify and solve issues you otherwise might not even know existed.

For this example we are using Airbrake, and while Airbrake works for multiple different languages our examples will be using its Ruby library.

The purpose of this post is not to teach how to use Airbrake, if you need to learn the basics Airbrake has a guide for that. In this article we will be showing a less common but very useful usage of Airbrake.

Sending custom errors to Airbrake

Let’s say we are doing bulk updates of a bunch of data, 1000 entries at a time and on each iteration some of the items fail to update. Our bulk_update method responds something like this:

result = [
           { item_no: 1, status: "updated" },
           { item_no: 2, status: "updated" },
           { item_no: 3, status: "failed"  },
           { item_no: 4, status: "updated" },
           { item_no: 5, status: "failed"  },
           ...
         ]

As you can see, not all the elements failed to update, so we need to report just those elements that failed, and throwing an exception wouldn’t be accurate since there are a lot of elements that actually got updated.

In this case, we can do something like this:

failed_items = result.select{ |item| item[:status] == "failed" }

Airbrake.notify_or_ignore(
  error_message: "Bulk update failed",
  error_class:   "Custom::BulkUpdates::UpdateFailed",
  parameters:    { failed_items: failed_items }
  )

Let’s dig into this method call more deeply:

  • Airbrake.notify_or_ignore: Notifies Airbrake of the error, the or_ignore part allows us to tell Airbrake to ignore a certain type of error if we want to.
  • error_message: A description of what failed.
  • error_class: This allows you to categorize errors and optionally ignore them later from Airbrake’s UI.
  • parameters: Extra info that can be sent to provide more context on why the error occurred. You can add whatever you want here and make your reports as verbose as you want. In our case we chose to send a list of the failed items.

Sending the failed items as a parameter using a custom error allows us to silently collect data without interrupting the user experience. If anything goes wrong that starts to cause these bulk updates to fail, we’ll be notified proactively and have a wealth of data without ever needing to connect to a machine to check logs or debug the application. This means problems get solved better, sooner, and faster, ultimaltey resulting in a better experience for the user.

Conclusion

Error services can be an useful wingman when it comes to finding why your application is not working as expected. And we don’t even need to raise unneeded exceptions to take advantage from them. Just make sure you add enough context to rapidly identify the issue and be able to fix it.

I hope my example encourages you to think of more creative ways to get proactive information about what is going on inside your application and enables you to build better software!