Boilerplate vs. Magic

Just about a month ago, Phil Webb tweeted:

What would you like to complain about?

[ ] Too much magic
[ ] Too much boilerplate

Phil Webb - @phillip_webb

and just a little bit later, Josh Long tweeted:

“too much magic” = you haven’t read the docs on @SpringBoot ’s auto-configuration

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference...

Josh Long - @starbuxman

Here's my take...

Magic

Magic means that things happen at runtime that I cannot immediately explain just by reading the code. At least to me. I know, this definition is quite fuzzy. There is always something that happens at runtime that cannot be expected by reading the code, even if you program assembler (processors are weird). The difference between "not magic" and "magic" here is just "How big is the surprise?".

But I would definitely consider a method that violates the "Single Responsibility Principle" or "Command / Query Separation" to be magic (from the caller's point of view):

public User getUser(final long userId) {
    transactionManager.newTransaction();

    User user = userRepository.get(userId);
    user.setLastAccessed(System.currentTimeMillis());
    userRepository.save(user);

    transactionManager.commitTransaction();
    return user;
}

When you just read code that calls this method, you will never expect that this method changes the user in the database. But you can just open the code of the method and find out. So the magic here is not quite strong (i.e. on a very low level).

Here's another one:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void persistLastAccessTime(User user) {
    user.setLastAccessed(System.currentTimeMillis());
    userRepository.save(user);
}

Now the method is reasonably named. It sets the access time and persists the user. It still does two things (violating the SRP), but they are somewhat related. And it always runs in its own transaction.

The magic that happens here cannot be found in the code itself anymore. The magical thing here is how Spring implements the Transaction - Especially the "REQUIRES_NEW": It suspends the current database connection and opens a new connection for the new transaction. It's the only way REQUIRES_NEW semantics can be implemented with most (if not all) relational databases.

Your code will work fine in your unit tests, it will work fine in your integration tests, it will even work fine when you test it on your staging server. But then you start a load test, and everything breaks down. What has happened? You didn't carefully configure your database connection pool size to be much bigger than your thread pool size, and you ran out of database connections.

This doesn't have to happen. If you read the documentation really carefully, you can find out that you will need much more parallel DB connections with REQUIRES_NEW than without. But the point is: To find out what's really happening, you have to read the documentation and know what that means for your application, or you have to load test. This is a stronger kind of magic.

One last example, again with Spring, but this time it's Spring Boot:

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

This is the complete source code of a demo application. When running this class, Spring Boot will start an in-process web server. If we go with my definition from above, this is definitely magic.

Boilerplate

Boilerplate code is code that you have to write again and again. You'll write it in every application you work on. Sometimes you even have to write it multiple times in the same application. It's the boring code, that nobody really likes to write.

Boilerplate, Magic

Magic and boilerplate are both subjective. Look again at the transaction annotation example from above... Some developers I know would immediately say "Well, of course this requires a new DB connection" while others would be quite surprised that the defect I described can really happen under high load.

And with the Spring Boot demo application, the whole point of writing the application like this is to have Spring Boot do a lot of work for you in the background. So I, personally, hope that people who write code like this know what they are doing (i.e. that they know Spring Boot well enough).

Magic and boilerplate are not necessarily bad. Consider the Spring transaction magic from above: Yes, writing begin and commit is not really hard. And if you would have done it yourself, you definitely would have spotted the too-many-sessions-but: You would have realized that you cannot implement the "REQUIRES_NEW" on the current session. On the other hand, if you always write "begin" at the beginning of a method and "commit" at the end, this is quite redundant.

Or let's look at Spring Boot: Yes, configuring and starting an in-process web server is not hard - it's just a couple of lines of code. But if you have to configure multiple things (a web server, JPA, REST endpoints, ...), it will become quite a bit of boilerplate and Spring Boot can really help you focus on what is important in your application.

Boilerplate is not the opposite of magic! You can have both in the same code base, even in the same class. You can have a single class where there is a lot of boilerplate and where you cannot explain everything that happens just by reading the code.

And you can also have neither: If you find the right abstractions, you can have really expressive, self-documenting code. Code where everything you need to know about what it does can be found within the code itself. And this code will contain no boilerplate, because you found the right abstractions. This is easier with some languages than with others, but I don't think there is any language which make reducing boilerplate impossible (at least for most boilerplate).

Too Much Magic

Boilerplate is mostly a problem when writing code. Magic when reading & debugging. We read & debug more than we write.

David Tanzer - @dtanzer

Libraries and tools that do magic for us help us to develop software faster. They make the boring tasks go away and help us focus on our business code. That's the theory, at least.

On the other hand, they sometimes make reading and debugging our code harder. You want to step into a method, but can't, since the method was created with a byte code manipulation library. Or you read the code of some method you want to call, but don't see all its behavior, since some aspect oriented programming tool adds behavior at run time.

Libraries and frameworks where I constantly have to read documentation or debug code I didn't write are in this category for me: They make it harder for me to understand my own code. Don't get me wrong: I know that I have to learn and understand the tools I am using. But with some tools, I have to come back to the documentation or open the debugger more often and for longer than with others.

Since we spend much more time reading and debugging code than writing new code, I really don't want too much magic like this - i.e. magic that makes reading and debugging code harder - in my code.

Too Much Boilerplate

Too much boilerplate is worse for me.

I would mostly prefer annotation generation magic over writing stuff myself. If I understand the tools it is less code to read.

Janusz Leidgens - @Killerdackel

Boilerplate is mostly a problem when writing code for the first time. You have to write it, and you have to get it right. But after that, you won't touch that code anymore.

Boilerplate is not so much a problem when reading code. It is often easy to understand and easy to recognize. So you can just skip it.

Since we spend much more time reading and debugging code than writing new code, I usually don't mind a little bit of boilerplate code. But Janusz has a point here: If you have a lot of boilerplate code, you have more code to read and more code to maintain.

What Now?

Check boxes, not radio buttons. *takes hat off*

What would you like to complain about?

[ ] Too much magic
[ ] Too much boilerplate

Samir Talwar - @SamirTalwar

Of course, too much boilerplate and too much magic both are problems. As I said above, boilerplate is not the opposite of magic. You can have both, and you can have none of them.

You can write expressive code that gives you a lot of information about what is happening just by using good names and following some general design principles. And when you find the right abstractions for your program, you will still have very little boilerplate.

If you have a statically typed language, you can write your code in a way that your type checker or compiler tells you if you got something wrong. This will make your code even more expressive and safer. For example, when you use Spring, use the Java configuration whenever possible. The compiler will prevent you from making a big number of mistakes you could make with the XML configuration.

And always try to eliminate both: Boilerplate and magic.

Posting Type: 

My name is David Tanzer and I have been working as an independent software consultant since 2006. I help my clients to develop software right and to develop the right software by providing training, coaching and consultanting for teams and individuals.

Learn more...