Search This Blog

Don't Comment Your Code - Write Better Code

Update: Somehow, I managed to remove the middle section of this post and most of the code example when I was updating labels, or adding a link, or something. It read like nonsense because of that, but I've rewritten the middle section to the best of my recollection. If you were confused before, it should make more sense now.

As I've gained programming experience, I've noticed a significant change in how I write code. I tend to write less and less comments, and the nature of them has changed. Where I used to explain what the code did and how it worked, I now leave those explanations up to the code itself. I try to more directly express what the code is doing, and in the rare cases where that is not sufficient, I may put a few words in a comment to explain why the code is doing what it's doing. If I find comments answering 'what' or 'how', I take that as an indicator that the code is not written well enough. Then I refactor the code to make the comment redundant and eliminate it.

I do this refactoring primarily because I hardly ever read comments. When I'm trying to understand a block of code, I focus on the code because that is what gets executed. The comments are a distraction, and I don't trust them. They very easily get out of sync with the code, and then they lead you astray instead of aiding your understanding.

I've come to think of comments like a writing device that I find more than mildly annoying - footnotes and end notes. I understand their use for citing references, but when an author feels the need to add explanations and anecdotes in footnotes that should have been in the main text, all it does is break up the flow of the text. If the footnotes were so important that they had to be included, they should have been integrated better with the main text. If they don't fit in the main text, then they should have been cut out completely.

The same reasoning applies to comments. Footnotes should not be a substitute for better writing, and comments should not be a substitute for better code. And like writing, better coding involves making the code more directly express what the programmer intends. This is not an easy thing to do, and it can take many drafts to reach a version of the code that does an adequate job of expressing its purpose. This process is a form of optimization that improves readability instead of performance, yet it is just as important as performance optimization because confusing code is a minefield of potential bugs and performance losses.

An Example of Bad Code Badly Commented


To make my reasoning a bit more concrete, here is an example of one particularly messy method that I refactored recently that illustrates how I go about making code more clear. Keep in mind that some months prior I had already done a fair amount of work assigning better variable and method names, but the code still went through significant changes before reaching a clear and concise result. As the method name implies, it runs a filter over a set of samples:

void CFilter::Run(void) {
   // Update stage 0 after EDMA writes into the Stage 0 buffer
   _rgStageInfo[0].SetWriteIndex(_pWrFirstStage);

   // Run filter stages
   int i = 0;
   int rgixFirOutput[MAX_ADCS] = {0};
   for( i = 0; i < _cStages; i++ ) {
       // Determine if there are enough Samples to produce at least 2 results
       int cResultPairs = _rgStageInfo[i].NumResultPairs();

       if ( 0 == cResultPairs ) break;

       bool fIsInternalStage = i+1 < _cStages;
       int dixFirOutput = 1;
       if (!fIsInternalStage) {
           if (_fDownConverting) dixFirOutput = 2;
       }

       // process all available samples in this stage
       for( ; cResultPairs > 0; cResultPairs-- ) {
           if ((i == 0) && _fDownConverting) {
               _rgStageInfo[i].DownConvert(_centerFrequency, _cChans);
           )

           int *pixFirOutput = _rgixFirOutput;
           Sample *pFirOutput = _rgFirOutput;
           Sample *pFirOutputAlt = _rgFirOutput + 1;
           if (fIsInternalStage) {
               GetFirOutputIndexes(rgixFirOutput, i+1);
               pixFirOutput = rgixFirOutput;
               pFirOutput = _rgStageInfo[i+1].GetBuffer();
               pFirOutputAlt = _rgStageInfo[i+1].GetBufferAlt();
           }

           _rgStageInfo[i].CalculateResultPair(fIsInternalStage, _cChans, 
                                               pixFirOutput, dixFirOutput, 
                                               pFirOutput, pFirOutputAlt);
       } // for (cResultPairs)
    } // for (all Stages)

    // Is this the final stage?
    if ( i == _cStages ) { 
       // Reset EDMA src addr to simulate a 4-word linear buffer src
       // then trigger EDMA to move filtered results to vInput buffers
       if ( _csHoldoff ) {
           _csHoldoff--;
       } else if (_fDownConverting) {
           CFft::EdmaTriggerAll(_mid, _rgStageInfo[_cStages].GetBuffer(),
                                _rgixFirOutput, _fDownConverting);
       } else {
           // This should be unecessary since the parameter set will reload itself?
           EdmaSetSrc(_hEdmaFirOut, _rgStageInfo[_cStages].GetBuffer());
           EdmaSetChannel(_hEdmaFirOut);
       }
   }
}

Did your eyes glaze over? It's fairly confusing, and frankly, ugly code. There is so much going on here that doesn't have much to do with running the filter stages, and the comments are not at all helpful. But before getting into that, I should briefly explain the Hungarian notation being used. I use a variation of Apps Hungarian Notation to prefix variable names with information about the variables in shorthand. I was skeptical of Hungarian notation at first, but I've found that using prefixes that are actually meaningful in the context of the application are quite helpful for naming and understanding variables. Once you get used to the prefixes used in a given app, you no longer have to waste much time thinking of good variable names because, most of the time, they quickly come to mind. A lot of these conventions are the same across applications, but some are specific. Here are the ones that are relevant to this code:

'_' = member of a class
'c' = a count of something
'd' = a difference between two things (i.e. an offset)
's' = a data sample, specific to this DSP app
'f' = a boolean flag
'p' = a pointer
'h' = a handle to a system resource
'rg' = a range (i.e. an array)
'ix' = an index

These prefixes can be stacked, so 'cs' is a count of samples and 'rgix' is a range of indexes. Not all variables will lend themselves to this notation, but those variables normally have a specific purpose that's easily named. For example, _centerFrequency doesn't have a prefix except for the member designator, but it doesn't need one because it's clearly the center frequency that the sample stream is being down converted to.

An Intermediate Step on the Way to Something Better


Getting back to the problems with the code, the first line of the method shows the main problem with this whole piece of code. It is too detailed for the level of meaning that this code should convey. The method is running a filter, so it should clearly show how it runs the filter, not muck around with setting the write index of the first stage. The write index was optimized out, which I'll get to later, so this line and its accompanying comment were removed.

The next improvement comes from knowing that this code is only called when a pair of samples are available for processing, and only one pair of samples is ever available when the method is called. These constraints are not apparent in the comments. In fact, the comments lead the programmer to believe that any number of samples could be ready, even zero, but that is not the case. Moving the test for available samples to the end of the for loop and checking if enough samples have been accumulated to run the next stage makes more sense. Oh, and that redundant comment? Gone.

