Dennis Forbes on Pragmatic Software Development
Subscribe to RSS
 
Sunday, May 28 2006

I've been playing with Team Foundation Server, Whidbey (Visual Studio 2005), and Yukon (SQL Server 2005) since early in the beta cycles. All three of them are remarkable products, with enormous advances over their predecessors (in the case of TFS, I'm spuriously considering Visual SourceSafe the predecessor, although TFS is a elephant compared to the mouse of VSS), and all of them should be critical components for anyone developing in the Microsoft camp.

All three of them also happen to be a little unpolished, with odd little quirks and errata, hilariously incomplete documentation, and a tendency towards resource hoggishness.

One thing I've found remarkable, however, given that the three of them have been in final form for anywhere from two months to over half a year, is how little real information and first-hand accounts are available online. I'm continually hitting roadblocks where there are marginal functions or incomplete documentation, and it's surprizing to find zero references to the same problems or questions on any of the normal forums (e.g. Google Groups, online searches, etc). Among the development community, outside of the desperate-to-get-anointed-free-support-MVP crowd, they just don't have the aura of excitement they probably deserve.

Given that there are literally millions of developers and technology hobbyists out there, it's usually the case that any problems one faces are well trodden, and a quick search on the newsgroup usually yields exactly the answer one needs, so this dearth of time-travel support really is disconcerting.

The only conclusion I can draw is that there simply aren't that many developers seriously using these technologies. Visual Studio 2005 is of course seeing some use, but there are still huge armies of developers sticking with 2003 (given the break between .NET 1.1 and 2.0). A lot of SQL shops are still taking a wait-and-see approach with 2005. Team Foundation Server, primarily because of the cost of the Team editions, and the cost of a TFS Server license if you grow past a 5-user team, seems to be fairly rare.

Thursday, March 16 2006

Before I go about possibly reinventing the wheel, I thought it worthwhile to ask: Could anyone point me to .NET / Windows server modules for SXIP 2.0 and/or OpenID? They're both fairly trivial identity solutions, so if I can't find one I'll implement one or both. Not only for personal needs, but because I can see some uses for them in client projects.

Thank you kindly.

Tuesday, March 07 2006

ASP.NET has improved dramatically with v2.0, to the point of making ASP.NET v1.x look like a bit of a hack job. One of the great improvements covered in this entry is the addition of Master Pages.

Master Pages allows you to define a template layer (and coupled back-end code) to be used on content pages using that master page. For instance a master page might define all linked scripts, CSS, and script blocks, along with a navigation header and footer that exist on all pages on the site (or at least those pages using the master page). An ugly example sits a subdirectory away -- at the root pages for yafla, where the navigation header and footer exist in a master page.

ex. MasterPage.Master

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>My Master Page - This Title Will Be Overridden
       By the Title Element
In Content Pages</title>
    <link rel=stylesheet type="text/css" href="stylesheet_in_all_pages.css" />
</head>
<body>
    <h3>This is my universal header!</h3>
    <form id="form1" runat="server">
    <div>
        <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
        </asp:contentplaceholder>
    </div>
    </form>
    <h3>&copy; 2015 Robot Inc.</h3>
</body>
</html>

You can also define code for the various events in the master page, which will run on the pertinent content pages.

Content pages then define what will fill the content block (or multiple content blocks as the case may be), and of course implement their own back-end code.

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master"
AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2"
Title="This is my overridden title" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
This is my content for this content page
</asp:Content>

Very trivial.

Of course this isn't the only way that this result could be achieved -- I could derive from a page object that imperatively creates all of the common elements, or I could use multiple user controls that defined the basics, but neither of those solutions, or similar workarounds, seem as elegant as master pages to me. There are equal or superior solutions in other platforms, however I'm sticking to the topic of ASP.NET in this entry so they are irrelevant.

