My submission to Euruko was rejected. ☹ That’s a pity, would have loved to visit Rotterdam. I still have 7 other proposals that might come through. Fingers crossed 🀞

External forces

I am occupied with learning these days. Learning on my own about visualizations of data among other topics. But also learning about learning. For that I read what other people think about learning. There are many things I have to learn about this whole topic. One thought I saw repeatedly, was about external forces, or limiting factors.

Let me elaborate what I mean by that: There are people that can motivate themselves more easily than others can. They reach their goals or at least try very hard. Others give up more easily when they face some resistance. As always, there are people in the middle between these extremes. You know best which group you belong to. πŸ’ͺ

What has this do with software quality? I am getting there… πŸ˜‰

I am wondering how external forces could help improve quality. If you need to reach your goal and you don’t belong to the group of highly self-motivated people there are options like hiring a coach. Athletes do that all the time. I pay for a “virtual” coach that guides my running efforts.

How could you hire a “virtual” coach for your coding efforts, for reaching your targets on your software quality metrics? You could hire me or other “real” coaches, of course. But that doesn’t scale too well and might be too expensive.

Again, for some people it is easy enough to use static analysis or linting β€” a kind of coach in it’s own right β€” and follow their guidelines. Yet, still there are people that ignore the warnings or guidelines imposed upon them by the tools. Reasons may be a hard deadline or too much workload. How could we offer external forces, limiting factors that help them, guide them, towards doing the right thing?

One solution I can think of is to have a robot not accept your code when it is below standard or ignores guidelines. A robot could be anything that measures and grades your code and reports back to your team. Some tools already offer this, for example GitLab. If you want to merge code that decreases the overal quality metrics, you are not allowed to do so. So that would be one.

Another idea: If you try to commit or merge such code, you need to consult with another developer about the code. Once you worked on it together, the other dev has to enter her secret key, to remove the lock on the merge. This forces you to pair on code more often.

When it comes to teaching there is this saying of the “glass has to be empty (enough).” You cannot pour water into it, when it’s already filled. Said ideas πŸ‘†probably won’t work for a team that isn’t aiming for learning and improving.

I will continue to think.

You probably know AC/DC. But have you heard of MC/DC?

If you need to test a complex conditional, you should take some time to learn about this one.

holgerfrohloff.de/newslette…

Today I am working on a new visualization of fragmentation of ownership for Git repositories. The more fragmented a repo is, the more developers work on it together. This leads to no clear ownership and makes it easier for defects to creep into the code.

Complex conditionals

The other day we dealt with code coverage and gnarly conditionals. I promised to offer a way to be able to test them properly.

THERE IS NONE.

Ha, what a bad joke. But the real answer might not be better, depending on your point of view. What you have to do is create a table.

(A || B) && C

This is our conditional.

| m | A | B | C | RESULT |
----------------------
| 0 | T | T | T |    T   |
| 1 | T | F | T |    T   | x
| 2 | F | F | T |    F   | x
| 3 | F | T | T |    T   | x
| 4 | T | T | F |    F   |
| 5 | T | F | F |    F   | x
| 6 | F | T | F |    F   |
| 7 | F | F | F |    F   |

# m is the test case
# A, B, C are the atomic parts of the conditional
# RESULT is the result of the evaluation of the conditional

For three terms in a conditional, you can have 8 different cases (2^3). You don’t need to test every case. You have to find those cases where switching one term (A, B or C) changes the RESULT. You take those cases and write tests for them. You can ignore the rest as they don’t bring you any new information. For our example these could be the test cases 1, 2, 3, 4. I marked them with an x,

The general rule of thumb is that you can solve this with n + 1 test cases where n is the number of terms.

This technique is called Modified Condition/Decision Coverage or short MC/DC. I like this name, it’s easy to remember 🀘.

It gets harder to do, when one term of the conditional is used more than once (coupled). Another thing to take note of is that depending on the decision statement in the code, it may not be possible to vary the value of the coupled term such that it alone causes the decision outcome to change. You can deal with this by only testing uncoupled atomic decisions. Or you analyse every case where coupling occurs one-by-one. Then you know which ones to use.

You’ll have to do this in the aerospace software industry or where you deal with safety-critical systems.

If you read this far: Congratulations. This is one of the more in-depth topics of testing software. You deserve a πŸ‘ for learning about these things!

I am reading the new edition of Refactoring by Martin Fowler these days. I am preparing a new workshop on Software Design and Architecture. I bet there are some ideas in there that will inspire me to create some specific exercises. #refactoring

I am now using Micro.blog for my microposts. I will crosspost my newsletters there. And whatever I think makes sense. I will figure it out by just trying. 😎

Code coverage can be misleading

During the last week, I had two discussions about code coverage. Code coverage is the metric of how many lines of code are covered by your automated test suite. Many test frameworks have built-in ways to measure this. Other times you have to install another tool manually. When you run your tests you then see how many lines are not covered by a test. That means that no test was run where this line of code was evaluated or executed or interpreted.

