Test Driven Development 101
I’m back after a long hiatus, and I’m probably talking to the air right now, but that’s OK. For the two or three of you actually listening to me, listen on. We have a number of projects of different sorts at the company I work for, and you’d be surprised at how few do test driven development—and how few even write unit tests. In this day and age, not writing tests that can be automated at all is inexcusable. At the very least, the tricky stuff should be thoroughly tested. This article is for anyone who is either skeptical about or interested in Test Driven Development. That includes managers as well as developers.
What’s the Problem, Man?
We all ( at least should ) want to write quality software, and we will take this as the “given” in geometry. Since we want to write quality software, how do we do it? In the early cowboy days of software engineering, it was painful to write code. First you did all your planning, wrote what you hoped would work, printed the stacks of punch cards, and then loaded it on the main frame. Which explains all the texts on the huge design up front methodology. However, that was before my time. When I first started, we wrote a little, ran the software, and debugged. Which explains why debuggers were so important at that time. However, times have changed. We now have a plethora of unit testing frameworks and maintainable build scripts to incorporate the tests into the build process.
As soon as we ran into some hard problems that really couldn’t be completely designed up front (like TCP/IP stacks), we developers had to figure out a way to make sure things worked properly. I mean, the spec is pretty complete, but there are a lot of details and practical limitations imposed by hardware that you won’t find in the spec. If you attempted to alter the spec to include all these corner cases, you would also lose any way of understanding how it is supposed to work. This is how the automated unit test was born. All these corner cases had to be accounted for so that future changes to the code won’t break the existing functionality. I think everyone recognizes the importance of testing. It’s just how, when, and where testing happens that people disagree on.
Another problem that is often brushed under the table is code slipping in that doesn’t need to be there. How often have you tracked down a problem only to discover code that was never supposed to exist be the culprit. You remove the offending code, and violá it works! The code could have been there from old legacy requirements that no longer apply, or it could be a developer without experience over thinking a problem. Nevertheless the code is there and it is causing problems.
If you adopt a clean as you go philosophy to developing software, you had better make sure you don’t accidentally breaking anything as you refactor your code. Even with “safe” refactoring, there is an inherent risk that you can inadvertently break something you didn’t think you would. If only there was a way to make sure all the important functionality keeps working…. Oh yeah, you do have a full test suite don’t you? Oh, it didn’t get written because you were under the gun and you were on a roll? Too bad.
Test Driven Development (TDD) was designed to not only address these issues, but to instill a discipline so that your unit tests would get written. Let’s face it, all people are lazy. It’s in our nature to not do anything we think is a waste of time. Sure we’ll do a little work now to avoid a bunch of work later, but we need to know it really is going to avoid a bunch of work later. What some people fail to realize about TDD is that the time will be spent somewhere. Either you write your tests up front or you spend time in a debugger later. Either you prove that your approach will work now, or you do it later. Either you break up your work into testable chunks now, or you attempt to do it later and fail at it.
OK, So How Does It Work?
At its heart, TDD is pretty simple—you simply perform a prove, fix, prove cycle. First you prove it doesn’t work . Then you fix the problem. Finally, you prove your fix worked . If you are lucky, your first “prove” step will prove the code already handles what you were thinking about. Write the test anyway, because you need to make sure that any changes will still support that test case. It’s fairly easy to see how to perform the mechanics of TDD, but less about it’s design implications without a little more explanation.
One of the side-effects of writing your tests before you write code is that it makes your code easier to test. Remember, people are lazy? Since a proper unit test sets up the environment and checks the effect of the call after, we make it easy to set up the environment. In fact, the less we rely on network connectivity or environment variables, the better. If a bit of code can be tested just by passing objects into it and examining the return value, then you aren’t going to be overly clever in your implementation. Easily testable code, also happens to be more modular. If you can pass in a mock object for something that would normally talk to the network so that you can have the mock imitate the situations you would encounter there, you can more easily test just the small unit you are working on.
Unit Tests vs. Integration Tests
In the best of all worlds, a project would have both. Unit tests make sure that the smallest unit (such as a method on a class) is doing what we expect it to do. A proper unit test also tests only one aspect of that method. It’s not uncommon to have several tests for the same method to make sure that all the corner cases are taken care of. Integration tests make sure all the different units work together like we thought they would. A unit test uses mock objects to isolate the thing we are testing from everything else. An integration test sets up the complete environment and runs the test against that environment. They are different tools for different problems. To do TDD, unit tests are required, but additional testing is a plus.
More often than not, code that is properly unit tested will work just fine. However, there are some issues that only crop up when the whole system is put together. Perhaps there is some race condition, or some complicated event loop that only appears in certain conditions. Once you track down the cause of the problem, you can write a unit test to reproduce the condition that caused the mess in the first place. With the new unit test, you fix the code, and everything should work in integration again.
Many times, the integration tests are all done manually. The challenge is that it is hard to set up a complete environment, test the user interface or system messaging, and evaluate the results automatically. The problem is that over time the number of tests that people have to do for a release becomes daunting. People are less picky than machines. If there is a tool to support integration testing like Selenium or some other testing framework, use it to at least catch regression issues. You know those issues where you accidentally break something that used to work when you add a new feature? Anything that was working and gets broken needs to be tested every time. Machines are good at doing rote repetition like that.
Battle Rhythm
When you sit down and start doing TDD, you’ll develop a battle rhythm. Most IDEs these days have support for running your unit tests without going through the whole build process. It’s pretty convenient, and it makes TDD a lot easier to do. So, you’ve got a new requirement and you need to get it working. It’s good to have a general idea of where you want to go, or how it is supposed to work, but don’t be a slave to that idea. We start writing the unit test at the easiest place it is to begin—whatever that may be. I personally find it is best to start with the happy path (the path where everything works as expected). For example, look at the pseudo code below:
Test String is a URL -------------------- 1. Use the string "http://bloritsch.d-haven.net" 2. Pass the string to the String Utility "isURL" method 3. Assert the response is "true"
Of course, it’s pretty easy to make this one test pass. All we have to do is write the StringUtility.isURL() method to simply return true. No evaluation or anything. When we run the test, we prove that solution is good enough for now. So we need to start thinking about the next test. What if the string is not a URL? So we add the next test:
Test String is NOT a URL ------------------------ 1. Use the string "I'm not a URL, ignoramus!" 2. Pass the string to the String Utility "isURL" method 3. Assert the response is "false"
Now we’ve proven we have some work to do. So we change StringUtility to return true if the string starts with “http:”. It’s the simplest thing, right? But what if we want to include SSL encrypted URLs, or mailto URLs? So you add tests for them, and make them pass—without breaking the other tests you’ve written. All these tests are cumulative, so while they may have been extra work at the beginning, they can save your bacon later. Don’t forget about those corner cases, what if the string is null ? Etc.
By the time you are done with this method, you’ll have done the following things without even realizing it:
- Documented what you consider a URL and what is not a URL (design documentation side-effect)
- Proven the design works (design proof side-effect)
- Tested the implementation (implementation proof)
- Provided a safety net to do refactoring (supporting implementation malleability)
Sure the method is just a part of the overall design, but you’ve thought about and decided how the method is going to be used from the perspective of someone using the method. It’s a shift in thinking from being the “implementer” of the method, which usually results in code that is easier to use elsewhere. You’ve also introduced a level of trust in this method that you wouldn’t have if you just reached for that regular expression you found on the net to determine if something is a URL or not. You’ve also introduced a boundary where the implementation can be as simple or complex as you want—but code that uses the method won’t care about those details.
The battle rhythm of proving, fixing, and proving actually improves your development speed. It may not seem like it at first, but by testing all along the way we’ve minimized the time we will have to spend in a debugger. The ramp up time is a little slower, but as you get your battle rhythm going, you stay at a constant pace. Without TDD, I find myself working with bursts of productivity interrupted by long periods of finding out exactly where I went wrong in a debugger. With TDD, I find myself working at a steady pace, and those occasions where I missed something I spend much less time in the debugger. Once I’ve discovered the culprit, I add the test case that reproduces the error condition and then make it work. Now, when I refactor code I can make sure I don’t reintroduce the problem accidentally.
Silver Bullet?
There is no silver bullet, and no golden hammer that will make things work perfectly the first time. There are only tools that help you get closer to the ideal. TDD is a tool that helps improve quality from the start. Most detractors of TDD look at the claims of documenting your design as being false—or at least unreadable to normal people. I may give them that argument, but TDD isn’t about documenting design, it’s about building a better quality product with the minimal amount of investment. It’s about improving your productivity over the course of a software project. It’s about reducing the number of “doh!” bugs to virtually none saving your brain cells for the more complex problems. Finally, it’s about minimizing the risk involved in refactoring or even rewriting your software.
All of these benefits are things that the text books say are a good thing. It’s also done in a way that is less painful to developers. Writing documentation is a pain in the butt, however, writing test cases is something that directly benefits the developer. It benefits the project over it’s period of performance. Bottom line? More bang for the buck.
Tags, Users, Preconceptions, and Real Estate
For us power users, the benefits of folksonomies are self evident. The metadata we apply to something , whether it is a picture, an article, a movie, or something else assists us in a variety of ways. It can be used to help us find that thing again, or it can help us understand what went into making that thing, or something that only benefits you. Of course, those tags can help other people as well, but that benefit is by coincidence and not necessarily design. However, for people who are completely new to tagging, the benefit is elusive. There is some user education that needs to go on. If a third party creates a cool thing that consumes a set of tags, that is enough to educate the newbies.
The wide disparity of user experience makes it hard to design systems properly. On one hand, there is a distinct education gap that needs to be there for the uninitiated. The folks who just don’t seem to get it aren’t necessarily Luddites (AKA technophobes). Just like anything, nobody is going to do something unless they get benefit out of it. Tags do help with refindability, notations, and integrating separate sites together via mashups.
The issue that social site designers face is balancing ease of use, education, and supporting all the baggage that seems to come with these sites. After all, there is a certain expectation that power users have of a “web 2.0” social site. They expect to be able to use their information across several sites, pulling data in through loose, but well defined interfaces. It’s also near mandatory to provide some way for users to find and talk to each other. All these functions have to be accessed in some way.
The danger in letting the power user define the user interface, is that they want nearly every feature at their fingertips. It’s just a physical impossibility. A screen has only so much real estate, and only a handful of features are necessary right there. Simplicity and negative space go out the window, and you get a crowded interface that will scare away any new users. You really need something that gives normal users the impression that they can use the site. Negative space is reassuring to people. Having only the right tools available at your fingertips also helps. I think that Flickr has done a good job for doing this for people sharing pictures, but borrowing the design wholesale for something completely unrelated probably won’t work so well.
The danger in designing to the lowest common denominator is that you provide an interface that is too restrictive, and becomes painful to do anything cool. You want to give your users the feeling that they can do cool things with your site. That includes the power users and the newbies as well. Too much negative space, and simplisticness don’t provide enough of a clue for what a user can do. Sure they get over the “I suck” stage pretty quickly, but they never reach the “I rock” stage. Worse, if it is too hard to figure out how to do advanced things your users will sink into the “you suck” stage and leave.
As always, the balance is difficult to work out—but not impossible. But to add to the design challenge is the nature of social interaction. When you design a site for social interaction at any level, you have to address a few issues. First, a community that is too small can’t sustain itself. People need to find other people that they can trust to help them improve in some way. Second, if a community gets too big, it will collapse under its own weight. When there are too many people the individual voice gets drowned out and people lose interest when their voice can’t be heard. Sure you might have a couple shining stars, but breaking the barrier to become a star becomes near impossible unless you started in that position when the community was small. It’s the nature of human interactions. When the only currency that you can trade is ego, we need to create a way for that currency to get you as far as possible. Just creating new groups for discussions isn’t the best. It’s hard to break the small community barrier. There needs to be something bigger than personality to generate interest. There has to be a common goal, which is more difficult to achieve than common interests. Sure you may like to see sunsets, but how much can you really talk about them?
Finally, I apologize for taking so long to post. I’ve been very busy with design meetings with my client. There are things we may not see eye to eye on, but we will eventually get to a happy medium where we can both be happy. That’s the longest part of the design process when the customer has their own opinions. At the end of the day we have the same goals, and we both want the users for the system to be happy. We just have different ideas of how to achieve those goals.
RAD to a Point

