WebForms In Retrospect

I’m not much for technology recommendations.  Most technology choices should probably be driven by familiarity and cost.  For instance, Ruby on Rails is easily the most mature web stack out there, but if you’re a Python programmer with no familiarity with Ruby, Flask or Django is likely to be a better choice.  It’s not helped by the fact that a number of “technology change” stories turn out to be stories of replacing bad code in one stack with good code in another.  The rest tend to be stories about reducing running costs by moving off Windows or Heroku.  Most technology choices, especially in the web space, tend to be “good enough”.

There’s one huge exception to this rule: Asp.Net WebForms.  It’s poison for productivity and it’s poison for production.  It took me a long time to accept this.  I had been developing uSwitch.com for nearly four years.  We had developed the majority of the site in WebForms (this was before MVC was even an option).  Except, weirdly, for the most profitable parts.  They were in an unholy mix of ASP, XSLT and C# for the business logic.  Although a pain to work with, I’m no longer convinced it was actually worse than WebForms where it counts.

The Choice That Destroyed a Company

Now, rather than the implementation success stories that glut the internet, let me tell you about a failure.  uSwitch is now a successful project run by ForwardTek.  However, it’s shocking what happened to uSwitch in the last years I was there.  The firm was bought for $366 million in March 2006 with big plans for expansion.  In July 2007, these plans were dust and there were huge layoffs (I was already gone).  Within two years, with no expansion in sight, February 2008 saw the purchaser effectively write down the firm to zero.  Now, the reasons for this are always many and varied (buy me a beer sometime), but a fair proportion of this horrible crash can be ascribed to one project, and a fair proportion of that to its technology choice.  I’m not going to claim that there weren’t bad project management decisions, or that all of the code we delivered was perfect, but choosing WebForms was a mistake, and a big one.

The truth is, I thought the writing was on the wall by the start of 2007.  The project known as the redesign, which began at about the same time as the buyout, had been live for a couple of months.  It was a disaster:

  • It had frozen the entire website for over six months, allowing competitors to eat our lunch.
  • Our flagship energy product was actually slower at the end of it than at the beginning.
  • The new structure wasn’t actually flexible enough to handle the changes we were then asked to implement.
  • It took over 50 developers, including quite a few contractors, about seven months to deliver.  We had to pay contractors to work weekends.  When the project was over, so little leave had been taken the firm had to institute buy-back.  The cost of the project was just plain more than the firm could take.
  • And, worst of all, the uptick in conversion the project promised just plain didn’t happen.

(There is an irony associated with that last point.  The original project proposal a 10% improvement (don’t quote me on the exact figures after all this time).  Some quick-win patches we instituted in February garnered about half of that.  This left us with a large project to gather the other 5%.  With that in mind, it’s not clear the project was worth doing even at the start.  That and the decision that we couldn’t have an inconsistent look on the site really, really hurt us.  I did say there were other factors.)

This post is getting extremely long, but hopefully I’ve got your attention.  I distinctly remember one day in July, the fourth month of this project.  I had just finished a two hour debugging session with another developer on some issue to do with dynamically generated content, one of the many issues that plagues WebForms development, when it occurred to me that after three years of working with the technology, I was far from convinced the productivity benefits of learning it were there at all.  It’s pretty tough accepting that you’ve spent the last three years driving in the wrong direction, but the longer I thought about it, the more solid my opinion became.

Wait, What Has This Got To Do With WebForms?

You’ll note that I haven’t said the code was bad.  It wasn’t.  It had a fair number of automated tests against it, some nice automated health checks in Watir.  A little while after release, it even gained an automated deployment system.

WebForms has a number of things that just make it a horribly inappropriate technology for a dotcom website.

  • It wants to generate all of your HTML.  This is a serious problem if you’re trying to produce a skinnable application.  Yes, I’m fully aware of the skinning technology in WebForms; it’s completely inappropriate for the kind of work dotcoms actually want to do.
  • It generates huge hidden state fields.  This means your programmers end up fighting WebForms every time they need a page to be fast.  Sadly, this is all of the time.
  • It generates large numbers of complex munged IDs.  In addition to making your page slow, it obstructs debugging.
  • The standard model is to post back to the same page and then redirect to the next.  This involves reconstructing the state of the previous page in order to process the events.  This is way too slow at a computational level and involves too many round trips.
  • Specifying your own URLs was generally regarded as deep magic until 2010.  Seriously.

