Tuesday, December 27, 2011

Dynamically Adding a Style Sheet

I've been looking at some of Google's code for their Html5Slides project and like the way they dynamically link in a style sheet:
var el = document.createElement('link');
  el.rel = 'stylesheet';
  el.type = 'text/css';
  el.href = 'http://www.Example.com/...' + 'styles.css';
  document.body.appendChild(el);
I think they code it this way so people using their HTML5 slide template don't have to include a bunch of extra css files, just this one JavaScript file. If people get the file dynamically from Google all the time, Google can change the styles without breaking people's links. It's just one less dependency.

HTML5 is not XML, Time To Get Over It

I feel like Captain Malcom Reynolds who fought for the resistance against the Alliance, but failed and now lives in the shadows of the all powerful Alliance.

I love XML ( ... I know there's 12 step programs for people like me). XML makes it so easy parsing well-structured, schema-verified XML. I applauded the efforts of the W3C to make the next generation HTML to be XHTML2.0 with all the clean syntax.
But the fight for XML championed by W3C.org has failed. WhatWG (re: the Alliance) has won over W3C.org (re: the Browncoats ) and HTML5 is not XML. It's time to get over it.
What does this mean?
1. Well, we shouldn't close void tags (those who can't have nested elements) like "<br />, it should be "<br> (Although some people I respect disagree like Estelle Weyl.) Reading WhatWG docs, I sense they really don't want us to close void tags.

The w3's documentation in section 8.1.2 Elements defines void elements to be

area, base, br, col, command, embed, hr, img, 
input, keygen, link, meta, param, source, track, wbr

2. Attributes do not have to have a value
<input type='checkbox' checked='checked' ...>
This looked a little silly anyway. Now you can have an attribute without a value.
<input type='checkbox' checked ...>

3. Attributes values usually do not have to be quoted
<input type=checkbox ...>

4. We can't process html5 pages with an XML parser. Really in the old days, you could only parse your pages anyway, and any included files from Google or your ad server would probably break your XML anyway.

In the WhatWG FAQ they recommend not using an XML parser on pages, but using an HTML to XML parser.

We fought the Alliance and they won. Come on all you XML-loving Browncoats like me, it's time to move on.

Monday, December 26, 2011

jQuery just makes life too easy

One of my most popular pages on my web site is the history of the world timeline. I've always wanted to make if configurable so people could select just certain topics, like "military" or "science" and then concentrate on the topics they love. I did a little research and jQuery just makes this so easy.

This just takes three steps:
1. Include jQuery. We try to get it from Google, but if Google fails us, we get it from a local cache. I got this method from Paul Irish and his excellent HTML5 Boilerplate.
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
  <script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.2.min.js"><\/script>')</script>

2. At the top of the page in a form we have this scrap of JavaScript which just toggles the visibility of rows which have the class of "military".
  <input type="checkbox" id="military" checked onclick="$('.military').toggle();">
  <label for="military">Military</label>

3. Lastly we add the class "military" to the row with that topic.
  <tr class="military"><td>70</td><td>After 6 month siege the Romans 
under the direction of Titus destroy Jerusalem killing one and a half million Jews.</td></tr>
Now the user can toggle on categories at will.
One of the nice things about this is that the work is all done on the client side, so the server is untaxed and the user has faster response time.

Wednesday, December 21, 2011

Agile Exchange at HomeAway

Agile Austin, a group dedicated to improving software development practices, sponsors an Agile Exchange where developers can visit other companies and hear about their Agile software practices.
Last month's Agile Exchange was with HomeAway and Jack Yang gave a dozen of us a tour of the HomeAway office and a description of their software development framework.
Below are my brief notes:

Initially software was developed in an ad hoc fashion with inconsistent results and a lot of disappointment. Jack ran one team on Agile principles and it was a success. The agile team members were dispersed to other groups to encourage them to adopt agile. It failed to catch on. Two more teams were trained and produced good results with Agile. Slowly other teams started to be interested, but it was a very organic growth. Now all twenty teams are doing Agile, with some of the more advanced doing Lean (Kanban). Software development is more predictable now.
Some teams do twice weekly deployments, others once a month.
Each team implements it's own version of Agile.
Cucumber is used for testing.
Each programmer is expected to do 5 hours of actual "hands on keyboard" programming a day although this varies by project; the rest is spent in meetings and other activities.
BigLever is used for variation management for over 20 brands.
The company, like everyone else, is moving to Git for version control.
Ruby on Rails will be used for the front end of the website, although the front end is expected to be language agnostic - whatever works best for their teams to get the job done well.
Java is used on the back end.
The business people at HomeAway really look at the Business Intelligence reports on how customers use their web sites.
Programmers use Macs.
Rally software is used to handle software issues.
Ruby scripts are created to access the Rally software from scripts.
Problems in Agile are the same - communication with people.