The one hiccup I faced in the use of master pages was my desire to have meta keywords (which exist in the header) vary by page, despite the fact that the meta keyword is basically dead. I want the keywords to vary, similar to the way I can declaratively override the title in content pages. Unfortunately this required some code workarounds, which in my case included adding a public property on the master page, MetaKeywords, with a default keyword list, which I then added to the header in the PreRender stage of the master page (the following example is simplified for demonstrations sake, however a real implementation would scan the headers to ensure that the pertinent header doesn't already exist before adding it).

    public string MetaKeywords = "default keywords";

    protected void Page_PreRender(object sender, EventArgs e)
    { 
      SetMetaValues(this.Page.Header, "keywords", MetaKeywords);
    }

    public static void SetMetaValues(System.Web.UI.HtmlControls.HtmlHead head, string name, string content)
    {
      HtmlMeta metaValue = null;

      metaValue = new HtmlMeta();
      metaValue.Attributes.Add("name", name);
      metaValue.Attributes.Add("content", content);
      head.Controls.Add(metaValue);

      return true;
    }

Any content page could access its Master property to set the property, and the meta keywords would be appropriately set when the page was rendered. By using the MasterType directive the Master property of the page automatically resolves to the proper type.

Unfortunate that a declarative mechanism wasn't added for arbitrary header elements in the content pages.

The goal of master pages, of course, is to avoid the scourge of copy/paste coding: Unnecessarily having a single line of code in multiple places is an evil in software development, yet it's often the easy, thoughtless solution, yielding volumes of redundant code that invariably diverges and causes maintenance problems for years to come, reducing the quality and agility of the codebase.

I despise copy/pasted code. It truly is a peeve of mine.

When analyzing the quality of code bases, one of the first checks I usually perform is to use one of the automated code duplication checkers (available for most languages). There is a remarkable correlation between code duplication rates and code quality.

The benefit of master pages isn't limited to a single master template, however, but instead you can actually layer multiple master pages. For instance on the yafla site the services category pages use the Services master page, adding additional service specific back-end code and layout, while it uses the web site wide master page. It mirrors the templated way in which many websites are developed.

The downside of layered master pages is that the GUI team apparently didn't have time to build multiple level parsing into the web designer -- wherever you're working on content pages that have more than one level of master pages above them, you are limited to the source view. To attempt otherwise yields a "Design view does not support creating or editing nested master pages. To create or edit nested master pages, use the Source view." Unfortunate, but not deadly.

As an aside, one of the big improvements with ASP.NET v2 is better support of per-page development, similar to classic ASP and competitors such as PHP. This solves one of the primary problems many had with ASP.NET, which is that they didn't prefer to work within the "web site as a monolithic application" model that ASP.NET v1 pretty much enforced. Strangely the improvements bringing these benefits has been met with little fanfare, and few are even aware of it. I do plan on doing a feature on it shortly.

Tagged: [], [], []

  .NET 
Friday, March 03 2006

I've received a couple of fantastic comments about troubles that people have faced adding items from here to their del.icio.us bookmarks, namely because Radio Userland uses a constant title for all entries (and del.icio.us automatically uses the title, so three different entries get the same title if you fail to manually override its choice). The common title problem was one of the reasons I created the notables static listing, though of course that listing is just a subsection of entries.

To help with this issue, I've added quicklinks below each entry to add it to your del.icio.us bookmarks, furl bookmarks, to Digg it or to Reddit it (which will link to an existing entry if one is already on there), and to check for Technorati links (there are seldom Technorati links because most of the readers here aren't bloggers, or they aren't the sort of bloggers that comment on every site they visit. I'd get a big boost in the Technorati rankings if I started pandering to the incestuous blogging community). I've mirrored these items to the static section as well.

  .NET   Blogging   IT   Software Development   SQL 
Monday, February 27 2006

Many users have a small number of passwords -- often only one -- that they use everywhere. For their corporate domain account, their blog, their photo site, their email, their banking and PayPal accounts, and their discussion groups, one key opens them all. Despite the incredible risks involved with this practice, it is more prevalent than ever.

