Five Reasons ASP.NET Developers Shouldn’t Worry About Node

No Comments March 24, 2014


I devoted my last post to reasons why ASP.NET developers should pay attention to Node.js. Taking a queue from high school debate team, and because no technology decision (or opinion) exists in a vacuum, I’d like to turn the tables and consider a few reasons why ASP.NET developers might stay away from Node (or at least might think twice before making the leap).

Don’t get me wrong… I really like Node and I think the concepts and patterns it embodies will continue to have a profound influence on server-side web programming for a long time. If and when the popularity of Node plateaus or even wanes over time, we’ll still feel its effects (good and/or bad) in the Next Big Thing. And in the meantime, plenty of us will choose to use it, happily and productively so.

But there is always more than one way to solve a problem, and while Node may be “where the cool kids are” right now that doesn’t mean it’s the only way to roll on the web server. Plenty of Real Work is done every single day with boring old web frameworks like ASP.NET, Java EE, Rails, PHP (!) and a thousand others. Hell, there are even some lunatics out there serving up HTTP in COBOL! MVC in COBOL… that’s brilliantly crazy. I love it (just don’t make me work on it).  Smile

For me one of the benefits of being in this industry for (sigh) almost two decades is a bit of perspective… Node is a relatively fresh and interesting approach to solving a relatively old and boring problem. That doesn’t diminish the utility of Node. But it does mean that its not the only way. Not yesterday, today or tomorrow.

So by all means… if you’re an ASP.NET dev fired up about working with Node, go for it! Shoot me an email or a tweet, let me know what you’re working on (or send us a resume… we’re hiring!). But if you’re on the fence and wondering amidst all the Node-fanboy-love what the possible arguments against might be… read on.

1. Node’s Best Feature (Asynchronous I/O) Is Readily Accessible In .NET, Too

One of the strongest arguments for Node is the path down which it walks you, toward the use of asynchronous, non-blocking execution for potentially long-running I/O operations. In English, this means that most of the real work that occurs in your server-side web application code (database queries, outbound requests to web services and other network resources, file access, etc.) will not occur on the main request processing thread, keeping it free to continue serving other inbound HTTP requests. Instead, such work is dispatched to an available thread pulled from a pool expressly maintained by Node for just this purpose. You define a callback which is invoked for you by Node when one of these long-running operations eventually returns. Here’s an example of querying a MongoDB database using the popular mongoose.js API for Node. Note the callback function argument and the lack of return value assignment in the invocation of ‘findOne’:

var query = Kitten.where({ color: 'white' });

// the lone argument to findOne() is a callback which is invoked
//   when findOne() completes execution
// the callback is passed an error (if one occurs during the call
//   to findOne()) and the result of the query (which could be null)

query.findOne(function (err, kitten) {
  if (err) return handleError(err);
  if (kitten) {
    console.log(kitten);
  }
});

This type of continuation-passing-style programming is sometimes hard to grasp for the uninitiated, particularly as you get more than a few levels deep in your code. But it has a huge advantage over the more familiar sequential style: it provides a natural point in your code for the executing framework to do something else instead of waiting for (in this case) query.findOne() to return. For Node, that something else is often “handle lots more incoming HTTP requests”. Indeed, a core motivating hypothesis behind Node’s original design (since largely proven true, I think) is that most of the throughput bottleneck for typical web server applications lies in waiting around for I/O to complete. Much of Node’s observed performance benefits and ability to scale stems from explicit design choices in light of this observation.

Here’s a depiction of Node’s high-level design:

node.js event loop

So this is all well and good if you’re working in Node. But here’s the interesting part… you can often use this very same pattern today in ASP.NET! Many of you are likely familiar with C#’s async and await keywords, first introduced in C# 5… the use of these along with supporting API constructs in libraries like ASP.NET, Entity Framework, and others allows you to achieve the same non-blocking execution style in .NET web application code that we see in Node.

Here’s a simple async ASP.NET MVC controller implementation that issues an Entity Framework query (this pattern also works great for Web API):

private DataContext db = new DataContext();

// GET: /Employee/
public async Task<ActionResult> Index()
{
    return View(await db.Employees.ToListAsync());
}

// GET: /Employee/Details/5
public async Task<ActionResult> Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    Employee employee = await db.Employees.FindAsync(id);

    if (employee == null)
    {
        return HttpNotFound();
    }

    return View(employee);
}