I would encourage developers to attend one of the Agile Exchange sessions; this last one was fun and informative.
Thanks to Brad Bellamy for organizing Agile Exchange.

Monday, November 28, 2011

Shopping for the End Times

I don't know who the Antichrist will be, but while shopping today at Goodwill, I learned his suit is ready.

Wednesday, November 23, 2011

Arduino Boot Camp III


We had our third Arduino Boot Camp on Saturday. I got my second project working - a green light that slowly increases and decreases in intensity. It's not much, but it's a start. And programmers love all our babies, no matter how small.
The source code:
const int redLedPort = 11;
const int greenLedPort = 10;
const int blueLedPort = 9;
int minBrightness = 0;
int maxBrightness = 255;
int deltaBrightness = 10;
int currentBrightness = 0;
int wait = 200; //milliseconds
unsigned long previousMilliseconds = 0;
unsigned long currentMilliseconds = 0;

void setup() {
pinMode(greenLedPort, OUTPUT);
pinMode(redLedPort, OUTPUT);
pinMode(blueLedPort, OUTPUT);
Serial.begin(9600);
Serial.println("Started");
previousMilliseconds = millis();
}
void setColor(int red, int green, int blue) {
Serial.print(red);
Serial.print(", ");
Serial.print(green);
Serial.print(", ");
Serial.println(blue);

analogWrite(redLedPort, red);
analogWrite(greenLedPort, green);
analogWrite(blueLedPort, blue);
}
void loop()
{
currentMilliseconds = millis();
if(currentMilliseconds > (previousMilliseconds + wait)) {
setColor(currentBrightness/2, currentBrightness, currentBrightness/2);
currentBrightness += deltaBrightness;
if(currentBrightness >= (maxBrightness - deltaBrightness) || currentBrightness <= (minBrightness)) { deltaBrightness *= -1; } previousMilliseconds = currentMilliseconds; } }

Monday, November 21, 2011

sp_executesql executes extremely slowly

On a legacy system, we had a query that in query analyzer is very fast, around a millisecond.
But in production it is slower than molasses in winter, up to three seconds.
The table being queried is simple and tall, around 25 million rows, with a clustered index on "bid":
CREATE TABLE [dbo].[BookHistory] (
[bid] [varchar] (64) COLLATE SQL_Latin1_General_CP1_CI_AS  NOT NULL,
[source] [varchar] (16) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[src] [varchar] (32) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,  
[bookCode] [varchar] (32) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[bookStatus] [varchar] (32) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[actionTime] [datetime] NOT NULL 
) ON [PRIMARY]
GO
CREATE CLUSTERED INDEX [BID_INDEX] ON [dbo].[BookHistory] 
(
 [bid] ASC
)
GO
The code is trying to retrieve all the records, typically a dozen, for a particular "bid" which should be faster than greased lightening.
exec sp_executesql N'SELECT bookCode as name,bookStatus,
DATEDIFF(DAY, actionTime, GETDATE()) as days  
FROM BookHistory with(nolock) 
WHERE bid = @bid AND source = @source AND src = @src'
,N'@source nvarchar(5),@bid nvarchar(10),@src nvarchar(4000)'

As the more astute of you can probably see now, the issue is with the data type of "bid". In the database it's a varchar(64), but in the trace it's a nvarchar(4000).
In the C# code the type of the parameter of "bid" was left to the default, which is "nvarchar", so...... SQL server was thinking, "I've got this nifty little index for 'bid', too bad I can't use it since the incoming query is an 'nvarchar', oh well, I guess I'll do a scan and convert all those 'bid' thingys to nvarchar on the fly." That's why the SQL statement was glacial.
For the interim, I patched the SQL statement with a cast, since that can be done without a full deployment, to be
exec sp_executesql N'SELECT bookCode as name,
bookStatus,DATEDIFF(DAY, actionTime, GETDATE()) as days  
FROM BookHistory with(nolock)
 WHERE bid = cast(@bid as varchar(64)) AND source = @source AND src = @src',
N'@source nvarchar(5),@bid nvarchar(10),@src nvarchar(4000)'
And the statement that used to take a few seconds now takes a few milliseconds. Mystery solved.
Postmortem: The clue was that the execution plan was doing a scan instead of using the real index.
Moral: Always set the type of the SQL parameter when using SqlCommand directly.
Moral2: Don't use SqlCommand directly. Use NHibernate or some other ORM so it can figure out all that stuff for you (Although in legacy systems you don't always have that luxury).