thinking in geek tagline’s are so web2.0

ASP.NET == Usability disaster?

[This has been in the queue for a long time - over 12 months - codebetter just tipped me over the edge!]

Peter Van Dijck asks:

I’ve only worked twice with companies using .NET for web interfaces, and in both cases the UI was a disaster and the usability problems guaranteed lots of consulting hours.. Why is that? Or was that a fluke? (2 cases is hardly proof of anything) I know it’s possible to make usable and elegant web UI with any technology, but does .NET somehow encourage bad UI?

This is a great question and in my opinion - the answer is yes. (Assuming that Peter really means WebForms.)  spike pit of death

This is demonstrated in so many small ways by framework defaults which are totally brain dead (__doPostBack anyone?). Rather than lead dev’s into the pit of success - the framework leads devs to the spiky elephant trap of death. 

 
What’s that - an example? How about the <customErrors> defaults. When an error occurs during processing an ASP.NET request - the default behavior of a new web.config is to redirect the user to another URL. This is what a new web.config looks like in VS2k5. 

        <customErrors mode=”RemoteOnly” defaultRedirect=”GenericErrorPage.htm”>

            <error statusCode=”403″ redirect=”NoAccess.htm” />

            <error statusCode=”404″ redirect=”FileNotFound.htm” />

        </customErrors>

This is really brain dead behavior! Let’s count the ways: 
1. There’s no way to change the HTTP status code from a html page. (E.g. - should be 404, 500 etc). This is just flat out bad - status codes are important for lots of reasons.
2. The effing URL will change. This makes it difficult to correct typing errors or retry a request in case of a server error (500). 
3. Those files don’t even exist! You can get a 404 when the real error is a 500 because the file that your getting redirected to don’t effing exist! 
This just happened to me - I was just wondering if there is a port of the monorail SmartGridComponent to ASP.NET MVC. I do a Google search and get a likely looking result for Billy McCafferty’s blog. I click on the link and I get the following page: 
Oops something went wrong!
Either the site is offline or an unhandled error occurred. We apologize and have logged the error. Please try your request again or if you know who your site administrator is let them know too.

 

The URL in the browsers address bar is: 

http://devlicio.us/error.htm?aspxerrorpath=/themes/blogs/default/post.aspx

HOW the F$%K do I try my request again?
If I’ve opened this result in a new tab (which is my habbit) I’m screwed! There is no back button, refresh (F5 or whatever) just reloads the error page and will never show me the correct page.
If I just clicked the link - the back button takes me back to my google search where I can click on the link again - refresh is still borked. 

Another example? Linkbutton? Anyone who’s tried to open a link in a new tab/window and got a blank window with a URL like: javascript:__doPostBack(’ctl00$Masthead1$LocaleManagement$ctl00$ctl05′,”) - knows the pain I’m talking about. 

Go on - right now - go try and open a MSDN library page in different language in a new tab. (It’s the little menu up the top right where it says (e.g.): United States - English). 

In my apps - I fix this by disabling customErrors and writing new error handling code which both maintains the url and returns the correct HTTP statuses - but this is code which I should not have to write. This is code which all web servers/apps need to implement correctly - it should be automatic. We shouldn’t have to write custom code to get a HTTP/404 returned when an app encounters a missing file!


A week on mars.

[Warning - digression ahead]

Over the last two weeks I’ve been trying to buy some speakers for (2 channel) music listening.  I’ve listened to a lot of speakers: 

I’ve finally chosen some Jamo c803’s.

The best floorstanding speaker I listened to was the Krix Phoenix - it was also the cheapest (and locally made). 

The best bookshelf was maybe V3M or the XQ20 or the Focus 140’s. They weren’t enough better to justify more than 2x the price though and since I didn’t listen to them in the same session or location - it’s possible they weren’t even better. 

Anyway - it’s been interesting. It turns out that the HiFi/AV/Audiophile world is another planet. A planet where:

Best of all - many of these “subjectivists” claim that double blind testing interferes with the results. 

Which is to say that after this - arguments about ORM’s seem welcome.


The worlds atwitter…

..and so am I.

Somehow - twitter has clicked for me. It could be related to checking out clients and liking Twitterrific enough to keep it running. 

It’s fair to say that twitter itself it pretty unreliable - but it doesn’t seem to matter too much. I’m living proof - somewhat available is good enough for some apps. 


