Examples for this kind of magic are:
- automatic dependency injection
- Many aspects of Ruby on Rails
- Monkey patching
- Type inference
- Convention over configuration
These kinds of mechanisms can be very helpful, because they often reduce a lot of boilerplate and make software development more efficient. But because they usually are leaky abstractions, they can also be pitfalls. In order to evaluate the benefits and trade-offs involved in these mechanisms I tend to use the following (highly subjective) guidelines. If you don’t like the word „magic“, just substitute the word „abstraction“ and you get about the same mileage out of them.
Magic should be useful
Any use of magic should solve an actual problem and provide a demonstrable benefit over the alternatives. Magic should never be an end of itself, or be employed simply because it is traditional. If it’s not useful, it merely adds complexity for no benefit.
Magic should be understandable
While magic is often used to hide complexity, it should be possible to „look behind the curtain“ and figure out what is happening behind the scenes. There should be no black boxes that are completely opaque. Note that it does not necessarily means that you have to understand them, just that it is
possible to do so if you want or need to.
Magic should be observable
A good way to ensure that magic is understandable is to make it easy to observe. This allows inspecting its behavior to determine what inputs it takes, which rules it follow, what effects and consequences it has and what assumptions it makes. In many cases these observations can be validated using tests to ensure that the magic keep working properly.
Magic should be explicit
Magic should always be invoked explicitly. This prevents magic demons running amok without anyone knowing about it. Being explicit does tend to make magic less magical, but it also helps understanding the system as a whole. There might be highly complex boxes in it, but it is clear where they are. Think of them as signs that say „This is where the magic happens“.
Magic should be fail gracefully
Magic is great, until it stops working. Because it usually works so unobtrusively when everything is going fine, failures can be really difficult to deal with. One of the worst cases is when magic fails silently, e.g. when some automatic security mechanism is no longer correctly authorizing access because of a misconfiguration. This kind of issue is hard to detect, and often harder to diagnose. Other failure modes produce screens full of stack trace that point back to some arcane internals inside the magical mechanism, but offer no clues as to the actual causes. If a magic gadget fails, it should do so gracefully, point out the source of the issue, and offer possible solutions.
These are the guidelines I rely on when evaluating the costs and benefits of these kinds of magical mechanisms. As all guidelines they are quite subjective and not absolute. They merely a set of criteria to keep in mind for these kinds of decisions.
Have fun. Enjoy coding.
Your INNO coding team.