Friday, November 14, 2014

Exploring Java 8: Lambda expressions and default interface methods

The introduction of lambda expressions in Java 8 is arguably the biggest change in Java since generics and annotations were added to the language in Java 5. This week, I got a chance to experiment with lambda expressions a little bit when I helped answer a question posted on JavaRanch.

The topic was about filtering a portion of a list. I have changed it up a little here but the idea is essentially the same.

The challenge is to write a method which, given a list of numbers, would remove all odd numbers that occurred between a starting index (inclusive) and an ending index (exclusive). Elements that are outside the given index range should not be affected.

For example, given a list of eleven numbers, {3, 18, 7, 1, 16, 11, 9, 4, 33, 5, 10}, a starting index of 3, and an ending index of 9, the method would remove the odd numbers in the highlighted range: {3, 18, 7, 1, 16, 11, 9, 4, 33, 5, 10}, producing a new list, {3, 18, 7, 16, 4, 5, 10}.

To appreciate how lambda expressions can dramatically improve Java code, let's start with a solution that doesn't use them.

Revision #1 - The "standard" implementation

    List<Integer> removeOdd(List<Integer> aList,
            int fromIndex, int toIndex) {

        List<Integer> newList = new ArrayList<>();

        for (int i = 0; i < fromIndex; i++) {
            newList.add(aList.get(i));
        }

        for (int i = fromIndex; i < toIndex; i++) {
            Integer e = aList.get(i);
            if (e % 2 == 0) {
                newList.add(e);
            }
        }

        for (int i = toIndex; i < aList.size(); i++) {
            newList.add(aList.get(i));
        }

        return newList;
    }


This solution uses three for-loops to process the head, body, and tail of the list, respectively. The head includes elements with an index less than the starting index, while the tail includes elements with an index greater than or equal to the ending index. It works but it's not exactly the most succinct piece of code you've ever seen.

The trouble with this solution is that it doesn't clearly reveal its intent. Anyone coming into this code without prior knowledge of it will have to spend a bit of time reading through it to grok what's going on. We can improve this with a little bit of refactoring.

The bookend for-loops can be eliminated by using the List.subList() method which returns a view of a portion of a list. This helps make the code that copies elements from the head and tail portions a little bit clearer. Now we are left to contend with only one for-loop.