Seriously, there’s no other web stack I can think of that makes it so hard to just deliver some HTML in a form and get a post back.  Now, if you’re a WebForms expert, you’ll be taking a look at the previous list and thinking “but there’s ways around all of these issues”.  You’d be right, but that’s missing the point.  There’s no other web stack that requires expertise to get these things right.  But even if you can crack these issues, your problems are just beginning.

For instance, have you heard of the “off by one” issue?  Let’s say you have a form that changes the number of controls on the page under certain circumstances.  It’s quite easy to get into a situation in which it’s showing the wrong stuff on the page.  If you add a dummy button, pressing it will correct the page.  Debugging issues like this is a nightmare.  Things get worse if you have data driven controls.  While we’re on the subject, I remember watching one developer trying to replicate the functionality of repeater.  Even after decompiling the original code, he couldn’t get it to work with the declarative syntax, leading us to believe that the repeater is actually magic.  The kind of magic that comes out of Neville Longbottom’s wand.  Oh yes, and you’ve got to understand the ASP.NET event model, a construct more complex than Cloud Atlas and significantly less fun to read.

Oh yes, and I haven’t even mentioned theses issues yet:

  • there’s no support for any kind of CSS or JS asset pipeline.  If you want some, you’ll have to roll your own build system.  Rails comes with this stuff baked in.
  • testing?  You have two options: Selenium or clicking on a web browser.

WebForms: Just Say No

WebForms is productivity poison; in fact, it’s so bad it’s probably not just hurt little firms like mine, it’s a technology (again, part of a larger picture) that killed Microsoft as a cloud platform.  Someone reading this undoubtedly believes WebForms is better in 4.5.  It probably is, but it remains the wrong idea in the first place.  It’s quite hard to fix that.  I don’t write that many websites these days, but even for internal stuff I never, ever, touch WebForms. 

Of course, it’s worth pointing out there are other sites on the internet that used WebForms at the same time.  Not many, but it’s worth considering that the two I can think of are Orkut and MySpace.  They were both technical disasters and unable to match the pace of a firm like Facebook that had delivered its entire functionality in PHP, a technology that’s closest relative in the Microsoft world is classic ASP. 

Technorati Tags: ,,

New Coding Standard: Don’t Be A Jerk

Unlike many, I do actually believe that code aesthetics matter.  On the other hand, I’ll also admit to being as much of an arrogant elitist about code as I am about music.  This is why I like CoffeeScript and Anton Webern.  2 spaces vs 4 spaces?  Spaces vs Tabs?  Indent case statements within a switch?  I’ve got an opinion.  The fact remains that it’s much more important that everyone uses the same conventions than exactly what that convention is.  So I keep my stylistic tics to myself and listen to Polly Harvey on headphones where the noise doesn’t frighten my wife.

One funny thing is that people don’t seem to extend this logic out of their team or project.  There’s usually a dominant answer to these stylistic questions for whatever programming language you’re using.  Don’t like it?  Write your own programming language while listening to Laurie Anderson (or whatever you’re into).

For the most part, just doing what everyone else does answers most aesthetic coding standards questions that may arise.  It reduces stupid fatiguing stylistic discontinuities and lets you get on with actually reading the code.

Standards Creep

So, do you need a coding standards document?  Well, that’s really going to depend on how many violations you’re seeing.  Bear in mind that beginners aren’t going to perceive the patterns as easily as the competent.  However, here’s where the spectre of best practices rears its head again.  Once you start to document what people’s code looks like, it’s very tempting to move into what their code actually does.  Again, you end up creating a document that, at best, annoys your best programmers.  At worst, it creates a culture in which your best programmers are regarded as loose cannons and mediocrity is regarded as the highest goal.

So here’s my own contribution to “best practice” coding for readability: don’t be a jerk.  At the risk of being prescriptive and contradicting everything I’ve already said:

  • Don’t write bad confusing code and then a long comment explaining it.  Write better code.
  • It’s OK to have one character identifiers with tight scope.  Especially if you’re going to use it repeatedly.
  • When have wide scope, write words out in full rather than using a contraction, unless it’s a very well understood one.  Searching for stuff is hard when you have to guess.
  • And needless to say, spell things correctly.  If you’re not sure, type the word into Google.  I do.
  • If jargon doesn’t make things more concise, don’t use it.  That said, use commonly accepted terminology.
  • Don’t reuse identifiers to mean different things at different times.
  • Don’t be vague.  isActive and shouldBeActive are different concepts.  It’s amazing how many people just call the identifier “active” is both circumstances.*
  • Identify things by what you’re doing with them, not what they are.

