Category: Pattern

Two Ways of Solo Programming

Occasionally, especially in times between (paid) projects, I program solo. This morning, I realised that I operate in two ‘modes’ which slightly differ in the way I leave the project I’m working on in the evenings.

One way of working is when I program along while working through a book. Currently, I’m reading ‘Agile Web Development with Rails 7‘ (by Sam Ruby & Dave Thomas). I very much leave the ‘Depot’ app (the example application used in the book) with a completed section and passing tests. The Part of the next section is a fresh starting point in the following morning.

The other way is when I’m progressing in a project, i.e. a library or tool, I work on. In these cases, I prefer to leave a (read: one) failing test in the evening, so it’s easy to remember what I was planning to do in the morning: To fix/implement the code to make the tests pass. Note though, that I do not commit & push this failing tests to version control.

This is neither a new nor my idea. Nick Holden wrote about in 2018 already, in his blog post ‘Try ending today with a failing test for a great start tomorrow‘.

Have you noticed differences in developing software (whether it’s the coding, testing, UX, or any other aspect of it), between times of working alone versus working in a team? What are they? I would be seriously interested in hearing about this.

Expressing Expectations

Long long ago, while preparing experiments for my diploma thesis in physics, my tutor taught me to express my expectation of the outcome of experiments before actually running them. I was to not only to express them in my head, but speak them out loudly, may be even make a note.

This helped a lot, when figuring out where my thinking didn’t match the experimental evidence. There was no denying when my expectation differed from the empirical result. Typically, there were two sources for the differences:

  1. My mental model wasn’t good enough to match the result of an experiment.
  2. The experimental setup wasn’t designed well enough to show the effect I was trying to measure.

I find that this is still working well in software development (both the coding part as well as testing). A related article about this is Peter Naurs seminal paper ‘Programming as Theory Building’.

When doing TDD (test driven development), explicitly expressing the outcome before running a test may not always lead to a surprise. In the very beginning, when a test tries to create an object, without having a class definition, it will cause an error message that is easy to predict.

However, when work has progressed a bit, I regularly run into situation where I expect the next new test to fail in a certain way, but when running the test, the actual error message is a surprise to me. These are situations where learning can happen, by figuring out why the actual behaviour does occur, instead of what I predicted.

Near the end, the surprise is caused differently: I’ll write a new test and expect it to fail. – It doesn’t. Again, this is a good reason to explore why exactly I thought the test would fail.

Particularly in a pair (or ensemble) programming setup, the inability to come up with a new failing test is a sign, that the implementation is good enough … for now.

Recently, I did a programming exercise as part of a technical interview, and expressing my thoughts and expectations while working on the code helped me to find a solution. Additionally, the interviewer didn’t only see what I was typing, but could follow my way of thinking. This relates to Naur’s paper mentioned above: The testing and tested code I wrote isn’t everything there is to my mental model of the given problem or its solution. The way of thinking is important too. This is why I find vocally expressing my expectations & thinking while actually doing the work.

What are your preferred way to actually do the work you’re doing? I’d love to hear about it.

Collecting Lists In a Ruby Hash and the ‘<<=' operator

The other day, I needed to quickly analyse a data set that came in form of a large CSV file. I wanted to collect a particular column of that table and collect all entries categorised by a key in another column.

A simplified version of the table could look like this:

Key Value1 Interesting_Value Other_Value
foo1723.5X
bar211.75Q
foo4212.6B
baz2717.8F
bar4947.2K

I strived for something like this:

result = { 
  foo: [23.5, 12.6],
  bar: [1.75, 47.2],
  baz: [17.8],
 }

Iterating over the rows is easy, and getting to the columns is no problem either: The CSV gem is well documented and supports this easily.

A nice way to accumulate data is Enumerable#each_with_object. Since I wanted the result to be grouped by a key value, I’d pass a Hash as the initial argument.

Step 1: each_with_object({})

However, since I’ve planned to append values for changing keys, the default value needed to be an Array, not the default of nil.