SVN 1.5

Well - (finally) Subversion 1.5 is out! 

For the first time svn has built in support for repeated merges without spurious conflicts or manually managing the list of previously merged revisions.

This is _fantastic_ news. The basis of the merge tracking feature in 1.5 was svnmerge.py which means that many svn users will already have a reasonable idea how it works. (List of merged revisions are stored in svn properties on a branch). 

I’ve been watching the development of svn 1.5 for the last 18 months (give or take) crying everyday that I have to manually manage merging work as I oggle the feature list. 

TortoiseSVN has a significantly enhanced release as well. I’m particularly excited about this because it supports caching the change log which will make a huge difference for me given that my svn server it 2 continents and 300ms away.

It also looks like there may be some progress on cleaning up the working copy implementation.1. Which would involve fixing the whole every folder with .svn folders problem (a la git or svk). 

  1. http://subversion.tigris.org/roadmap.html []

The only way is up

From an altnet list discussion about Team Build (part of TFS):  

On Thu, Jun 12, 2008 at 7:35 PM, David Kean wrote:  

Team Build is more than just MSBuild, it includes build management (retention, CI, drop management, etc). The next version will be even more powerful and based on Windows Workflow.  

 

On Fri, Jun 13, 2008 at 2:43 AM, Ayende Rahien wrote: 

And here I thought that the only way to go was up. 

 

Pure gold. 


Crappystrano

Note: After I started this series Jeremy Miller has posted a very relevant article about source code hygiene issues.

(This is the third part in a series - you should probably read part one and two first.)

Today I’ll present a sanitised copy of my deployment script. I’ve based the design of deploy.cmd on Capistrano1 . The script is nicknamed “Crappystrano”. Here’s the output (pretty htmlized source is here):

>deploy.cmd

Crappystrano - the shit script which (sort of) deploys stuff

deploy.cmd staging|live [branchpath]

Here’s breakdown of the steps2 - skipping some error checking3. :

  1. Do we have the required dependencies4 on the path? If we don’t copy them to the path if they’re just 1 exe - otherwise exit with an error.
  2. Check the command line parameters. We must have the environment we’re releasing to? Are we releasing from the trunk or from a SVN branch?
  3. Construct a release time stamp. This is YYYYMMDDHHMMSSMS.
  4. Perform a “svn export” from the trunk/branch to %tmp%\staging|live%timestamp%. [E]
  5. Use msbuild to build the solution. [E]
  6. Rearrange the folder structure so we only get the artefacts we need in the release package.
  7. Zip up the release package using the environment name + time stamp.
  8. move the release package back to the root directory of the project and delete the export from %tmp%.
  9. Make a SVN tag (svn copy trunk/branch /tags/environment-timestamp).

I’ve uploaded a zip file with the script and folder structure + a little readme. This is not the best way of doing things - it’s just a very lightweight way. Comments, questions, feedback etc… are gratefully received.

  1. http://www.capify.org/ - I know some people are going to say - just use Capistrano. There are two reasons for me not to: 1. I’m the only person in my team familiar with Ruby. 2. Even if you argue that they don’t need to understand ruby to run a cap deployment recipe - they still have to have it installed. It’s just yet another dependency which I’m not willing to take currently. []
  2. This is all simplified as the actual script supports a SaaS product which has multiple instances - one per client []
  3. noted with [E] []
  4. gnu zip.exe, svn []

A deployment process

I know I said my next post would be my deployment script but after posting - I realised that I should do a 1000 foot overview of what I’m trying to achieve.

For me - a successful deployment has a number of components:

  • Build
    1. Code successfully builds 
    2. tests pass.
  • Versioning
    1. I know exactly which revision was deployed from subversion.
    2. I know what environment it was deployed to.
  • Deployment Safety
    1. On my server I have the ability to rollback a deployment.

  • Support
    1. I can compare the current deployed version to previous releases when I need to do post-deployment production debugging.

To achieve this I use a number of conventions which I’ve borrowed and adapted from Capistrano.

My Subversion repository is structured so that every release get’s it’s own tag - including the environment which the release was made to1 :

subversion-release-structure

The tags are kind of self explanatory - the format is:

[environment]-YYYYMMDDHHMMSSMS 

This is much easier than trying to remember that version 2.54.33.2 was released on Tuesday to client X.

