A collection of thoughts related to the challenges of software engineering

stay connected

Posts Tagged with ‘agile’

September 26th, 2011

I recently came across a great blog post asserting - based on numerous studies - that size is the best predictor of code quality.

According to my own experience, this is spot on.

I can think of a couple of explanations:

  1. Concise code is generally the work of experienced and skilled developers.
  2. Removing dead code greatly increases code quality.
  3. Refactored code tends to be smaller (Adding features is not refactoring).

Point 2 and 3 result in a smaller code base and are a sign of a continuous code review process ("no code is set in stone" principle). In my experience, re-reading the code regularly is one of the greatest contributors to code quality, much more than unit testing and continuous integration (which are nevertheless required to make frequent refactoring possible).

I know that the best bug fixes I did were generally fixes that reduced the line count. Those bug fixes were generally a case of "Who the hell did something that complex for a problem that simple?! Oh wait, I did...".

In other words if you put efforts into code quality, this will generally result in reduced code size. This, to me, explains more the correlation between size and quality: concision is more an indicator of quality than a cause.

A while ago I technically screened a developer for one of our biggest client. This developer was a huge fan of unit testing. Whatever software puzzle I submitted could be solved with an unit test.

Here is a sample of the interview:

  • Say I have a C++ program that crashes because it exhausts its virtual memory space. What do you do to solve the problem?
  • Well... Err... I'd write an unit test... And then... Err... Problem solved!

Let's sum this up with a chart:

Unit test chart

Unit test chart

No need to say that the person wasn't hired. What I realized by then is that unit testing was not only often misunderstood but overrated. I don't imply we ignore unit testing at Bureau 14, we just have some degree of moderation in their usage...

The obvious unit test

Let's say we want to implement a string library in C++ for some reason. I'm talking something very simple with interfaces similar to the STL string. We're going to call it xt_string.

Test driven development is about writing tests before writing code. If you ask me, when you write the tests is not that important. I'd even say that writing tests before writing code will make you believe you don't need to write specifications or documentation (agile or not, write specifications, thanks).

Nevertheless, we're going to write obvious tests that I often call "space continuum integrity tests". Basically when these tests fail, something is terribly wrong either with your code or the physical laws of the universe. Generally, it's a problem within the code.

1
2
3
4
xt_string st1;
BOOST_CHECK(st1.empty());
xt_string st2("test");
BOOST_CHECK_EQUAL(st2, "test");

The bad unit test

Because we care about performance, our xt_string uses custom allocated buffers aligned on the cache. And lucky us, we have a function to validate that! Quick, to the unit tester!

1
2
xt_string st("oh my");
BOOST_CHECK(is_cache_aligned(st.buffer()));

The buffer method returns the underlying buffer, as you probably inferred.

This test is horribly bad. Horribly. Why? Well because it's in "The bad unit test" section for instance.

It seems clever at first, but you're shooting yourself in the foot with a bullet that travel through the future. You pull the trigger now and your foot explodes one year later.

We have a simple policy here: each unit test must be ignorant of the internals. In other words, we only do black box unit tests.

Here is a non-exhaustive list of reasons :

  • The most obvious : you're making inner rework of the class twice more expansive. First change the class, then the unit test.
  • Unit testing is all about enabling you to modify your code and get some early validation. If you need to change the unit test when you change the class, you don't have that validation anymore. You're just doing the same work twice. That just means a twofold increase of errors.
  • People reading the unit test might base some code on it, as unit tests are often used as an example of "how to use the object". Thank to this test, users will go on assume things preventing - or making it really risky - modification.

In short: a good unit test validates features and their implications without assuming anything about the implementation.

The good unit test

So that's all there is to a good unit test? Actually, there's more to it.

Let's have a look at this:

1
2
3
4
5
6
7
8
9
10
11
12
xt_string st1("yes");
xt_string st2("no");
BOOST_CHECK_EQUAL(st1, "yes");
BOOST_CHECK_EQUAL(st2, "no");
st1 = st2;
BOOST_CHECK_EQUAL(st1, st2);
xt_string st3("maybe");
BOOST_CHECK(st3, "maybe");
st2 = st3;
BOOST_CHECK_EQUAL (st2, st3);
BOOST_CHECK(st1 != st2);
BOOST_CHECK(st1 != st3);