Revision #2 - Refactored to use List.subList()

    List<Integer> removeOdd(List<Integer> aList, 
            int fromIndex, int toIndex) {

        List<Integer> newList = new ArrayList<>();

        newList.addAll(aList.subList(0, fromIndex));

        for (int i = fromIndex; i < toIndex; i++) {
            Integer e = aList.get(i);
            if (e % 2 == 0) {
                newList.add(e);
            }
        }

        newList.addAll(aList.subList(toIndex; aList.size())

        return newList;
    }


The remaining for-loop still is not quite up to snuff with the Single Level of Abstraction Principle or SLAP for short. To make this a well-composed method, we can extract the detailed code into another method. This results in the following, which is relatively clean as far as Java code goes.


Revision #3 - Refactored to SLAP

    List<Integer> removeOdd(List<Integer> aList, 
            int fromIndex, int toIndex) {

        List<Integer> newList = new ArrayList<>();

        newList.addAll(aList.subList(0, fromIndex));
        newList.addAll(evenNumbers(aList, fromIndex, toIndex));
        newList.addAll(aList.subList(toIndex, aList.size())

        return newList;
    }

    List<Integer> evenNumbers(List<Integer> aList, 
            int fromIndex, int toIndex) {

        List<Integer> newList = new ArrayList<>();
        
        for (int i = fromIndex; i < toIndex; i++) {
            Integer e = aList.get(i);
            if (e % 2 == 0) {
                newList.add(e);
            }
        }

        return newList;
    }
   

The removeOdd method is about as close to having a single level of abstraction as we can get but the evenNumbers helper method is still a bit of an eyesore. The business of having to create a new list and return it is a bit repetitious if you ask me. Refactoring evenNumbers a little bit more makes it somewhat better but you may have trouble even noticing the difference from the previous revision.


Revision #4 - Refactored helper method

    List<Integer> removeOdd(List<Integer> aList, 
            int fromIndex, int toIndex) {

        List<Integer> newList = new ArrayList<>();

        newList.addAll(aList.subList(0, fromIndex));
        newList.addAll(evenNumbers(aList.subList(fromIndex, toIndex)));
        newList.addAll(aList.subList(toIndex, aList.size());
    }

    List<Integer> evenNumbers(List<Integer> aList) {

        List<Integer> newList = new ArrayList<>();
        
        for (Integer e : aList) {
            if (e % 2 == 0) {
                newList.add(e);
            }
        }

        return newList;
    }

Now, let's see what lambda expressions can do.


Revision #5 - Using a lambda expression

    List<Integer> removeOdd(List<Integer> aList, 
            int fromIndex, int toIndex) {

        List<Integer> newList = new ArrayList<>();
        newList.addAll(aList);

        newList.subList(fromIndex, toIndex).removeIf(e -> e % 2 != 0);

        return newList;
    }

If you're wondering why I didn't choose to modify the given list in place, it's because I program defensively. I prefer to not do anything that would change the List passed to the method and avoid potentially giving callers an unexpected and unwanted surprise. This approach will also help me transition to a more functional style later.

The lambda expression, with the help of the new List.removeIf() method, replaces the arguably ugly helper method and dramatically simplifies the code. The removeIf method was introduced in Java 8 as a default method of the Collection interface. Default methods are also new in Java 8 and are a boon for developers of libraries who want to unobtrusively extend their existing APIs.

One subtle difference here is that the expression used to select elements changed from (e % 2 == 0) to (e % 2 != 0). This difference is key to getting the correct behavior but it seems like every time I look at it, I have to do a double take and pause to make sure I didn't make a mistake. That's a little annoying.

This annoyance made me feel like I could do a little more to make the code really reveal its intent. To ease my angst, I performed one final refactoring to introduce an explaining variable.


Revision #6 - Introduce a local explaining variable for the lambda

    List<Integer> removeOdd(List<Integer> aList, 
            int fromIndex, int toIndex) {

        final Predicate<Integer> oddNumber = (e -> e % 2 != 0);

        List<Integer> newList = new ArrayList<>();
        newList.addAll(aList);

        newList.subList(fromIndex, toIndex).removeIf(oddNumber);

        return newList;
    }

Looking back at the previous versions again, I noticed that there was a little bit of a shift from the way the problem is stated, "remove odd numbers from the list" versus the name of helper method, evenNumbers. I used that name because it's the only one that makes sense as the argument to the call to addAll.

This little shift goes away with the name oddNumber. It matches the problem statement and it works in both the context of the lambda expression and as an argument to the removeIf method. I'm hard-pressed to think of a way to make that line of code any more expressive. That's pretty cool.

Meanwhile, back in the JavaRanch thread that started this, I got into a bit of a discussion about the merits of introducing a local explaining variable. Some may think that adding that line to create a local variable defeats the purpose of using lambdas but I think that the small sacrifice made to brevity is worth the gain in clarity. I suppose it's just a matter of taste at this point. I like this one more.

In conclusion, this exercise helped me appreciate just how much lambda expressions can help to make for better, cleaner code.  And as an added bonus, I also saw the utility of default interface methods in making it easier to extend existing APIs.

Sunday, November 9, 2014

The Sound of Code Smells

Caveat: A friend told me that this is a bit of a rambling post so proceed if you must but be prepared to bear with my blabbering if you have nothing better to do. I will work on refactoring this for clarity and post an update when I can get around to it. Thanks.

If a piece of code smells and there are no developers around, does it make a sound?

That's an interesting twist to the tree in the forest thing, isn't it? We may forever ponder the question about that poor tree in the forest--one company even advertises its opinion about it--but my answer to the code smell question is a definite and resounding "Yes!"

What sound does a code smell make?

At worst, I would imagine that it sounds something like whatever inspired Edvard Munch to produce his arguably most famous piece of work, The Scream. The eternal juveniles and jaded cynics out there will probably say "Plop!" or "Schploosh!" or "Really bad code smells are silent but deadly." If you've read Uncle Bob's "Clean Code" book, you might suggest that the sound of a code smell is "Dude, WTF?!" As crude as some of these retorts may be, you have to admit there's a distinct ring of truth to them.

Seriously though, you'd think that a very apt metaphor like "code smell" should be enough to motivate developers to take another look at their code and clean it up, right? So why would you want to add to a perfectly good metaphor? In this day and age when everyone is supposed to be familiar with—and presumably doing!—refactoring and unit testing, shouldn't this "smell" thing make sense to developers already?

Well, Virginia, I hate to break it to you like this but apparently, the "code smell" metaphor is still not good enough to help a large number (I shudder to think that "a majority" applies here) of developers in the real world start writing better code.

While I'm sure it would be easy for your average agile developer to think as far back as a day ago to remember when he last got a whiff of bad code, tracked it down, and cleaned it up, the average Joe Schmoe developer just doesn't have that kind of olfactory ability. At best, their code smell genes are still lying dormant somewhere inside them, just waiting for that moment of XP epiphany to finally break out in full glory so these developers can reach their true potential as lean, mean, code smell busting machines, the way nature intended all good developers to be.

Ok, it isn't that glorious either. Not usually. For many, "breaking through" those walls of ignorance that imprisoned us in rotting, smelly code doesn't conjure up images of emerging from a pit of darkness into glorious sunshine, birds singing, and gentle breezes blowing over endless seas of undulating green grass.

Getting code smells is really more like XP puberty, right? It's like those episodes of adolescent "break outs" that meant painful hours of intense scrutiny, self-loathing, and daily scrubbing in front of a mirror. For the unfortunate few who don't get it quite right and are left permanently scarred, code smells are just a fact of life that you learn to live with and things like Agile, TDD, and refactoring are nothing more than bottles of snake oil that unscrupulous traveling salesmen hawk to the many eager but desperate, unsuspecting, and gullible developers out there.

For those who do get code smells, TDD, and refactoring, "breaking out" is often followed by a few of those post-escape Andy Dufresne moments when our initial jubilation of making it out of the sewer is replaced by cold fear, confusion, and self-doubt. And certainly there are those poor old souls like Brooks Hatlen whose senses become overwhelmed--everybody is such a big damn hurry!--that they just simply give up in despair, longing for those simpler times before they were given so much freedom and a new lease on life. Some people just can't handle the truth. There has to be something that can be done to help out guys like that.

Who among us, apart from the very fortunate and brilliant few, have never said to themselves after they first started truly grokking and experiencing code smells, things like "I can't believe I wrote that sh*t!" or "Was I even sober when I wrote that cr*p?!" or just simply, "Dude, WTF?!" and then follow it up with the inevitable "Now what?" and "Maybe I was better off back in Shawshank and not having to deal with this."

Can any of us claim that we can always point a finger at the source of a code smell and decisively and incisively take care of it lickety-split? Heck, even gurus like Kent Beck admit to getting stuck sometimes. What hope is there then for us mere mortal developers who aspire to keep our code as clean as humanly possible? Ok, that's a bit too dramatic, I admit, but if you've been there, you know what I'm talking about.

Anyway, lately I've been asking people on JavaRanch to read their code out loud. Here's one reply where I do that (look for my reply that has a bunch of sample unit test code). This is actually what motivated me to write all this but there are quite a few other threads where I've asked the OP to do the same thing: read the code out loud.

If you try it and read some smelly code out loud, you might see what I mean by "the sound of code smells". Or rather, you might hear what I mean.

For example, the sound of redundancy is often a stammer, as with the Booking thing in that JavaRanch thread (Listing 1).

Listing 1. The sound that Redundancy makes

    public void addBooking(Booking booking) {
        bookings.add(booking);
    }


Comments that smell of redundancy sound very maddenesque (Listing 2).

Listing 2. The sound that Redundant Comments make

    public void addBooking(Booking booking) {
        // adds a booking to bookings
        bookings.add(booking);
    }


The sound of poorly chosen names can be heard in this thread and this thread and there are quite a few others where you may hear different kinds of sounds.

(Sorry if the links to JavaRanch are off-putting to you. I'm not trying to click-bait or spam-link you with these -- it's just that I have yet to ask the bosses at JavaRanch if I can take code that was posted there and put them up here as examples. I'll update this later if I get a nod from them, which I'm hoping I will).

I'm sure there are many other kinds of code smell "sounds" out there that I've heard but, just like with "smell", it's difficult to describe "sound", especially to someone else who has never heard the same sound before. To someone who doesn't know any better, "The sound of a poorly chosen name" is very Zen-like. I tweeted the other day about how difficult it would be to train a dog named "Stay". The sound of a poorly named method inspired that tweet. Here's my reply on JavaRanch about it.

If you think about it, adding the sense of hearing to the already good metaphor of "code smell" makes a lot of, well, sense. We're already saying things like "the code tells a story" and "the code is telling you what it wants to do" and "that code just screams out to be refactored" and "that variable is asking to be renamed," right? These all suggest that we need to listen more carefully to our code. What better way to get developers to listen than to get them to read their code OUT LOUD?

The point of all this, and thank you for listening to me blabber away for this long, is that I think it would help those developers, the ones who don't quite know how to recognize a code smell yet, if we older and more experienced developers who do would tell them more often to read their code out loud and listen to the sounds that their code is making.

Even for us old paroled jail birds, who sometimes get stuck and are experiencing that "maybe I was better off back in Shawshank" feeling, it might help us to read our code out loud once in a while. Something seems to happen when we actually hear what the code is doing and sometimes that's all it takes to get unstuck.

Maybe for us developers, the best way to develop our metaphorical sense of code smell is by exercising our literal sense of hearing.

So, to all the young developers and all the old fans of Casablanca, "Here's to hearing from you, kid."

(Update, Tuesday 11-Nov-2014)

I was looking around for other references to code sounds and found one on Ward's Wiki on, of all places, the page on Code Smells. Russell Gold mentions the auditory metaphor in a comment about how saying code "smells" has a different connotation than saying that it is "off-key" or "doesn't sound right." While his point is not quite the same as the one I'm trying to make here, I still think it's interesting that others have felt that it's at least worth considering other sensory metaphors besides smell.

Sunday, October 26, 2014

Mob Programming for Distributed Agile Teams

Looks like Woody Zuill (@woodyzuill) is getting pretty busy promoting the practice of Mob Programming these days. I applaud his efforts and I'm happy to see his apparent success with it. I think it's a great practice and I think more teams should at least try it on for size.

Responding to Tampere Goes Agile (@tmpgoesagile) question on why you should use mob programming, I said that mob programming has helped our distributed agile team get better by keeping everyone on the same page. @tmpgoesagile still wanted to hear more about it so here are some details on how that came to be and how it works for my team.

It's a Pattern

I think it was either Esther Derby (@estherderby) or Jeff Patton (@jeffpatton) whom I heard say something like "I didn't invent this. Other people have told me that they've done the same kind of thing before. So I guess the only thing I can claim here is to have discovered and documented a pattern." They were, of course, talking about something other than mob programming, but the same thing seems to apply here.

I first heard the practice being called "Mob Programming" at the Agile 2013 conference in Nashville, TN. A team from a company in California was talking about it in an Open Jam Huddle in the expansive lobby of the Gaylord Opryland Conference Center and I thought I'd stopped by for a quick listen while I was between sessions. A group of young guys were talking about how they did all their development anymore and how much better and enjoyable mob programming was than just plain old pair programming.

We had been doing the same thing on our team for at least a year already, except we called it "swarming," a name we'd picked up somewhere for the practice of having the whole scrum team work on a single user story at a time. I liked the ring that "Mob Programming" had to it so I started using the name with my team, too, although I still tend to say "mob or group programming" when I describe what we do to other people in our organization. Now that Woody had made the name more popular, I guess I'll go with "mob programming" from here on.

First Rule of Distributed Teams

We are a distributed team with members in Ohio, Texas, and California. Our concession to not being co-located was to require that team members be no more than three timezones apart. This gave us at least five hours in a day when nobody had to make crazy adjustments to their schedules. If we did have to schedule something that was a "little bit" early or late for one or two people on the team, we could at least avoid swinging out more than an hour or two on either side of our core work hours. Any crazy adjustments to schedules, which a few of us did anyway, were totally your personal choice and discretion.

I think if you're going to do mob programming with your distributed agile team, you have to at least have this rule in place. If you have a few guys on your team like me and a couple of my colleagues, that's fine. But make sure people agree that crazy hours are the exception rather than the rule.

Share Images to Build a Shared Understanding 

In his book "User Story Mapping", Jeff Patton notes that pictures play an important part in building shared understanding in a team. This is especially true with distributed teams and it has been borne out by our experience.

The right collaboration tools are very important for distributed teams like us. Before I go on, I want to make it clear that this is by no means a thinly veiled attempt at promoting our company's products, no matter what it looks like. I assure you that I mention the tool we use purely as a matter-of-fact. I have to admit though that it is nice to be a rare case of the cobbler's kids getting nice shoes for a change.

Anyway, we use Cisco WebEx for meetings and collaborative development all the time. Most of us work from home and we love the flexibility and convenience that comes with it. At the start of our journey to agility, people would just email each other or use IM to have conversations. I nipped that in the bud. I reminded everyone that if we couldn't have face-to-face conversations, the next best thing was to have a live conversation on WebEx. We could share our screens, which we often do, and we could even share our cameras, which we still don't do very often for some reason.

I guess that's one curious observation to note: Being able to share your screen, priceless. Being able to share your face... meh.

Nowadays, our conversations usually start with the simple IM: "WebEx?"

Pair-Programming to the Extreme

As I alluded to earlier, we kind of just fell naturally into the practice of mob programming from "swarming" on a user story.  As with most distributed teams, we'd been having trouble completing user stories when we divided them up among ourselves and tried to work on them concurrently. We often ended up moving stories to the next iteration to complete them. Swarming was our attempt to get at least one story fully done by the end of the iteration that it was pulled into the first time.

I'm also a big proponent of Test-Driven Development. I had first instituted the practice of code reviews which naturally led to my goal of having frequent pair programming sessions so that after-the-fact code reviews had to be done less frequently.

When we started swarming on user stories, this led to us doing group programming on WebEx for at least three or four hours a day. Yeah, we called it "group programming" and we liked it a lot. The mechanics were kind of the same for pair programming except that there was a lot more discussion and different perspectives brought to the table. Also, there were more people to do little sidebar tasks like Google for something or experiment with an API call or write documentation or whatever.

As a tech lead, I loved that we didn't have to do as many after-the-fact code reviews. I could keep my finger on the pulse of the design and head off any design choices that could potentially make trouble for us later. I could also do my mentoring on refactoring practices and design principles all at once and not have to repeat myself with other team members at a later time.

The Uncomfortable and Unspoken Rule of Distributed Teams

If you've never seen the movie "Office Space" then any claim you make to geekdom rings a bit hollow. Anyway, I think mob programming helps you avoid having any Milton Waddamses on your distributed team. It's all too easy for someone to say, "Let me work on this for a little bit then I'll get with you for a code review on WebEx tomorrow." Then next thing you know, that guy is in the basement plotting to set the building on fire. Ok, maybe that's a bit of a leap but you know what I mean, don't you? If you don't, Google and NetFlix are your friends.

Since everyone is working together for a good portion of the day, mob programming makes it almost unnecessary to even think about the Uncomfortable and Unspoken Rule of Distributed Teams, which is that "Team members will have the integrity and discipline to work even when nobody is watching."

I know we all want to assume the best of our team members but not giving anyone a chance to give in to the temptation of working alone can sometimes be the best way of establishing trust on the team.  I think mob programming helps with that.

Conclusion

I'm really glad to see that Woody Zuill's efforts to spread the word about Mob Programming are succeeding. Mob programming is great for any agile team and it can be especially beneficial for distributed agile teams.

Thursday, August 29, 2013

Use curl as an alternative to wget on Mac OS X

Mac OS X does not come with the wget command so I needed to find an alternative.  It's pretty simple really.  Basically, you use curl instead. Darian Brown has a nice writeup on how to do this.

Apple's man page for curl can be found here if you want to see what options are available.

Monday, August 26, 2013

Not All Debts Are Equal (Part 1)

This is the first of a series of posts that I will be making in preparation for a panel discussion on Technical Debt at SPLASH 2013 which will held in Indianapolis, Indiana from October 26-31, 2013. 

Does having different interpretations of the Technical Debt metaphor mean that it suffers from semantic diffusion or does it simply provide evidence of its linguistic evolution and not necessarily its diminished usefulness? In this series of posts, I will explore the different things that Technical Debt has come to mean throughout the industry and whether these different meanings make the metaphor more or less useful.

"You keep using that word. I don't think it means what you think it means." -- Inigo Montoya, The Princess Bride

+Martin Fowler coined the phrase "Semantic Diffusion" to describe how the meaning of a word or phrase can weaken as its use spreads through the wider community.  For example, the terms "agile" and "refactoring" have arguably suffered from semantic diffusion over the years as people use them in different contexts and apply them to things for which they were perhaps not meant to apply.

Fowler posits that semantic diffusion is more likely to occur with things that are broad concepts rather than hard technologies.  Technical Debt seems to be one such broad concept whose meaning has been morphed and applied to a wider range of circumstances than was originally intended.

A Quick Confession


I have to admit that prior to this writing, I had an entirely different idea of what constituted technical debt. I thought that technical debt was anything that slowed you down: messy code, poor designs, misunderstood or missing requirements, lack of security, poor performance, and pretty much any other technical issue.

I'm not really sure how or why I had settled on this divergent and arguably incorrect definition of technical debt.  I can only speculate that it stemmed partly from my lack of insight into the original meaning--Ok, I admit I was too lazy to look into it in depth--but more likely from a healthy dose of "assume" (we know what happens when you do that) and a generous coating of personal bias from past experiences of being hampered by technical issues.

I do know for sure that I'm not the only one who had this view of technical debt.  Note that I wrote "had this view." I'll explain more about that later.

First, let's establish a baseline understanding of the term as originally proposed by +Ward Cunningham. It's important to understand the original meaning so we have a basis for comparing other, perhaps divergent, meanings.

Ward Cunningham's Debt Metaphor


Ward mentioned the debt metaphor in a 1992 OOPSLA experience report.  He also explains the debt metaphor in a YouTube video and a transcription of it can be found on Ward's wiki.  He relates how he coined the term as a way to explain the refactoring being done on the WyCash financial product he was working on at the time. It stands to reason that he would choose a financial concept to explain the technical choices they made while developing a financial product.  Had the product been of a different nature, we might have ended up with a different metaphor altogether but as chance would have it, Ward chose debt. The rest, as they say, is history.

Let's take a closer look at what Ward says in the video and try to identify different analogies that we can derive from the debt metaphor.

Analogy #1: Reaping a benefit sooner


Ward says, "With borrowed money, you could do something sooner than you might otherwise."

Let me add a little bit of nuance to that.  Borrowing money allows you to benefit much sooner than it would be possible without money. With borrowed money, you can buy a car or a house, go on vacation, or start a business. Likewise, charging purchases to a credit card allows you to avail of goods and services well in advance of actually paying money for them. Presumably, having or doing these things is beneficial to you. Therefore, borrowing is a way for you to reap a benefit sooner rather than later.

Ward compares borrowing money to rushing software out the door before it reflects a full understanding of the system. Doing this allows you to get some experience with the program and learn more about it sooner. It's interesting to note that it really doesn't matter that the software is still conceptually incomplete or that the problem is not completely understood. You're not really concerned with that at this point. You're only interested in the opportunity to use what you currently have to explore and learn more about what should be the correct and complete solution.  When this is the case, you benefit by going into technical debt as a way to gain experience and understanding sooner.

This leads to the first analogy of the debt metaphor:
Getting working software out the door so you can get some experience with it sooner is like borrowing money so you can reap some benefit sooner.
This kind of reminds me of a quine, a program with no input but whose output is the source code of the program itself.  There's a hint of this recursive nature when you use a program to help you make improvements to the program itself. It's also reminiscent of the saying "You need money to make money" but in this case it becomes "You need working software to make working software."

Analogy #2: Assuming an obligation to pay


When you borrow money, you assume the obligation and responsibility to pay someone back. When you take out a loan, you have to pay the lender back. When you charge purchases to a credit card, you're obligated to pay the credit card company.

But what is the currency of technical debt and what obligation and responsibility comes with borrowing it?

Time is the currency of technical debt. Specifically, you are borrowing time with which to use working software so that, as already noted, you can learn and understand more about the problem and the solution. The obligation that comes with borrowing time is the same that comes with borrowing money:  you must pay it back.

Of paying back borrowed time, Ward says "you would repay that loan by refactoring the program to reflect your experience as you acquired it."  That is, you fulfill your obligation to pay back borrowed time by spending time to refactor and update the program so that it reflects a clearer and more complete understanding of how it should behave.

This is the second analogy of the debt metaphor:
The obligation to make the program reflect what you learned on borrowed time is like the obligation you have to pay back borrowed money.

Analogy #3: Interest Payments


When you assume the obligation to pay back borrowed money, you also assume an obligation to pay back slightly more than the amount that you borrowed.  This, of course, is the interest that you pay on the loan and it represents a cost to you, the borrower.

Ward notes that borrowing money so you can do something sooner is a good idea but "until you pay back that money, you'll be paying interest."

Thus, Ward reasoned that if they "failed to make the program align with what was then understood to be the proper way to think about financial objects," then they would "continually stumble over that disagreement" and this would slow them down.

This again indicates that time is the currency of technical debt.  Getting slowed down is a cost that is measured in terms of the time it takes to get things done. The longer it takes to pay a lender back in full, the more interest you will have pay over time.  Likewise, the longer it takes for you to reflect new learnings into the software, the longer it will take for you to get things done.

This leads us to the third analogy of the original debt metaphor:
Making slower progress because you didn't reflect learnings back into the software is like paying more interest on an outstanding debt.

Analogy #4: Defaulting on a loan


Problems with debt arise when borrowers start defaulting on their loans. When you don't make regular payments on a loan, you can get socked with fees and increased interest rates. The longer you default on your loan, the more trouble you get into. If you stay in default long enough, you'll eventually find yourself in an untenable position where any amount you pay will only be enough to pay down the interest and fees and none of the original amount you borrowed. So instead of owing less, you end up owing more and more over time.

Ward explains the parallels with technical debt this way:
"People rush software out the door but never put learning back into the program. This is like borrowing money thinking that you'd never have to pay it back. If you do that eventually all your income goes to interest and your purchasing power goes to zero. If you develop a program ... and never reorganize it to reflect your understanding... eventually the program does not contain any understanding and all efforts to work on it take longer and longer.  In other words, the interest is total and you'll make zero progress."
In short, the fourth analogy of the debt metaphor is:
Not putting learning back into the program is like defaulting on a loan.
A related analogy is this:
A program that does not contain any understanding is like a loan for which you can only afford to pay the interest. 

So, what exactly is "Technical Debt" then?


After carefully studying Ward's explanation of the metaphor and reading between the lines a bit, I have changed my opinion of what Technical Debt means.  If I understand Ward's original intent, it all boils down to "a lack of understanding of the software."

As a metaphor, I think that it's really more important to view technical debt as something you choose to do rather than as something that you have. That is, thinking in terms of "going into debt" rather than in terms of "having debt" is more useful and I think Ward may have even alluded to this somewhat in his video.

Going into technical debt is not so much a problem as it is a conscious choice and commitment. At least that's the way it should be. Just as borrowing money so you can benefit sooner is a good thing, taking on technical debt so you can benefit from using the software sooner is also a good thing. You just have to be responsible and remember to pay back your debt by refactoring the program to reflect any learning you acquired from using the software sooner. Diligently paying back your debt helps you stay out of trouble and allows you to make the debt metaphor work to your advantage.

Summary


In this post, I went over the debt metaphor as originally proposed by +Ward Cunningham. I identified several analogies that can be derived from the metaphor.  I proposed that going into technical debt is really not so much a problem as it is a choice and commitment that you make so you can benefit from using working software to gain more understanding about a problem and the software itself.  It is also important to remember that you have an obligation and responsibility to repay your debt by reflecting any learning you acquire back into the program.

Next


In upcoming posts, I'll continue to explore more nuances of the original debt metaphor and other aspects of the analogies that can be derived from it.  I'll explore the meaning of the phrase "software that does not contain understanding" and point out gaps in the metaphor that can lead to misunderstanding and weakening from semantic diffusion.

Friday, August 23, 2013

ACM SPLASH 2013: Technical Debt Panel Discussion

SPLASH 2013 (formerly OOPSLA) will be held in Indianapolis IN from October 26 - 31 and out of pure (dumb) luck of being in the right place at the right time, I find myself recruited to be a panelist at the conference for a second time in as many years.  Lightning does strike more than once after all. Well, I guess it also helps to work for the same company as the panel organizer and for our company to be one of the conference's major corporate sponsors. I still can't help but wonder why I'm getting invited to do this though.

Last year, I was asked to fill in for a colleague who was unable to make it to Tucson, AZ to be on the Software Tools Research Panel at SPLASH 2012.  That was actually kind of fun and I thought I was able to hold my own during the panel discussion.  As an added bonus, I got to meet some well-known and respected people in our industry like Jim Coplien and Tryvge Reenskaug, Rebecca Wirfs-Brock, and William Opdyke, among others. There's never a dull moment when Cope is in on a discussion.

This year, I've been asked to participate in the "Technical Debt Panel Discussion," sitting on the podium with Bill Opdyke and others.  It's kind of strange to think about sitting up there, a virtual unknown whose bio seems disproportionately thin in substance when compared to those of the other panelists. My only real claim to any kind of celebrity is a "bartender" designation in a volunteer-basis moderator role in an online community for Java developers but I wasn't about to include that in my bio. Don't get me wrong, I'm proud to be a part of a great community and I have a great time volunteering on it but it just doesn't seem to measure up to the kind of credentials the other folks have.

Anyway, my relative anonymity may work to my advantage. I guess I can go there and present myself as the unofficial, self-appointed representative of the average developer who is at that very moment actually fighting his or her way through nasty, debt-ridden code. 

If you have some opinions about Technical Debt, it's past, present, and future, please leave a comment or two and let me know what you think the panel should discuss.

I will be posting more thoughts about Technical Debt in the next few weeks leading up to conference and hopefully, with some help from folks who might stumble upon this blog, I will be able to contribute some things of substance to the panel discussion in October.

Thursday, August 22, 2013

How to change the default login shell

The chsh command allows you to change your default login shell.

My default login shell on one server that I used frequently was ksh and having to manually switch to bash got old after a few times.  Here's how to make bash the default login shell:

1. Make sure that bash is listed in /etc/shells.
    $ cat /etc/shells
    /bin/sh
    /bin/bash
    /sbin/nologin
    /bin/ksh
    /bin/tcsh
    /bin/csh
    ...

2. Run the chsh command
    $ chsh -s /bin/bash

That's it.  The next time you log in, you'll be in the bash shell.

BTW, to see what the current shell is, run this command:
    $ echo $SHELL