I really don’t have time for this, but I’m going to do it anyway. I’m starting a new blog post series that’s roughly “How do I know what the best way to write software is?” Before I get started on it, I wanted to talk just a little bit about how we know something.
I took a couple classes in college on Philosophy, and one particular set of lectures stands out to me all the way to this day. We spent a long time talking about epistemology, the (not so) simple subject of how we obtain knowledge. The first approach is the Rationalism of Rene Descartes. In this view, we “know” something only if we can know it to be infallibly true. To a rationalist, “knowing” Separation of Concerns is a good practice means that you understand exactly why and how Separation of Concerns leads to better results.
The other view promoted by David Hume and others is Empiricism. As the name implies, empiricists “know” something to be true if it has been observed to be true. To an empiricist, the only way to know that Separation of Concerns is valuable is to have made and recorded observations that show improved quality and sustained productivity.
The unfortunate trap is that we can be wrong in a variety of ways. As an empiricist would say, our reasoning can be false and lead us to erroneous conclusions about what we should do. Descartes himself fell victim to this in his own postulated theory of light, which isn’t consistent with modern physics. The Rationalists will tell us that we can’t trust our senses. So even if hard numbers and measurable observations can lead us to “know” something, what does a study or even our own experience really tell us? We can easily make false deductions from our observations or fail to make the observations that are right in front of us.
There was recently a study published about the effectiveness of Test Driven Development debated by both Phil Haack and Jacob Proffitt that seemed to suggest that using TDD leads to higher quality software — or maybe it doesn’t do anything of the sort. I’m not taking the study seriously either way because:
- My personal experience has led me to believe that TDD is effective and leads to better structural qualities of code and higher efficiency. Generally, I’ve learned how to effectively apply TDD and I feel like I understand why and how it works.
- The study was performed on college students. TDD has a nontrivial learning curve, and punishes developers who try to write code with tight coupling or lack of cohesion. I don’t think I could have effectively utilized TDD as a college student. Heck, I struggled on my first experience with TDD and at the time I had 3+ years of lead experience under my belt. Now, if I’d had a coach with TDD experience, maybe that would have been different.
Agile practices are criticized, and maybe rightly so, because of a lack of empirical numbers to prove the claimed benefits of those practices. I’d certainly worry if I saw multiple studies showing that Agile practices were creating worse results than traditional methods. I won’t dispute the value of empirical data and methods like Six Sigma. All the same though, I think that rationalism is in some ways more important to us in software development because I think it’s just too hard to use empirical data alone to make decisions in software development. We simply don’t have enough history behind us to have accurate empirical proof for just about anything. If nothing else, we need to apply a lot of analysis to any empirical study we do have to understand the causes for the numbers.
One of the things that keeps software development from being a straightforward science is that our “laws of physics” are changing rapidly. A couple years ago I read some older authors questioning the effectiveness of TDD by quoting several studies performed in the late 70’s and 80’s about the ineffectiveness of COBOL developers doing unit testing. The reality is very different for .Net or Java development today than it was for COBOL development. TDD is viable with modern programming languages where the atomic units of code are much smaller for isolated unit tests and the compilers are fast enough to enable the TDD feedback loop. I really don’t think those unit test studies on COBOL are all that relevant to me.
It’s important to us as software developers to consider why and how a practice works because that examination of the underlying forces will help us to better apply that practice. Most of the benefits of the tools and practices that we use aren’t automatically delivered to us on our initial implementation. The benefits are largely determined by the manner in which we apply practices. I think Agile practices are advantageous, but yet I’ve seen instances where they failed my team and I. I feel like I can rationalize why the successes worked and the failures failed: feedback cycles and reacting to that feedback, orthogonal architectures, clean code, good collaboration between team members, seamless handovers, and an environment that actively supported an adaptive approach. Forgetting whatever you happen to call your approach or what your approach actually is, I think that being mindful of the first causes will point you in the direction of success. In the next post I’ll kick off my discussion of just what I think those first causes are.
When I worked at ThoughtWorks, we had an Analyst with a background as a professor of philosophy. I had a conversation with another developer about Descartes vs. Hume one day in the team room as we were pairing. From the look on my analyst’s face, I don’t think I would have passed his philosophy class;-) I’m going to email the link to him just so I can imagine how much he’d cringe over this post. There’s a good discussion on Rationalism versus Empiricism from the Stanford Encyclopedia of Philosophy that explains the differences in much more detail. Please feel free to correct my interpretation of Descartes and Hume in the comments, and I promise never to dip this shamelessly into pseudo-intellectualism ever again.