# The virtue of incremental development

Here’s today challenge: can you write a command-line tool that allows converting to and from various measurements units?

For instance, you could input “3 miles in meters” and get “4828.03”.

I submitted this challenge to my Python students last weekend,
asking them to write the code from scratch. ^{1}

1 hour later, something miraculous happened that I never would have expect.

But let me tell you the full story.

# Getting started

I told the students that they could start by writing some “exploratory code”.

“Just hard-code anything you have to and keep everything in the `main()`

function “, I said.

After a few discussions, we agreed to only write code that converted kilometers to miles, and that we’ll read the values from the command line.

Here’s what we came up with:

```
import sys
def main():
kilometers = float(sys.argv)
miles = kilometers / 1.609
print(f"{.2f}", miles)
if __name__ == "__main__":
main()
```

I then pointed out that the code was not generic. Indeed, “kilometers”, “miles” and “1.609” are hard-coded there.

# Naming a new function

The students understood there was a three-parameters function waiting to be written. So we went to the drawing board
and after a while we decided to have a function called `convert(value, unit_in, unit_out)`

.

Note that we did *not* make any assumption about the *body* of the function. We just wanted to see how `main()`

could become
more generic, and we were still allowed to hard-code parts of the code:

```
def convert(value, unit_in, unit_out):
coefficient = 1 / 1.609
result = value * coefficient
return result
def main():
value = float(sys.argv[1])
unit_in = sys.argv[2]
unit_out = sys.argv[3]
result = convert(value, unit_in, unit_out)
print(f"{.2f}", result)
```

Some notes:

- The
`main()`

function is now completely*generic*, and we probably won’t need to change it. - The signature of the
`convert`

function almost dictated the command-line syntax:

```
def convert(value, unit_in, unit_out):
...
```

```
# Usage: convert.py value unit_in unit_out
$ python3 convert.py 2 meters miles
```

# Computing the coefficient

Now it was time to get rid of the hard-coded coefficient. This time finding a function name was easier:

```
def get_coefficient(unit_in, unit_out):
...
```

Then we tried to figure out how to implement it. We knew we would be needing a dictionary, but the structure of it was unknown.

“Back to the drawing board”, I said. “Let’s write down what the dictionary should look like”.

Here’s our first attempt:

```
units = {
"km": { "miles": 1/1.609, "meters": 1/1000, ....},
"yards": { "miles": 1/1760, "meters": ..., "km": ...}
...
}
```

“This won’t do”, I said. “Look at what happens if we add a new measurement unit, such as `feet`

”.

We’ll have to:

- add a new ‘feet’ key to the
`units`

dictionary, - compute all the coefficient to convert from
`feet`

to all the other units, - and add a
`feet`

key to all the other dictionaries

There has to be a better way!

After a short brainstorming session, we decided to limit ourselves to *distance* measurements, and to *always convert to SI units* first.

So we draw the new structure of the `units`

dictionary:

```
# Coefficients convert from "meters"
distances = {
"km": 1/1000,
"yards": 1.094,
"miles": 1/1609,
}
```

And then we thought about the algorithm. We found three possibilities:

- If we want to convert
*from meters*, we just have to look up the coefficient in the dictionary - If we want to convert
*to meters*, we can look up the coefficient in the dictionary and return its inverse - Otherwise, we combine the two procedures above and return the product of the two coefficients.

“This is looking good”, I said. “Let’s try to implement the algorithm but just for the first case and see what happens”.

# Testing the algorithm

I showed my students how they could use Python’s interpreter to check the get_coefficient() function was working properly.

We quickly managed to get the first case working:

```
def get_coefficient(unit_in, unit_out):
# FIX ME: only works with distances for now
# Coefficients to convert from "meters"
distances = {
"km": 1/1000,
"yards": 1.094,
"miles": 1/1609,
}
if unit_in == "m":
return distances[unit_out]
```

```
>>> import conversion
>>> conversion.get_coefficient("m", "km")
0.001
>>> conversion.get_coefficient("m", "yards")
1.094
```

“Cool, this works”, I said. “Let’s see what happens when the input value is not in meters:”

```
def get_coefficient(unit_in, unit_out):
# FIX ME: only works with distances for now
# Coefficients to convert from "meters"
distances = {
"km": 1/1000,
"yards": 1.094,
"miles": 1/1609,
}
if unit_in == "m":
return distances[unit_out]
else:
reciprocal_coefficient = 1 / distances[unit_in]
return reciprocal_coefficient * distances[unit_out]
```

```
>>> import conversion
>>> conversion.get_coefficient("miles", "yards")
1760
```

“Look how readable the code is”, I said. “We have a value that’s called
`reciprocal_coefficient`

and we get it by calling 1 over something else. Isn’t
this nice?“.

# The miracle

I then pointed out that the ‘else’ after the return was unnecessary.

```
def get_coefficient(unit_in, unit_out):
# FIX ME: only works with distances for now
# Coefficients to convert from "meters"
distances = {
"km": 1/1000,
"yards": 1.094,
"miles": 1/1609,
}
if unit_in == "m":
return distances[unit_out]
reciprocal_coefficient = 1 / distances[unit_in]
return reciprocal_coefficient * distances[unit_out]
```

And then it happened. “Hey”, one of the students said, “what if we added meters in the distances dictionary with `1`

as value?
We could get rid of the first `if`

too!“.

“Let’s do it”, I said:

```
def get_coefficient(unit_in, unit_out):
# FIX ME: only works with distances for now
distances = {
"m": 1,
"km": 1/1000,
"yards": 1.094,
"miles": 1/1609,
}
reciprocal_coefficient = 1 / distances[unit_in]
return reciprocal_coefficient * distances[unit_out]
```

```
>>> import conversion
>>> conversion.get_coefficient("m", "m")
1
>>> conversion.get_coefficient("km", "m")
1000
>>> conversion.get_coefficient("m", "yards")
1760
```

And of course, this works. When `meters`

is either `unit_in`

or `unit_out`

, all operations will involve multiplying or dividing by 1.

That was a really nice surprise for several reasons:

- One, when I thought about the problem alone, before starting the workshop, I was pretty sure I would need a much more complex data structure.
- Two, one of the students just refused to believe the code would work, even after having seen it in action in the interpreter ;)
- Three, we killed one comment :)

# Lessons learned

We found a beautiful algorithm and a nice data structure, not by trying to
solve *everything* at once, but by slowly building up more and more generic
code, getting rid of hard-coded values one after the other, and by carefully
thinking about naming.

I hope you find this approach useful, and I highly suggest you try using it next time you implement a new feature.

Cheers!

- I’m using mob programming during my Python classes. It works really well.
^{[return]}

Thanks for reading this far :)

I'd love to hear what you have to say, so please feel free to leave a comment below, or read the contact page for more ways to get in touch with me.

Note that to get notified when new articles are published, you can either:

- Subscribe to the RSS feed or the newsletter
- Or follow me on Mastodon, dev.to, or twitter.

Cheers!