Let's move on to the inner for loop. As I said, this method is only called when two results will be generated for the first stage. Additionally, each subsequent stage can only generate a maximum of two results as well. That means the inner for loop will only run once. It's useless! And so is the redundant comment preceding it. They'll get axed.

Finally, there is a lot going on with a couple of FIR output buffers that is mucking up the inner for loop. All of those xxFirOutput variables are rather confusing. They are used to keep track of the stage buffers and the channel indexes within the stage buffers so that filter results get moved to the right place after each stage is processed. Instead of having this method keep track of all of this stuff, the stages themselves should keep track of it, and they should each have a pointer to the next stage so that they can coordinate the movement of their filter results. Moving the buffer handling code into the stage class simplifies the moved code, and reduces this Run() method to its fundamental operations:

void CFilter::Run(void) {
   // Run filter stages
   int i = 0;
   for( i = 0; i < _cStages; i++ ) {
       if ((i == 0) && _fDownConverting) {
           _rgStageInfo[i].DownConvert(_centerFrequency, _cChans);
       }


       _rgStageInfo[i].CalculateResultPair(_cChans);


       // TODO integrate this better and possibly use a while loop
       if ((i+1 < _cStages) && _rgStageInfo[i+1].DecSamplesUntilResult()) break;
   } // for (all Stages)


   // Is this the final stage?
   if ( i == _cStages ) {
       // Reset EDMA src addr to simulate a 4-word linear buffer src
       // then trigger EDMA to move filtered results to vInput buffers
       if ( _csHoldoff ) {
           _csHoldoff--;
       } else if (_fDownConverting) {
           CFft::EdmaTriggerAll(_mid, _rgStageInfo[_cStages].GetBuffer(),
                                _rgixFirOutput, _fDownConverting);
       } else {
           // This should be unecessary since the parameter set will reload itself?
           EdmaSetSrc(_hEdmaFirOut, _rgStageInfo[_cStages].GetBuffer());
           EdmaSetChannel(_hEdmaFirOut);
       }
   }
}


That already looks much better. Now it's much more clear what the method is doing. For every filter stage, it does an optional down conversion if it's the first stage, it calculates a result pair, and it checks if enough samples have accumulated in the next stage to run it as well. If not, it breaks out of the for loop. If all stages have run, then the results are passed on to the next step of the process. But wait, look back at that down conversion code. It only runs in the first stage and the first stage is guaranteed to run, so it can be moved above the for loop. Also, that first comment isn't saying anything useful so we can get rid of it.

Next, notice that TODO comment? That is one kind of comment I don't hesitate to put in my code. It's there to remind me to go back to something that I might forget, but it is temporary. As soon as I finish the TODO task, I remove the comment. In this case the DecSamplesUntilResult() call can be moved inside the CalculateResultPair() call so that the latter call returns true if the next stage has enough samples to run. Then to convert the for loop to a while loop, we can use a pointer to the current filter stage instead of array accesses and put the CalculateResultPair() call inside the while loop condition. Then all we have to do inside the while loop is increment the stage info pointer to the next stage.

Finally, did you notice the question in the last comment? That's been there for quite a while, and I finally got around to answering it. The answer was actually right there in the previous comment, but it's not very clear. The reason why the DMA source needs to be reset is because the destination size is bigger than the source size, and if it wasn't reset, the DMA source pointer would keep incrementing until the destination was full - right past the end of the buffer. The relevant comment was moved and reworded. The redundant comment before the test for the final stage was removed.

Better Code With a Single Relevant Comment


Now look at how much cleaner the final code is:

void CFilter::Run(void) {
   SStageInfo *pStageInfo = _rgStageInfo;

   if (_fDownConverting) pStageInfo->DownConvert(_centerFrequency, _cChans);

   while ( (pStageInfo != &_rgStageInfo[_cStages]) &&
           pStageInfo->CalculateResultPairForNextStage(_cChans) ) {
       ++pStageInfo;
   }

   if ( pStageInfo == &_rgStageInfo[_cStages] ) {
       if ( _csHoldoff ) {
           _csHoldoff--;
       } else if (_fDownConverting) {
           CFft::EdmaTriggerAll(_mid, pStageInfo->GetBuffer(),
                                _rgixFirOutput, _fDownConverting);
       } else {
           // Reset EDMA source address to simulate
           // a FIFO source to a larger sink buffer.
           EdmaSetSrc(_hEdmaFirOut, pStageInfo->GetBuffer());
           EdmaSetChannel(_hEdmaFirOut);
       }
   }
}

The code pretty much stands on its own now and clearly shows its intentions. The only surviving comment explains why the DMA source is getting reset because that's normally an odd thing to do. When I have to come back to this code six months from now, I'll be able to easily see what it does. I can read the code without having to slog through irrelevant or redundant comments or tedious details that should be handled at a lower level. Instead of becoming mired in byzantine logic, I can get on with the task at hand because the code's intent will be obvious. That is the goal of well-written code.


Follow Up: A Taxonomy of Code and Comments

Beware: Premature Optimization Can Happen at Any Time

I'll be the first to admit that I love optimization. No matter what type of code I'm writing, my mind will be constantly formulating and experimenting with alternative ways of achieving the design goals in the most efficient way possible and weighing the trade-offs. I would have a hard time choosing between optimizing and debugging as my favorite programming tasks. I know. That probably makes me weird, but honestly, I'm okay with that. Optimizing and debugging involve a kind of problem solving that I find extremely enjoyable during the process and satisfying when completed well.

Optimization can take many forms. Over the years I've learned to focus on the ones that yield more bang for the buck - architectural, data structure, algorithm, and readability optimizations - and avoid those that are more trouble than they're worth. These troublesome optimizations can generally be classified as premature optimizations and micro-optimizations - categories that are not mutually exclusive. It is all too common to see micro-optimizations that are done prematurely.

Before we go any farther, some definitions are in order. Premature optimization is simply optimization that is done before it's known to be necessary, i.e. before you have actually measured the time consumed by the particular piece of code you want to optimize and found it to be a little CPU piggy relative to the rest of your program. Micro-optimization is twiddling with small code sections to try to beat the compiler at its job without making significant gains in performance, i.e. moving the deck chairs around on the Titanic.