Looks pretty much like an obvious test of affectation and equality, isn't it? Except that it's more than that. This also tests the underlying memory management. What's good with this test is that if you're unifying your string buffers somewhere in the future, it's going to make sure that copy on write works.

Of course this example is far from complete and much more should be written to have some reasonable degree of validation.

Whatever implementation of strings you chose to have, the above test must remain true. You can rewrite the string class from scratch, you won't have to change the test. It's obvious, easy to understand and detects side effects.

A good unit test must have a certain degree of extra-lucidity: ability to detect future issues is the hallmark of a good unit test.

Think about what you might do with the tested code in the forthcoming years and you'll write good unit tests.

More about writing good unit tests

The real benefits of unit testing comes when you validate the interactions of your different structures and functions.

Ideally, your unit tests should reflect what the users are going to do with your program. Don't try to have 100 % coverage immediately (or ever actually). Instead, aim for all typical usage scenarii. That means that after the build, when the tests are run, you know your program won't crash immediately or spit blatantly wrong results. This doesn't replace integration testing and regression testing, but it gives a quick feedback about what you're doing.

The second good reflex is that whenever a bug is found and reproduced, write an unit test that exhibits the problem and then fix it. In doing so you reduce the probability of getting the same bug twice to almost zero.

Few more words

Passing unit tests doesn't mean your program works. Never overestimate the reliability and coverage of unit testing.

Most of all, never forget that unit testing is here to increase software quality and save time. Never put your team in a position where writing tests takes too much time from designing news features and fixing bugs.

Your customers won't like it.

Paris

I'm obsessed with theorizing everything in life. That can be a good thing, and that can be a bad thing. When you know how to stop, I found it can be quite a strength.

It happens that I write software for a living. I've done that for about 10 years now and you can guess I spend a great deal of time inside my mind creating and destroying abstract theories about what makes a successful software project.

With Shambling++, everything is so simple!

If there is one conclusion I have reached, it's that there is No silver bullet.. Of course choosing the tools is important: you don't nail with a screwdriver. We've worked with a lot of technologies, more often than not, it's always the same principles. It was available in yellow, now it's available in blue.

If your problem is solved only in using the right middleware or tool, you never had a real problem to start with.

Being agile, lean, nimble

I have a lot of interest for the lean approach, but once again, this is nothing new. For example, our process is strongly SCRUM influenced.

Unfortunately, being lean is most of all about improving efficiency. You can be efficient all the way, at some point you need something more to succeed.

Another way to see it, is that agility and nimbleness will prevent certain cause of failures, not all of them.

How to successfully deliver?

It all comes down to having talented people at every link of the chain. This conclusion will strike even stronger the day you realize that the chain is a strong as the weakest link.

Your developer must have outstanding software development skills, communicate well and be able to handle the extreme pressure of working for an egocentric lunatic (that would be me). Your assistant must be nice, spell flawlessly and have a keen sense of organization and diplomacy. Your translator know the subtleties of foreign languages and know that translating really means transposing.

You see where I'm getting at. It's pointless to design a process if the components of the process (i.e. the human beings) are not good enough.

When you hire intelligent people - because we're talking about intelligence here - and give them the right amount of autonomy - which happens to be a lot - they will see what's wrong and fix it. They will find a way around problems.

They will journey out of mediocrity. They will do this for the simple reason that talented people hate mediocrity.

When you hire less intelligent people - add snobbery, elitism and the proper amount of French accent here - they will blindly apply whatever process you have, even when it doesn't make any sense.

You might be lean and agile all the way, don't worry, they'll find a way to make that your worst nightmare to the point you will devote the rest of your life to the holy church of waterfall development.

Software is intelligence packaged in instructions that solve problems. A software company is an intricate, subtle and delicate intelligence distillery. That intelligence comes out of the brains of your employees.

Never compromise yourself with sloppy recruitment.