Errors are the universal language of programming. Make them count.

A well-designed error helps fix bugs

The other day, I saw this server error in my inbox:

Traceback (most recent call last):
File "/usr/local/lib/python3.8/asyncio/events.py", line 81, in _run
self._context.run(self._callback, *self._args)
File "/usr/local/lib/python3.8/site-packages/aiormq/base.py", line 115, in <lambda>
future.add_done_callback(lambda x: x.exception())
asyncio.exceptions.CancelledError
Error during render of workflow [REDACTED]
Traceback (most recent call last):
...
carehare._exceptions.ConnectionClosedByServer: RabbitMQ closed the connection: 501 FRAME_ERROR - type 3, all octets = <<>>: {frame_too_large,160193,131064}

The most important part of every API is its errors

Healthy error design helps programs hum.

Users have features; programmers have errors

Users play until something breaks. Programmers play until nothing breaks.

… and now, some shade for Python asyncio

Over my 20 years of programming in over 20 languages, I have never seen an error as badly-designed as Python’s asyncio.CancelledError. It can happen at any time, for any reason, without stack trace or error message. Python’s own documentation recommends against catching it.

The Rules of Errors

  1. Obey your programming language. Java, Python and Ruby use Exceptions. Go uses “errors”. C uses errno. Rust uses Result. Scala uses Either. Don’t waste everyone’s time writing Results in Python or catching panics in Rust.
  2. Design for all possible errors. If you’re using a network, design for disconnects. If you’re writing to disk, design for failed permissions and full disks. If you’re accessing the database, design for an SQL error. And remember: you can have two errors at once.
  3. Design error abstraction layers. An HTTP library should treat ClosedConnectionException and SSLException as distinct errors. A Twitter API wrapper can probably treat them all as IOException. Many languages use inheritance for this.
  4. Document every function’s errors. Emulate Java’s API documentation (example): for every function, list all the possible problems, what they mean, and how to fix them.
  5. Plan what happens next. Should an SSL error crash an app? Decide! Write documentation and error-message text to suggest what your hapless reader should do next. Make stack traces are legible. If an error causes another error, help the programmer log and address both.
  6. Test each error. Unit tests are quickest. They prove that a programmer who experiences the error can see it and handle it.

And then … you’re done!

The next time you set down to work, try this: design and implement all your errors first.

Journalist, ex software engineer