Step 2: each_with_object(Hash.new([])

This, however, returns the same empty Array, when a key isn’t found, but I wanted a new empty Array:

Step 3: each_with_object(Hash.new { [] })

This executs the block every time a default values is needed (i.e. the given key isn’t yet in the Hash).

The next step is to append the value found in a row to the (potentially new and empty) Array for the given key.

I thought it would work this way:

data_table.each_with_object( Hash.new { [] }) do |row, acc|
  acc[row['Key']] << row['Interesting_Value'] 
end

But, no, the result of this code is an empty Hash! It needs to be the <<= operator to work, as shown in the snippet of a pry session:

[2] pry(main)> data_table = CSV.read 'table.csv', headers: true
=> #<CSV::Table mode:col_or_row row_count:6>
[3] pry(main)> data_table.each_with_object( Hash.new { [] }) do |row, acc|
[3] pry(main)*   acc[row['Key']] <<= row['Interesting_Value']
[3] pry(main)* end
=> {"foo"=>["23.5", "12.6"], "bar"=>["1.75", "47.2"], "baz"=>["17.8"]}

It seems to me, that the Hash lookup with the given default value [] returns an Array, and the append operator << does in fact append the passed object to that Array, but then the result of that does not end up as a (new) value fo the given Hash key. In contrast, the <<= operator does assign the result of the append operation.

Physics And Testing

At a number of conferences I attended in the past, people connected several fields to software development in general and testing in particular, which (at first) seem unrelated.

Since then, I pondered this for a while (read: more than 4 years). With my background in physics, I see a number of parallels to software testing. This is also a great opportunity to answer a question I get asked frequently: How did you enter software testing, given your background in physics?

Physics

Let’s start with a definition for physics:

Physics is an experimental science. Physicists observe the phenomena of nature and try to find patterns that relate these phenomena.

— Young, Freedman. “Sears and Zemansky’s University Physics: With Modern Physics”. Pearson Education.
Also see the wikipedia article on physics.

The patterns that relate those phenomena are the theories (or laws) of physics. They are models that describe an aspect of reality. None of these models is complete, in the sense that it describes everything. There is no one physical theory that explains everything. A nice view of the landscape of physical models is shown in the image by Dominic Walliman (see sciencealert.com for details):

The landscape of physical theories, see
Dominic Walliman’s ‘Domain of Science’ video on YouTube

In physics (as in science in general), experimental results and predictions  created by models are compared, in order to find out how a model does not match observed behaviour. This is important: Experiments can only ever invalidate a model, but not generally confirm its correctness.

Software

To me software systems are models, too: Even though they may represent reality closely, a software system is not the thing it represents.
Peter Naur described the relationship between theory building and programming in his paper ‘Programming as Theory Building’ (Microprocessing and Microprogramming 15, 1985, pp. 253-261).

Testing

My mental model of software testing is very similar to the one of physics: I see software systems as partial implementations of aspects of a real expected behaviour. In my view testing a system means it and comparing observed results with expectations.
The expectations may come from requirements (written or otherwise), previous experiences with similar systems (i.e. another web application from the same company) or other sources.

There are many approaches to testing, in a similar way to the many approaches to physics. Some of them work good in one area but not so well in another. What kind of testing is done, heavily depends on the kind of the software system: Testing embedded software used in medical devices is drastically different from testing, say, a text editor.

Science

It is interesting to go one step further, from physics to science. The Cambridge Dictionary defines science as

the intellectual and practical activity encompassing the systematic study of the structure and behaviour of the physical and natural world through observation and experiment

https://dictionary.cambridge.org/dictionary/english/science

With a different wording, this seems to fit software testing as well:

Software testing the intellectual and practical activity encompassing the systematic study of the structure and behaviour of software systems through observation and experiment.

The definition with a different wording

To me, many very basic principles of scions in general and physic in particular apply to software testing, too. And that’s why I think that my physics background is very helpful in software testing.

Now, I interested in this: What is your background and how does it help you in software testing (especially if your background is not in computer science or software engineering)?

The Computer Is Always Right

The other day I had trouble getting a Cucumber scenario to work. Here’s what happened, partly to have a post I can come back to, when (!) my future self runs into a similar problem.

I am using Cucumber and a very stripped down version of the project looked like this:

% tree
.
└── features
    ├── example.feature
    ├── step_definitions
    │   └── step_def.rb
    └── support
        └── env.rb

All code is entirely contrived and tailored to demonstrate the issue I bumped into. The example file features/example.feature first:

Feature: What is happening?

Scenario Outline: Matching step definitions -- or not

Given a step that mentions '<a_string>'
And another step that uses <a_number> like this
Then all is good

Examples:
|  a_string |  a_number |
|      word |        42 |
| two words | 3.1415927 |

There are very plain step definitions too (in file step_definitions/step_def.rb):

Given("a step that mentions {string}") do |string|
  pending # Write code here …
end

Given(/another step that uses ((\d+)|(\d+\.\d+)) like this/) do |number|
  pending # Write code here …
end

Then("all is good") do
  pending # Write code here …
end

Running this gives the expected result: Cucumber kindly informs that there are pending steps:

% bundle exec cucumber
Feature: What is happening?

  Scenario Outline: Matching step definitions -- or not # features/example.feature:3
    Given a step that mentions '<a_string>'             # features/example.feature:5
    And another step that uses <a_number> like this     # features/example.feature:6
    Then all is good                                    # features/example.feature:7

    Examples:
      | a_string   | a_number  |
      | word       | 42        |
      | two words  | 3.1415927 |

2 scenarios (2 pending)
6 steps (4 skipped, 2 pending)
0m0.012s

However at some point, I started getting another result (comments added by cucumber removed):

% bundle exec cucumber
Feature: What is happening?

  Scenario Outline: Matching step definitions -- or not
    Given a step that mentions '<a_string>'
    And another step that uses <a_number> like this
    Then all is good

    Examples:
      | a_string   | a_number  |
      | word       | 42        |
      | two words  | 3.1415927 |

2 scenarios (2 undefined)
6 steps (4 skipped, 2 undefined)
0m0.009s

You can implement step definitions for undefined steps with these snippets:

Given("a step that mentions {string}") do |string|
  pending # Write code here that turns the phrase above into concrete actions
end

Wait. What?!? I stared at the existing step definition for a while, comparing it with the one printed in the message above:

Given("a step that mentions {string}") do |string|
  pending # Write code here …
end

Confusion and disbelief kicked in.

One of the principles I use is this:

The computer is always right.

— Not sure where I picked this up. If you know the (or a) source, tell me please.

This is true even if the behaviour is wrong. In this case: If cucumber cannot find a step definition … it CANNOT find a step definition. But why would that be? Why did it happen in this particular case?

I even called in colleagues (remotely) and we stared at the code collectively. Still nothing.

Luckily a trace of a previous successful run was still available in the console output. So I copy-and-pasted the scenarios, and compared them piece by piece in a Pry session:

% pry
[1] pry(main)> works = File.read 'features/works.feature'
=> "Feature: …"
[2] pry(main)> broken = File.read 'features/broken.feature'
=> "Feature: …"
[3] pry(main)> works == broken
=> false

So there is in fact a difference. But what? Where?
Using the same pry session we found out:

[4] pry(main)> works.each_codepoint.zip(broken.each_codepoint).select{|el| el[0] != el[1] }
=> [
    [0] [
        [0] 32,
        [1] 160
    ]
]

What this does: The codepoints of both strings are combined in pairs, and then the pairs that are different are selected. Here’s what the Ruby Documentation says about each_codepoint:

Passes the Integer ordinal of each character in str, also known as a codepoint when applied to Unicode strings to the given block. For encodings other than UTF-8/UTF-16(BE|LE)/UTF-32(BE|LE), values are directly derived from the binary representation of each character. If no block is given, an enumerator is returned instead.

“hello\u0639”.each_codepoint {|c| print c, ‘ ‘ }

produces:

104 101 108 108 111 1593

https://ruby-doc.org/core-2.7.1/String.html#method-i-each_codepoint

The small piece that changed the behaviour was a space, just not a ‘normal’ space, but a NO-BREAK SPACE (see, for example, https://en.wikipedia.org/wiki/Non-breaking_space for more about this topic).

Lessons I learned (again):

  1. Some problems are hard to see, and in this case it was even invisible.
  2. The message was correct: The step was not defined, it only looked (to the human eye) as if it was.
  3. Using <spacebar> gives a different result than <option>-<sapacebar>.

Navigation

%d bloggers like this: