No Frameworks, Part 1

Twitter is not good for deep conversations

In a recent talk, I told the story of a very successful software project where I was the tech leader. In this project we built a web service + web application, in Java, without using any framework: no Spring, no Hibernate, no MVC framework, nothing like that. Contrary to prevalent opinion, we delivered quickly, faster than even I had believed.

And the code was reasonably clean and maintainable: five years after I left the project, many developers told me that they liked working on that product, and it was a formative experience.

At the conference, I quipped that “not having to spend time on Stackoverflow to solve framework configuration problems saved us a lot of time,” and that indeed was part of the reason why we delivered fast, but it is not the whole story. Common reactions to #noframeworks include:

  • You make the customer pay for reinventing the wheel
  • By your reasoning, we shouldn’t use compilers or standard libraries
  • You’ll write your own quirky framework
  • Developers will have a hard time understanding the codebase

All of the above objections are wrong, if you do things the way I recommend, which is, by doing Extreme Programming for real. Doing XP for real, for me, includes focusing and investing on simplicity, in a way that most developers are not usually willing to do.

Applying lean thinking to software development

Lean thinking says that we should optimize the end-to-end value stream, from when a customer need is identified, to when that need is solved; if that need is to be solved with software, the value is obtained when we deliver working software in a production environment.

What should we optimize? Obviously, for cost and time. However, since costs in software development are dominated by personnel salaries, the cost is dominated by the number of person/days spent to obtain value: hence, we should optimize for delivery time.

Once you know what to optimize for, lean thinking suggests that you observe the software delivery process, looking for waste, and act to remove that waste.

Terminology: In Lean, an activity is classified as “value adding” if it directly contributes to building the value that customers want; it is “waste” if it does not. For instance, if you’re building a chair, cutting and shaping the wood are value-adding activities; moving the wood from the warehouse to the worktable is waste. Sometimes it’s difficult to decide if an activity is waste or not. In this case, a simple thought experiment can help: if we did the activity more and more, would the customer be happier? Or not?

[Waste is further classified in “necessary waste”, something that if we don’t do, we will end up making the customer pay more, and “pure waste”.]

In the world of software development, writing the lines of code that will end up as part of the solution is certainly value-adding. On the other hand, a standup meeting is waste: it does not directly contribute to the delivery of software. If we imagine the standup meeting being extended to two hours or a full day, we clearly see that it would not make the customer any happier :) The standup meeting is probably *necessary waste*, because if we don’t do it, we reduce communication within the team and we end up creating more waste elsewhere. However, it is still waste, and we should try to keep it short.

There is a world of waste in building the wrong product, or the wrong features: you should certainly look in the disciplines of Lean Startup and service design, to make sure you are solving the right problem. I will not talk about these because they’re well covered elsewhere.

The kinds of waste that I want to talk about are the things that take away the best time from developers. Picture this: the developer comes to work; perhaps he’s 20 minutes late because of traffic. He comes in, settles down, checks email, has coffee with his collegues, he chats a little. Nothing wrong with this. Then he has a standup meeting, and perhaps other meetings for discussing planning or technical choices. All of this is still acceptable; they are not value-adding activities, but often they are necessary. Then comes the magic moment when the developers are in front of the keyboard, with their code open in the editor or IDE, and they are ready to do the value-adding activities that we care about. And they write a line of code, which requires, say, one minute, and they need to check if it works; so they start the application, and they wait; because the application takes one or two minutes to compile and start up. They spend another minute logging in the application and clicking through to get to the screen they are working on, and they confirm that their line of code works (or not.) Then they write another line or two of code, (time required: one minute) and again they need to check if they work, so again they waste three minutes restarting the app and testing it manually.

They could try to save time by batching changes: write 10 or 100 lines of code, and then test if they work. Alas, this does not usually save time, as the number of mistakes increases, and the mistakes interact with each other, and the end result is that debugging 10 or 100 lines of code takes significantly longer than debugging 1 or 2.

The point I’m trying to make is that the compile-link-start-login-navigate-to-the-right-screen cycle eats minutes from the best time that developers have: the time when they are sitting at their workstation, ready to write the lines of code that they know they have to write, having completed all the meeting obligations and coffee breaks and all other necessary wastes.

When you start looking at the work of developers in this way, you start to see the value of dynamic languages, where building the app is istantaneous, and the time saved in this way may trump the supposed gains obtained by static typing. You also start looking at the long compile times of C++ or Scala, and you see that those languages were not designed with the need of fast software delivery in mind.