Avoiding these types of optimization does not give you the right to be sloppy. You should still be picking appropriate data structures and algorithms for the job at hand. Joe Duffy has a great writeup on ways that premature optimization has been used as an excuse for bad programming choices. Don't be yet another example of that.

Why Not to Optimize


There are a number of great reasons to avoid these bad optimizations, the most obvious being that you are likely wasting your time. If you are optimizing code that doesn't have timing constraints or already has good enough performance, those optimizations are worthless. If your optimizations are getting optimized out by the compiler or the compiler would have done the same thing anyway, your hard work would be all for naught. Instead of fighting on the compiler's turf, you could be spending your time optimizing at a higher level in an area of your program that will matter. Measure first, then optimize only where you need to.

When you optimize, you are committing to potentially more complicated code for the sake of potentially more performance. If the optimizations are done at a high level, that commitment is probably fine. The mental overhead of the optimizations is integrated into the architecture of the program, and so it is manageable and possibly simplifies the design instead of complicating it.

If the optimizations are at a low level, you are at risk of competing with the compiler or the libraries and frameworks or even the hardware you're using. After all, processors do all kinds of optimizations including branch prediction, out-of-order execution, and memory caching, and they are changing and improving all the time. Every time you upgrade your programming environment, you will have to remeasure your optimized code to make sure it still performs well. And besides, are your users all using the same environment that you are? Even changes to other parts of your program could change the assumptions that made the optimization work and nullify the performance gains. Any changes, both within and outside your control, could make the optimization obsolete, so it will have to constantly be tested and verified. Trust me, you want to avoid that rabbit hole.

Finally, you should be optimizing for readability over performance whenever you can. That may seem a little harsh, but in reality, readable code begets performant code. I can't begin to count the number of times I refactored a complicated section of code to be more readable and only when I was finished simplifying did I see another way to refactor it to make it faster and use less memory. Often times I could see that the original code was trying to be a performance optimization, but the complexity was getting in the way. It wasn't until I made the code comprehensible that I could make it performant, and without fail the newer, faster code was much easier to read and smaller. That's a win-win-win. The bottom line is that in almost all cases, you should optimize first for readability.

When Measured Optimization Becomes Premature


Even though I try to follow these guidelines to the best of my ability, I still get caught by premature optimization sometimes. Last week was one of those times, and it brought out another reason to resist the urge to optimize until you are sure of how the program is working. But before getting into the code, here's a little background to hopefully make better sense of the example.

I'm writing embedded C++ code for a real-time application running on a TI DSP processor. A big part of what makes the real-time data processing possible for this application is the DMA (direct memory access) controller. The application has a number of memory buffers to stage the data so the processor can do calculations efficiently on a contiguous block of data before it's shuttled off to the next staging buffer. The DMA controller takes care of moving the data to the processor's internal memory and back out to external memory so that the processor is free to do program control and computation.

This DMA controller is packed with features to automate different types of memory transfers and kick off transfers from external events, but one of the more basic features is the ability to send a set of commands to the controller to initiate an immediate block transfer. The controller will go off and move the data and then interrupt the processor when it's done. Great!

The issue that I was dealing with was that sometimes there was nothing to do while waiting for the controller to move the data, so the processor had to wait for it to finish. It seemed like the controller was taking a fair amount of time, and I wanted to see if a plain old memcpy() call would be faster. Here's the little program I used to compare them:

void main() {
    int cs = 2000;
    Sample *pSrc = new Sample[cs];
    for (int i = 0; i < cs; i++) pSrc[i] = i;
    Sample *pDst = new Sample[cs];


    int cTests = 10;
    for (int j = 0; j <= cTests; j++) {
       unsigned cb = (j+1)*cs/cTests*sizeof(Sample);
       unsigned tStart = CLK_gethtime();
       int id = DatCopy(pSrc, pDst, cb);
       DatWait(id);
       unsigned tDatCopy = CLK_gethtime();


       memcpy(pDst, pSrc, cb);
       unsigned tMemcpy = CLK_gethtime();


       UTL_logDebug2("Test %d copying %d bytes", j+1, cb);
       UTL_logDebug2("  DatCopy: %d, memcpy: %d", 
          tDatCopy - tStart, tMemcpy - tDatCopy);
    }
}

The DatCopy() function call sets up the DMA transfer of cb bytes from pSrc to pDst, and then the DatWait() call waits until the transfer is complete. The CLK_gethtime()call is a special function that returns a count of the number of processor clock cycles since reset, and the UTL_logDebug2() calls are special print statements that log the formatted strings to a memory buffer that can be viewed with an emulator.

The real application doesn't copy more than 4000 bytes at a time, so this loop measures the amount of time that the DMA and memcpy() take to copy from 400 to 4000 bytes. Here are the results I got:

Data Copy Comparison graph

So according to this measurement, memcpy() is always significantly faster than DatCopy(), and I should pretty much always use memcpy()unless there is some other computation that can be done after kicking off the transfer to hide the latency in DatCopy(), right? That's what I thought, too. It seemed pretty straightforward, and I figured it would be an easy performance gain. I was about to go change all my uses of DatCopy()-DatWait() to memcpy(), but I had a nagging feeling that this couldn't be right.

The Premature Optimization Bug


Because of the memory architecture of this processor, memcpy() has to use the DMA controller to do its copying. All memory operations go through the DMA controller, but memcpy() was doing the transfer in small pieces under program control while DatCopy() should have been using the controller directly to transfer the data in one big block. It shouldn't be possible for memcpy() to be faster. Indeed, it isn't.

There was a bug in the DatCopy() code. The problem was that DMA transfers are set up by default to link to a null transfer that tells the controller to do nothing. The null transfer can be replaced by another DMA transfer so that when one transfer completes, it starts the next transfer automatically. However, if the null transfer is left there, then the DMA controller flags it as a missed transfer and takes its sweet time getting back to tell the processor that it's finished.

Since all of these DatCopy() transfers are one-offs, they shouldn't link to another transfer. They should return immediately. Once I figured this out and flipped a controller bit to make the transfers static instead of linked, I got this instead:

Corrected Data Copy Comparison graph

Ah, the world is right again. DatCopy() is generally faster than memcpy(), as it should be, except for transfers less than about 1000 bytes because of the time needed to set up a DMA transfer. There's only one place in the application where the transfers were guaranteed to be that small and could improve throughput if they were faster, so that's the only place where I put in the memcpy() optimization. As a bonus, all the other transfers sped up because the DMA setup bug in DatCopy() was fixed.