Password reuse is often the habit of jaded users who've been bitten by the "lost password" bug a few too many times, especially against sites that they seldom visit (or even frequently visited sites in cases where they've relied upon their browser or password utility to remember all of the variations...a hard drive crash or system migration leaves them helplessly flailing about, unable to access dozens of sites).

It's so much easier to remember one password than it is to remember dozens.

Of course, few will actually admit to recycling passwords like this, and instead it's the exceptions using unique 20 character random sequences that are most likely to speak out. Yet impromptu prodding of acquaintances, clients, and contacts, along with the results of several recent security surveys, has me convinced that these security best-practice aficionados are the exception, and a large number of users, perhaps even a majority, are dangerously reusing the same password prolifically.

If someone discovers your password on site A, there's a very good probability they can use it to access site B, and C, and D, and E, and so on. The security of your account relies upon the faith of a lot of people who you shouldn't have faith in, not to mention that it depends upon the weakest link before it all potentially unravels.

Despite all of the safeguards that I've put in place in the architecture and design of 360notes, I've tried to minimize the potential damage if an exploit ever did happen by eliminating any unnecessary information where possible (reducing the surface attack area). Following this philosophy, not only do I not want to store your password -- of course I only store a hash and not your original password, which should be a universal practice even among "low value" sites -- but I never want your probably-reused password ever hitting the site in the first place.

Instead of sending your password to be hashed on the server, I want it hashed on the client end, before it even gets sent down the wire.

This wouldn't be a possibility without JavaScript, however the functionality makes JavaScript pretty much mandatory, so in this case it's reasonable to require it for even the basic functionality of the site. As such, the account creation and logon system incorporates functionality that hashes a combination of your username, password, and the domain on the client end, passing through the hash to the server as your "password". As a secondary benefit, the server can generate single-use variants (salts of sorts) which it provides with the form. If such a variant is provided, after the client script has created the hash, it then hashes the first hash with the variant, which the server can do as well, offering basic line encryption as well presuming that the server is tracking the variants, and ensuring they are server provided and not reused (it doesn't replace SSL, so there is still avenues for man-in-the-middle attacks and untrusted remote servers masquerading as the official site, however it's a step in the right direction where SSL can't or won't be used).

The SHA1 algorithm is well known, and in this case I decided to go with the excellent SHA1 implementation by Paul Johnston. Implementing it was trivial, and a simple example demonstrating how to use it for this purpose follows.

<script language="JavaScript" src="sha1.js" type="text/javascript"></script>
...
<form name="loginForm" id="loginForm">
<input id="passwordHash" type="hidden" value="">
Email Address: <input id="emailAddress" type="text" size="20"><br/>
Password: <input id="password" type="password" size="20"><br/>
<input type=button onclick="DoLogon();" value="Logon" />
<input type=hidden id="variant" value="" />
</form>
<script language="JavaScript">

function DoLogon()
{
  var domain = "
www.360notes.com";
  var username = document.getElementById("emailAddress").value;
  var password = document.getElementById("password").value;

  var hashString = domain + "|" + username + "|" + password;
  var hash = hex_sha1(hashString);
 
  /* Variant - trivial "encryption" if the server has provided
     a tracked, single use pseudo-salt. */
  var variant = document.getElementById("variant").value;
  if (variant.length > 0)
  {
    hash = hex_sha1(hash + "|" + variant);
  }

  document.getElementById("passwordHash").value = hash;
 
  /* Remove the password element from the form before submitting */
  document.getElementById("loginForm").removeChild(document.getElementById("password"));
   
  /* Submit the form. */
  document.loginForm.submit();
}

Voila. Now I never know that you use the password 4muppet8 on every site, and instead I only ever see a unique hash specific for this domain.

Of course this scheme still suffers a critical weakness: If, somehow, a nefarious agent could replace the server side scripts, and somehow my remote server validation scripts failed, they could simply alter it to pass through the original password. While that scenario is far more remote and unlikely than the already remote and unlikely database delving or line monitoring, it does demonstrate why the optimal situation would be intrinsic browser support: Instead of creating a site-specific custom script to secure and individualize the password for a specific domain, which allows users to reuse passwords without actually giving the password to any specific site, the browser should support this functionality directly, and it should be evident in the UI.

