The Truth Is Out There…I Want To Believe

hurlbert
4 min readNov 19, 2017

“Uncle” Bob Martin said that “the truth is in the code.”

Don’t get me wrong, I’m a big fan of Bob Martin. He was referring to the idea that, whatever people think, the code is the source of truth. I disagree. What got me thinking about this was that I was doing research on how to make code more understandable. I ran across The truth is in the code.

I understand the spirit of what they’re saying. But days later I had an interesting debugging experience.

It would shock a lot of .NET developers to learn that their code does not execute in the order typed! I know this sounds crazy, but it’s true. Calling a delegate with BeginInvoke causes it to execute on another thread. But that’s not to what I’m referring.

You’re C# code gets shuffled around by the compiler/IL engine. This is to make it run more “efficiently.” It’s to avoid blocking (where and when possible). When you call 3rd party code you never know what is executed. It could be async or multi-threaded within the 3rd party code.

This is a great summary of async and other threading issues that is well worth reading.

The link to TryRoslyn is not to be missed. The “my-code” vs “what-the-compiler-sees” is stunning. Your trust in the compiler may be misguided:

So, back to my debugging session. In this app the serializer defaults for NewtonSoft are overridden when the app starts up. No big deal, but for my tests I need control of exactly how the serializer is working. I installed my own serializer settings and yet when the code ran, my settings were not used.

My code controlled serialization (or so I thought). I was creating the settings and installing them into the serializer. I used the modified serializer in my test. But the *tests kept returning results with different settings.*

Code buried in the framework was using the “default” settings for serialization. This code assumed a modified environment. For testing this is no good. Tests need to control the environment in which they execute. Running code in a controlled environment is often the only reason to write a test.

This particular code was serializing a DTO, but enums were not being converted to strings. Conversion was the expected behavior. My modified serializer was configured for substitution. The enums were coming out as their int values. [In other words an enum for Fair, Good, Excellent should not serialize as 0, 1, 2, but the strings “Fair”, “Good” and “Excellent”].

Because I had the code for the default override, I could call that from my test. When I did, the enums started working. Crazy, but at least I had code that caused the behavior to change. I could move the call further and further down my test to try and locate which parts of the code were using the overrides of the defaults.

After a couple of hours of screwing around I had found a section of code that looked like this:

if( some-condition ) 
{
ProcessNormalFields();
}
else
{
ProcessCalculatedFields();
}

With the call to override the defaults before the ProcessNormalFields, nothing happened. If I put it before ProcessCalculatedFields the enums got serialized. After each call I was setting the defaults to null to negate the settings. The fact that the defaults took effect during the ProcessCalculatedFields was very odd. *Obviously* enums are not calculated fields. After another hour or so of debugging I found the answer.

Below that entire block was this line:

obj[val] = JToken.FromObject( val );

It turns out that JToken.FromObject() was “serializing” a single value and storing it in the array. Fine. Whatever. That’s not the strange part. Here’s the strange part.

ProcessCalculatedFields() was *just slow enough* to allow the defaults to still be in place when the JToken.FromObject code executed. *WAIT! WHAT?* The line following ProcessCalculatedFields was setting the defaults to null. How could this happen?

Yep. Not kidding. When you override the NewtonSoft defaults you do it by installing a delegate. The delegate call happens slowly (and *probably* on a separate thread). Changing the NewtonSoft defaults before the call to ProcessCalculatedFields *changed the order of execution!* My removal of the defaults was effectively happening *after* the JToken.FromObject. This was true even though that call was several lines of code after the line that set the defaults to null.

How crazy is that?

So, _is the truth really in the code?_ I don’t think so. [I guess you could say that the truth is the compiled code. But in C#, the compiler emits IL code, which itself can be shuffled around. I’m sorry, what’s the truth again? — ha!]

The truth is in the specification. The question we developers need to ask is, “Does this code execute the ex-spec-ted behavior?” That’s the only truth and even that is largely an illusion.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response