If I hadn't listened to that nagging doubt, I would have changed the code in dozens of places in the mistaken belief that I was improving performance, when in fact I was papering over a performance bug. Don't fall victim to that kind of hastiness. If something doesn't seem right, think it through and make sure you're not optimizing prematurely. You could be trying to do the compiler's job. You could be committing to code maintenance that isn't worth it. You could be unnecessarily complicating your code. Or you could be ignoring bugs that are preventing real performance gains. Do your homework instead, and only do the optimizations that matter.

People Tend to Do What They Know, So Learn What You Don't

People will do what they know. It's inescapable. When the spotlight is on, and you are expected to perform, most people are going to go with a tried and true response, not an untested experiment with unknown risks. In the context of engineering, people will tend to use designs, architectures, and tools that they've used before. It's entirely natural and rational, and in most cases going with a known design will yield predictable results. But that is not always the case.

Sometimes adhering to what you know can lead to some prickly design traps. Probably the worst of these is NIH (not invented here) syndrome. In its strongest form, this disease presents itself as a team's complete inability to accept design components or subsystems from anyone else, even within the same company. Everything must be designed and implemented in-house from the ground up. In weaker forms the team may use some standard libraries and frameworks, but the default state of their design process is still roll-your-own. NIH normally becomes a problem only if the entire design team is afflicted. If a minority of designers have this tendency, they can usually be overruled or have the impact of their decisions minimized. But if company culture is saturated with this mindset, teams will have an awful lot of work to do.

Another design trap born of only doing what you know is forcing a solution to fit the problem. If the design warrants a solution that doesn't exist in your repertoire, and you don't take the time to properly understand the problem and research its design space, you may end up trying to use a design that is ill-suited to the problem or solves an entirely different problem. This is the classic case of trying to fit a square peg into a round hole. It can't be done without significantly distorting the peg or the hole or both, and the result ain't gonna be pretty.

Possibly the most common trap is plain old suboptimal design. You may know a decent solution that fits the problem, but a better one exists that makes the design more elegant or more efficient or more flexible. The better solution could be an algorithm, architecture, or framework that you put off learning or are not yet aware of. You end up using the suboptimal design because it's what you know, and it can happen at any level of design.

All of these traps have something in common. They can be disarmed with the will to learn what you don't know. If you adhere to only those concepts you already know, it is easy to fall into one or more of these traps, but if you are determined to research the problem at hand, deepen your knowledge of the application domain, or experiment with new tools on the side, you can overcome the pitfalls and make better design decisions.

Let's examine each of the traps listed above in reverse. To avoid suboptimal design, the simplest thing you can do is take a look at what others have already done to solve the problem you're working on. It is almost a certainty that someone else has already run into the same problem, solved it, and published the solution for others to use.  It's almost as likely that multiple solutions are out there for you to choose from, if you use some creativity.

Leonardo da Vinci invented armored vehicles, helicopters, and hang gliders in the 15th century, long before engineers built functional versions of them. He worked out a lot of the main design problems before the materials and manufacturing processes even existed to make his inventions a reality. If he could do that centuries ahead of time, it's probably safe to assume that almost all problems have already been solved in some context. Most innovation today is the refinement of those solutions or the application of those solutions to new fields. Your problem is probably not so unique that nobody has come across it, yet. Spend a little time looking around before diving into a solution that may be suboptimal.

Forcing solutions to fit the problem can be thought of as a more extreme case of suboptimal design. To avoid this pitfall, try learning more about the problem domain first. The more you know about a domain, the more you come to appreciate its subtleties and nuances, and the more likely you will realize how it is different from things you have done in the past.

For example, on the surface, system modeling and real-time signal analysis seem to have a lot in common. They utilize many of the same DSP concepts, but the details are what set them apart. In a real-time system, a lot of effort is put into streamlining calculations with multiple stages of computation separated by memory buffers to relax timing constraints. In contrast, a system model is attempting to accurately model a real system on different hardware while making simplifying assumptions to speed up the execution of the model. Even though they may be performing the same calculations, the different constraints of the two problem domains will make the solutions' architectures drastically different. Even within each of those domains, the constraints of the specific problem being solved is important, and slight changes to the requirements can have a ripple effect on the rest of the system. Learn more about the problem domain to avoid the misapplication of an old solution to a new and different problem.

And now we arrive back at NIH syndrome. Things get more complex here because simply learning more about the application domain or researching solutions to the problem at hand will not dissuade someone from writing their own solution. There is a certain comfort and familiarity in writing your own code that is difficult to overcome. You know the code inside out because you wrote it. You can debug it and fix it if there are problems. And let's face it, your code is a gold-plated work of art while every other programmer's code is crap, right?

Let's discuss this a little in the context of an example from the video game industry. When embarking on a new game design, one of the basic choices that needs to be made is whether to license a game engine or build your own. If you build your own, you own it for better or worse. It's your own code, and you have total control of it and full knowledge of how it works. But you have to start at square one and it's going to take time to develop. If instead you license an existing game engine, you can start with known working code that hopefully comes close to your needs. You have to pay for it, but having a working engine can be a huge asset. And depending on the support you can get, you could have a robust platform with critical help for developing your game.

So how do you choose? Well, even if you license the engine, you're probably going to be changing it quite a bit and writing lots of code. You're going to learn a lot about that engine by the time you're done. Then on the next game project, it will be something you already know - another tool in your programming toolbox. If it was acquired with less time and effort than a home-built engine, that's a big win. If it came from a blockbuster game, it's already been tested and proven in the wild, another big win.

I'm not saying that licensing game engines is always the way to go. Sometimes existing engines don't fit the requirements or they're too expensive. The details of each project are important. The point is that the decision should be made using careful analysis of the project's requirements and constraints, not dictated by a team suffering from NIH syndrome. Knowledge of multiple game engines helps tremendously in that decision. The same is true for any software project when deciding whether to write your own code or use existing libraries and frameworks. The deeper your knowledge about what's already out there, and I mean functional knowledge that comes from having already used the tools under consideration, the more informed you are in making that decision. Knowledge is power.

Because people tend to do what they know, you need to keep learning what you don't know to make better design choices. The pursuit of knowledge is never-ending. Get comfortable with that. Every tool you can add to your programmer's toolbox is an asset that you may find useful at any time, and you want to keep freshening and maintaining your toolbox, lest you be caught by a problem for which you have no ready solution. And the best tools in your toolbox? An inquisitive mind and a will to learn.

