Software is a unique industry because it is constantly self-improving its own productivity. It acts as an accelerating concept like interchangeable parts or the assembly line, rather than a linear incrementing body of study. The use of software applies that acceleration to everywhere it is used, including itself.

So why don't we have lower skilled people building software by drag/dropping building blocks as many have predicted? After all, we have that self accelerating improvement applied to software over decades. Exponential increases in programmer productivity would suggest that lower skilled people could be employed, dropping average salaries, right? That kind of logic leads toward thinking there is a tech worker shortage.

There is no "tech worker shortage". Companies might not be able to find low priced labor, which they believe they should be able to do based on the reasoning above. But like the linked article quotes: "I'm not sure that qualifies as a shortage, any more than my not being able to find a half-priced TV."

What's wrong with this train of thought?

First off, there are many "building block" type software packages available. It's just hard to see them because the demand for new and more complex software has grown so dramatically. There are website builders, time tracking software, component based development tools galore. They've satisfied an enormous amount of what people wanted to do 20 years ago. But in the meantime, demand and complexity of requirements have grown exponentially. The acceleration of productivity is just barely allowing the software industry to run in place.

At the same time, there is an increasing demand for custom built software that is tailored directly to the data and business logic of the client, particularly in the LOB arena. It used to be too expensive for anything other than massive companies to afford custom development. Now, productivity increases allow even small businesses to have staff or contract programmers building software to enhance their business, again, pushing demand for programmers back up.

Finally, and most importantly, software is getting more complex in general. The requirements are bigger, in features, volume, and reliability. The software enabling productivity increases is sitting on layer upon layer of other software packages. 20 years ago, you'd build a simple desktop app in C++ or VB. Or a CGI app for web. It'd be a couple of components, sitting on a single server (or maybe a cluster of two machines if you were really rich and important), with a simple database. The "cloud" was still a twinkle in Jeff Bezos's eye until less than 10 years ago. Now, you have an MVC app using 20 different packages imported by a package manager running on a load balanced set of web servers, a database sharded across several servers, some memcache servers on the backend to accelerate data access, some varnish caches on the front to accelerate static pages, some CDN to accelerate content. Etcetera etcetera.

A senior software engineer that knows all of the possible tools and can orchestrate them together, someone that knows the concepts from the underlying cpu/cache/memory up to the networked cloud, and can use the right tool (out of 100s) for the right job, getting the job done (not to mention done right, and done fast) -- that person's value and productivity is multiplied by the layer upon layer of tooling developed over the past couple of decades. They can do the work that 10 senior people did 10 or 15 years ago, or 100 junior devs, while the junior dev's productivity has remained about the same.

That dichotomy between productivity of a senior and junior is what is throwing big companies for a loop. Their mental model of staffing is hierarchical, with a couple of seniors at the top, then some mids, and a bunch of juniors filling out the crowd. Their sort of thinking around cost is what leads to outsourcing to low skilled body shops. They can't fathom that it would be cheaper and more effective to hire a bunch of seniors and a couple mids to get the job done right -- they just see the dollar signs on the salary packages and scream "tech shortage".

Software development is becoming another market with winner-take-all dynamics. Sure, anybody can get started easy and for free. But the thousands of hours invested over years that it takes to gain an innate understanding of the innumerable tools and techniques involved? That’s another matter entirely.

I just finished building and setting up my first Home Theater PC. It went very smoothly. I didn’t run into any of the typical config issues of yore when installing the machine -- driver issues, incompatible hardware, etc. Everything pretty much just worked. Of course, I didn't get into the hard part of HTPC -- handling live TV. I'm on DirecTV right now, and there's no way I'm aware of to do live TV on a PC with them. No CableCARD support, etc. And CableCARD just got shot down in congress anyway, so we'll see how long it lasts elsewhere.

Hardware:

Software:

