Monday, January 28, 2008

The Mysterious Case of the Missing BigDecimal Constructor

My current project has recently been experiencing runtime exceptions of the form:

NoSuchMethodError: java.math.BigDecimal.<init>(I)V

These exceptions only showed up when we started creating a codebase that mixes code written for Java 1.4 and code written for Java 1.5 (we wanted our design to use generics and annotations).

Looking at the stack trace it was clear that the source of the runtime exception was construction of a BigDecimal using an int argument, for example, new BigDecimal(5). Looking over the Java API reveals that in Java 1.5 BigDecimal has a constructor that takes a double or an int argument while in Java 1.4 BigDecimal has no constructor that takes an int argument.

Working through all the permutations with a colleague we were able to create the runtime exception when we built our code using a Java 1.5 build path, compiling for Java 1.4, and running the code on a Java 1.4 JVM. It turned out that the key mistake was to use the Java 1.5 build path. This only happened on certain misconfigured developer machines. Once we reconfigured the developer machines the runtime exception was gone.

Still, I was curious and I wanted to really understand the runtime exception. I installed the Bytecode Outline plugin for Eclipse so that I could see the bytecodes generated for new BigDecimal(5). You could also use the javap command line tool. Below are the interesting fragments of bytecode.

Build Path: 1.4.2

NEW java/math/BigDecimal
DUP
LDC 5.0
INVOKESPECIAL java/math/BigDecimal.<init>(D)V


Build Path: 1.5.0

NEW java/math/BigDecimal
DUP
ICONST_5
INVOKESPECIAL java/math/BigDecimal.<init>(I)V

Now it is easy to see what is going on. When the build path is for Java 1.4 the compiler converts the int literal 5 to a double. When the build path is for Java 1.5 the compiler generates a call to a constructor that takes an int argument—a method that does not exist in the 1.4 runtime libraries.

Case closed.

If you want to know the meaning of operands such as DUP and LDC, you can read Chapter 6 of The Java Virtual Machine Specification.

Monday, November 19, 2007

How To End Incessant Arguing

I have a rule of thumb: when there is truly nothing at stake an argument can go on and on and on and on...

There is a way out. Get everyone involved to recognize that there is nothing at stake and lead the group to make a choice. Or, take the lead and choose to give way (because you have recognized that there is truly nothing at stake.)

When positions remain entrenched, work hard to surface the missing information because there may be something essential at stake.

Try this example: what colour should we paint the garden shed?

Black. Red. Blue. Yellow. Black. Red. White. Orange. Black...

Suppose that you recognize that we will be using the shed mostly in the summertime. Now you can make the argument that the shed will get too hot if we paint it black. Instead we should use a lighter colour. Congratulations. You have surfaced new information. Now lead the group to make a choice between yellow and white.

Whenever you find yourself arguing the same point over and over again, stop and be sure that there really is something at stake before you pitch in to keep the argument going.

Thursday, October 11, 2007

Learn to Use a High-Level Filter

When you’re asked for a “high-level” plan do you panic? I don’t.

Whenever I hear “high-level”, I apply a High-Level Filter: I simply replace “high-level” with “vague”. Let me give you some examples of how the filter works.

Filtering a High-Level Plan

Imagine that you’re in a meeting with me, we’re drinking crappy office coffee, and someone sitting across the table says, “Let’s make a high-level plan.”

You might think, “How the heck are we going to do that? There is only half an hour until lunch.”

I apply a High-Level Filter and what I hear is, “Let’s make a vague plan.” We can certainly do that before lunch.

Filtering High-Level Requirements

Imagine that you and I are asked to provide high-level requirements for the next phase of a complex project and we have to be done by end of day tomorrow.

You might panic and mutter, “Crap. More unpaid overtime.”

I apply a High-Level Filter and smile knowing that we have been asked to provide vague requirements. We can certainly do that by end of day tomorrow.

Use a High-Level Filter

You will panic less often and work less overtime. More importantly, you will understand where people are coming from when they ask you for high-level plans or high-level requirements.

Thursday, July 05, 2007

Refactoring in the Small

When I started writing programs I was given small problems to solve. I was learning by programming in the small. How could it be any other way?

Programming in the large requires skills that you can’t learn from programming in the small. For example, there is no need for me to come up with an error handling strategy when I’m writing a command line utility implemented by one or two classes. But I don’t want to use any kind of framework that doesn’t have an error handling strategy.

What I’ve noticed is that the skills that I learn while programming in the small are still valuable when programming in the large.

Try something similar with refactoring. Begin by learning how to effortlessly refactor in the small. Learn how to create a Composed Method by applying Extract Method a few times. If things go wrong, just apply Inline Method and try an alternate breakdown. Maybe you need to apply Encapsulate Field prior to Extract Method. Perhaps your newly extracted method is not sufficiently general so go ahead—apply Parameterize Method. Nice.

Learn the names of the refactorings. Use your programmer honed abstraction abilities. Start to think of a refactoring task as a sequence of refactorings rather than arbitrary text manipulation. The skills you learn while refactoring in the small are skills that you will need to refactor in the large.