The Rapid Application Development (RAD) tools of the past were more like sprinting toward the goal. It might work if you are running 100 yards, or maybe even 400 yards. If you are running a 10k there is no reason to start out at a sprint. In fact you’ll find that the limitations of the RAD tools will get in your way and keep you from finishing the 10k.
The point of doing at least a few projects using test first practices is to develop some conditioning so that you can bring your pace up. With a comprehensive test suite you tend to do things so that as few red flags appear as possible. That means that your software is more truly modular. It helps develop good practices and style, which all contribute to the overall goal of increasing your normal pace.
What’s important is that you choose tools that compliment what you are doing. A good pair of running shoes will do you better than some basketball high tops. Same goes for the frameworks you use. Sometimes the framework can get in your way and hold you back. Sometimes it helps keep you on point and even helps push you forward. One isn’t necessarily better than the other in every situation.
Now, when you race cars there is a counter-intuitive concept where good braking will actually help you go faster. As you round the corners, you brake going into the turn so that you can accelerate coming out of the turn. It’s the same concept when you manage your software team. By adding review periods so that you can take stock of where you are and where you are headed, you can refocus the team so that they can perform quicker. The trick is to provide just enough of a slowdown to keep the team under control while not grinding them to a halt.
A well tuned team of developers can get the job done quicker, more consistently, and using less cost. The trick is careful tuning and tweaking of the team and how you do work to make sure that everything just works. To truly do rapid application development, you don’t need a tool to do everything for you. You need a team of professionals who know how to get the most out of the project.
Taking Her Slow
There are several ways to approach making good software, and you have to balance that with what works for you. I’ll pull from my experience with photography to pull an illustration. You can take hundreds of rapid-fire pictures with the hope that maybe a dozen are worth keeping, or you can carefully craft each picture so that almost every picture is a keeper. If I left it just at that you might get the impression that every project should be carefully crafted and architected. However, if you don’t know how to take it slow, you’re rapid fire might be nothing but misses or just OK shots.
A view camera has several kinds of adjustments that allow you to align the film and lens perfectly for sharp pictures. By aligning the film plane so that it is parallel with the side of a building you can make that face perfectly square. The verticals are vertical and the horizontals are horizontal. Then all you have to do is adjust the lens board so that the picture is in focus. Add some vertical and horizontal shift and you can ensure the picture is just right. There’s even more you can do in the darkroom to ensure that the end product is what you envisioned when you started. The nice thing about a view camera is that the film and the viewing port are the size of your proofs. 4×5 for the smaller view cameras and up to 8×10 (and 11×14 if you have a ridiculous amount of spending money) film sizes. It’s as close to WYSIWYG photography as you can get.
The process is very slow and deliberate, but the camera is used for things that don’t move much like landscapes, products, and architecture. You can use them for portraiture if the person you are taking a picture of doesn’t mind sitting and waiting while you fiddle with all the settings. Some photographers might have someone stand in while they get the settings and then bring in the real person so as not to waste time.
An SLR is much simpler to use in comparison, particularly when there is a small computer in there to ensure your exposure is correct based on the light coming in the lens. In fact, some SLRs can take 6 to 10 pictures per second. While not movie camera speeds, it increases the chance of capturing that “critical moment” with subjects that move rapidly. For example, the situations in basketball are constantly changing and capturing the drive to the basket, or a player juking the defense requires capturing that “critical moment” when you can tell in the picture what is going on without seeing the other pictures before and after it. The critical moment when everything comes together and the picture just takes your breath away.
The process is very quick, and you do have to rely on some of the assistive technologies in the camera to help you focus on the part that is important. SLRs are best suited for rapidly changing environments where the subject is always in motion. That doesn’t mean that you can’t get very nice pictures of what view cameras are best suited for. However, your options are limited in what you can do, and even the $1000+ tilt/shift lens doesn’t give you the ability to perfectly align everything like a good view camera. The pictures from and SLR are much smaller in comparison, so tiny imperfections are magnified to make pictures you can actually see and use.
I bet you are thinking I’m tying the analogy to software process aren’t you? Well, you’re wrong! Ha! I threw you for a loop. I’m talking about the design of your software, working with tools, and when you should take it slow versus going high speed. The truth is, very few software projects have a fairly static set of requirements. Unless you are creating command and control software, assembly line automation, or focusing on implementing a new search algorithm, the requirements tend to keep moving. They may move as fast as a basketball game, or they may move as slow as a game of tiddlywinks, but they are constantly changing. That’s why we reach for our favorite assistive technologies like frameworks, object/relational database mapping tools, logging, etc. Anything to make our jobs easier.
However, you owe it to yourself to take it slow every once in a while. Create your own framework for once. Solve that issue that’s been bugging you where your tool is almost good enough, but not quite. Take a fresh perspective, and start playing with looking at the problem differently. There’s always a little downtime between projects, and there’s always something stuck in your craw because things didn’t work out quite right. If you never shift your perspective, everything you do will look like what everyone else is doing. In a world with 20 competing projects that all do something similar, looking like the competition is death. Unless there is something to set you apart from the rest, why would anyone bother using your software? Instead of always following others, lead for once.
One of the things that separates successful photographers from people who take family snapshots is the ability to visualize the end product before you press the shutter release. Whether you are working with a big view camera or a small SLR, you need to have an idea what you are after. There will be happy mistakes, and there will be times where the vision changes as you are in the situation. Without a vision or a goal to aim for, you will end up with a bunch of crap. It might be OK to share with your buddies or your family, but you wouldn’t expect anybody to pay money for it.
It’s the same with software. I subscribe to the continuous design mindset for software, particularly as you get feedback you can adjust the outcome to better match what you are trying to do. That does not mean that you should be completely devoid of any idea of what the software should look like or do. The vision in your mind has to deal with whatever is the most important thing you are trying to do. Are you trying to make it easier for people to do work? Does the current user interface look like a kindergarten art project? Yes, allow the vision to change and be refined through the process, but have one at the beginning.
Repitition is Good for the Soul
In martial arts, we perform kata (also known as Hyung or just simply forms). Contained in the kata are a selection of techniques, stances, transitions, etc. to help train your body to move properly. There’s usually a general focus of the kata, and as you progress each kata builds on the lessons from before. According to the rules of progressing through the belt ranks in Tae Kwan Do, they explicitly say you should have performed the kata 300 times that are prerequisites for the next belt. That’s a lot of repetition. Essentially, you really have to know the kata inside and out before you can progress.
If you do it right, you learn the areas you have problems with and you work on fixing those issues. Each repetition of the kata shouldn’t be a mindless action because you will never have reached perfection. There’s always something to improve. You learn subtle nuances as you slow down and visualize your opponent attacking. You learn that minor changes in your stance, shifting of your weight, etc. will have a profound impact on the power you can deliver and the ease of moving into the next technique. You’re constantly learning and improving.
As software writers, we tend to have to solve the same solutions over and over again. Each time through, we learn from our past experience and seek to improve on what we’ve done in the past. It’s one of the reasons why I think pattern chasers have missed the boat. If all you do is mindlessly slap together some patterns you learned from a book or an online resource, you never learn. You never can appreciate what the technique buys you, and how much (or little) of it you have to do for the application at hand. Some parts of the problem are so ingrained you really don’t have to think about it. That’s OK, and that’s expected. However, the corner cases, the challenging parts are where you have to focus a little extra mental effort. Following a recipe book isn’t going to get you over the hump.
In martial arts, physics is our friend. When we apply force to one part of the problem (the body), it will move in predictable ways—even if that problem is resisting. When we’ve gone through the repetition of performing the forms, one-steps, etc. we can instinctively apply the right force to get the problem to move in a way that we want. If we do our job right, that problem will be in subjection in short order.
In software, logic is our friend. When we apply control to one part of the problem (the software), it will move in predictable ways—even if system events try to throw a monkey-wrench in it. Each time we deal with the issues that a machine can throw at us, we can instinctively know which system events we can ignore and which we need to control. If we do our job right, the application will be finished in short order.
Unit tests are the best way we have to understand how the code works in an application. The goal of a good unit test is to express in as few lines as possible how the code should behave under specific circumstances. If you put too much in your unit tests, you never know just quite what might be going wrong. With unit tests, you can ensure a certain amount of predictability in the system.
Integration tests are the best way we have to understand how the application works in its environment. The goal of a good integration test is to express in as few lines as possible how the system should behave as a whole under specific circumstances. The integration tests should simulate or force certain system events to ensure the application handles it robustly.
The best way to describe it with a good mental picture is again with the forms. As we are performing our forms, we can focus on each technique one at a time. Some techniques are going to be particularly troublesome for you (one I’m learning right now is kicking my but with some jumping techniques). However, you can isolate parts of the whole kata and focus on just that technique (or two if you are having problem with a transition from one technique to the other). These are like unit tests. However, your training is incomplete if you never put it all together and perform the kata as a whole. Your sensei may have people attacking you as the kata is designed, but it doesn’t change the form itself. The practice of performing the whole kata is like the integration tests.
Repetition provides you with several advantages over cookie cutter approaches: deeper understanding of the problem, better understanding of your weaknesses, and more confidence in the solution you chose. Another, more subtle, advantage is that for the parts you know well your brain does disengage allowing you to move “in the flow” for longer stints.
Annotations: The Language Shoehorn?
A while back, before they had such a wide variety of shoe sizes to fit everybody’s foot we had this tool called a shoehorn. The shoehorn was there to help force your foot into a shoe that no longer fit. Well, it’s purpose was to keep the heel of the shoe from collapsing—but back in the day we used it to get a little more life out of a shoe that was too small. Buying a new pair of shoes was out of the question, because it was a choice between spending the $30 on shoes or some other need that we had. Times have changed, and so has my budget.
Background
Software languages have undergone many different fundamental shifts in thought over the years as well. Originally we had machine language and its symbolic cousin Assembly Language. Originally they had little structure and GOTOs (AKA JMP instructions) were written to specific addresses. Some natural evolutions happened as processors became more complex and subroutines were born, enabling some of the more Calculus like breakdown of applications. The first thought shift was Structured Programming which replaced hard addresses with symbols and registers with variables, and enabled an arbitrary number of variables—despite the limitations of the processor underneath. Then came Procedural Programming with C and its collection of similar languages. You’d think that Object Oriented Programming came next, but that was just one of several other shifts in thought. You also had declarative languages, logic languages, etc. For a longer breakdown you can visit Wikipedia’s Programming paradigm page. Each programming train of thought lends itself to a particular set of problems.
The ultimate sophistication is simplicity. Leonardo da Vinci
There are many aspects to simplicity, and it’s easy for me to throw this term around and add the disclaimer about how it’s easier said than done. Instead, let’s explore what makes software simple. What goes into this word simplicity that I keep harping on?
- Economy of concepts. There should be no more than one or two fundamental design principles that are used consistently, and without exception. If there are two principles, the choice between them also has to be consistently applied and clear when to use which principle.
- Consistency. As soon as you add one exception to any rule, you’ve just made it a lot more difficult to understand. For you math happy folks think of the complexity in terms of Re+1 where R is the number of rules and e is the number of exceptions (in total). Smaller numbers are more simple and easier to understand. For example, if you have 9 rules and rule 1 has 2 exceptions and rule 3 has 4 exceptions the complexity is 96+1 or 4,782,969. That’s bad.
- Predictability. Every action that you can make has to have a clear and predictable outcome. To understand the impact of unpredictability, treat the math for predictability like the math for consistency. For everything that can go wrong, you’ve just made the system that much harder to comprehend.
- Self evident. Based on the consistently applied principles and the predictability of actions, the course of action for the user to take should be self evident. In short, once you’ve learned how things work you shouldn’t need anyone to hold your hand when you have to make a change.
Now you get an idea of what I mean by simplicity, and how it is actually hard to pull off for anything but simplistic systems. I mean if you only have two rules and two exceptions you still have a relatively simple system. Of course, once you start adding more rules and actions the complexity increases exponentially.
Annotations Are Mixing Concepts
I’ll be the first to say that Annotations are not completely useless, and they can help address some otherwise potentially sticky problems. However, as soon as you use Annotations to perform a programming task you are using declarative programming. Java and C# are supposed to be Object Oriented Programming Languages. Build languages like ANT and Make are declarative languages. You declare some tasks and their dependencies and allow the build process to sort it all out. Java wasn’t designed like that, but because there were some aspects of programming that are too hard to do in Java’s version of OOP we introduce annotations.
Annotations provide a way to mix concepts in a declarative fashion.
All the Annotation does is mark something in the class with some sort of tag. In essence it’s like a marker interface for the class, method, attribute, or parameter. The most innocuous use of Annotations is simply to mark a method for documentation purposes. It doesn’t take up compiled class file space or runtime memory, just gets added to the documentation. That’s a reasonable and predictable use for annotations.
The tricky part is when we change what the application does when we use annotations. For example, marking methods as a web service endpoint changes how the method is used. Marking a method as a test method affects how that method is called. Or in the case of Java ActionPack, marking a method with @Filter has more side effects. That method will now never be used for an Action, and the filter will be called before/after all the actions that it applies to. I recently added a convenience method so that you can query whether an action is marked with an annotation or not.
I used this solution so that I could mark all my AJAX support methods with the @AJAXSupport annotation. Why would I want to do that? Two reasons: for documentation purposes, and so that I don’t have the Single Sign On (SSO) service bring me back to an AJAX support page. The system would automatically set a callback URL in a filter applied for every action in the system. If someone called an AJAX support action that returns a partial page result, and then clicks on the SSO link the return URL from the SSO service would be for the partial page result. I had to do something (other than manually listing the actions and testing each one of them) to prevent that from happening. So I added an annotation so that I could check if the action has that annotation or not.
When I explained that to someone on the team, they responded with “I would never have guessed that”. Of course they wanted the equivalent of a modal dialog box on the web—which I believe is bad even in desktop application design. Another complaint was that I was mixing application logic with display logic. To be fair, it is navigation logic which is sometimes considered application logic and sometimes considered display logic in different situations. However, from a pure “separation of concerns” principle I was now mixing concerns in my code.
Does that mean that the solution was bad? It works, and it was the simplest thing to maintain and perform. It does not mean it is the most elegant solution or the purely simple solution. For one thing, I’m mixing declarative logic with object oriented logic. Another problem is that I’m mixing navigation logic with application logic. To some extent you can’t get around that, but that can be a symptom of design smell as well.
So am I saying that Annotations are evil, and because they are mixing concerns and programming paradigms we should avoid them at all costs? No. I’m saying that they trade code complexity for conceptual complexity. The complexity is not really gone from the system, it’s just that it has been moved to a different form of complexity. You might be able to grasp each concept in isolation, but mixing the two will probably make it hard to maintain in the future.
Part of the reason that Ruby on Rails works so well is that they did keep the concepts as simple as they could. There are relatively few rules to learn, and the rules are applied consistently. It was also built on a language that was flexible enough to keep everything object oriented, so there is no need to resort to the declarative annotation model.
Justification for On Complexity
I chose the formula because as soon as you introduce one exception, the human mind has to sort out whether or not that exception applies to this rule. Even if there is one exception to a list of 10 rules, we have to recall whether the rule we are using has the exception or not, and if it does whether the exception is in effect. A list of rules with no exception still has the complexity of deciding which rule (or set of rules) applies so it has a base exponent of 1. You add one exception, and you’ve squared your complexity. For example a set of ten rules with no exceptions has a complexity score of 10, and as soon as you add one exception it becomes 100.
The complexity values have no units and no real meaning other than to provide a relative indication of what you are getting yourself into. It helps you understand the number of decisions the brain has to sort through before it is satisfied that it can predict that you are doing the right thing. Sometimes a little change can provide a huge impact on the relative complexity. The idea is to keep the numbers as small as you can.
Make Learning Your Software Fun
I have to say that both academia and corporations suffer from taking themselves too seriously. Sure there is a certain amount of propriety that you have to have with your clients, but people like having fun. They want to enjoy themselves. So why is it that science becomes such a fun-sucker? I mean you would think that having subjects like “funology” would be a great topic, but the scientific approach makes everyone stuffy. Then you visit the campus of MIT or something like that and people have robot powered couches and other cool stuff. Why? Because it’s fun!
If you play video games, you’ll invariably have to learn something before you really get into the game. The rules of fighting, how to get around, etc. Some games are better than others about teaching you what to do without making it feel like a classroom setting. Ok, press the ‘X’ button two times really fast… Thankfully the education levels are usually pretty quick. It’s a pity you can’t say “I already played the game, can’t I skip the class?”.

