How to not get a question answered

Raymond has had lots of great posts over the years on how to not get a question answered. Some of the ways he points out that help ensure that a question goes unanswered are:

I would add that phrasing the question in terms of the attempted solution rather than the underlying problem is a great way to have a question remain unanswered. But I already did that. Today’s rant, rather, is:

A great way to not get your question answered is to treat the person that you’re asking to share their time and knowledge like they’re not a human being.

I understand that this is easy to do.

I understand that when someone is asking a technical question, it is almost always because they’re deeply frustrated by their inability to make some important specific complicated thing work the way they’d expect.

I understand that software makers are a big source of frustration in a more general sense. We all use imperfect software every day; little frustrations mentally accumulate in a way that little victories do not.

I understand that programming languages and software in general is often counterintuitive, and that understanding the complex reasoning behind a counterintuitive result is often seemingly of little practical import compared to just getting the problem at hand solved.

I understand that it is very easy to use an email client as an impersonal and automatic information resource something like a search engine, where you just type in your query, you get your result back, and you move on with your day.

I understand that there is an inherent and pervasive bias in pure-text communication which makes statements intended to be good-humoured sound sophomoric, makes statements which were intended to be friendly sound smarmy, makes statements which were intended to be enthusiastic sound brash, makes statements intended to be helpful sound condescending, makes statements which were intended to be precise and accurate sound brusque and pedantic, makes statements which were intended to be positive sound neutral, and makes statements which were intended to be neutral seem downright hostile.

Boy, do I ever understand that. For over four years I have deliberately tried to pitch the tone of this blog as good-humoured, friendly, enthusiastic, helpful, precise, accurate and positive; I suspect that most of the time I fail badly at most or all of those. Writing is hard.

I understand all that, from both sides; I’ve certainly been on both the sending and receiving ends of all of the above, many times.

Which is why I try very hard to be helpful to the complete strangers who send me overtly rude and frequently profane emails capped off with a request for me to to take time out of my day to explain something to them.

I try to see an upset or confused user as an opportunity to make a good impression; sometimes people do a complete 180 when you help them out and are appreciative and grateful. Sometimes they even send an unsolicited note to your manager, which is always pleasant.

But more often, I never hear from them again after their question is answered.

None of this is in their own best interests. It makes the human information sources they rely upon less likely to help them now and in the future.

So, just a friendly reminder. (A friendly, good-humoured, helpful, enthusiastic, positive reminder!) The people you are sending email to about your technical problems are people. It would be smart to briefly introduce yourself, describe your problem without insulting the people who created it, indicate what you’ve done to attempt to solve it yourself, actually ask for help, and later say thank-you for their time whether they manage to actually help you or not.

It’s smart because doing so is in your own best interests.

It would also be nice, but I actually am not particularly concerned about “nice” today. Nice is nice to have I suppose. But not being smart is just dumb.


Commentary from 2021:

Obviously this rant was born of frustration; one of my predecessors on the C# team, Anson Horton, recently told a story of the early days of answering C# questions internally at Microsoft that reminded me of this article. His story ended well but most of them did not. And of course it was similar answering JavaScript questions internally; it had nothing to do with C# as C#. The underlying problem was how Microsofties (and their customers!) were socialized to treat their colleagues with contempt, and not the software systems which facilitated it.

There were a lot of good comments posted on the original article; to summarize a few of them here:

  • Commiseration from people whose time had also been imposed upon by people asking rude and often unanswerable questions.
  • Some people said that this was post-it-note wisdom, and that was a compliment: they put a post-it note on their monitor that said “little frustrations accumulate, little victories do not”.
  • More ranting from me; I pointed out in a follow-up comment that there were entire multi-week stretches when I worked on subtle problems in method type inference for eight hours a day, and that maybe if I don’t answer your question on that subject within five minutes, there’s a reason for that. It takes research to answer a question correctly, and research takes time, and must be prioritized against many other important concerns.
  • A number of readers felt called out, and said yeah, sorry I never thanked you for solving my problem. One of which was a question I answered ten years previously! You’re welcome!

I was expressing some frustration here; I was not fishing for compliments or for thanks, but hey, I’ll take both if they’re offered.

 

