A few days ago we had one of our Coding Dojo's at
Gutefrage.net. This time we tried to implement the "Roman Numerals kata".

I always learn a ton, solving such seemingly simple problems.

Have a look at our (unfinished) code

Here are the tests:

describe RomanNumeralsConverter do
  let(:converter) { RomanNumeralsConverter.new }

def convert(number)

it 'returns an empty string for 0' do
convert(0).should == ''

it 'returns I for 1' do
convert(1).should == 'I'

it 'returns III for 3' do
convert(3).should == 'III'

it 'returns IV for 4' do
convert(4).should == 'IV'

it 'returns V for 5' do
convert(5).should == 'V'

it 'returns VI for 8' do
convert(8).should == 'VIII'

it 'returns X for 10' do
convert(10).should == 'X'

it 'returns IX for 9' do
convert(9).should == 'IX'

it 'returns XIV for 14' do
convert(14).should == 'XIV'

Here the implementation:

class RomanNumeralsConverter
  SPECIAL_VALUES = {0 => '', 5 => 'V', 10 => 'X'}

def convert(number)
result = ''

 while number > 3
   if SPECIAL_VALUES.include?(number+1)
     result += 'I'
     number += 1

   nearest_boundary_under_number =


   if nearest_boundary_under_number >= 0
     result += SPECIAL_VALUES[nearest_boundary_under_number]
     number -= nearest_boundary_under_number
 number.times do
   result += 'I'


def nearest_boundary_under_number(number)
SPECIAL_VALUES.keys.delete_if {|i| i > number }.max

Duplication in tests

We tried to remove duplication from the tests. I think we failed.
One of my teammates pointed out that PHPUnit has
DataProviders for this kind of test data.

I didn't think that this was a good idea - but looking at the tests now I have to admit that
there is virtually no value in writing the tests spec style for this kind of problem.

Write the simplest tests first

We had quite a hard time with the substraction rule (4 => IV).
We wrote a test for the 4 very early - I think we could have made it
easier for us by first ignoring the substraction rule and focusing on
all cases where it does not apply.

  • Write the easy tests first
  • If you hang try another test

Accept weired code

We "cheated" quite a bit using an array for the special cases. It just
didn't look right. Supprisingly it came together at the end of the session anyway.

  • Accept weired code

When to abstract

Right at the beginning one of my colleagues pointed out, that we just
could create a big array with a mapping between all the arabic number to
the roman numerals and use that. I think he had a point..

But since we wanted to create an algorithm that was no option. We
started with a big if / elsif / else statement. That was fine for 2 or
3 tests, but then we were moving sideways. No algorithm was going to emerge.

That's when you need to stop and think about how to abstract a little bit further.

  • Identify when you are moving sideways - then start abstracting

The end

No world moving lessons - but valuable non the less. Thats why I like coding dojos -
they are a good place to reason about the nature of programming, without
having to write production code.