If there is one thing that video games can teach us “serious” programmers (you can take yourself too seriously) is that if you properly reward your users for good actions, then they will keep doing it. Whether it was the intention or not, the Ruby on Rails framework teaches us how the writer of the application prefers to do web applications. The naming scheme for tables and the wiring of controllers, helpers, views, and models into one application all have their rewards. The fundamental reward for us developers is that if we do it the blessed way, we have less work to do with configuring and wiring things together. Even the code generators teach by example how to do things like how to do CRUD actions (the scaffolding feature). It now becomes fun to write web applications because we are rewarded for most things. Our imagination will help us overcome those obstacles that are beyond the basics we learned from the framework.
There is nothing that says we can’t use the same model for our applications. If our users learn how to use the app to make their lives better in simple, yet profound ways, they will want to become power users. If the users never get over the “I suck” threshold, even after examples and handholding, you will lose them. Making learning to be a fun activity doesn’t require a hippie teacher standing in front of a classroom with a guitar singing the times tables to the tune of “Gilligan’s Isle”. It just means that you make your users feel empowered, cool, or at the very least useful.
Better is Better, not More
In this day and age, we are used to having more and more shoved in our faces. We go to a coffee shop and we have to figure out how caffeinated, flavored, and masked over we want our coffee. Because of all the crap that they put in your $5 cup of coffee you can’t tell if the beans are burnt, replaced with dirt, or if the coffee is actually good. The overwhelming choice is there to make the coffee user feel empowered over their addiction. I happen to like my coffee, but the exorbitant prices and crappy coffee keep me from going to places like $tarbucks.