Notice how each method uses async/await and returns Task<ActionResult> instead of ActionResult? This pattern ensures the same general continuation-style execution semantics as found in Node. An ‘await’ keyword indicates the location at which execution is paused (and the current thread freed for use handling other requests) while an EF query occurs outside the ASP.NET process. When the EF query returns, another thread is used to resume execution of the code immediately following the ‘await’. This gives us the pause/resume semantic in ASP.NET that the callback function represents in Node. The implementation is different, but the basic principle is the same… valuable server resources are not wasted waiting for expensive I/O operations to complete.

A caveat… Node proponents will tell you that Node’s advantage here is that it’s design leads you straight into the “pit of success”… async is the default pattern in Node. Do the easiest thing in Node, and you (often) win. And while you can do async in a framework like ASP.NET, many (most?) .NET developers don’t do so. And that’s undoubtedly true. But while this might make the average Node application “more async” than the average ASP.NET app, that doesn’t necessarily translate into better throughput or greater scalability on its own. It doesn’t matter if the average app on any given framework scales well. What matters is if your app scales well. If you’re doing ASP.NET today, take the time to learn and apply the async model before you determine that “ASP.NET doesn’t scale”, etc.

2. Node’s Simplified Application Model Is Also Available In .NET

Another big benefit of Node is its simplified programming model and the self-determined pace at which you can opt into more powerful, more complex features as you learn them and need them. It’s not necessarily true that Node requires mastery of fewer concepts than ASP.NET in order to build “real” applications… it just feels that way. To be sure this is subjective, but it seems to hold true for many Node beginners.

Contrast this with the range of interlocking concepts needed to get up and running with ASP.NET MVC… HttpApplication, global.asax, web.config, models, views, controllers, routes, actions, bundles, etc. etc. As experienced ASP.NET developers we take most of this for granted… it’s just background noise like the blondes and redheads in the Matrix. But imagine what this feels like for the average 19 year old just cutting his teeth on web development… given the choice between diving into the vagaries of global.asax and web.config or simply scratching out a few line of JavaScript, is it any wonder Node has found an audience? Every generation has its archana it unconsciously (and ironically) drags around with pride… “back in my day we served up HTTP with a horse-drawn plow, a pitchfork, and some gumption! And we liked it!” And every generation also has another, younger generation just behind them, rolling their eyes and casually moving on to new tools and techniques.

Ah… but here again we see ASP.NET learn some new tricks and evolve. Over the last few years Microsoft has worked within the confines of a larger community of .NET developers to define the OWIN specification (for Open Web Interface for .NET). The basic idea is to decouple .NET web applications from the underlying server infrastructure on which they run, by defining formal-yet-abstract layers for the .NET web stack (host, server, middleware, and application) and also defining the interfaces through which adjacent layers communicate with one another. This formality enhances portability across multiple web hosts. It also serves as the basis for the Katana project, which is Microsoft’s implementation of OWIN for serving HTTP requests via the traditional ASP.NET pipeline, HttpListener (a .NET Framework type), IIS, custom hosts, and more.

OWIN and Katana borrow heavily from Node and other lightweight frameworks, so the programming model is simple and approachable. Remember how easy “hello, world” is for Node? Check out Katana:

public class Startup
{
   public void Configuration(IAppBuilder app)
   {
      app.Run(context =>
      {
         context.Response.ContentType = "text/plain";
         return context.Response.WriteAsync("Hello World!");
      });
   }
}

How easy is that? Simply define the behavior you want for any given URL endpoint (the above code uses a single behavior common to all endpoints for this app). Note how the code is async by default, just like in Node. Who says an old dog can’t learn new tricks!

To be clear, the code above will work on any Katana host… IIS, OWIN.exe (an MS-provided console host similar in concept to Node.exe), or your own custom host implementation. This ability to choose between multiple host and server options for executing the same application code is a very useful feature of Katana that Node doesn’t offer today.

But wait, there’s more! As great as Katana is, it suffers from a relative weakness in the common scenario of IIS host + ASP.NET server infrastructure… it’s dependent upon System.Web, the monolithic assembly that defines not only the core ASP.NET pipeline types (HttpApplication, HttpContext, IHttpModule, IHttpHandler, etc.) but also the types that make up the old-school Web Forms programming model… pages, controls, view state (!) and the rest. Do you really want to drag all that stuff into your modern, supposedly lightweight, Katana-enabled AppDomain? Yeah, the ASP.NET team didn’t figure you would, either.