Suppose you need to restructure some complex procedural code, full of intertwined conditional logic, and plan to use object composition to manage variation. Are you thinking of the Strategy design pattern? Now you’re refactoring in the large. Thinking about the overall restructuring task dominates the refactorings. So you must learn to effortlessly apply refactorings or you’ll mess up your restructuring.

So what is refactoring in the small? Is it making changes in only a single class? Nah—too simplistic. What if the class is one of your central classes? What if it has that Large Class smell? Besides, once you get good at it, a straightforward refactoring may affect many classes.

For me, I’m refactoring in the small when the refactorings take place in a single programmer episode. If things go wrong it’s easy for me to back out. I can’t try anything too ambitious. Programming episodes are where I learn to apply refactorings.

So c’mon folks. Start with baby steps. Practice. Learn. I want you to become a refactoring in the small ninja before I catch any of you restructuring your code.

Wednesday, June 13, 2007

Why Do Teams Discourage Refactoring?

I’m frustrated by my inability to convince members of my team to learn and practice refactoring. I’m frustrated by how often I see refactoring actively discouraged.

Martin Fowler defines a refactoring as a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior. Refactoring as an activity means to restructure software by applying a series of refactorings without changing its observable behavior. Notice that Fowler distinguishes between the activities of refactoring and restructuring: refactoring is a very specific technique to perform the more general activity of restructuring.

Michael Feathers defines legacy code as code without tests. If you work with such code you have to be very careful to avoid breaking working code whenever you change code. You might choose to adopt a rule of thumb like “whenever I make a change I will try to perturb the existing code as little as possible.” Anyone who has internalized this rule of thumb is going to be very wary of refactoring.

As developers we are constantly under pressure to deliver features. You may knowingly choose to take shortcuts while imagining some future where you can go back and instead do things right. You might choose to adopt a rule of thumb like “I know what I’m doing isn’t quite right but given a chance to go back I would know how to make things right.” Anyone who has internalized this rule of thumb is going to view refactoring as a sanction to unleash their pent up desire to go back and restructure code to make things right.

The combination of these two rules of thumb often produces a strange set of team behaviours. Developers are reluctant to refactor as they develop features. Instead they lobby their team for permission to explicitly refactor afterwards. By not refactoring at a smaller scale the team usually doesn’t have the experience nor the skills to perform refactoring as a sequence of small behavior-preserving changes. If those same developers are able to negotiate for explicit refactoring time, they often botch it up and now project leaders also become wary of refactoring.

It doesn’t have to be this way.

Wednesday, August 30, 2006

Break Your Coding Habits

I'm re-reading Mastery: The Keys to Success and Long-Term Fulfillment by George Leonard and I'm learning to love the plateau. If I were to chart my ability to create valuable code against time I would not see a gradually rising curve. Instead, the curve would be flat most of the time punctuated by occasional jumps to a higher level.

For Leonard one of the 5 keys to mastery is intentionality. I find it easy to slip into coding habits that keep me on the plateau. Whenever I create a new class I'm applying these habit-breaking rules:

  1. No implementation inheritance.

  2. Do not use the new keyword to create any objects.

  3. No getter and no setter methods.

  4. No boolean arguments.


Of course, it often makes sense to create classes that intentionally violate these rules. However, just for now, if you find yourself wanting to violate one of these rules: Stop. Are you violating the rule out of habit or do you have a good reason to violate the rule?

Code that follows these rules is more valuable than code that does not. I'll be explaining my reasons in subsequent postings.

Sunday, July 30, 2006

What Is It About Code That Makes It Valuable?

I believe that code is valuable when it works and it can be changed.

I find this way of thinking to be useful in two ways: (1) it reflects my latest understanding of what it means to be valuable code and (2) it helps me to coach people to create valuable code.

When I first started writing code all that mattered to me was that the code worked. I started coding in high school using mark sense cards. They were like punch cards but you could use a pencil to mark the cards with your program rather than using a card punch machine. At the end of every day a long cardboard box of cards was transported from my high school to the Ottawa School Board's mainframe. Some three or four days later, the box of cards would reappear along with any printouts. As turn around time for fixing a syntax error was three or four days, I learnt to always be very careful to check my code for syntax errors before submitting to the mainframe.

Even so, I would still make syntax errors. Once I had fixed the syntax errors and my program would compile and run, I usually found that I had made both goofy and subtle logic errors. Since each run of the program was so valuable, I included lots of print statements to instrument the dynamic behaviour of my simple programs. Eventually I would get my program to work. Once my program was working there was no reason to change it. I would strip out all my diagnostic print statements and hand in my program listings for marking confident that I wouldn't have to ever change the code.

I think that when most people start to write code their overwhelming goal is to make their code work. My early experiences with coding affected my attitudes towards code in that once my code worked I was done with it. Now when I coach people about coding my most hated response to my hints is "sure, but why do anything since the code works." As long as I cannot convince programmers that it works is a necessary but not sufficient condition for valuable code I'm finding that I'm not making very good progress with my coaching.

Once I add "and it can be changed" on to the end of my definition of valuable code I have some leverage against programmer indifference. I can say "you're right it does work but is it easy to change?"