The same thing has made its way into the world of photography. People behind the counter at your local camera shop have the audacity to say that digital makes you a better photographer because it’s easier to take a butt-load of shots ! I don’t know about you, but something like that smells bad in my mind. A butt-load of shots with no thought put into them are just as bad as only 36 shots with no thought put into them. Yet they tell you these things because they need to move these $1000 cameras off their shelves, and people buy them thinking this magic bullet will suddenly make them better. The cameras come equipped with all this auto-focus, auto-exposure, auto-forget-about-thinking stuff and people think that if the camera takes care of everything they will be good. If you want a camera that does everything for you, save some money and buy a point and shoot camera. They do everything the big boys do and cost hundreds less.
But the point is not about the rant on cameras. It’s the rant on a false sense of security , and the “silver bullet” mentality we tend to get ourselves into. When it comes to the designer’s view of the world, there is a very difficult balance between enabling your users to be better and turning your users into brainless idiots. Take for instance the View camera. That’s the camera pictured in this post. It’s a large format camera, which means that it is slow to set up a shot, expensive to take a picture, and requires a lot more thinking in advance. So why have one? Photographers who learn how to use these properly become better with all their cameras because they are forced to think more about what they are doing before they do it. It’s the thought that makes the users better, not the camera or the fact that it is slow and expensive to operate. Those are side-effects of providing all kinds of operations that normal cameras just can’t do. You can have a several thousand dollar Hassleblad with a tilt-shift lens, and you won’t be able to match what you can do with a view camera. They are different beasts altogether. Granted, you probably won’t be able to tell the difference when the pictures are small. But when you blow your picture up to over four feet tall you’ll be able to tell.
Too much automation is just as damaging as not having enough automation. The balance we need to strive for is to help the user think about their job and not the tool . Its about finding the right options and the right level of automation for the task at hand. Sometimes the point and shoot is the right tool for the job. Sometimes you really need to take your time and craft things perfectly. Sometimes, removing all automation is the right thing to do.
If we start with the premise that everything can be done manually, we start creating the libraries with that in mind. That’s how picocontainer was conceived. If we want to add automated plumbing, we do that on top of the core. Scripted wiring of your components? That’s built on top of the fully manual core. Even though I don’t use that project, its mere existence forced me to rethink some things about automatic plumbing some time ago. The same thought processes went into the Java ActionPack library (Yes, I know I need to put more of a web presence up for that project).
So what about automating the parts that are annoying to do each and every time? Absolutely. We shouldn’t be forced to think about things we don’t want to think about. The more we get the machine to do things like creating a build, branch, etc. the more we can get them to be done consistently. However, some things can’t be done automatically, nor should they. When you evolve your code’s design you should be thinking about what you are doing. However, if you want to do simple things like rename a method, it helps to have your editor find every instance where the method is used and change your code for you. That’s not something you want to spend time thinking about.
Software Engineers, Craftsman, and Artisans
You may associate these words with the types of things that they create. In our minds, we might associate engineers with creating system integration projects and mission critical systems. Craftsmen would be the kind of folk who create kitschy programs that serve no purpose other than entertainment. Finally an artisan would be someone who creates something that is both useful and beautiful. But that really isn‘t true. The difference doesn‘t lie in what these people create, but in how they work.
An engineer is concerned with metric proofs and solid evidence. They decide which approach to take based on tests, and typically choose the approach based on tests that show program efficiency. These are the guys that might color by numbers, so the approaches tend to be sterile, cookie cutter approaches. Nothing new is ventured as long as all the existing approaches fit the numbers they are looking for. Management likes this kind of person because they fit nicely into the corporate mold. Nevertheless, if something works, they don’t see if they can make it work better . That is the major problem.
A craftsman cares about the little things. They like elegance even in the code. They don‘t mind spending extra time on the areas that matter most to them. A craftsman is also quite adventurous, and will try new things just because they can. If it works out, they‘ll probably try it on a bigger scale. As a result of all the extra attention and experimentation, they are willing to gamble a lot more. Sometimes this pans out, other times not. Management doesn‘t like this kind of person because they are hard to control, and worse, hard to predict. Artistic things matter to them, and numbers do not.
An artisan is somewhere between the craftsman and the engineer. They perform all kinds of tests to know their tools and their materials. They still have an eye for elegance, but it is always balanced against the whole project. After all, what good is having one good part in the midst of mediocre parts? It looks Frankenstein like. They are fond of experimentation, but it is always within the larger confines of their vision. Management can respect these people, because they are predictable, and they do bring issues to light that can‘t be measured accurately, but have a profound impact on the project. Aesthetics matter, but not to the exclusion of function.
Of the three, it is the artisan that can produce higher quality apprentices who can themselves become artisans. Engineers are too machine-like for their own good. Now, there are people who call themselves engineers but behave like artisans. There are also people who are engineers to the bone and scorn any artistic influence at all — particularly on code, like that can do any good. Artisans are very pragmatic and knowledgeable people. They do realize the importance of elegant, understandable code that performs its task without half trying.
So, which one are you?
Cognitive Dissonance--Design Feature?
I‘m digging in the past on this one, but because my focus has been on design lately I think this applies. A long time ago someone I respect defended his position about a framework being complex using cognitive dissonance as a feature. Of course, I may be a bright guy but I‘m also self taught. That means I‘m not up on the latest from philosophy and psychology, but I‘m OK with that. I complained about there being too much cognitive dissonance from the perspective of audible dissonance. With my background in music recording, dissonance means two notes that sound bad together. They are discordant, or work against a musical chord. They stand out like a sore thumb and it can be painful to hear. It‘s an experience that is associated with pain and general badness. Imagine my surprise when this respected friend of mine said that cognitive dissonance can be an effective learning tool. I was taken aback, without any real response to that.
Thanks to Kathy Sierra's blog I was able to find out a bit more behind that statement. If you've followed her blog for any amount of time, you'd know our brains are equipped with a crap filter . This ingenious feature keeps our brain free of clutter and useless facts… most of the time. Some of the surefire ways to get past the crap filter include surprise, seduction, puzzles, and humor. If you reinterpret cognitive dissonance as surprise, then what my esteemed colleague said was true — from a certain perspective. However, there are good surprises and bad surprises.
Good Surprises
A good surprise includes things like saving a butt-load of time once you learn something. It‘s when the payoff is more than worth the effort you put in to learning the trick. Things like Ruby on Rails, and now my Java ActionPack have that good payoff for minimal effort. Another technology that I was pleasantly surprised with was the Expression Language that is now part of the JSP standard. It cleans up the code so that it looks nice without all the angle brackets and percent signs all over the place. Sure it has some rough edges, but it gets you most of the way there with very little effort.
Bad Surprises
A bad surprise includes things like finding out that you missed a small but all too important step so your code doesn‘t work even though it looks exactly like an example. Consider Cocoon Forms, and other technologies that try to be too smart for their own good. With all the different XML files you have to put together to generate the form, and the configuration settings you have to make to enable CForms, you‘ll wonder if the payoff is worth the effort. With all the knowledge spread out across several bits, and a little voodoo thrown in to boot, it‘s not clear what exactly is going on. Compare that with writing dedicated form handling code, and using a well written JavaScript library to help out on the client side.
Good Framework Design Practices
Cognitive dissonance might be en effective learning tool in writing documentation, but it is terrible in your code. If something doesn‘t make sense to you, if it isn‘t striking a chord with you, then that is your brain telling you that what you are trying to do is crap . Non-sequitur can be a useful way to break the ice, but it is a terrible way to learn how to use a library. The purpose of a framework is to be the binding to which all the pages of your application fit. It‘s to provide structure, and a way to help understand the application as a whole.
A good framework is one that becomes so sensible that you really don‘t notice it at all. No one part is emphasized above another. The whole thing works together as a unit. The framework should take care of the little things that the developer shouldn‘t be bothered with — but in a predictable and debugable manner. When something goes wrong, the framework should help by pointing out in as clear a manner as possible where it happened. The key word here is that the application and the framework become symbiotic. The framework needs the application to justify its existence, and the application needs the framework to get the job done.
The best frameworks are the ones that you don‘t feel like you are using, but are sorely missed when they are gone. Different libraries have differing levels of success in this area. Some examples of libraries I think excel in this area are Ruby on Rails, ImageMagick C++ bindings (a true example of generic code done right), and the JGoodies suite of Java Swing libraries. What sets them apart from the rest is how well they fit with the design of the language itself. There is a certain thinking process each language encourages, and when you find that sweet spot it is best to work with it instead of reinvent how its done. JGoodies demonstrated quite impressively how JavaBeans were meant to be used. The C++ interface for ImageMagick demonstrates just how nice and succinct C++ code can be if designed right. Ruby on Rails takes advantage of the features of Ruby in such a way where you will never be able to truly duplicate it on another platform. Even my Java ActionPack framework deviates from Ruby on Rails because there are things that Java just can‘t do, and there are things that have to be done differently to feel natural to Java developers. I‘d like to believe I‘m on the right track, and I like what I have so far.
