Wednesday, July 22, 2009

The Uncle Who Won't Shut Up

Following is a rebroadcast of my comment on Hanselman's blog.

Looking forward to hearing Uncle Bob again. I find him very insightful on coding issues, less so on community issues like how software developers should carry themselves, envision the job, etc. Then there's his politics, which I only mention because it is a large fraction of his tweets (i.e., he mentions it first and puts it in the public sphere). The farther he gets away from technical questions and the closer to sociological theories, he gets less empirical and more loopy. Worse, he also gets more strident, or at least no less so.

I guess he's not the first geek to go a little crackpot when outside his tech strengths. Noam Chomsky, anyone?

Anyway, here's a few questions I'd like someone to ask him. Maybe in part 4?

1. He's pretty big on craftsmanship lately. For the most part, the lesson of history has been, when craftspersons compete with engineering (i.e., industrial processes and capitalist management), the craftspersons get crushed. ("Outcompeted.") The industrial products are more reliable, they scale better, they don't have single-source dependency risks, etc.--and having scaled, their unit cost is cheaper. The "craftsmen" he mythologizes never got around to looking at their products that way; instead, they sat around, pretty happy with themselves, their traditions, and their traditional notions of "quality" and "reliability" until engineers came along and ate their lunch. The engineers innovated, while the "craftsmen" celebrated the past. Given the lessons of history, therefore, why on earth would we want to emulate craftsmen? Or does the "craftsmanship" vision only work for boutique shops, authors, and consultants?

2. He seems pretty ignorant of history with his "professionalism" speeches, too. Strictly speaking, a profession is something that is licensed by the state: doctor, lawyer, even plumber, and (notably) every sort of engineer except software engineer. A profession usually has generations of lore and experience behind it, and a core association (the AMA, the state bars, etc) that maintains "professional standards" and a code of ethics. Thus, in general, an industry has to be a couple generations old AND coalesced around a consensus set of practices before it can even THINK about becoming a profession. (Indeed, that's why the phrase "world's oldest profession" is actually quite a bit funnier, in a dry way, than most people realize today, now that the modern usage of "professional" has stretched to mean "white collar" and/or "not qualifying for amateur status".) I'm pretty sure that if Uncle Bob thought about it, he wouldn't want the government or anybody else telling him who's qualified to write software, or what his ethics should be. Ergo, either he hasn't thought about it, or he doesn't understand the connotations of the words he uses. Either way, he sounds unconvincing.

3. From what I can gather in his tweets and blogs, he's emphatically against not only health care reform, but regulation of the health sector in most any form. I'd love for you, Scott, to ask him how an unregulated insurance market would serve insulin-dependent diabetics, or anyone else who's acutely or chronically dependent on medical treatment. I'd love for you to look him in the eye and ask him what carrier would cover you, and whether you'd ever be able to work for a company smaller than Microsoft and still have coverage. (Of course, maybe you'd rather not entangle your blog and podcasts with that much off-topic drama. It would be quite professional of you, so to speak, to avoid it. But I have several close relatives with diabetes and other health issues, so I'm pretty tired of Uncle Bob's free ride. If he wants to evangelize ideas that would have awful human consequences, I think people should stand up. Your decision is yours, of course.)

Monday, July 13, 2009

INotifyPropertyChanged, C# lambdas, MethodBase.GetCurrentMethod()

I'm on a WPF project at the moment, and Silverlight before that. Both have this notion of INotifyPropertyChanged for bindings, whose only requirement is that you implement
event System.ComponentModel.PropertyChangedEventHandler PropertyChanged
and fire it off whenever any of your properties changes. So far so simple.

The downside is, the PropertyChangedEventArgs payload is not just a typewashed object, it's merely a string of the name of your property, so you see a lot of

public Foo Bar
{
get { return _bar; }
set
{
_bar = value;
OnNotifyPropertyChanged("Bar");
}
}

which works dandy until you rename something or forget to update your copy-and-paste. ReSharper will look in your literals for you, but I'm usually too impatient and sloppy to trust that.

Instead, I've been on a kick to use lambdas and MethodBase.GetCurrentMethod().

First you need a base class like ViewModelBase. Add the following:





   1:   

   2:          protected bool Set<TProperty>(Expression<Func<TViewModelInterface, TProperty>> expression, TProperty newValue)

   3:          {

   4:              TProperty oldValue = _propertyBag.Get(expression);

   5:              string propertyName = expression.GetPropertyName();

   6:              return SetObservable(oldValue, newValue, t => _propertyBag.Set(expression, t), propertyName);

   7:          }

   8:   

   9:          protected bool Set<TProperty>(MethodBase setter, TProperty newValue)

  10:          {

  11:              TProperty oldValue = _propertyBag.Get<TProperty>(setter);

  12:              string propertyName = setter.GetPropertyName();

  13:              return SetObservable(oldValue, newValue, t => _propertyBag.Set(setter, t), propertyName);

  14:          }

  15:   

  16:          private bool SetObservable<T>(T oldValue, T newValue, Action<T> updater, string propertyName)

  17:          {

  18:              bool changed = HasChanged(oldValue, newValue);

  19:              if (changed)

  20:              {

  21:                  updater.Invoke(newValue);

  22:                  // don't fire property change until oldValue is updated

  23:                  OnPropertyChanged(propertyName);

  24:              }

  25:              return changed;

  26:          }

  27:   

  28:          private static bool HasChanged<T>(T oldValue, T newValue)

  29:          {

  30:              bool changed;

  31:              if (ReferenceEquals(null, oldValue))

  32:              {

  33:                  changed = !ReferenceEquals(null, newValue);

  34:              }

  35:              else

  36:              {

  37:                  changed = !oldValue.Equals(newValue);

  38:              }

  39:              return changed;

  40:          }