In addition to the password input type, there should be a secure password type (with obvious, non-spoofable graphical indicators that it is a secure pasword box) as a basic HTML element, automatically incorporating this sort of enhancement. HTTP already supports digest autentication, which is similar, but unfortunately it is incompatible with the form logon approach commonly used, not to mention that it has its own failings.

Thursday, February 23 2006

I've always wanted to mention Quake 2 on here, so here goes.

One of the big advantages .NET brought to desktop applications, at least in regards to official Microsoft dogma, was XCopy deployments: Instead of long, convoluted setups installing dozens of components into the system, shared libraries into system folders, and registry settings in the registry (and maybe some win.ini settings for real historical fun), XCopy allows you to build applications that can be "installed" by simply copying a folder.

Everything old is new again. Years back this was the DOS way, and in many cases it's still the UNIX way.

Some time back my favourite game on the Windows platform -- I did, and though the time is extraordinarily rare still do, play "shooter" games -- was Quake2, in particular with the ActionQuake mod. This game lived in my computing ecosystem for quite a while, not only because it was great fun, but also because the application existed as a directory "island" of sorts: John Carmack and crew had disregarded the Windows developer guidelines, ignoring centralized libraries, components, and registry settings, and stored settings in little config files in the application directory, with mods, extensions and libraries appeared in that directory or subdirectories.

I could "install" the application on a new machine, including all of my settings and extensions, just by copying the Quake2 directory to the new PC. Similarly, if I installed a new harddrive I could move it there and it was fully functional immediately. No complaints about missing libraries, or ridiculous dependencies upon fixed drive letters or fixed paths.

It just worked, and happily adapted to wherever it found itself.

This seemingly trivial "feature" made this application live on my hard drive far longer than it would have otherwise. Without the hassle of reinstalling the app everytime I upgraded machines or reinstalled the OS --  a nuisance that led to many apps getting left behind -- which would also have meant reinstalling all of the patches and mods, and then laborious reconfiguring the settings to a close proximity of my tastes, it just migrated with me. It was always there waiting to provide a quick diversion during a time of thought.

With XCopy deployment, Microsoft has shown that they've seen the light, and have realized that the whole "tentacles throughout the system" approach has been a terrible mistake.

