DEV Community

loading...

A better way to handle magic values and constants?

MN Mark
・1 min read

It seems pretty well known that "magic values" in code are a bad idea and should be given descriptive names instead. Instead of this:

throw new Exception("invalid state: 0x02")
Enter fullscreen mode Exit fullscreen mode

it's preferred to define "magic values" as constants:

Integer STATE_OK = 0x01;
Integer STATE_BAD = 0x02;
...
throw new Exception ("invalid state: {STATE_BAD}")
Enter fullscreen mode Exit fullscreen mode

This works well thus far. However, what the user sees in either case is:

$ invalid state: 02
Enter fullscreen mode Exit fullscreen mode

I am thinking of frameworks and tools designed for technical users here, not non-tech end users.

In my actual use case I was able to find the source code and search out the constant name of the value reported to discover the actual problem, but this is rather inconvenient and not always possible like when using closed source tools or the user isn't familiar with the given language.

As a developer I am interested in how this experience can be improved. On the surface it seems string values would be an improvement but that clearly doesn't scale well. Is this just a documentation failure and no better way to deal with it in the code? Ever see a drastically different approach?

Discussion (8)

Collapse
val_baca profile image
Valentin Baca

On the surface it seems string values would be an improvement but that clearly doesn't scale well.

Why not?

AFAIK most languages with exceptions accept a string message in their constructor, so you can get typed errors with meaningful messages. In fact, I make a point to call out in code reviews where someone throws an exception but gives no indication of the state the code was in that threw the exception.

For example: it's one thing to throw a JSONParseException, it's another to point out where (line & column) it happened!

Collapse
mjb2kmn profile image
MN Mark Author

Really I was thinking of the Error or Exception itself. You might have an exception with a name and numeric id that could be thrown from many different places. Each time it's thrown could require a different message. I think it's mostly poor implementation I'm seeing. As pointed out, there are decent ways to deal with this, implementors just need to use them.

Collapse
avalander profile image
Avalander

String messages are great to inform the developer about what went wrong, but are not that great to handle errors programmatically. In Java, you use exception classes to decide how a given error is going to be handled. Therefore, you don't need error codes to identify the type of error because the exception class does that job.

However, error classes are a luxury that you don't always have. Imagine that you are sending errors over a network. If you only send error messages, the code at the other side of the network might have a hard time deciding how to handle any given error, while error codes make the discrimination much easier.

Collapse
alainvanhout profile image
Alain Van Hout

It sounds like an enum would be perfect for your use case: you can pass a meaningful object to your Exception class, and the enum value itself can offer a either a meaningful error code or a meaningful string, depending on the situation where you catch the exception.

throw new CustomException(INVALID_STATE);

It also has the added bonus of constraining your potential values. If however, there are too many different situations, which can overlap in error codes but could benefit from having specific error messages, the custom exception could be expanded to include a(n optional) message.

throw new CustomException(INVALID_STATE, "my context-aware error message");
Collapse
avalander profile image
Avalander

As you say, integer values are hard to understand by humans and string values don't scale well, so, why not both? A simple way to improve it is to send the error code and an error message. The error code can still be used to handle the error programmatically, and the error message should give any humans reading it an approximate idea of what went wrong. As far as I know, that strategy is customary in several languages and frameworks.

Collapse
mjb2kmn profile image
MN Mark Author

You may be right and it's just that simple. Perhaps I am thinking too much in the context of Java which is usually about as human-friendly as a brick.

Collapse
avalander profile image
Avalander

Back in my days doing Java I used to create a custom exception class for about everything that could go wrong in the application, like PlayerNotFoundException, CommandExecutionFailedException, MissingFieldException, RedditIsDownException, you call it. In the exception message I would put what data caused the exception and, if applicable, how to correct it.

In the code, you can simply catch the type of exceptions you are interested and react accordingly, and when you read a stacktrace saying my.app.PlayerNotFoundException: couldn't find player with id '-1' it's quite easy to diagnose what went wrong.

Collapse
databasesponge profile image
MetaDave πŸ‡ͺπŸ‡Ί

This seems like it may be a documentation problem. I don't see an issue with emitting a code, as long as it is well explained elsewhere. Digging through source code doesn't seem like a "good experience" :)

Oracle do this quite well, FWIW docs.oracle.com/cd/E11882_01/serve...