6 Ways that Programming is Like Juggling



In my continuing effort to draw connections between programming and every other activity in my life, I thought I would tackle the similarities I see between programming and juggling. I find that juggling is a pretty fun way to kill some time when I don't have the time to do anything more involved. It's relatively easy to find three or more relatively small round objects that I can try to keep in the air.

Since I have a couple of small children, I can generally look down at my feet and find a multitude of toss-able things readily available on my very own floor. Building blocks, plastic eggs, wooden fruit - it's all fair game, and entertaining for the kids as well. I once tried using a couple of medicine balls, but that didn't work out so well. Balls that heavy tend to screw up your tosses, and boy do your arms get tired fast. I suppose they would be good for training to juggle chainsaws, though.

Juggling has some nice advantages beyond wasting time. It improves your hand-eye coordination, reflexes, and spacial awareness. It's a neat party trick, for kids at least, and looks pretty impressive once you get a few tricks beyond the basic three-ball juggle under your belt. Okay, enough about the virtues of juggling. How does it relate at all to programming?

You have to keep multiple balls in the air. And when those balls are in the air, there is a lot going on. Your eyes are spotting which ball to catch next. Your hands are adjusting to where the balls are going to land. You're launching the ball you just caught so that hand is free for the next ball that's already on its way down. Each of your hands and your eyes are acting independently, but they are still connected through the paths of the balls and acting synchronously. At no point can you be aware of everything that's happening at once. Your hands have to know what to do and react without conscious thought.

With programming, you are juggling with your mind instead of your hands, and you have to keep numerous variables, functions, and processes in the air at the same time. The bigger the program, the more that's potentially going on at once. If you had to consciously think about everything, you would have a hard time making progress. You have to depend on your mental reflexes to retrieve the necessary information at the right time for the particular code you're writing. If you have to constantly jump around the code to remember which variables do what and where that other function is called and what were the side effects, you're dropping the balls instead of keeping them all in the air.

Consistency and accuracy are your two best friends. When learning to juggle, one of the first things you have to train yourself to do is throw the balls exactly the same height and distance every time. If you watch a good juggler, the balls or rings or clubs will all travel along exactly the same path as if they are wired together. When they deviate from that path, the juggler is either inserting a quick trick into the juggle, or he is transitioning into a different juggle that will become the new path.

A routine is made up of a series of transitions from one steady state to another with slight deviations thrown in to surprise the audience. The absolute consistency and accuracy of every single throw allows the juggler to think at a higher level about the next part of the act instead of how to throw and catch each ball as it's happening. That ability to step back from the basic mechanics and let the subconscious take over allows a smooth, dynamic performance to happen.

The same principles free a programmer to think at a higher level. If you consistently use the same design patterns and conventions when you're programming, you can do more forward thinking. You can get to this state of mind more easily by adopting standard naming conventions, method and class structures, and coding style. Once you are thinking less about what to name things, what you did name things, and how to structure code, you can relieve your mind to concentrate on achieving your goals. Increasing your accuracy frees up even more time that you were spending correcting compiler errors and simple coding bugs, leaving more time for higher level thinking and design.

Juggling requires efficiency of motion. It's quite tiring at first. It seems like you have to move your arms and hands in a dozen different directions, as if they need to be everywhere at once. After a few minutes all of your muscles get pretty worn out. As you get better, you figure out how to combine separate movements into more fluid motions that conserve energy and use the momentum of the balls to your advantage. As you move your hand to catch a falling ball, you let your wrist relax at just the right moment to load it like a spring and then bring your forearm in slightly and relaunch it to your other hand with a light flick of your wrist. You make less course corrections and all of your movements get smaller and more focused. Eventually you have all of the time in the world to catch the next ball, and you conserve your energy to keep juggling for much longer stretches of time.

In programming, efficiency of motion would apply directly to typing skills. That much is obvious, but let's cast our net a little wider. Efficiency of motion also applies to the entire programming environment. Take the time to learn the shortcuts of the operating system, IDE, and editor you're using. The more efficiently you can move around your programming environment, the faster you'll be able to program, and the more mental energy you'll have to put towards programming better. Whenever you find that you're making a course correction by interrupting your coding flow to accomplish some task, take a little extra time to see if there's another way to do it that will flow more smoothly. Eventually you'll be able to dance around your programming environment much more efficiently, spending less time wrestling with the environment and more time writing great software.

It is hard to learn new tricks. Once you have the basic three-ball juggle down, you'll want to expand your repertoire. Maybe you want to learn to juggle two balls in one hand or sneak a throw behind your back. You'll quickly find that every new trick is practically as hard to get right as the original three-ball juggle. Your hands have no idea what to do, and you have to force them to move where you want them to go because they will no longer do anything right on their own. Suddenly, juggling takes all of your mental focus again. More than in any other activity I can think of, in juggling it is difficult to learn new tricks.

Sticking with the same old tricks is boring, though, so there's quite a bit of motivation to challenge yourself and learn new ones. Learning new programming tricks can also be difficult, but also necessary. If you don't take the time to learn new languages and frameworks, your skills will stagnate and programming could become quite tedious. Overcoming the difficulty of expanding your bag of tricks is well worth it. Don't let the fear of temporary awkwardness stop you. You'll get past the initial pain of learning something new, and you'll be a stronger programmer when you're done.

Juggling has a natural progression. Start out passing one ball back and forth between your hands. Then add a second. Once you're comfortable with that, add the third. Then try juggling two balls in one hand, then two in the other hand, and then try two in each hand at the same time. Once you can juggle four at once, add a fifth. You can also branch out into bounce juggling or juggling clubs or passing with a partner. I'm drastically oversimplifying, of course, but the point is that there is a definite progression to getting better at juggling.

I don't believe that learning to program has a natural progression. For every programmer, there is likely a different path to getting where they are. But there are parts of programming that do have progressions. When you're trying to refactor a section of code, even if it is a great big tear-up and redesign, there is likely a natural progression that will get you from the current code to the desired code through a series of smaller, simpler steps that maintain working code. If you can find that progression, you can make your life much easier because you'll know everything is working at each step of the way instead of doing a major overhaul and finding that nothing works at the end. Take the time to find that natural progression.