So they’ve created Project Helios. Helios is an incubation project meant to publicly flesh out ideas around hosting OWIN-enabled applications within IIS (where they can take advantage of things like IIS security, caching, process lifecycle management, etc.) but without the necessity of dragging System.Web into your AppDomain. Helios is a pre-alpha release so we have to tread lightly with comparisons; nevertheless, Helios does demonstrate significantly reduced per-request memory consumption compared to ASP.NET-hosted OWIN apps, which bodes well for a simplified, highly scalable .NET web stack in the (hopefully) near future. This is great news for ASP.NET developers; you can prepare today by taking a closer look at OWIN and Katana. And finally, let the ASP.NET team know that you want an official, fully supported Helios-like stack from them, pronto! Smile

3. Node’s “JavaScript Everywhere” Is Great! Unless It Isn’t

One of the big arguments for Node is that you get to use JavaScript throughout your entire application stack (I’ve presented this argument favorably myself). And frankly there are some really nice advantages to that.

Unless you don’t have any solid JavaScript programmers on your team. And unless you’re willing to pay good money to hire some of the most in-demand tech talent in the market today. And unless you’re willing to rewrite your entire application in JavaScript (or you’re starting from scratch… in which case you may not have the rewrite problem but you’ve certainly got the “where do I find solid JavaScript developers” one).

For folks coming from a more traditional OO language like C#, mastery of (hell, proficiency in) JavaScript can be an elusive thing. Prototypal inheritance can be very powerful, but is also a lot of rope to hang yourself with. Variable scope “rules” are, um, interesting. Type coercion is, at times, seemingly random. “typeof NaN === ‘number’”. Trust me, there’s more where that came from. None of this is insurmountable and productivity in JavaScript is not only possible but demonstrably so. Things like TypeScript really help, especially for the C#-to-JS converts. Just don’t expect overnight success.

This is part of the reason that, for all its trendiness, Node is still a relative newcomer to the enterprise. Sure, plenty of Valley cool kids have been hacking away with it for years (and no doubt interest from the enterprise guys will be their cue to move on to the next hipster app framework). But in the near term, if you’re looking to bring in Node talent to work on BigCorp.com, you’re likely in for a bit of a struggle. That doesn’t mean you shouldn’t care about Node… just set realistic expectations.

Too, it’s important to note that client-side JavaScript development is similar but distinct to server-side dev… its not so much that the language is different (its not), but that the concepts and patterns have only a certain amount of overlap. You need folks who not only understand JavaScript, but also understand network latency, asynchrony, server security, scaling out vs. up, cloud testing and deployment, efficient data access, etc. etc. Even an otherwise excellent client-side JS dev might not have a complete grasp of these. On the other hand, if your company has been doing .NET web development for more than five minutes, I bet you’ve got a few folks on your team right now who know about this stuff. They might not be Node gurus (yet), but they’ve probably got valuable knowledge you can and should be taking advantage of.

Here again, we see that context matters. It doesn’t matter so much that JavaScript and Node are the flavor of the day/week/year. It only matters if they’re applicable to your specific situation. Yes, some thought to future technology viability and “where the talent market is going” is of course important… but how confident are you in that crystal ball you’re holding? We can make some educated guesses about tomorrow… but that’s all they are, guesses. Here’s a news flash for you… C# may not be “cool” anymore, but its not going away anytime soon, either.

More on this below. So by all means, get excited about Node if you like, and start to kick the tires a bit to see how it suits you and your team. But do yourself a favor and stay focused on today’s needs first and worry less about how you’ll adopt the next Shiny New Thing. When you’re ready for it, it’ll be there.

4. If Your ASP.NET Application Is Slow or Doesn’t Scale, Maybe the Problem Is You

The odds are against rewriting your web app in Node fixing whatever current issues exist in your ASP.NET implementation. I’m not saying it definitely won’t help. But I am saying it probably won’t help.

This may spill some double-chai skinny mocha lattes down at Ye Ole Software Guild. But the reason switching to Node probably isn’t going to fix all your problems is simple enough… those problems are likely your own fault, not Microsoft’s.

Ask yourself (honestly) where you fit in the descriptions below:

1. You and your team are thoughtful, reasonable software craftsmen who take the time to understand widely recognized ASP.NET best practices and apply them to your project. Your design is SOLID. You have unit tests for everything. You know your code coverage metric, and you know why its not 100%. You dispassionately considered the good and bad of Entity Framework, NHibernate, naked ADO.NET, and a million micro-ORMs and made a reasoned choice based upon the specific constraints of your project. You thought long and hard about whether you wanted the locus of control in your app to exist at the server or the client… and considered the downstream impacts of that decision to things like available skill sets, tooling, productivity, performance, etc. You worked diligently with stakeholders to derive meaningful real-world use cases and stories, and worked even harder to set up performance and load testing scenarios early on in the project, so that you’d have a realistic picture of perf and scale over time. You’ve profiled your application and your data access literally hundreds of times, to the point where you know down to the source line where your bottlenecks are, and you’ve either fixed them or determined that no additional effort will making a meaningful difference. Management understands that these things take time, and you have what you need to deliver quality code.