With IIS 7 we might finally see the same sort of benefit for web applications. As it is IIS is a bit of a mixed-up configuration mess, with many directory-specific settings being stored in the IIS metabase existing somewhere else (on Windows 2003 it's a convenient, well-documented XML file that you can find at %sysdir%\\inetsrv\\metabase.xml) -- mime types, directory and file security, where virtual directories/web apps start, cache settings, etc. With IIS you can't simply XCopy the app and have full configuration, but instead you have to have appropriate permissions (generally administratrive) to open the IIS administrative console and set, for instance, that your CSS files should be cacheable for a day but the frequently rewritten XML file shouldn't be cached all, which tells IIS to add the appropriate HTTP headers to each.

Compare this to Apache, which lets you configure the vast majority of directory and file specific settings via .htaccess files in each directory, saving the "system wide" httpd.conf for settings that are truly web server wide. Configuration is logical and single point, and an application can be migrated with virtually all related setup with tremendous ease. A remote developer with access to only his folder of the web app has the ability to configure things as they should be configured without ridiculously requiring administrative rights.

IIS 7 adds this sort of functionality, moving a lot of the virtual directory and folder/file settings into files that you put in your web app file structure (obviously it won't, or rather won't under ideal conditions, allow these files to be read by web users). No more hydra-setup where half the setup exists over there and half exists over here.

Everything old is new again.

Friday, February 17 2006

I've received some great feedback regarding the entry on setting up a MediaWiki install on Windows. Many of the comments were kind words of thanks (which I really appreciate. Knowing that it helps people is my greatest motivation), and others helpfully suggested improvements to the instructions.

As an example of comment-driven improvements, my instructions have you installing the GNU diff utilities, in particular for the diff3.exe utility, however the MediaWiki setup scripts don't properly find it (e.g. as the instructions are currently written the GNU diff utilities are completely unused, although they can still be useful in your day-to-day travails). This is because a prior revision included fairly involved changes to the MediaWiki config/index.php script so it would properly locate diff3 on the Windows platform, as it is currently Unix-centric and doesn't look for the proper executable, not to mention that it parses the PATH environment variable incorrectly . After receiving two comments that those steps were a little too complex, however, I removed that section.

My goal was to get people experimenting with MediaWiki, or even just wikis in general, so diff3 functionality really wasn't critical. I pared the instructions accordingly. Similarly one early draft included the building and installation of a PHP memory cache to improve performance, but that too is unnecessary to simply try out the product.

Another line of comments involved asking:

  • Why would I give instructions for Windows. People should just set it up on Linux and go with its native home.
  • -or- Why would I recommend a wiki product that largely caters to the open source crowd. Instead I should be pushing Sharepoint, or something properly anointed by the Microsoft camp, enabled with all of the latest Microsoft buzzwords.

To answer this I really need to describe the philosophy of this blog, along with my resistance to "technology alliances".

In the byline of this blog I describe my philosophy as "pragmatic software development", and this really drives my recommendations. In this case there are a lot of development shops that are Windows-centric, with little or no UNIX/Linux experience, yet MediaWiki is one of the best, most featurer rich, "standard" wiki products out there. Choosing a solution that leveraged what shops already know with the best solution is a pragmatic approach.

Which brings me to my general philosophy towards Microsoft, as comments indicating that I'm either a Microsoft hater, or a Microsoft drone parroting the corporate line, have hit my inbox over the short history of this blog.

I am not subservient to Microsoft.

Unlike many Microsoft technology advocates (I truly love both SQL Server, and .NET, and I think they're remarkable solutions), I have no desire to ever work for Microsoft (Microsoft has some top notch, world-class talent, and I've met and worked with a lot of great talent from there, but they also have their share of both jerks and duds). I'm not going to praise their every move in hopes that I'll get noticed. yafla, my consulting/ISV company, has chosen to avoid any partnerships or tying to the Microsoft brand because we don't want to become another drone "consulting" company single-mindedly acting as a third-party sales force for Microsoft, desperately racking up Microsoft partner points by pushing less-than-optimal solutions on customers. We didn't choose to use .NET for our software because we're hoping to nestle into the Microsoft family -- we chose it on technical merit, and a pragmatic analysis of our current and prospective clients.

We work for our clients and ourselves, not Microsoft. This is a very important mantra for our services, and for the technology of our software, and if Microsoft wants their products to get recommended to our clients, and their technology to the foundation of our software, they need to make great products at competitive prices. No sales gladhanding, or sad career dreaming, is going to change that.

Am I saying that Microsoft solutions are second rate? Of course there are examples of Microsoft products that are terrible, and customers are being misled into buying buzzword-laden atrocities because a Microsoft partner is hoping to get invited to the next Microsoft dinner party. Yet there are also Microsoft solutions that are extraordinary. Windows 2003R2 is a superlative operating system, and where you need the breadth of its functionality, it can be well worth the money. Microsoft Small Business Server can be an amazing package of value for some small organizations, within the constraints of the product. Other times, however, if you have the appropriate skills, a Linux machine is the best choice, along with a stack of the many available free or close to free server products on that platform. Sometimes IIS 6 is the superior solution for a problem, while other times Apache would be your best bet. Sometimes PHP and MySQL is a great solution, and other times C#/ASP.NET with SQL Server is the perfect combo.

I don't blindly assume the Microsoft product to be the best, but neither do I automatically presume it to be second rate. Instead I evaluate on merit, and propose solutions based upon the customer and their needs.

To do otherwise would be just biased noise, and wouldn't be to the service of clients and peers.

Tagged: [], [], []

Earlier EntriesLater Entries

Dennis Forbes - Dennis Forbes is a Toronto-based software architect and technology writer