Why Do Initializers Run In The Opposite Order As Constructors? Part Two

As you might have figured out, the answer to last week’s puzzle is “if the constructors and initializers run in their actual order then an initialized readonly field of reference type is guaranteed to be non null in any possible call. That guarantee cannot be met if the initializers run in the expected order.”

Suppose counterfactually that initializers ran in the expected order, that is, derived class initializers run after the base class constructor body. Consider the following pathological cases:

class Base
{
  public static ThreadsafeCollection t = new ThreadsafeCollection();
  public Base()
  {
    Console.WriteLine("Base constructor");
    if (this is Derived) 
      (this as Derived).DoIt();
    // would deref null if we are constructing an instance of Derived
    Blah();
    // would deref null if we are constructing an instance of MoreDerived
    t.Add(this);
    // would deref null if another thread calls Base.t.GetLatest().Blah();
    // before derived constructor runs
  }
  public virtual void Blah() { }
}
class Derived : Base
{
  readonly Foo derivedFoo = new Foo("Derived initializer");
  public DoIt()
  {
    derivedFoo.Bar();
  }
}
class MoreDerived : Derived
{
  public override void Blah() 
  { 
    DoIt(); 
  }
}

Calling methods on derived types from constructors is a bad idea but it is not illegal. And stuffing not-quite-constructed objects into global state is risky, but not illegal. I’m not recommending that you do any of these things — please, do not, for the good of us all. I’m saying that it would be really nice if we could give you an ironclad guarantee that an initialized readonly field is always observed in its initialized state, and we cannot make that guarantee unless we run all the initializers first, and then all of the constructor bodies.

Note that of course if you initialize your readonly fields in the constructor then all bets are off. We make no guarantees as to the fields not being accessed before the constructor bodies run.

Why Do Initializers Run In The Opposite Order As Constructors? Part One

Pop quiz!

What do you expect the output of this program to be?

using System;
class Foo
{
  public Foo(string s)
  {
    Console.WriteLine("Foo constructor: {0}", s);
  }
  public void Bar() { }
}
class Base
{
  readonly Foo baseFoo = new Foo("Base initializer");
  public Base()
  {
    Console.WriteLine("Base constructor");
  }
}
class Derived : Base
{
  readonly Foo derivedFoo = new Foo("Derived initializer");
  public Derived()
  {
    Console.WriteLine("Derived constructor");
  }
}
static class Program
{
  static void Main()
  {
    new Derived();
  }
}

I got a question from a user recently noting that the order was not as they expected. One naively expects that the order will go “base initializers, base constructor body, derived initializers, derived constructor body”. In fact the order actually is that first all the initializers run in order from derived to base, and then all the constructor bodies run in order from base to derived.

The latter bit makes perfect sense; the more derived constructors may rely upon state initialized by the less derived constructors, so the constructors should run in order from base to derived. But most people assume that the call sequence of the code above is equivalent to this pseudocode:

// Expected
BaseConstructor()
{
  ObjectConstructor();
  baseFoo = new Foo("Base initializer");
  Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
  BaseConstructor();
  derivedFoo = new Foo("Derived initializer");
  Console.WriteLine("Derived constructor");
}

When in point of fact it is equivalent to this:

// Actual
BaseConstructor()
{
  baseFoo = new Foo("Base initializer");
  ObjectConstructor();
  Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
  derivedFoo = new Foo("Derived initializer");
  BaseConstructor();
  Console.WriteLine("Derived constructor");
}

That explains the mechanism whereby the initializers run in order from derived to base and the constructor bodies run in the opposite order, but why did we choose to implement that mechanism instead of the more intuitively obvious former way?

Puzzle that one over for a bit, and then read on for a hint.

The “readonly” modifiers in there were no accident. The code gives the appearance that any call to derivedFoo.Bar() and baseFoo.Bar() should never fail with a null dereference exception because both are readonly fields initialized to non-null values.

  1. Is that appearance accurate, or misleading?
  2. Now suppose initializers ran in the “expected” order and answer question (1) again.

I’ll post the answers and analysis next week. Have a fabulous weekend!