One note on Windows Media Center – if you decide you want that, just go ahead and buy the key online for $9.99. It’s ten bucks. Don’t try to find a working key somewhere like I did. It’ll appear to work, but it’ll bounce Windows into deactivated mode. And then Windows won’t let you downgrade and also won’t let you buy a key. Most people suggest just reinstalling at that point. I was able to sidestep the issue by buying a key on a different machine and applying it back on my HTPC. MS tech support aren’t very supportive either, as they assume you’re trying to pull some shenanigans. Be forewarned.

I typed out this entire post on the HTPC, looking at my TV and using the K830 keyboard. It’s surprisingly comfortable and usable, with big fat keys and fairly responsive key travel. The built in touchpad is brilliant -- you can easily mouse around without having to dig through the couch for a mouse.

My goals for this setup are:

  • Be able to easily consume my recorded media from the TV. You can sort of do this with streaming to a 360, maybe with a TVersity server in back, but it’s fiddly.
  • Hook in cloud streaming apps like Netflix, Pandora, etc. so they can be used as well
  • Play Steam games that support the controller on the TV
  • Full browser and potential for other apps in case I want to look up something, or do stuff like write this blog post

I’ve satisfied the basic requirements of these goals, but I’m a little disappointed in the lack of ability to do advanced orchestration. I haven’t found a way to do everything from one UI/app yet, or be able to do everything from the remote. Ironically enough, the best integrated things are the less than legitimate options. The paid apps like Netflix all wall themselves off in their own little gardens and don’t play with others.