In a deployment environment (staging or live) - each release is unpacked into it’s own folder.2

iis-release-folders

After a deployment package has been unpacked on the server there will be a new folder for the release. Making a release active involves a couple of (currently) non-automated steps:

  1. Updating the database connection details (this is a matter of copying a database.config into the release folder3 ). 
  2. running a DB upgrade
  3. changing the webroot in IIS

People who are paying attention will notice that this process is not necessarily atomic. Depending on the DB changes - it’s possible that the old/new code cannot run on the same database - in this case I drop a app_offline.htm - into the previous release folder before I run the DB upgrade.

  1. The folders in /tags are client names. This is because our SaaS app is released per-client - not multi tenanted. i.e. Each client is running a different version at any given time - and is upgraded to the latest trunk as necessary. []
  2. The occluded bits are the client name - this is for convenience in managing release packages (zip files) as the server has a folder for each client []
  3. this is the only thing on the server which is not versioned in subversion. My web.config has the following: <connectionStrings configSource=”database.config”/> - this file just contains the connection details for the application. []

[ControllerAction] and sane defaults

[Ed: For anyone who's living under a bush Microsoft have released a CTP of the new ASP.NET MVC framework. ]

I’m reluctant to get involved in this - but I feel like there’s another side to this story which is worth considering.

Since it was first presented at altnetconf - the decision to require users1 to have to specify which methods on controllers are available to be routed to ([ControllerAction]) and which view (RenderView()) to render has come in for a lot of criticism.

Lots of the brightest and best minds in the .net world are screaming about this in various forums. Claiming that it violates:

  • the DRY principle
  • convention over configuration
  • best practice in other frameworks
  • commonly accepted laws of physics

Not one of the people complaining is going to use this framework vanilla. They are going to add their own layer of behavior over the top. It’s been designed to make it easy (trivial in this case) to extend and change the policy.

Sane defaults are really important for frameworks. Yes they get in the way of power users. Yes it normally takes a power user 10 minutes to work around them and add that piece of code to their toolbox from then on.

I don’t know how many people were following the emergent rails community back in 2005 when the Google Web Accelerator was released - but many of them (including the smartest2 ) got a salient lesson in GET idempotence. (i.e. GET Order?id=23&action=delete - should never delete anything).

Here was a situation where the framework (rails) came with not so safe defaults and bad example code which encouraged doing the wrong thing. These days - rails has absorbed this lesson so completely that it could be described as a REST server and in some ways leads developers into following best practices for web apps by default.

I really don’t understand what the huge deal is here? This seems as clear cut to me as the built in RequestValidation in ASP.NET or automatically escaping SqlParameters for SQL injection. Sure - if your a power user it can be a pain - but it takes 10 seconds to turn off and then your responsible when your site get p0wn’d.

It’s not like ControllerBase is sealed.

  1. by default - but easily modifiable []
  2. or is that loudest? []

Wordpress 2.3.1

<blog.service.announcement>

I’ve just upgraded from 2.2.x(?) to the latest and greatest. I think everything is back to normal - but if you notice anything unusual - please let me know.

</blog.service.announcement>


Automated deployment == CI for Operations

Deployment is a subject that is near and dear to my heart. I’m almost always the guy who gets to debug production problems, deployment problems and generally wears a developer + infrastructure hat.1

When it comes to web applications - most teams don’t bother automating their deployment processes. This is something that I think is in the process of changing2 - but is not yet a generally accepted practice on a par with TDD, user stories or iterations.

At a minimum - it should be possible to:

  1. Issue a single command which creates a release package for your project. (i.e. built, tested, tagged in subversion and zipped up). For me - this tool/command is called: deploy.cmd and is just a batch file.
  2. Issue another command in your deployment environment which will configure the new release. (i.e. update IIS root, upgrade the database, update connection strings + any other configuration required).

In my current project I’ve built 1. For me 2 does not exist as a single command line (It’s currently a combination of several tools - this should be changed!).

In my next post I’ll outline my very simple solution for this. Just to get the conversation started.

  1. Ten years ago - part of my job was building deployment packages for desktop applications (this is pre MSI). Identifying all the dependencies of an app and scripting them into an install package and testing the deployment was at times almost a full time job. []
  2. there have been a couple of good threads about this on the altnetconf mailing list []

← Before