2. You and your team are short-sighted, irrational “developers” clinging to jobs you’re under-qualified to hold and desperate to keep. By definition you’ve never read a book or article about ASP.NET best practices (if you’re reading this you got here via a link on Stack Overflow, found via GDD, or “Google-driven development”). Your design is, um, “solid”. A guy who used to work on your team wrote a couple unit tests but you commented them out when some code changed and the tests wouldn’t compile anymore (though your manager still references those tests in his weekly report to the CTO on code quality). You heard once from some guy you worked with three jobs ago that Entity Framework “sucks” and that NHibernate is “too complicated” so you wrote your own hand-crafted DAL that pieces together SQL statements in-memory (and per-request… no caching) and ensures that your dynamic database fields (“ExtraColumn1”, “ExtraColumn2”, … “ExtraColumn25”) are all pulled in as appropriate, on a per-record basis (alternate… you heard from another guy that EF is “awesome” so you based your entire server-side infrastructure on manipulating deep hierarchies of EF object graphs pulled from your fifth-normal-form-compliant database designed by a “database architect” who’s never seen a 7-table join he didn’t love). You have no idea what a profiler is. “SQL execution plan” sounds to you like something you’d go to prison for. You “tested” your application for months without ever attempting concurrent user requests, and your scalability plan is “buy more hardware”. Etc. etc. etc.

3. You’re somewhere in the middle between 1 and 2.

Let’s face it… we’re all somewhere between 1 and 2. And that’s okay… no project is perfect, no project team is perfect. So sure, if you skew pretty close to scenario 1 above, and you’ve really done all you can to squeeze every last drop of perf and scale out of ASP.NET and its still not enough, then perhaps it is time to consider another framework. Perhaps.

But it’s a poor craftsman who blames his tools, and blaming your web app’s problems on ASP.NET is the modern-day equivalent of finding a bug in the compiler. Select is not broken… it’s probably your fault. That doesn’t mean ASP.NET is perfect (far from it, in fact). It’s a tool, and a useful one… millions of sites run just fine on it every day, and at least some (most?) of those are likely more complex and/or have higher traffic than yours. The likelihood that you’ve coded beyond the capabilities of a framework that’s been battle-tested for years is, frankly, pretty low.

None of this means that Node is or isn’t “better” than ASP.NET, for various definitions of “better”. It does mean that if you’re looking to switch from ASP.NET to Node, that’s great but don’t blame it on performance or scalability issues with ASP.NET. Node isn’t magic pixie dust to “apply liberally for more scalability!” Node is a tool just like ASP.NET… used properly, it works very well. Used carelessly and without a good understanding of its strengths and weaknesses, and you’ll likely spend a lot of time and money on a Node rewrite and end up right back where you already are today.

And that makes for some pretty sad trombone, my friend.

5. “Microsoft is dying!/dead!/evil!/boring!/uncool!/undead!/etc.” Is No Way To Conceive A Technology Strategy, Son

 

 

Right now, as you read this, someone out there in Enterprise Software Land is making the switch from ASP.NET to Node based on little more than an irrational, logic-free dislike of Microsoft. These people are Trusted Decision Makers within their organization. They’re responsible for technology decisions with millions in potential downstream impact (positive or negative) to their business. And when you go one or two layers below their PPTs and executive summaries, the flimsy rationale melts away and their argument pretty much boils down to “I don’t like Microsoft and I don’t like ASP.NET and I want to do what the cool kids are doing.”

In the quarter ending December 31, 2013 Microsoft had the fourth-highest market cap of all companies tracked by the FT Global 500 index. It also had record revenues of $24.5 billion in that same quarter. Microsoft is not dead; far from it. Azure is a vibrant, competitive offering well-positioned for the continued enterprise migration to the cloud, which we’re only in the early stages of. Office 365 has become a viable cloud-based alternative to the traditional Office shrinkwrap. Microsoft continues to invest as heavily in R&D as anyone (how “boring” is that?). So is Microsoft “uncool”? If you’re asking that question in the context of defining your enterprise IT strategy, haven’t you already failed?