Somewhere else are your helpful extensions:



   1:   

   2:          public const string GetterPrefix = "get_";

   3:          public const string SetterPrefix = "set_";

   4:          private static readonly int _prefixLength = GetterPrefix.Length;

   5:          

   6:          public static IList<Type> GetInheritedInterfaces(this Type type)

   7:          {

   8:              IList<Type> direct = type.GetInterfaces();

   9:              IList<Type> results = direct;

  10:              foreach (Type implemented in direct)

  11:              {

  12:                  results = results.Union(implemented.GetInheritedInterfaces()).ToList();

  13:              }

  14:              return results;

  15:          }

  16:   

  17:          public static string GetPropertyName<TInstance, TProperty>(this Expression<Func<TInstance, TProperty>> expression)

  18:          {

  19:              return ReflectionHelper.GetProperty(expression).Name;

  20:          }

  21:   

  22:          public static string GetPropertyName(this MethodBase method)

  23:          {

  24:              if (method.Name.StartsWith(GetterPrefix)

  25:                  || method.Name.StartsWith(SetterPrefix))

  26:              {

  27:                  return method.Name.Substring(_prefixLength);

  28:              }

  29:              throw new ArgumentException("Method was neither a setter or a getter.", "method");

  30:          }



where ReflectionHelper is basically lifted wholesale from Fluent NHibernate under BSD. It's not rocket science, though, just some manipulation of C# expression trees to extract the name of a property from a lambda that uses it.

Also, _propertyBag is basically a Dictionary where the strings are the names of the properties. Actually it's a Dictionary to keep some other stuff like original values, dirty bits, etc., but that's another topic.

The implementation of Get is like Set, only simpler.

The net-net of all this is that you can have

interface IMyClass
{
Foo Bar { get; set; }
}

class MyClass
{
public Foo Bar
{
get { return Get(x => x.Bar); }
set { Set(MethodBase.GetCurrentMethod(), value); }
}
}

which is fewer lines, no backing variable (e.g, _bar), and ReSharper-izable. Plus it breaks at compile time until you expose Bar on your interface, so it's as close to idiotproof as I've been able to get so far.


Against software "craftsmanship"

Lots of talk on web lately, esp from Object Mentor types like Uncle Bob and Michael Feathers, about the need for software developers to be more like craftsmen. This is true in small ways, I suppose, but it also seems false in large ones.

The decline of craftsmen, when possible, has usually been industrialization. One can get into frets about alienation of workers from their product, and in fact I usually agree or at least learn from such Marxist critiques. The moral core of those arguments, though, is the degree to which workers are left without meaningful tasks, in the aftermath of initial industrialization. That's a valid point when it applies, but it goes too far when you're just talking about how a software developer who's a dev today, and will be a dev tomorrow, should work.

So I don't think industrialization is evil or without useful lessons in this context. Mostly I think it's an important turning point in technical practices, a sort of coming of age. An inflection point, to use the modern parlance.

Back to software as craft: one of the revolutionaries of the Industrial Revolution was Eli Whitney, he of the interchangeable part. He famously figured out that an army could fight better if its rifles were each made from a finite number of said parts which could be carried into battle, pre-fabricated, rather than requiring a slew of blacksmiths and metallurgists to repair everything on demand. Or, more chronologically, he figured out that this was a better way to make a rifle, then he made a fortune selling rifles of that description to the U.S. Army, because the Army realized he was so, so right.

From interchangeable parts, computer scientists later borrowed the metaphor of subassemblies and, more broadly, decomposition. It's a natural enough idea mathematically, but making it intuitively clear to beginners almost always involves some example from the physical world--especially, the manufactured world.

From decomposition, we get the encapsulated object and the component. From these, we got to the unit test.

Would the craftsman have invented the unit test? To me, the answer is tautologically no. To break a task into subsitutable, commodity parts is, by definition, anti-craft. It is industrial. It is engineering. It's applying arguments (or motivation) of scale; ideas of aggregate, statistical value; perspective at the macro, systemic level; appreciate for complexity, emergent behavior, and the like. By definition, craft appreciates fuzzy essences, "things in themselves," metaphysics rather than physics. Those essences, those things, those human endeavors are valuable, but they are not engineering. I'd argue that the products they produce are not reliable in the way that an industrial product is, and therefore, that software developers should not be craftsmen.

Software needs ideas from the 21st century; the 19th already played out, and the craftsmen lost.

Postscript: Is it me, or is Uncle Bob's software advice, at least as it involves the community of developers (as opposed to pure technical practices like SOLID) getting progressively more hidebound, verging on counterrevolutionary and possibly trending toward stupid?