Improvements come suddenly and without warning. When I was first learning to juggle three balls, I would keep a running count of how many throws I successfully caught. It was slow going at first, and I felt like I was stuck at six throws for a long time. Then all of a sudden I could do 15 throws, and I was stuck there for days of practicing. Then I jumped to 25 throws without consciously doing anything differently. Once I took the step beyond 25 throws, I could basically juggle three balls as long as I wanted, and I stopped counting and moved on to other tricks. None of these advances were gradual. It was as if the motions had suddenly sunk into my muscle memory, or some subtle change improved my efficiency of motion, and I took a big leap forward in skill.

I noted that when learning the guitar, techniques or pieces of music that were once difficult could become easier without noticing. Juggling isn't like that because the feedback is immediate and obvious. You have an absolute reference to always compare against: are you keeping everything in the air, or not? Programming can have absolute references like that, too. Can you suddenly write 100 lines of code without making any syntax errors? Or maybe you've done a large refactoring without introducing any new bugs. Or you've become so proficient with a new framework that you can keep coding without having to look things up in the documentation. These kinds of improvements will all be immediately noticeable.

Experiencing these leaps in programming skill is quite satisfying. Something clicks, and suddenly you can do things that you previously only admired in more experienced programmers. Keep practicing, work for those improvements, and your efforts will be rewarded.

The 6 Best Blogs for a Software Developer

I read a lot. I love books, both technical books and novels, and over the years I've found that it is by far the best way for me to learn new things. Partially due to my obsession with learning from books, it took me longer than normal to discover that blogs are another great resource for learning, but in a different way. Blogs are good for getting insight into the culture of a field, for keeping up with the most recent developments of a topic, and for participating in a discussion with the community.

Blogs are not better than books. They are a different medium for the dissemination of knowledge, and certainly will not be replacing books. In fact, they compliment books quite nicely. The medium they seem to be replacing is periodicals.

In my relatively short exploration of blogs, I've found a few dozen that I read regularly, most of which seem to have gone dormant or are updated only infrequently. Almost all of them focus on technical or scholarly topics. If I only wanted to be entertained, I would read a novel or watch a movie. I read blogs to learn and expand my viewpoint. These six blogs have qualities that make them truly exceptional. Since it would be meaningless to rank them because of how different they are, here are the six best blogs for a software developer in the order I discovered them:

Joel on Software - This is a great blog to get started reading software blogs. Joel Spolsky is an incredibly good writer. His ideas on software design and development, project management, and leadership are delivered with vibrant examples and clear reasoning. Plus, you get to experience the growth and development of his software company, Fog Creek Software, almost first hand. It's a fascinating look into how a real software company can start from almost nothing and grow organically through hard work and determination. I'll warn you, by the end you'll likely be convinced to start using his company's software products, or start your own company.

Coding Horror - Whereas Joel clearly entered blogging with strong opinions and an excellent, fully developed writing style, Jeff Atwood seems to have started blogging with the intention of exploring and developing his ideas and improving his writing. His first posts were stilted and awkward, but with years of practice he became a great writer who could clearly express his thoughts with sharp reasoning and emotional force. It was inspiring to read along and witness his evolution as a writer and a software developer. During the course of the blog, an idea for a software company occurred to him and he decided to run with it. He partnered with Joel Spolsky and together they created Stackoverflow and the Stack Exchange network of Q&A sites. He went on to other pursuits as well. The entire progression of his career is an excellent read and quite enlightening.

Paul Graham's Essays - Like Joel Spolsky, Paul Graham started blogging as an excellent writer. However, his focus is quite different. Instead of slowly growing a small company into a sustainable business, Paul writes convincingly for the flip side of the coin - the fast-growing startup. He relates his own experience running ViaWeb and selling it to Yahoo!, and shares an ongoing stream of great insights for running a successful startup. A few years into his blog, in the summer of 2005, he puts these ideas to the test by starting a startup school called Y Combinator. The school starts small, but after gaining a lot of inertia, it now turns out dozens of startups per year. Reading about the whole theory and process behind it helps you understand this part of the business world better, and it's a great contrast to the slow and steady way of starting a company.

Stevey's Blog Rants - Steve Yegge's articles are spread across multiple places. The bulk of them are at his Blogger site, but he also has some posts on his Google+ site (+Steve Yegge), and a bunch of articles that he wrote while working at Amazon are stored at Stevey's Drunken Blog Rants. He now works at Google, and he writes a lot about programming language design, working at large software companies, and staying current as a software developer. His posts are filled with wisdom, wit, and sarcasm, and they are long. He packs them so full of information, and he is so entertaining that I don't mind that a bit. He's worth a read if only to get a clear view of the programming language landscape delivered with a healthy dose of cutting satire.

The Conscience of a Liberal - This is not a technical blog, but it will flex your logic and reasoning skills. Paul Krugman's blog is a wickedly smart critique of modern economics and politics, and he doesn't pull any punches. If there's some policy he doesn't agree with, and there are many, he'll explain every deficiency and delinquency as clearly as you could possibly imagine. It doesn't matter if the ideas are coming from the left or the right, if the logic and reasoning are wrong, Krugman will neatly tear them apart. He always backs up his criticisms with solid data and a tremendous command of the English language, easily making him the best writer in this list. Reading him will teach you how to deliver an argument. He also shows a sharp wit combined with an expansive cultural knowledge that is always entertaining. The true mark of wisdom is shown when someone can explain complex topics simply enough that anyone can understand them, and Krugman delivers this in spades. This blog and his weekly columns are an extremely worthwhile read.

Bruce Bartlett - Economix Blog - Bruce Bartlett is a conservative economist who has served in the Reagan and Bush I administrations as well as the staffs of multiple Republican congressmen. His writings are a study in how to let the data do the talking, and he often advocates solutions that you wouldn't expect of a conservative. His reasoning is always straightforward and nonsense-free, and he has an amazing store of political and economic historical knowledge that gives his writing excellent context. He steadfastly lets the facts determine the most rational policy recommendations without letting his own subjectivity intrude. Reading his posts will give you a firm appreciation for careful analysis and measured conclusions.

Even though two of these blogs are about economics instead of software, they all have much to teach you about how to analyze incomplete data, reason about problems, and express your ideas in good writing. These are invaluable skills for every software developer. Each of these blogs contains a wealth of knowledge written in their own unique, engaging style. They are a pleasure to read and learn from, and following their progression over the years has been a real treat. All of them have challenged my assumptions and changed my way of thinking for the better. Check them out and give them a chance. They won't disappoint.