Don’t get me wrong… there are plenty of valid reasons to choose something other than Microsoft at the outset of a big, new project. Upfront licensing costs can be difficult to swallow, particularly in light of various open-source alternatives (although there are ways to mitigate those costs). The long-term viability of some of Microsoft’s core technology is, at minimum, undecided at this point. And for all the happy talk and promise of Satya Nadella’s rise to CEO, we still don’t know if he has the right stuff to both capitalize on what Microsoft’s done right, and fix what’s wrong.

However, if you’ve already invested heavily in and built significantly upon a given technology stack, it’s almost always a mistake to throw it out and start over. Joel wrote about this 100 years ago. In my experience, this is almost always an emotional decision, not a rational one… regardless of which technologies we’re talking about.

If you’re a CTO sitting on a mountain of legacy ASP.NET code and you think re-writing in Node is going to solve all your problems, you’re doing your company, your board, and your customers a disservice. They’re not paying you to twist in the wind with every technology fad that comes along. They’re not paying you to play out your visceral dislike of all things Microsoft in soap opera tales of “which M$ technology sucks this week?” They’re paying you to deliver business value… predictably, economically, and with high quality. When it comes down to it, your choice of technology stack has much less to do with achieving those things than we want others to realize. Most of the variability in a software project is human-centered. But it’s so much easier when we can blame our own shortcomings on “well, everyone knows ASP.NET can’t scale”.

So let’s suppose you’re an ASP.NET shop, but you’re a bit concerned about over-reliance on a single vendor. What options do you have beyond “rewrite everything in Node”? First, you can identify pieces of your architecture that might benefit from various open-source alternatives to what comes in-the-box with ASP.NET. The rise of a vibrant open-source ecosystem for .NET over the last several years has been one of (the primary?) saving grace for the platform. Entity Framework is great, but have you considered alternatives like Massive or Simple.Data? Maybe your data access strategy needs a facelift, and your application would benefit from considered application of NoSQL data storage using MongoDB or RavenDB? Perhaps you built a proper services layer and can leverage new modalities like responsive UI in a straightforward way. Making these types of architectural changes is never easy, and shouldn’t be considered lightly, but they may address specific issues you’re currently facing without the even-bigger expense and risk of throwing everything out and starting over.

Second, consider more radical ideas like running on top of the Mono runtime, and choose-your-own host OS. Mono runs ASP.NET and MVC quite nicely today. If you’re interested in the cloud but prefer to avoid Azure, you have multiple options for running ASP.NET elsewhere… AWS EC2 virtual machines, AWS Elastic Beanstalk (Amazon’s PaaS offering), smaller cloud vendors like AppHarbor, and the recently announced Red Hat OpenShift support for .NET.

Finally, before you start making big decisions about technology and platform choices, make sure you’ve got the facts on your side. There’s more to consider than just upfront licensing costs. Total Cost of Ownership is measured not just by upfront costs, but ongoing ones, too… some of which aren’t so easy to quantify. What’s the opportunity cost to your company of a Node rewrite, relative to what your team could have done with that time otherwise? What’s it worth to you to have a support line you can call 24x7 for help troubleshooting your ASP.NET app, vs. finding answers to your tech questions on a random Node developer forum? Is your geographic area a hotbed of leading edge talent like Node, or does it consist of more mainstream technology experts like .NET or Java? How easy will it be for you to find, attract, and keep talented Node developers at your company? Are you prepared to fend off attempts to hire them away? Many of your current developers will have gained valuable domain expertise… do you plan to keep those developers around when you switch to Node? How well will that expertise translate in a new technology stack? And if you’re not planning to keep them around, are you sure you’re prepared to watch that hard-won domain knowledge walk out the door?

There’s no black-and-white here… its not like hiring “outdated” developers, versed in older technologies and unattractive to recruiters, is a winning staffing strategy. And to be fair, it’s not like sticking with ASP.NET (or any other single technology) forever is likely the right call, either. But all the factors above (and a bunch more) must be weighed rationally and not emotionally before you decide to make the Big Switch from ASP.NET to Node. Be deliberate and be rational.

Hopefully I’ve given you something to think about here. I like Node a lot, and I’m looking forward to spending more and more time with it over the next few years. But I like ASP.NET a lot too… both for what it does today and the promise of where its going tomorrow. You’d be wise to pay attention to both, and not pick sides prematurely.

In the end, there’s no simple answer for which to choose and when, but a few guiding principles are probably in order. Let’s tease some of those out in an upcoming post.


No Comments

Have a Comment?