First, no matter what the methodology is, we all write software for our users (a.k.a. customers, project sponsors, end users, or clients). Second, no matter what the methodology is, we write incrementally, releasing features and bug fixes one by one. Maybe I’m saying something absolutely obvious here, but it’s important to remember that each new version should first of all satisfy the needs of the user, not of us programmers. In other words, the way we decompose a big task into smaller pieces should be user-targeted, and that’s why you always work top down. Let’s see what I mean through a practical example.
Say I’m contracted by a friend of mine to create a word-counting command line tool very similar to wc. He promised to pay me $200 for this work, and I promised him I’d deliver the product in two increments—an alpha and beta version. I promised him I’d release the alpha version on Saturday and the beta version on Sunday. He is going to pay me $100 after the first release and the rest after the second release.
I’ll write in C, and he will pay in cash.
The tool is very primitive, and it only took me a few minutes to write. Take a look at it:
#include <stdio.h>
#include <unistd.h>
int main() {
char ch;
int count = 0;
while (1) {
if (read(STDIN_FILENO, &ch, 1) <= 0) {
break;
}
if (ch == ' ') {
++count;
}
}
if (count > 0) {
++count;
}
printf("%d\n", count);
return 0;
}
But let’s be professional and not forget about build automation and unit testing. Here is a simple Makefile
that does them both:
all: wc test
wc: wc.c
gcc -o wc wc.c
test: wc
echo '' | ./wc | grep '0'
echo 'Hello, world! How are you?' | ./wc | grep '5'
Now I run make
from a command line and get this output:
$ make
echo '' | ./wc | grep '0'
0
echo 'Hello, world! How are you?' | ./wc | grep '5'
5
All clean!
I’m ready to get my $200. Wait, the deal was to deliver two versions and get cash in two installments. Let’s back up a little and think—how can we break this small tool into two parts?
On first thought, let’s release the tool itself first and build automation and testing next. Is that a good idea? Can we deliver any software without running it first with a test? How can I be sure that it works if I don’t ship tests together with it? What will my friend think about me releasing anything without tests? This would be a total embarrassment.
Okay, let’s release Makefile
first and wc.c
next. But what will my friend do with a couple of tests and no product in hand? This first release will be absolutely pointless, and I won’t get my $100.
Now we’re getting to the point of this article. What I’m trying to say is that every new increment must add some value to the product as it is perceived by the customer, not by us programmers. The Makefile
is definitely a valuable artifact, but it provides no value to my friend. He doesn’t need it, but I need it.
Here is what I’m going to do. I’ll release a skeleton of the tool, backed by the tests but with an absolutely dummy implementation. Look at it:
#include <stdio.h>
int main() {
printf("5\n");
return 0;
}
And I will modify the Makefile
accordingly. I will disable the first test to make sure the build passes.
Does my tool work? Yes, it does. Does it count words? Yes, it does for some inputs. Does it have value to my friend. Obviously! He can run it from the command line, and he can pass a file as an input. He will always get number “5” as a result of counting, though. That’s a bummer, but it’s an alpha version. He doesn’t expect it to work perfectly.
However, it works, it is backed by tests, and it is properly packaged.
What I just did is a top-down approach to design. First of all, I created something that provides value to my customer. I made sure it also satisfies my technical objectives, like proper unit test coverage and build automation. But the most important goal for me was to make sure my friend received something … and paid me.
What from would you start making a new webservice?
— Yegor Bugayenko (@yegor256) May 24, 2020