To Lease or Not to Lease, That is the Question

2013 Prius Two hatchback

I just had a wonderful experience leasing a car for the first time. Isn't it beautiful? I'm starting to think of it as our turtle hot-rod since it looks nice and sporty in red. We spent the afternoon at Smart Toyota in Madison, where an excellent salesman by the name of Justin Jackson helped us through the trade-in and leasing process. I brought along an internet quote, and the negotiation process was smooth and pleasant. The hardest part of the whole process was keeping the kids under control, but they were excited about the new car and Justin was happy and accommodating the entire time. If you're in the area and looking for a car, Smart Toyota and Justin come highly recommended.

The Changing Auto Market


We decided to trade in our aging Toyota Corolla before it lost all of its value for something with better fuel economy. Right now the Toyota Prius is still the king of that hill while still having the range for longer trips, but things are starting to change rather quickly with more hybrids and electrics coming out every year. That environment of change and the fact that the Prius might be right for our family now, but not once our kids are a little bigger, contributed to our decision to lease instead of buy.

In another few years there may be a viable hybrid minivan. They already have them in Japan. Or something like the Prius V could get more fuel efficient. Or we might see more electric cars with a 300 mile range that are a bit more affordable than the Tesla Model S. That last one's probably wishful thinking, but six years out, it's a much more likely possibility. The auto market is changing faster than it has in decades, maybe even its entire history, as more manufacturers are jumping into the hybrid/EV market and competing on mileage, charge time, features, and range.

Three years ago plug-in hybrids didn't exist. Now there's half a dozen of them with more on the way. Manufacturers are converting more of their models to hybrids at an increasing rate, and once a model is a hybrid, it's a natural progression to make it a plug-in. It's becoming obvious that there are two paths to a fully electric car future. One is adding battery capacity to plug-in hybrids until the gas engine is no longer needed. The other is increasing the range and charging speed of fully electric cars until they can be useful beyond the daily commute or running errands around town. And as battery research and manufacturing gets more focus, charging times and prices are going to drop precipitously. The Nissan Leaf already shaved 4 hours off of its charge time and $4,000 off of its price in one year.

I don't think it will be long before we see more extensive use of solar panels to extend driving range as well. With panel efficiency going up and prices coming down, it will only be a matter of time before someone decides to put a bunch of them on the roof of an electric car. Seems like a no-brainer to me. With all of these changes happening in the course of months instead of years, and gas prices continuing to increase, it no longer seems like driving the same car for a decade is a rational, cost effective option.

Can Leasing Really Be a Cost Effective Solution?


Like a lot of people, I used to be very skeptical of leasing. I didn't even consider it when buying a Nissan Leaf last year, but I probably should have. I harbored a basic assumption that leasing was more expensive than buying when you're planning on driving a car until it falls apart. It seemed quite logical, but I never ran the numbers. I didn't really run the numbers in detail until after leasing the Prius either, because the changing auto market was reason enough to not want to be committed to one car for a decade or more.

I read up on leasing, mostly from LeaseGuide.com, which I highly recommend, and then jumped in and tried it. I did some detailed calculations after buying the Prius, out of curiosity, and surprisingly I found that leasing three times was basically equivalent to buying a car and driving it for nine years. After that, a fourth lease may still have similar costs because the maintenance costs on a 9-12 year old vehicle can really start to add up. Here is a run down of how I came to that conclusion, starting with the total cost of owning a Prius for nine years.

How Much Does Nine Years of Prius Ownership Cost?


First things first, you'll want to negotiate the best purchase price for the car. This step is actually no different for leasing. You can use the same purchase price for buying a car and for the gross cap costs on a lease, but we'll get into that more later. I'll use Edmunds.com for most of the pricing information, and assume that we're looking at a new 2013 Toyota Prius II with carpeted floor mats:
2013 Toyota Prius II MSRP = $25,220
2013 Toyota Prius II Invoice = $23,683
You should be able to get invoice on a Prius II that you're buying off the lot. The dealerships are overflowing with them and will be happy to let one go for that price. Now let's talk maintenance. You can buy a ToyotaCare maintenance package to pay for maintenance up to 45,000 miles. It's a good idea because it forces you to at least take the car in for recommended maintenance for that amount of time, and it costs a bit less than if you pay for it at the maintenance check-ups. It will roll into the loan so you'd be paying it over the life of the loan anyway.

The next item to consider is financing. Let's be generous and assume you can get 0.0% APR financing for 60 months. The ToyotaTime sale just ended, and that was the financing deal they were running, so it is possible to get it if you wait for the right time. Taking into account Madison's sales tax of 5.5%, we get a purchase price and monthly payments of:
Invoice plus ToyotaCare plus Sales Tax = ($23,683 + $650) * 1.055 = $25,671
Monthly Payments = $25,671 / 60 = $428
I'm ignoring title and license fees since they are the same between buying and leasing, and I'm assuming you can get away with no down payment on the loan. If you need a down payment, that's going to make buying look worse, as you'll see later.

The loan takes five years to pay off, but we're planning on keeping the car for nine years. Assuming we drive 14,000 miles per year, the car will have 126,000 miles on it when we sell it. During that time it will need three major maintenance check-ups, tires, brakes, and some miscellaneous repairs:
Winter Tires = $500
60,000 Mile Service = $300
New Tires = $400
90,000 Mile Service plus Brakes & Repairs = $800
Miscellaneous Repairs = $700
120,000 Mile Service = $300
Total Maintenance = $3000
Finally, to assess what a nine year old Prius would sell for, we can check back at Edmunds.com and look at a clean 2004 Prius trade-in with 126,000 miles:
2004 Prius with 126,000 miles in clean condition = $4575
Total Cost = Total Purchase Cost + Maintenance - Trade-in = $24096

How About Three Leasing Terms?


Leasing is a little more complicated in some ways, but less in others, specifically in the maintenance area. If you always purchase the maintenance plan, you'll pay no service fees and the car is always under warranty for the length of the lease. To calculate leasing costs, we need to figure out the monthly payments. These payments will consist of a depreciation fee and a finance fee. The depreciation fee pays for the difference between the value of a new Prius and the residual value after the lease term. The finance fee is basically the interest rate on the depreciation fee.

