/ SOFTWARE DESIGN, CONTAINER, DEPENDENCY INJECTION, FRAMEWORK

On containers and frameworks

It seems pretty popular right now to bash widespread software design practices, e.g. dependency injection, frameworks, annotations, etc.

Here are some references that highlight this:

While there are some downsides of adopting those practices, I believe there are more pros than cons. In that post, I’d like to address those points, and come up with arguments in their favor.

Ditching invalid reasons

First, let’s get rid of the elephant in the room.

I look at a minimum of one hundred codebases per year.

I’m sorry I do not have time to learn all of your magic.

— Greg Young
~1:14 in the above video

Honestly, I was quite shocked when I first watch the video.

I’ll try my best not to sound rude or anything, so I’ll try to come up with a similar attitude: what if I told you that because I’m very busy, you have to rewrite your Ruby app in Java, because I have no time to learn Ruby? I’m afraid your first reaction would be to tell me to go away. And you’d be right.

It’s not up to the codebase to adapt to the person auditing it, it’s up to the person to adapt to the codebase. Or not to adapt - and then not take on the auditing job.

I have no love for Gradle, and make no mystery of that. But when I had to work on an Android app, I tried to improve the situation by moving the spaghetti code in the buildfile to a plugin. Another approach would have been to walk away Not ask the team to move from Gradle to Maven before I even touched the app.

If the only reason for not using a framework is because one is not skilled in it…​ Consider whether this is a valid reason. I’ll let everyone draws his own conclusion.

Practical examples

I’d like to use some examples from the Spring framework.

Transaction management

Despite the current hype, transactions still play a major part in enterprise software.

Let’s start with the following code:

// The transaction manager is available somehow
PlatformTransactionManager txManager = ...

public void executeBusinessLogic() {

    try {
        txManager.begin();
        // Business code
    } catch (BusinessException e) {
        txManager.rollback();
        throw e;
    }
    txManager.commit(status);
}

The framework lets you simplify all the above boilerplate code like this:

public void executeBusinessLogic() {
    new TransactionTemplate(txManager).execute(status -> {
        // Business code
    });
}

The following snippet is equivalent to the previous one, using an annotation:

@Transactional
public void executeBusinessLogic() {
    // Business code
}

The benefit of using an annotation is that a casual glance at the method informs that the it’s transactional. It will even be written in the JavaDocs, should you take care to generate it.

SQL data access

In Java, there are different ways to access a SQL database:

  • JDBC
  • JPA
  • jOOQ
  • etc.

But nothing beats the simplicity of Spring Data JPA, which will take care of basic SQL generation:

public interface ProductRepository extends CrudRepository<Product, Long> {

    List<Product> findByNameOrderByNameDesc(String name);
}

Can you infer what that method does?

What would be the equivalent using just JDBC? Or JPA?

Benefits of frameworks et al.

That being said, if I understand the content of the critics correctly, it all boils down to one core issue: loss of control.

By adopting a framework, using a dependency-injection container, relying on annotations, etc., one gives it away some control in order to get benefits. Those benefits include (but are not limited to):

Faster development time

The main benefit is faster development cycles. One uses code that has already been developed and tested, no need to write the same boilerplate code for the umpteenth time.

Code. Just. Ready. To. Be. Used.

More reliable code

It’s said that the best way not to product bugs is to not write code at all. But since code needs to be written anyway, I personally prefer code that is already battle-hardened. The fact that the code I’m using is also used by a lot of other developers make it more likely that most bugs have already been found, and fixed.

More focus on business code

While most of the boilerplate code is taken care of, that leaves more time to create code that actually focus on resolving business problems. Are you really up to code your own testing framework? If yes, what about your own persistence framework?

I’ve seen it - "because those Hibernate guys did really a crappy job". Obviously, developers who weren’t part of the decision and who ended up maintaining the custom proprietary "framework" had a very different opinion of their respective merits.

Usage of a container also allow useful features, such as monitoring, out-of-the-box. This is already the case with the Spring Boot actuator, and soon will be with Microprofile.

Easier change from one company to the next

I’m old enough to have been developing when there were no frameworks. Or to be more precise, no Open Source framework. Because the abstraction level of most API is too low, most companies did write their own framework. That meant one had to learn a new way of doing things for one company to the next. The ramp-up time of every new developer entering the company was disastrous. And whatever framework one learned in one place was just a loss of time regarding employment in other places. Using a limited set of frameworks makes knowledge reusable.

Improved employability

The previous point goes both way. Having a shorter on-boarding time is a net benefit for the employee, but also for the employing company.

Astute readers might answer that some of the above benefits may also apply to libraries, and not only to frameworks.

This is a valid remark. However, the benefit is bigger for a framework than for a library. For example, it will take longer for me to develop if the team’s way to use a library is different than mine. Likewise, it will be harder to change companies if both have different ways of using a library, etc.

Framework or library?

As its name implies, a framework frames the way you code. Depending on who you ask, tt can be a cumbersome constraint or a useful guide.

On the shoulders of giants

In my understanding, the evolution of software has been to build abstraction layers upon abstraction layers. Nowadays, not many people move memory chunks using Assembly (but I have a lot of respect for you if you’re are able to). And those who do are limited in the software they can design. More complex abstractions mean being able to design bigger and more complex systems.

This attitude reminds me the dawn of Java. Developers who were used to manage memory themselves were pushing back against automatic management. "Letting the JVM manage memory is a terrible loss of control. That is not the right way to do it!" It seems that history proved them wrong: in most contexts, the container-managed memory model has won, even if the beginning was less than optimal (to say the least). Even Go didn’t go as far as to force developers back to manage memory by themselves. That is not to say that there’s no use-case for C/C++ and their like. But the trend is clearly there.

Conclusion

In essence, frameworks and containers are not perfect. But neither is your code. There are reasons not to use them. But the Not Invented Here syndrome is not one of them.

Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with narrower interests like Software Quality, Build Processes and Rich Internet Applications. Currently working for Exoscale. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.

Read More