DekGenius.com
Previous Section  < Day Day Up >  Next Section

2.2 Process and Simplicity

Kent Beck, the father of XP, says "Pick the simplest thing that will work." Building the simplest house or making the simplest of car repairs is difficult without the right tools and process. Building great software is no different. If you want to build simple software, you've got to strip all the extraneous junk out of your process that clutters your mind, your motivations, and your code. As you've seen in Chapter 1, I don't think that most development shops are moving in the right direction. The same forces that bloat frameworks, languages, and tools can also convolute the everyday development process:


Overkill

Heavy-duty processes used in the mainstream are designed for the most difficult problems. For the most part, UML diagrams such as sequence diagrams, class diagrams, and the like provide more harm than value. To me, UML belongs in books, on white boards, and possibly in the classroom, but rarely in design documents.


Complexity

When you do need to add tools like UML, keep it simple. A box with the class name and the two most important methods is often more useful than a multisymbol mish-mash with every UML bell and whistle embedded.


Indirection

It's hard to keep those phone book-sized requirements documents in sync with the code. It's even harder to keep that 350-class set of UML diagrams up to date. I've got another, more effective rule for synchronizing documents: code always wins. Code is the primary artifact that you'll deliver.


Rigidity

Those that sell methodology also often sell dogma. It doesn't matter whether you're going with a high-end process like Rational Unified Process (RUP) or a lower-intervention process like XP.


Over-specialization

Too many complex development frameworks segregate team members into roles that are too specialized, and code becomes isolated from documents, programmers from testers, and code from the warning, healing light of day.

Effective development processes do none of these things. The best development processes add just enough rigor to get the job done. They let you work primarily on the artifacts that you will deliver directly to your customer, and minimize the work spent on other documents that live to support the process.

But few methods work out of the box. To make a development method effective, tailor it to your needs. Teams vary in size, skill, preference, and prejudice. If you don't like class diagrams or object interaction diagrams, don't use them. If pair programming feels like overkill, don't do it. If you can't deal with an on-site customer, use some other way to introduce an effective surrogate. If a particular diagram is not clear or useful, don't create it. As James Duncan Davidson, the author of Tomcat and Ant, once told me, "If it feels good, do it. If it doesn't, quit."

2.2.1 The Best of Agile

Programming methods like XP and SCRUM advocate simplicity, and make it easier to achieve. Many of the authors of these methods are part of the Agile Alliance, which defines Agile software development principles. These ideas are rapidly shaping the way modern teams build software. The methods run contrary to many of the other methods that you may use. These rules in particular cut against the grain:


Code rules

While other methods like RUP require you to build many different types of diagrams as artifacts, Agile methods encourage you to focus on working code as the primary artifact. Everything else is secondary.


Embrace change

Other methods try to limit change; Agile methods encourage it. Developers refactor whenever they think it's necessary or helpful. Safety measures like continuous integration and automated tests protect the code base. Customers understand that as new features are added, others are removed.

Agile methods make it much easier to develop simple code. They can help you to minimize your development process to the bare essentials that you'll need to get the job done. Even if you don't fully embrace all Agile ideas, you can make tremendous gains by embracing some of the Agile principles:


Strive for simplicity

This is a cornerstone of all Agile methods, and among the most important.


Get and make use of feedback as early as possible

The rest of the principles are based on this one: shortening the feedback loop and applying what you learn as soon as possible is vital to being agile.


Automate testing

Some of the methods are stronger, requiring test cases before code. While such a principle may seem cumbersome and time-consuming, most developers find that in the long run, testing actually saves you time by catching problems early, while they are still easy to solve.


Integrate continuously

Paradoxically, integrating more frequently actually takes less time. When you do, you catch errors quickly and find potential stumbling blocks before they get out of control.


Refactor

To be agile, you must respond to change. That means you need to refactor early and often. It also means you need to build in the safety precautions that protect you from potential damage caused by refactoring.

These principles stand alone, and work well with just about any development process. When you use them together, you multiply their benefit. All of the principles build upon simplicity, a core value, but simplicity is difficult to maintain through successive iterations without refactoring. Automated unit tests and continuous integration build in a safety net to protect the code base from errors injected through refactoring. JUnit is rapidly becoming one of the most critical Java tools in my toolbox and the toolboxes of the best developers that I know.

Other ideas can help you to tailor your process, too. You can remove some requirement documents such as rigid functional specifications and heavy-duty use cases, and replace them with better customer contact and simple stories. You can work from source code, and relegate heavy-duty diagrams to the whiteboard. Seek regular informal communication, whenever and wherever you need it. Eschew all wasteful meetings. Abhor complexity, in any form. These ideas are independent of any methodology. They represent a philosophy, from the inside out, based on simplicity.

2.2.2 Pick Your Battles