You also start to see other wasteful activities. When a framework does not do what you want, there is often not a lot of reasoning you can do. The framework is too complex for you to debug it; the documentation is too vast, or too incomplete, and you know from experience that looking there is not likely to provide a solution quickly. So you go to Stackoverflow and hope that someone else has solved your problem. Then you try a possible solution, and if it does not work, you try another. All of this is, of course, pure waste, borne from the fact that you are using complex frameworks that may behave in unforeseen ways.

If, on the other hand, you have a problem with a simple app that does not use a framework, you can most likely solve it by reading the code, or stepping through the code in the debugger. All of the code of the app is your own code, and you can step through it, with no jumps into framework code, or AOP-generated code like that, for instance, Spring or Hibernate put into your app and that make debugging much more difficult. The solution to your problem is in your own code, not on Stackoverflow, and the size of your own code is many orders of magnitude less than the code in frameworks.

Getting back to the length of the compile-and-startup cycle, we see that frameworks do not help: Spring apps, for instance, take minutes to start, because they search the classpath for configuration classes. On the other hand, a no-framework Java web app with an embedded Jetty starts in under a second, and when you debug it in the IDE it has nearly-instant reload of changed code. No need to install JRebel. You don’t have to believe me; just try.

Other framework-related wastes include:

  • The time required to learn it.
  • The time required to upgrade your app when a new version comes up (this is work that the framework developer inflict on you, without your consent)
  • The time required to upgrade an old app that was using an outdated version of the framework (this is expecially bad when vulnerabilities are discovered in an old framework, forcing the customer to pay for upgrading a framework that is old and by this time poorly supported. Pure waste, as there is no business reason to change the app)
  • Time required to argue on the relative merits of framework A vs framework B, which leads to the mother of all framework-related wastes, the Meeting To Decide Which Frameworks To Use :)

Now this is all about the wastes of frameworks, and we haven’t even started to look into the risks generated by them. In the next part, I will talk about how the benefits of frameworks, and how you can get the same benefits without using them.

7 Responses to “No Frameworks, Part 1”

  1. Claudio Perrone Says:

    Well, you’re definitely not alone in thinking like this! But I suggest that the core problem is that OO languages like Java simply force people to write a lot of boilerplate code to get anything done (hence the proliferation of such complex frameworks, with all the side-effects that you describe).

    Working without frameworks was one of the first things that surprised me when I discovered Clojure. Rather than relying on a winner-takes-all framework, pretty much everyone uses a set of extremely compact, surprisingly stable little libraries. You can genuinely walk all the stack and see what what’s going on without getting lost! (In addition, some templates, e.g. Luminus, allow you to glue some of these libraries together, if you need more help).

  2. Uberto Says:

    You are right Matteo, as usual, but what about developers CVs?
    I’ve got some experience now in un-springing projects and the main resistance is from dev they wanted spring in their cv.
    I hope #NoFramework shop catch up! :)

    Ps. Logging frameworks are also evil!

  3. Giorgio Sironi Says:

    I’m no particular fan of frameworks: in a world of [micro|mini|]services I’m more used to microframeworks, libraries or embedded containers than to the “monolithic framework to rule them all” like Spring could be considered.
    However, my experience with frameworks in dynamic languages is not the one described: even with heavyweight frameworks like Symfony there is no need to “start the app” to run tests, they are run in a command line process, and they are mostly unit tests.
    At some point, however, you will put together a feature purely by configuration rather than writing code. That requires very good documentation, and acceptance testing, to counteract the stackoverflow problem…

  4. matteo Says:

    @Claudio Thanks for your comment! Yes, there is this “boilerplate” problem with Java, but it’s not as severe as some people make it; for me the biggest offender are the “database entities” that have many getters, setters and no behaviour. If you avoid making those, you’re mostly good :)

    I’m an old LISP fan, and I’m sorry I haven’t yet found the time to learn Clojure. Will do sometime!

  5. matteo Says:

    @Giorgio Thank you for your comment!

    I’m not familiar with PHP frameworks. With Rails, you definitely have this problem, as running even an empty test in Rails takes 10 or 20 seconds.

  6. Franco Lombardo Says:

    What a good post, Matteo!
    Anyway I don’t completely agree with your point about dynamic languages. For sure it’s really easy to work quickly with them, but working the right way with (some) statically typed languages could be nearly that fast, as your Java/Jetty example tells us, and, in my opinion, it could avoid some stupid mistakes.

  7. matteo Says:

    Hi Franco, thanks for your comment!

    I don’t think we have yet a full picture of the pros and cons of static vs dynamic languages. One thing I understood is that static typing of separate components, with things like WSDL or similar things that are being invented for REST services, produces tight coupling between the two component, that then must be deployed together. Typing is coupling!

Leave a Reply