Yet Another Tutorial
Not sure what's come over me. Must be all the Coke ZERO. In any case, this tutorial is a continuation from my previous tutorials to setting up an automated build using Phing.
Test Driven Development
I've read a few anti- and pro-Agile rants in the past couple days. Because I'm somewhere in between, I can't really rant in either direction. Instead what I might try to do is give my opinion on the actual effects I've noticed from some Agile/XP practices on my own coding. Note that I'm probably only picking out the ones I like, so my posts on the subject will betray a pro-Agile bias. But, the sparse number of posts will hopefully balance the scales in demonstrating that there are only so many Agile/XP practices about which I actually care enough to write.
I'm a fan of Test Driven Development. I don't do TDD 100% of the time, and there are quite a few things I'm not sure how to automatically test (CSS tweaks, anyone?). But I agree with just about all the benefits I read in this pro-TDD article, though I'll re-arrange their listing by my personal opinion of their importance:
When you follow ... TDD, all your code will be testable by definition! And another word for "testable" is "decoupled". In order to test a module in isolation, you must decouple it.
When I use TDD, it forces me to write "decoupled" code. "Decoupled" is one of those magic words programmer types say to each other in intellectual flexing competitions. But TDD shows what it really means - code that can be isolated. The benefits of isolated/decoupled code are numerous - re-usability, less duplication, more concise, and I'd also say "testability" is a benefit.
This is not to say that you have to use TDD to write decoupled code. There are much smarter and more disciplined developers than myself all over who write excellent code without using TDD. But for me personally, TDD forces me into just enough structure and pre-code analysis to keep me from writing messy code that will need cleaning later. Speaking of which ...
Why don't we clean up code that we know is messy? We're afraid we'll break it. But if we have the tests, we can be reasonably sure that the code is not broken, or that we'll detect the breakage immediately. If we have the tests we become fearless about making changes. If we see messy code, or an unclean structure, we can clean it without fear.
The "safety net" feeling you get from gradually building up a big suite of automated tests is, like all "feelings", impossible to describe, but I'll try to relay an anecdote which might help.
I still don't consider myself a Java programmer, although I spend at least 50% of my time programming in Java. I don't "feel" comfortable in Java. But, in the course of our development, I made at least one deep and far-stretching re-factoring (more fancy talk for "change the guts of the code without changing its behavior") to maybe 30 different Java source files all over our code-base, with no hesitation before committing. My uncomfortable Java feelings were superseded by my comfort in the fact that all 800+ tests passed after I made the change. So I'm wasn't afraid of making the needed changes, even in a language in which I'm uncomfortable, because all the code was covered.
Again, this doesn't mean someone can't make sweeping changes unless they have tests covering all their code. I've simply noticed myself indeed becoming more fearless when I myself have to make those kinds of sweeping changes.
Have you ever integrated a third party library into your project? You got a big manual full of nice documentation. At the end there was a thin appendix of examples. Which of the two did you read? The examples of course! That's what the unit tests are!
This particular benefit is quite a distance behind the previous two. Mostly because the kind of example code you find attached to "nice documentation" is a better reference than unit test code. Unit test code is oftentimes performing some superfluous tricks for isolation, and/or hard to understand. It could be argued that if the test code is hard to follow, the tested code's design is to blame. But I think personally I'd rather move extra verbosity into my test code and keep my productive code clean. Could just be a matter of personal preference.
However, unit-tests are useful in understanding the intended use of the tested code. And programmers are more likely to spend their time to write test code that benefits themselves (see above) than they are to "waste" their time writing example code which benefits only others. So, lacking the refined, formal example code, unit tests can act as use-case specifications. (Though not 100% comprehensive design specs, as some might say.)
There are a few anti-TDD points with which I also agree...
For many, TDD is a new way of programming. As such, it has an associated learning curve, requires new thinking patterns, and takes time before it is comfortable to someone. However, I have found TDD easier and more enjoyable to adopt than Java. Some might say that isn't high praise for TDD, but to those people I would say, "Well, at least it's simpler than Java." TDD can be practiced in the language of your choice, and you will probably find that TDD resources from within your preferred language can really help to match your existing programming style with TDD.
TDD results in a LOT of code. In the course of adding tests for the ZF Tutorial app, I realized I was adding verbose testing code to already-verbose MVC code. Indeed, the test code for the controller was more code than the controller code itself. This is a simple fact of TDD - more code. There's no getting around it. You simply have to decide if the benefits of TDD outweigh its drawbacks, such as this one.
We have run into problems where our 800+ test suite takes a significantly long time to run (~10 minutes). This can be a real pain if you're working under strict CI rules in that every change you make, even that pesky css tweak, is supposed to be sent thru that test suite before committing to your source repository. Typically, though, once developers have the hang of TDD, they know what kinds of changes really need the entire test suite, and what kinds of changes can be simply checked-in, or can pass thru only a sub-set of tests. But the pain still exists in that you have an extra step of responsibility between writing your code and committing it. Again, weigh the benefits of TDD against this drawback, maybe come up with a compromise of some kind.
More tutorials
I've been neglecting my blog, but I've been working on more tutorials:
1. Tackling the Refactoring challenge from the Getting Started with ZF tutorial
2. Selenium Testing with PHPUnit
Controller Testing in Zend Framework
Ouch, the previous post here was pretty bad. Messy design and it didn't even work correctly. A better guide on the topic is here:
More on design patterns
Design patterns in the context of frameworks is a nice opportunity for me to take a break from my framework posts. I've been reading a bit on design patterns as a result of that previous post, and the poll activity on the Tulsa PHP Users Group.
In my readings, I came across this old post from the Loud Thinking blog re: Patterns & Practices over languages. Good stuff, and I'll recall my related personal experience.
Before joining SourceForge, I was okay in a few languages - Java, ColdFusion, WebMethods (shudder), Javascript, SQL, XSL/XPATH - and I was pretty good in one language - PHP. The first couple months at SourceForge were simply LOTS of PHP with a bit more architecture than I was used to - caching, entity objects, a Smarty view layer - but not much different than the PHP coding I had always done.
I was exposed to a pretty sophisticated Java architecture when I joined the Marketplace engineering team. But, my main task initially was to build a few simple wrapper classes that would allow our PHP scripts to communicate with the Java backend over STOMP to a message queue system (ActiveMQ, specifically). This would let us continue to write simple (display-oriented) PHP scripts which relied on the sophisticated Java backend for most logic. I still like that idea, and think it can work well if given proper time and attention.
But as we faced mounting time constraint pressure, and in the interest of leveraging our available talent pool, we stepped away from using STOMP and took out most of the PHP code - save for a proxy-type controller which speaks to the Java app server and some helper classes. We started using the Web MVC module of the Spring framework (in addition to the Core, Testing, Transaction, and Hibernate modules we were already using), and were therefore implementing everything, including display via JSP, in Java. So I poured over Spring reference material. (Highly recommended book)
It was quite a (beneficial) learning experience for me. As I learned (and accepted) the Spring approach, I found that I started to care less and less about Java - it just happened to be the language in which Spring is written - and I cared more and more about the ways in which Spring worked.
Those "ways" are, of course, design patterns. Inversion of Control, Object-Relational Mapping and Active Record, MVC, Front Controller, Command Controllers ... these are all just "ways" of doing certain things, solving certain problems, that are common to many systems. As I said, in learning Spring, I'm appreciating these patterns more than the Java language.
This is what lessened my resistance to frameworks, so much so that I became a fan of (some of) them! But, still having fondness for PHP, I started looking for PHP frameworks which work in the same ways that Spring does. But, it has really changed my perspective from analyzing languages based on syntax, (or types, or whatever) to analyzing languages based on the availability of design pattern implementations.
In this respect, and from my searching, I still think (though it may still be simple bias) that PHP is the best language available for the web. There are many PHP MVC frameworks (look at all the ones in the comments as well) which implement nearly all the best proven design patterns for the web. There's enough choice out there that PHP engineers can choose not only the design patterns to use, but also between many different styles of each design pattern. And of course, because PHP is so easy, it doesn't take an "architect" to craft their own style implementation of a favorite design pattern. (like the Active Record implementation I referenced in my last post.)
It was a big step for me to stop focusing on certain languages and instead focus on design patterns. I think it's been a big step upwards in my engineering aptitude/skill/whatever. And the more design patterns I learn, the more I realize there are so many others I don't know that could make my programming even easier. Sadly, I'm nowhere near the competence needed to recognize the "informal patterns" (i.e., unnecessary repetition/duplication) in my own code, but hopefully I'll get there.
For now, I'm still just learning my way into the topic and very glad I have an opportunity to do so.
