Most programmers develop in cycles. Code, Compile, Test, Repeat. Depending on your process, you might add stages like Test, Code, Compile, Document, Deploy, Verify, Commit, but, as a rule of thumb, the programs of highest quality are usually those that are not a chore to develop. The ability to execute code immediately creates an efficient feedback mechanism that increases quality. If your cycle is quick, you’ll be able to debug and correct bugs faster than if you are constantly having to wait minutes to see if your current set of changes works. A long development cycle is similar to the experience one has trying to use a command line editor over a 300 baud modem - the time lag between typing and reading what you’ve typed causes speed and quality issues.
Ruby, Perl, Python…scripting languages are technologies with very quick development cycles. In a scripting language, you can make a change to your code and run it immediately. Compiled languages such as C, C#, and Java have slightly slower development cycles because they require compilation, but if you make the right choices you can attain a quick development cycle in Java. For example, the Sysdeo Tomcat plugin uses a custom classloader that allows you to modify your servlet code on the fly without having to restart your servlet container, and there are various approaches to speeding up Java development cycles using swappable ClassLoaders and the like. But, don’t let anyone tell you that a speedy development cycle is impossible with Java, it is possible, but usually not the norm.
There are times when one bad product selection or one misconfigured framework can ruin everything by introducing a huge span of dead time into your development cycle. Read more for a discussion of Development Cycle “Dead Time”, and how it can affect your approach to development.
Development Cycle “Dead Time”
First, some definitions. What is the “development cycle”? I’d define the development cycle as the cycle a
developer repeats over the course of a single day. At a very abstract level this can be “Code”, “Compile”, “Test”, and this is cycle is repeated maybe 10-100 times a day depending on the languages and/or platforms used. A developer can have more than one development cycle; for example, if you are working on a system that requires Java code and stored procedures in Oracle, you may have a distinctly different approach to working on either of these tasks. For stored procedures in Oracle, you may have to rebuild a database and run a series of unit tests to make sure that your changes didn’t introduce critical errors. For Java code, you might make changes in Eclipse, run units tests with Maven2, and then start the application to make sure your changes work properly.
One rule of development cycles is that they can rarely be mixed with other tasks. For example, if you are working on a development cycle for stored procedures that involves a 10 minute span of dead time to build an instance from a set of scripts. It is unlikely that you will be able to effectively swap out to another development cycle. The human brain doesn’t have a very efficient timesharing algorithm, nor does it have reliable registers for constant context switching. In general, a developer needs to focus on a single development cycle at a time. What I’m talking about here is a single “unit of work”. Trying to work on a braid of intertwined development cycles is possible, but it usually means that every once in a while you’ll confuse development cycles.
Another rule of development cycles is that it usually takes a few to complete a given task. If you are trying to fix a bug or implement a feature, you can count on having to complete an average of 2-4 development cycles. If you are fixing a simple typo, you’ll only have to complete one development cycle, but if you are trying to change a core piece of code, you might find yourself burning up 5 cycles trying to get everything working properly. In general a good rule of thumb is that simple changes take 2 cycles and major changes take 4 cycles. But, again, this is a general statement, and your experience may vary. If you are working on relatively straightforward issues, you might lump multiple tasks into a single development cycle.
A Typical Development Cycle
At a very concrete level, a development cycle can be described as a series of steps. For development of a swing application in Eclipse, my development cycle is usually:
- Write Code using Eclipse (2-60 minutes)
- Write a Unit Test for Code just Created or Modified (2-5 minutes)
- Compile Code and Unit Test (30 seconds - 1 minute) DEAD TIME
- Execute Unit Test (30 seconds ) DEAD TIME
- Start Swing Application (30 seconds) DEAD TIME
- Verifiy Changes in Application (1-2 minutes)
So, a given code change can take anywhere from a 2 minute fix for a typo to a 60 minute session of concentration trying to figure out the best approach for a difficult algorithm. Even if you are in “the zone”, it pays to take a break from deep concentration at least once an hour, as the brief break gives your brain some rest. If we add up the estimated times, for this cycle, we get the following results:
- Smallest Development Cycle: 6.5 minutes total with 1.5 minutes of dead time - Dead Time Ratio: 0.23
- Average Development Cycle: 31 minutes total with 2 minutes of dead time - Dead Time Ratio: 0.065
- Largest Development Cycle: 69 minutes total with 2 minutes of dead time - Dead Time Ratio: 0.029
In this development cycle, even the most complex change requires only an additional 30 seconds of DEAD TIME, and the speed of compilation and unit testing tools means that I’m rarely spending any prime development time waiting for my IDE or application to initialize. In reality, I think most of my programming changes involve about 10-15 minutes of coding and testing. With the development cycle described above, I can realistically expect to finish four or five major features of bug fixes a day with time to spare, and my dead time just serves as a gentle reminder that I need to take a break or check my email. In other words, an average development cycle of 31 minutes with a Dead Time Ration of 0.065 means that every half hour I’m staring at a progress bar for about 2 minutes. Over the course of a 8 hour day, I’m forced to endure about 30 minutes of dead time, most of which is efficiently spent doing things like checking email, and I can expect to complete 1 major task and about 2-3 minor tasks. This is a very efficient development cycle.
When Dead Time Starts to Become a Problem…
I’m currently working with a product that takes approximately 14 minutes to start. It then takes about 10 minutes to build a cache, after which I then need to load a massive applet-based UI that takes an additional 2 minutes to render. At this point, I can run my test and tail the appropriate log file. Running this test takes about 1 minute, changing the code takes about 5 minutes and deploying takes an average of 2 minutes. In detail, the development cycle is as follow:
- Write Code using Eclipse (5-60 minutes)
- Build and Deploy JAR with Ant (2 minutes) DEAD TIME
- Start Content Management Server (15 minutes) DEAD TIME
- Generate Local Cache (10 minutes ) DEAD TIME
- Load CMS GUI Tool (2 minutes) DEAD TIME
- Run Test and Verify (1 minute)
- Worst Case: Total time:35 minutes, dead time ratio is 0.83.
- Best Case: Total time 90 minutes, dead time ratio is 0.32
In the worst case scenario, 83% of the time is spent waiting for a process to complete, and there is only about 5 minutes every half hour for analysis and coding. In this case, the developer needs to fill 25 minutes with something constructive. In the best case scenario, the developer spends an hour writing code, and then has to wait a little less than a half-hour to verify that the code changes were appropriate. With this development cycle, I can expect to be able to complete 1-2 minor tasks in one day and 1 major task every 2 days. The product I’m working on is almost perfectly opaque, little documentation and lot’s of opportunity to screw up - badly. Because of this, the scope of every change is limited by design to reduce risk. I end up spending most of my time in the worst case scenario - 5 minutes of coding, 25 minutes of waiting, 2 minutes of testing.
Working with this development cycle is like trying to drive a rover on Mars. Each change needs to be small because the risks are high, and it takes approximately 25 minutes to see if the changes I just made took, and there is always the possibility that I’ve just driven the system into a rock. I can’t be the only one out there who experiences this kind of development cycle, and I’d wager that anyone working on “enterprise systems” designed by “architects” using a whole host of inefficient proprietary technologies has had a similar experience.
The Costs of Lengthy Development Cycles
When your development cycle is longer than a few minutes and has a high dead time ratio, you’ll know how difficult it is to focus on a problem that requires you to wait minutes between change and verification. You’ll find yourself distracted and frequently “swtiching contexts”. coding for 10 minutes, waiting for 20 minutes, and testing for 2 minutes, will probably turn into coding for 10 minutes, starting a conversation with a colleague for 40 minutes, and returning to your desk only to realize that you’ve lost track of your own progress. Another variation of this is coding for 5 minutes, starting an application that takes 10 minutes, and then getting distracted by slashdot.org or cnn.com. Dead time kills productivity.
What Java needs to learn
Startup cost needs to be minimized across the board. If you are designing a product or a framework, make sure that you are not introducing a technology that takes mulitple minutes to startup. Persistence frameworks like Hibernate and service frameworks like Spring are great tools, but if you are designing a system with hundreds of beans and hundreds of model objects, both technologies can add a significant startup penalty, if you are not careful about configuration. Keep applications focused, small, and modular. This will reduce the development cycle for any given subsystem.
If you are justifying high startup to increase performance of an application in a production environment, find a way to decreases statup costs in development. While biulding a cache or generating proxy objects on statup might increase performance for end-users, high startup costs increase the effective cost of development. In general, Javaland needs to be more nimble when it comes to startup cost. Minimize dead time in your development cycle as much as you can, and you’ll see an improvement in efficiency and quality.