Tweaks I did:

  • Create a guest account and run all TV apps as a guest. This opens the HTPC up to everybody without worrying that somebody is going to screw things up or install a virus.
  • Registry changes to auto logon guest account:
    HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
    AutoAdminLogon = REG_SZ 1 DefaultPassword = "" DefaultUsername = "Guest"
  • Registry changes to disable lock screen:
    HKLM\SOFTWARE\Policies\Microsoft\Windows\Personalization
    NoLockScreen = 1
  • Don't require password on resume
    • Go to Power Options control panel applet
    • Click on "Require Password on Wakeup" on the left pane
    • Select the option "Don't require a password"
  • Change the guest profile picture to something good looking by overwriting guest.bmp and guest.png in C:\ProgramData\Microsoft\User Account Pictures
  • Add the unsupported appstore to Plex for access to unsavory and pirate stream content
  • Run plex as a service (thanks to http://cloudsurvivalguide.com/running-plex-service-windows/)
    • Set up plex server as you want it on your account or some other user account
    • When set up, remove it as a startup app (look in HKCU\Software\Microsoft\Windows\CurrentVersion\Run)
    • Install Windows 2003 Resource Kit to get srvany.exe. This app will let you run any other app as a Windows Service.
    • Execute the following in a Admin command prompt and/or .bat file to create the service:
      sc create Plex binpath= "C:\Program Files (x86)\Windows Resource Kits\Tools\srvany.exe" start= "auto" DisplayName= "Plex Media Server"
      
      REG ADD HKLM\SYSTEM\CurrentControlSet\Services\Plex\Parameters
      REG ADD HKLM\SYSTEM\CurrentControlSet\Services\Plex\Parameters /v AppDirectory /t REG_SZ /d "C:\Program Files (x86)\Plex\Plex Media Server"
      REG ADD HKLM\SYSTEM\CurrentControlSet\Services\Plex\Parameters /v Application /t REG_SZ /d "C:\Program Files (x86)\Plex\Plex Media Server\Plex Media Server.exe"
      
      Net Start Plex
    • Go into the local Services settings and open Plex properties. Change the user in the Log On tab to point to the user you used to set up plex

I spent some time looking for a simple compound interest calculation function in .NET today. I couldn't find anything usable, so I grabbed the source formula and reduced it to .NET.

Here's the formula:

A = P * (1 + r / n) ^ n * t
A = final total amount
P = principal amount
r = interest rate (decimal)
n = number of periods per year
t = number of years

The C# code:

decimal CalculateTotalWithCompoundInterest(decimal principal, decimal interestRate, int compoundingPeriodsPerYear, double yearCount)
{
    return principal * (decimal)Math.Pow((double)(1 + interestRate / compoundingPeriodsPerYear), compoundingPeriodsPerYear * yearCount);
}

For example, you can calculate the total loan amount for $10,000.00 (principal and interest), compounded daily for 2 years like so:

decimal total = CalculateTotalWithCompoundInterest(10000m, 8.5m, 365, 2);

I upgraded an ASP.NET MVC 3 project to MVC 4, and upgraded to VS 2012 at the same time. Everything compiled fine, and worked fine in the browser, but the intellisense was totally broken.

I couldn't for the life of me figure it out. Everything in web.config looked right, the correct assembly versions were referenced, etc.

Finally, I realized that the issue was in the Views\Web.config. The version numbers of assemblies there were pointing to MVC 3 and WebPages 1, rather than MVC 4 and WebPages 2.

To solve the issue, change all the version numbers in that file appropriately (4.0.0.0 for System.Web.Mvc references, and 2.0.0.0 for System.Web.WebPages.Razor references).

I just ran into an issue where DataBinder.Eval was exploding with an exception like this:

'System.Dynamic.ExpandoObject' does not contain a definition for 'Id'.

The problem here is that DataBinder.Eval uses either reflection and type descriptors to bind to properties. ExpandoObject (and dynamic types) don't support reflection. ExpandoObject doesn't implement ICustomTypeDescriptor either, leading to an exception when attempting to bind to an ExpandoObject.

After half an hour of googling, I found Bertrand Le Roy's excellent example of building custom type descriptors: Fun with C# 4.0's dynamic. I'm not sure why it took me so long to find it, so I figure I'll give him a bit of google juice and add some more keywords.

Also, that sample is rather out of date, so you'll have to do a couple of tweaks to make it work in .NET 4/4.5 (see below). Once that's done, you will have a bindable wrapper object that takes an ExpandoObject and can be directly bound to. Given and ExpandoObject "dyn" with a property named "Id", you can do this:

var bindableObj = new DynamicTypeDescriptorWrapper(dyn);

var value = DataBinder.Eval(bindableObj, "Id");

Changes required

First, change DynamicHelper.cs to the following:

public static class DynamicHelper {
    public static object GetValue(object dyn, string propName) {
        // Warning: this is rather expensive, and should be cached in a real app
        var GetterSite = CallSite<Func<CallSite, object, object>>.Create(
                Binder.GetMember(CSharpBinderFlags.None,
                    propName, 
                    dyn.GetType(),
                    new [] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }
                    ));

        return GetterSite.Target(GetterSite, dyn);
    }

    public static void SetValue(object dyn, string propName, object val) {
        // Warning: this is rather expensive, and should be cached in a real app
        var SetterSite = CallSite<Func<CallSite, object, object, object>>.Create(
                Binder.SetMember(CSharpBinderFlags.None,
                    propName, 
                    dyn.GetType(),
                    new [] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant |
                                            CSharpArgumentInfoFlags.UseCompileTimeType, null) }
                    ));

        SetterSite.Target(SetterSite, dyn, val);
    }
}

Then, change GetValue and SetValue in DynamicPropertyDescriptor as follows:

public override object GetValue(object component) {
    if (component is DynamicTypeDescriptorWrapper)
        component = ((DynamicTypeDescriptorWrapper)component).GetPropertyOwner(this);

    if (_owner != component) throw new InvalidOperationException("GetValue can only be used with the descriptor's owner.");
            
    return DynamicHelper.GetValue(component, _propertyName);
}

public override void SetValue(object component, object value) {
    if (component is DynamicTypeDescriptorWrapper)
        component = ((DynamicTypeDescriptorWrapper)component).GetPropertyOwner(this);

    if (_owner != component) throw new InvalidOperationException("SetValue can only be used with the descriptor's owner.");
            
    OnValueChanged(component, EventArgs.Empty);

    DynamicHelper.SetValue(component, _propertyName, value);
}