When you reach 100% code coverage, what then? Are you done? Could you guarantee that there are absolutely no bugs in your code?

If you are tempted to say Yes, or “maybe?” then let me tell you that you are wrong.

Consider this piece of code.

If you write a unit test for this method, the line eval... will be interpreted because of the if emergency at the end. The line is thus covered. But the code is not covered or tested.

Admittedly, this is a very trivial example that I made up. In reality, there are some more profound things to consider.

If you have complex conditionals you might need a logic table where you compare all possible combinations of the atomic parts of the conditional.

You cannot possibly evaluate this in your head and know whether you checked for every possible, sensible combination. Yet when you cover that line you are at 100% coverage and can go home, right?

So what do you do? Let’s look at this tomorrow.

Quick wins, part 4: YAGNI

We refactored some code yesterday, to move knowledge about an implementation of a function back to where it belonged: Into the function’s class and into the function itself.

Today I want to talk about another topic that often comes up when you are refactoring, or plainly “changing code.”

It happened the other day, during a workshop I was doing on software testing. The participant wanted to apply his new knowledge and write tests for a given JavaScript class. He had written the code a few weeks before we did the workshop. During the hands-on part of the workshop, he wanted to add tests, to make sure everything worked and to make sure that he understood what I had been talking about.

We were lucky in as so far that something happened that usually happens next: He noticed that his code was not easily testable. The design of his class made it harder for him than he would have liked. We talked about the problem, and he noticed the source of it. His class had too many responsibilities. He extracted the code in question into a new service and could then mock the new service when testing his original class. This was good. He was ecstatic. He made progress!

Having gained so much momentum, he went overboard: During his test-design, he wanted to be too clever and tried to write an elaborate test setup which was to be reused between different test runs. It was supposed to be a reusable, parameterizable do-it-all function that could setup the tests just right. With no duplication. In short, it was so much code with so much logic that it would’ve warranted its own tests. And the worst thing? It didn’t work and made trouble for writing his tests.

I was a bit thankful because that gave me the opportunity to tell him:

Premature optimization is the root of all evil.

Perhaps you’ve heard that saying already. Donald Knuth coined this phrase. There’s more to it, but that could be discussed in another email.

Back to my tester. After talking about the problems his function gave him and the difficulty in getting it right he settled for the simple solution: Write your tests. Accept duplication. Keep it simple, use copy & paste if it makes you faster and is more convenient. Write the tests you need and keep them green. And after all that, and only then, refactor your tests to remove duplication where applicable. Don’t try to write the perfect code from the start. Let the design evolve with the help of your tests. Don’t be afraid to make baby steps and don’t expect to have perfect code after the first try.

I hope you liked this series. Perhaps you could take something away from it. If you have questions, let me know.

Tomorrow I’ll be off; it’s a holiday where I live, and I’ll use it to spend time with my family. See you on Monday. πŸ‘‹

Quick wins, part 3: Keep it local

Yesterday I closed with this idea:

Spot places where knowledge about something does not belong.

What do I mean by that? Sometimes I come across some code that does not read right. I will use a pseudo code example to illustrate:

class Foo
  def initialize(bar_service)
    @bar_service = bar_service
  end

  def quux
    if @bar_service.greeting == "hello"
      @bar_service.greet("goodbye")
    else
      @bar_service.greet("hello")
    end
  end
end

class BarService
  attr_accessor :greeting
  def greet(message)
    @greeting = message
  end
end

What bothers me with this code? The method quux has too much knowledge about how the @bar_service works. Foo.quux knows that the @bar_service has an instance variable called greeting and at least one specific value it might have ("hello"). It also knows two values that the greet() method might be called with. Now it happens that this knowledge about how the greeting and greet work, is also spread into other parts of the application. What happens if you need to change something about the greet() method? You have to find every place in your application and update it to reflect the new changes. This isn’t good.

There are places like this inside many applications. You might need some practice to spot them, but with some practice it becomes easier. For this example I would like to suggest to move all knowledge about how greeting and greet work inside the BarService. Start with the conditional, like this:

class Foo
  def initialize(bar_service)
    @bar_service = bar_service
  end

  def quux
    @bar_service.greet
  end
end

class BarService
  attr_accessor :greeting
  def greet
    if @greeting == "hello"
      @greeting = "goodbye"
    else
      @greeting = "hello"
    end
  end
end

Now we are free to change the internals of the greet method. We could add a third option or change it completely. The class Foo does not need to change at all. It continues to call greet as if nothing has happened.

One of my overarching topics is testing. A refactoring like this should be covered with tests. Not only do you need tests for the Foo class, but also for how BarService.greet works. And for every part of the app that interacts with either.

Tomorrow we’ll look at another way to do a refactoring.