First, the depreciation fee is determined by the net cap cost, the residual value, and the lease term. The net cap cost is the negotiated purchase price plus an acquisition fee for the lease plus any other price adders like maintenance agreements or gap insurance. Sometimes gap insurance is built into the lease. If it's not, you should buy it because you're only financing the depreciation cost of the vehicle. If you total it in an accident, you're going to owe a lot of money. Toyota was also offering a $500 trade-in rebate that gets subtracted from the cap costs. The residual value is normally determined by the auto manufacturer, and it is their best estimate of what the vehicle will be worth at the end of the lease. It correlates closely with Edmunds.com assessment of a three year old Prius in clean condition:
Net Cap Cost = $23,683 + $650 + $650 + $695 - $500 = $25,178
Residual for 2013 Prius II with 45,000 miles = $14,762
Depreciation Fee = (Net Cap Cost - Residual) / Term = $289
The residual is interesting because this price is also the price you would pay at the end of the lease to purchase the car. If you decide you want to keep the car, this is the price you will pay, guaranteed. You won't be able to find an equivalent used car at the same price if the auto manufacturer was right about the depreciation. If their estimate was high, you can turn the car back in and they will have to deal with the car at its lower value. You ended up paying less on the lease than you should have, and it will be their loss. More likely their estimate was conservative to reduce their risk, and the car is worth more than the lease residual. You can trade in the car for some extra cash at the end to put towards the next lease.


The finance fee for the lease is determined by the net cap cost, residual, and money factor. The money factor is equivalent to an interest rate if you multiply it by 2400. That's an artifact of how business leasing is done with depreciation tables, but for our purposes we can just plug it into a formula. Normally money factors are a really low number because its multiplied by the sum of the net cap cost and residual to get a monthly finance fee. You can get an even lower rate during seasonal sales with leasing deals. ToyotaTime offered a money factor of 0.0002:
Finance Fee = (Net Cap Cost + Residual) * Money Factor = $7.99
Monthly Payment = (Depreciation Fee + Finance Fee) * Sales Tax = $313
Total Lease Cost = Monthly Payment * Term = $11268
Notice that we're only financing and paying sales tax on the difference between the purchase price and residual price, instead of the full purchase price of the vehicle. That's a big plus for a lease. Next, to determine the trade-in value, let's assume we drive 14,000 miles per year again, and that the condition will be outstanding at the end of the lease. This is much more likely over the shorter term of a lease while the car is under full warranty and maintenance:
2010 Prius II with 42,000 miles in outstanding condition = $15,767
Trade-In Value = $15,767 - $14,762 = $1,005
Total Lease Cost - Trade-In Value = $10,263
Now here is where things get interesting. The lease payments are significantly less than the loan payments for the first five years. What happens if we invest that money every month and then keep it invested for the last four years? Even if you don't actually save the money, it is a way of putting a value on having that money to use for other things instead of sinking it into a vehicle that's losing value. Using an online savings calculator:
Savings per month = $428 - $313 = $115
Savings Over 9 Years Invested @ 8% Minus Principle = $11624 - $6900 = $4724
Total of 3 Leases - Capital Gains = 3 * $10,263 - $4724 = $26065
So three leases costs only $1969 more than buying and $3385 of the leasing cost is extra gap insurance and maintenance plans, which add real worry-free value. Plus, you're getting to drive a car that's always less than three years old and has the latest safety features, comfort improvements, and mileage gains. At this point leasing is already looking better than buying, but the costs are basically equivalent.

What Could Tip the Balance?


There aren't many things I can think of that could make the lease worse than it is. As long as you're careful about the lease terms, it offers a very stable stream of payments with no surprise costs. That alone is quite valuable because your monthly payments will be entirely predictable, and you can budget for them. They are a known quantity, and they are guaranteed to be lower than loan payments for the same car because you are effectively spreading payments over nine years instead of five.

Even if you get in a bad accident, the lease should be the least of your worries, as long as you have gap insurance. The health and safety of everyone involved would be the primary concern. The worst that would happen with the lease is the car would be scrapped and you'd have to start over with another lease. Hopefully you could still get approved for another one. If that same accident happened on a loan, you'd have to start over with a new loan on a new or possibly used car, at significant cost to you.

There are hidden costs and risks all over the place when buying a car. Here are the ones I can think of:
  • You can't get 0.0% APR. Even if you can get 1.9% APR, that removes the last of the savings for the buy scenario above because you're paying an extra $1,255 in interest and your monthly payments are higher. If that extra money was invested instead, it would amount to another $840 so the higher interest rate would cost you a total of $2,095.
  • You need to give a down payment. Not only do you need to pony up that cash at signing, but you also could have invested that money instead. I'm not going to go through that calculation in full because it would lower the monthly loan payments and change all of the figures above, but consider that a down payment of $2000 invested at 8% for 9 years will net you $2100 in interest. The basic rule of thumb is you want to save more now, if you can, and spread spending over as long a period of time as possible.
  • The car is worth less than anticipated. A lot can go wrong in nine years that is completely out of your control. Accidents, dings, dents, scratches, and faster depreciation can all reduce the value of your car. These things can affect a lease, too, if they fall under excessive wear and tear, but if you get these things repaired, return the car in good condition, and it loses value faster than expected anyway, the leasing company will have to eat the cost.
  • Something goes wrong outside of warranty. Again, a lot can go wrong in nine years, and six of those years are outside of warranty. If mechanical problems start to crop up after the warranty is done, repairs can get very expensive in a hurry, and you have to come up with the money all at once instead of having regular monthly lease payments.
  • Gas prices keep going up. With leasing, you have the ability to trade-in to a more fuel efficient car every three years. The savings in fuel costs could easily make the switch worthwhile.
  • Cars are getting more efficient. As I said at the beginning, in a few years you may be able to lease a fuel efficient hybrid or electric car that didn't even exist before. If you bought your car intending to drive it into the ground, you'll have to try trading it in or live with the car you have as gas prices rise and newer cars get more fuel efficient and cheaper.
After considering the downsides of leasing, I figured that almost all of them are intangible or don't apply to my situation. Your circumstances may vary, but as long as you have good credit, are going to drive within the mileage limits, and are comfortable committing to a lease for three years at a time, you'll probably find that leasing is at least as good, if not better than buying in financial terms. This finding caught me by surprise. I chose to lease this time to avoid the hassle of trading in cars more frequently to keep up with new developments. The added comfort of knowing I quite likely won't lose money in the process, and I have predictable, low risk monthly payments, all combine into a pretty sweet deal.