A couple of years ago, I had a mental breakthrough: I was coding scared. I was afraid of trying simple solutions because I feared that they would not be rich enough. I didn't want to ever discard code; often, I'd invested too much of myself in it. I was afraid to change anything because I might break something else. I wasn't always that way. As the frameworks that I used and the algorithms that I used became more complex, I became more fearful. Fearful programming is an easy habit to make, and also an easy one to break. All you've got to do is embrace the simple solution instead of cracking open that jar of scorpions that you've been dreading. If you feel trapped, you'll code scared. The secret is leaving yourself an escape hatch.

The bottom line is this: you can't embrace simplicity without also embracing refactoring. Don't be afraid of serious changes, or even throwing away code. You'll likely find that you fear the wrong things. Because you've saved time with simple solutions along the way, you'll have more time to deal with your toughest problems when you need it most.

Think of your programming as the simple decision chart in Figure 2-2. Your goal is to keep as much code as simple as possible for as long as possible. The left-hand side of the chart represents simplicity. The right side is your escape hatch. You can use the escape hatch to inject as much complexity as you require. You'll find that you won't need your escape hatch nearly as much as you thought.

Figure 2-2. Your goal is to keep as many decisions as possible to the left of the diagram
figs/bflJ_0202.gif


The chart says to try something simple. How simple? Use your judgment. You don't want to waste time with solutions that you know will break; neither do you want to guess which things will break, or are likely to break. It's an important distinction that most programmers do not observe, especially as they become more experienced.

The bottom line is this: when you guess wrong, and you guess simple, it's cheap. When you guess wrong, and you guess complex, it's very expensive. You can apply this type of thinking in many places.

2.2.2.1 Algorithms

When you hear about simplicity, it's usually in the context of algorithms. I'm not saying that you should always reach for that bubble sort, but I am saying that you should leave all of the tiny, ugly optimizations out until you measure the need for change. Take the example of object allocation. Which code is easier to read, this one:

String middle = "very, ";
String prefix "This code is ";
String suffix = "ugly."
String result = "";
StringBuffer buffer = new StringBuffer( );

buffer.append(prefix);
for (int i= 0; i<5; i++) {
  buffer.append(middle);
}
buffer.append(suffix);
result = buffer.toString( );

or this one:

String result = "This code is ";

for (int i= 0; i<5; i++) {
  result = result + "much, ";
}
result = result + "simpler, and neater.";

If you've ever read an earlier Java book with performance tips, you were probably warned that the first example is better code. In fact, some applications had real problems with object allocation. As a result, you can still see huge blobs of code like the first one all over the place.

But remember what I said about developer intuition? It stinks, and things change. Now, compilers can heavily optimize object allocation. But let's give it the benefit of the doubt, and say that you are working on an older compiler. And let's further assume that the loop is a little longer. Would you really notice the difference? Unless you were doing nothing but processing huge numbers of strings, you would never see the difference. And you'd be forced to maintain a bigger blob of uglier code until the end of time. Trade a little less performance for better readability every time. Your correct guesses will save you more than enough time to refactor the wrong ones.

2.2.2.2 Architecture

Most of the applications that you build with Java are distributed, and it's easy to distribute more broadly than you need. Distribution can add flexibility, scalability, and availability. Distribution also forces some decisions and architectures that make your code much more difficult to write and understand. When in doubt, guess simple. I'm not suggesting that you hardwire your application so that distribution is impossible. I'm merely making the point that distance will cost you. Keep in mind that the vendors that build generic J2EE architectures sell either hardware, software, or both. The network may be the computer, but it's an expensive, unreliable computer. Use it when you must, but lean on it only when it's necessary.

2.2.2.3 Performance

Most of the developers that I know try to optimize as they go. The best of them are wrong more than half of the time. You probably can't predict exactly which piece of code will cause bottlenecks in your system. Don't try. When you do, you introduce (probably useless) complexity into your application, reducing your ability to maintain and adapt your code over time. Instead, code simple solutions and save your time for measuring and repairing the bottlenecks as they occur. Let your tools, like a good profiler, do the heavy lifting.

2.2.2.4 Design patterns

There's an inside joke among many Java consultants. You can often tell which books inexperienced developers are reading by reading their code. Sometimes, it's the reader's fault. When you read about a technique, you're anxious to try it out. But don't just start coloring on the walls. Use a coloring book first—like a sample application.

Sometimes, the problem lies with authors, who often oversell ideas or solutions. Other times, the problem lies with frameworks. If you want to know what I mean, pick up a book that deals with EJB design patterns. Now, count the number of design patterns that do nothing more than work around EJB features that stink. Or look to the seminal Gang of Four patterns; they are workarounds for problems with C++.

For this reason alone, many influential consultants abhor design patterns. I'm not in that camp. But let the need for a design pattern emerge before you work it into an application. In other words, wait until you have a problem before you look for a solution.

    Previous Section  < Day Day Up >  Next Section