Unless, of course, following these rules to the letter would make you act like a jerk.

*To get to nitty-gritty style point, the standard in C# is that you would identity “is active” with “active”.  Still, don’t ever identity “should be active” with “active”.  Equally, if you’re in lisp, “should be active” shouldn’t be named “active?”.

Code Fatigue: Performance Edition

It’s a truth universally acknowledged that sometimes you have to compromise your coding practices in order to eke out more performance.  However, I think it’s not appreciated how often just writing code well actually results in better performance.  For instance, I’ve seen more systems that would be faster with a more normalized schema in my career than systems that were crying out for denormalization*,

Let’s see how the two implementations of triangular number calculation from the last post compare in terms of performance.  In my unscientific testings:

  • The naive recursive algorithm calculates (t1 25000) in about 3 milliseconds.
  • The threaded sequence processing algorithm calculates (t2 25000) in about 5 milliseconds.

Well, it’s not looking good for readable code right now.  Although the fact that t1 can’t calculate (t1 26000) without blowing the stack indicates that it has bigger problems. 

Jean-Louis suggests an alternative: the tail-recursive implementation**

(defn t3 [n]
(loop [n n result 0] (if (zero? n) result (recur (dec n) (+ result n)))))

Let’s compare this to the original r1 in terms of readabilty:

  • We’re now using clojure functions zero? and dec, which requires a bit more clojure knowledge but reduces noise (and hence code fatigue)
  • We’re using a well-known accumulator pattern.  This doesn’t impose a cost on anyone familiar with how recur is used.
  • We’re still dealing with a branch and a recursive statement that interact.  On the other hand, the recur expression now only goes two deep.
  • We’ve got to keep track of positional parameters.  Not really 

So, in terms of readability, it’s better than t1 but we’d still prefer t2.  However, the figures speak for themselves: t2 can calculate (t3 25000) typically in about 1.5 milliseconds.  As I said at the start, sometimes the most elegant solution isn’t the fastest.  I doubt I could squeeze much more out of the sequence processing approach, although in the real world the application of fork-join reducers may outperform any explicit TCO solution.  However, is it possible to create a much faster implementation if we simply allow ourselves to assume more knowledge?  This time, though, we need more knowledge of our business domain (mathematics) than of our programming environment.

(defn t4 [n] (/ (* n (inc n)) 2))
(defn t4threaded [n] (-> (inc n) (* n) (/ 2)))

That calculates (t4 25000) in 0.1 milliseconds.  Needless to say, it can handle numbers much larger than any of the other implementations without breaking a sweat.  From a readability perspective, again, it’s excellent providing you have the required background knowledge.  The last example may feel like cheating, but the truth is that solutions like that are pretty common in the real world: insight into your problem area can radically simplify your code and improve your performance.

SIDEBAR:  I’m afraid I’m having trouble with the comment system on this site.  My apologies if this is affecting you (it isn’t affecting everyone).  It’s certainly affecting my ability to respond in anything other than a full post.

*although there is a special circle of Hell reserved for key-value pair implementations on SQL

**I should point out this differs stylistically from Jean-Louis’ original implementation, but the performance characteristics are the same.

Code Fatigue

Let’s say you needed to calculate the triangular number of n.  The triangular number of 4 is (1+2+3+4).  Which of the following implementations would you consider the more readable?

(defn t1 [n] 
(if (= n 0)
(+ n (t1 (- n 1)))))
(defn t2 [n] 
(->> (inc n)
(range 0)
(reduce +)))

If you answered t1, I’m willing to bet that by “readable” you meant “requires less experience to read”.  I’m going to argue that this is a bad way of evaluate readability.  Let’s assume for the moment that your command of Clojure is perfect, what are the challenges to comprehension then?

  • The first has a recursive step
  • The first has a branch
  • The first contains an expression that goes three deep.

Worse, each of these interact, meaning you have to hold them in your head all at once.  If you’re trying to solve a problem significantly harder than computing triangular numbers, sticking to “basic” code results in significant more lines of code and significantly more of these things that you have to simultaneously track.  Whilst each individual component is easy enough to parse, the overall effect is fatiguing.  This is bad news for humans, because they’re bad at maintaining mental function whilst processing large numbers of minor items.

Favour High Level Code

Let’s now assume that everyone has excellent command of the language we’re using.  What impedes readability in these circumstances?

  • The longer you need to track the value of a variable, the harder it is to understand.*
  • The more levels of control flow you need to track, the harder it is to understand.
  • The less code you can see on your screen at once, the harder the code is to understand.
  • The more times you see the same code expressions repeated with possible minor variations, the harder it is to understand.

Writing basic code in any language favours the comprehensibility of a single over the comprehensibility of the whole.  Not only that, but since each construct contains the possibility of error, using a basic style is much more likely to result in bugs.  A much better set of guidelines for writing readable code would be:

  • Use values close to their definition.  Make it clear that they are out of scope after that point.
  • Favour standardised control flow constructs such as reduce in Clojure and LINQ in C# over writing everything in terms of branches, loops and recursion.
  • Favour concise code over verbose code
  • Aggressively eliminate common sub-expressions

And next time trying to evaluate code readability, take the effects of fatigue more seriously and don’t worry as much about trying to compensate for lack of experience.

Technorati Tags: ,

*If it’s global and mutable, your chances of tracking it are nil unless you’re extremely disciplined.  Action at a distance is very hard to read and extremely error prone.

Clojure: Stages of Enlightenment.

I’ve tentatively identified seven stages of enlightenment in Clojure sequence processing.

  • Uses recursion
  • Uses recur
  • Uses loop/recur
  • Uses Clojure API functions such as filter and map
  • Uses reduce
  • Uses all Clojure API functions and understands implications.  At this point you can consider yourself a 4clojure 1st Dan.
  • Uses clojure.set as well

There may be higher levels, but Jay Fields hasn’t blogged about them yet.

Technorati Tags:

Sexism in IT

Let’s talk about Larry.  If you’re lucky, Larry isn’t in your team.  But he’s in a team you work with.  You find yourself trying to avoid dealing with that team because there’s a good chance you’re going to end up working with Larry.  Larry is a pain in the neck.  It’s not that he’s incompetent, he just doesn’t seem to care.  Nothing he puts together works, and when it does work it requires settings he forgot to tell you about.  Larry is the bottom tier of men in technology.

And yes, he’s a man.  90% of people in technology are.  What would happen if it was only 50%?  Well, frankly, Larry would be out of a job.  In his place would be a better woman.  Don’t get me wrong, there’s plenty of women out there as useless as Larry, but in a 50/50 world, they wouldn’t have a place in technology either.

We Are The 50%

Let’s talk about how we got to this point.  Women cannot be considered an “educationally disadvantaged minority”*, so we don’t have that excuse.  Computing was 90% male in 1967, when female participation in the workforce was much lower than it was today.   That was after a sexist purge of women programmers in the 1950s.  The gender ratio of computer science graduates in 1984 was 60/40.  So we’ve slid back from the 50/50 dream really quite dramatically.

It’s hard to avoid the conclusion that we (men) are fostering an environment that is subtly hostile to women.  I could spend all day adding to that particular list of links.  We need to stop.  Yes, we, meaning you right there and me right here.  I’ll be honest, it’s hard not to have a sexism bias when everyone you work with is a man.  That means the job is harder, not that it’s meaningless.

Full disclosure: I’ll admit that I’ve always been aware of this issue, but haven’t regarded it as my problem until the birth of my daughter.  It shifted my perspective quite dramatically.  I don’t aspire for her to follow in her father’s footsteps, but it offends me that chances could be denied her because of stupid rubbish like implicit sexism.  We can do better.  Moreover, we need to stop thinking of this as a problem that women have and we don’t.  The exclusion of women in the tech workforce affects us all and we’ve all got something to gain: better co-workers than Larry.  They probably won’t be as brilliant as Grace Hopper but, frankly, neither are we.

Unless, of course, you consider yourself to be in the bottom half of male programmers by ability.  Then you probably want to be as sexist and unwelcoming as possible.

*The observant will notice they’re not even a minority.

Post Script:  If you haven’t read Reg Braithwaite’s article about his mother, you really should.

Technorati Tags: