Friday, January 23, 2009

E=MO2

"When I was a kid, I used to pray every night for a new bicycle. Then I realized that the Lord, in his wisdom, doesn't work that way. So I just stole one and asked him to forgive me!"

http://en.wikiquote.org/wiki/Emo_Philips

Wednesday, January 21, 2009

Idiocracy

"What happened to 30%? Lies!" shouted customer Gabriel Ifrah, 52, at the Circuit City on La Cienega Boulevard in Los Angeles on Monday, where most items were priced at 10% off.

...

"I've been waiting in line for half an hour each day based on employees' promises that prices could come down, but they haven't," said McGinness, a TV commercial producer from Los Angeles. "It's very disappointing."

http://www.chicagotribune.com/business/la-fi-liquidation20-2009jan20,0,497931.story

Is it me, or does this really sound insanely stupid?

By the way, if you need any proof that recession has not REALLY started yet, this is it. I mean, people waiting in line for cheap electronics?

DIY: Enriching uranium

http://www.amazon.com/Uranium-Ore/dp/B000796XXM/

Monday, January 19, 2009

Redirection with query parameters bug in IE

I really, really, really hate this browser.

Today's bug: if you do meta redirection to the same path with different query parameters, it ignores and passes the same parameters that the original page had.

Suppose your page, accessed as http://foo.com/bar?baz=1&bleh=2 has the following redirect tag: <meta http-equiv="Refresh" content="5;http://foo.com/bar?baz=1" />.

The request the server gets? It's the original http://foo.com/bar?baz=1&bleh=2.

I really, really, really hate this browser...

Sunday, January 18, 2009

More people speak on leaving Google

An interesting compendium of opinions from people who worked at Google. A lot of negativity which is perhaps natural for the "former" team.

http://www.techcrunch.com/2009/01/18/why-google-employees-quit/

Still, I had a feeling that a lot of it is driven by the misunderstanding of Google psyche.

Google is not your typical software company - it is an advertising company. It is very much in the nature of advertising to tell the truth in a way that makes the advertiser looks as positive as possible.

It reflects in a way that the software is written, it reflects in compensation.

For example, when Microsoft tells you that they will pay for X (for example, moving you to/from a locale) they mean that the net economic effect to you will be X. Which means grossing up the taxes. The fact that they had to pay more is an implementation detail that is hidden from the user.

When Google tells you that they will pay for X (for example, a subsidy for buying a Prius), they mean that the net economic effect to them would be X. What you will get is really .6X - after taxes.

And so it goes ;-)... People who do not read the small script are bound to be disappointed.

It's not at all that the company is evil - far from it. Google tries to work very hard to appeal to the employees. It's just the advertising nature that show no matter what you do :-).

You have to read it all to fully appreciate the depth of stupidity of this guy...

http://www.windsorstar.com/Leamington+loses+Nigerian+scam/1173799/story.html

Thursday, January 15, 2009

Your taxes at work, Part III

The messages on Arab TV teaching children the "art of martyrdom" (AKA becoming the suicide bombers) has been dutifully covered by US media, and have become the part of the lore on why US should support Israel.

Here's the other side of the medal.

http://www.mystudydate.com/pg/blog/Martini/read/1462/message-from-israeli-children-to-arabs-die

Let's hope it is covered with an equal zeal.

Wednesday, January 14, 2009

Free code! Send email from your Exchage account - programmatically!

Note: This requires Exchange 2007.

In Visual Studio 2008, create a Visual C# Console application project. Call it SendMail for simplicity.

References->Add Service Reference->Advanced->Add Web Reference

In URL box, put a server name on which you have an account. The format of the URL should be something like this https://mail.microsoft.com/EWS/Exchange.asmx


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;

using SendMail.com.microsoft.mail;

namespace SendMail
{
class Program
{
/// <summary>
/// Shows usage.
/// </summary>
private static void DisplayUsage()
{
Console.WriteLine("Usage:\n");
Console.WriteLine(" sendmail [--to emailaddress]+\n");
Console.WriteLine(" [--cc emailaddress]*\n");
Console.WriteLine(" [--from emailaddress]?\n");
Console.WriteLine(" [--subject textlineinquotes]?\n");
Console.WriteLine(" [--user username]\n");
Console.WriteLine(" [--domain domain]?\n");
Console.WriteLine(" [--password password]\n");
Console.WriteLine(" [--ews exchangewebserviceurl]\n");
Console.WriteLine(" Text to send in email\n");
Console.WriteLine("+ = one or more, * = zero or more, ? = zero or one");
Console.WriteLine("Note: username and password are required.");
}

/// <summary>
/// Processes command line args, sends one email.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
string user = null;
string password = null;
string domain = null;
string ews = null;
List<string> to = new List<string>();
List<string> cc = new List<string>();
string from = null;
string subject = null;
StringBuilder text = new StringBuilder();
for (int i = 0; i < args.Length; ++i)
{
if (i < args.Length - 1)
{
if (args[i].Equals(
"--user", StringComparison.InvariantCultureIgnoreCase))
{
user = args[++i];
continue;
}
if (args[i].Equals(
"--domain", StringComparison.InvariantCultureIgnoreCase))
{
domain = args[++i];
continue;
}
if (args[i].Equals(
"--password", StringComparison.InvariantCultureIgnoreCase))
{
password = args[++i];
continue;
}
if (args[i].Equals(
"--ews", StringComparison.InvariantCultureIgnoreCase))
{
ews = args[++i];
continue;
}
if (args[i].Equals(
"--to", StringComparison.InvariantCultureIgnoreCase))
{
to.Add(args[++i]);
continue;
}
if (args[i].Equals(
"--cc", StringComparison.InvariantCultureIgnoreCase))
{
cc.Add(args[++i]);
continue;
}
if (args[i].Equals(
"--from", StringComparison.InvariantCultureIgnoreCase))
{
from = args[++i];
continue;
}
if (args[i].Equals(
"--subject", StringComparison.InvariantCultureIgnoreCase))
{
subject = args[++i];
continue;
}
}

if (text.Length > 0)
text.Append(" ");

text.Append(args[i]);
}

if (to.Count == 0 || user == null || password == null || ews == null)
{
DisplayUsage();
return;
}

ExchangeServiceBinding binding = new ExchangeServiceBinding();
binding.Credentials = new NetworkCredential(user, password, domain);
binding.Url = ews;

DistinguishedFolderIdType folder = new DistinguishedFolderIdType();
folder.Id = DistinguishedFolderIdNameType.sentitems;

TargetFolderIdType targetFolder = new TargetFolderIdType();
targetFolder.Item = folder;

CreateItemType createItem = new CreateItemType();
createItem.MessageDisposition = MessageDispositionType.SendAndSaveCopy;
createItem.MessageDispositionSpecified = true;
createItem.SavedItemFolderId = targetFolder;

MessageType message = new MessageType();

List<EmailAddressType> recipients = new List<EmailAddressType>();
foreach (string s in to)
{
EmailAddressType recipient = new EmailAddressType();
recipient.EmailAddress = s;
recipients.Add(recipient);
}

message.ToRecipients = recipients.ToArray();

if (cc.Count > 0)
{
recipients = new List<EmailAddressType>();
foreach (string s in cc)
{
EmailAddressType recipient = new EmailAddressType();
recipient.EmailAddress = s;
recipients.Add(recipient);
}

message.CcRecipients = recipients.ToArray();
}

if (from != null)
{
message.From = new SingleRecipientType();
message.From.Item = new EmailAddressType();
message.From.Item.EmailAddress = from;
}

message.Subject = subject;
message.Sensitivity = SensitivityChoicesType.Normal;

message.Body = new BodyType();
message.Body.BodyType1 = BodyTypeType.Text;
message.Body.Value = text.ToString();

createItem.Items = new NonEmptyArrayOfAllItemsType();
createItem.Items.Items = new MessageType[1];
createItem.Items.Items[0] = message;

CreateItemResponseType response = binding.CreateItem(createItem);
foreach (ResponseMessageType r in response.ResponseMessages.Items)
{
if (r.ResponseClass == ResponseClassType.Success)
{
Console.WriteLine("Message sent.");
}
else
{
Console.WriteLine("Failed to send the message. ");
Console.WriteLine(r.MessageText);
}
}
}
}
}

Tuesday, January 13, 2009

Diamonds are forever?

Yes, but only because you can't sell them!

Just read the absolutely captivating article in The Atlantic Monthly about the value of diamonds as an investment tool. The article http://www.theatlantic.com/doc/print/198202/diamond was written in 1982, but a casual Google search on "historical proces of diamonds" quickly confirms that all the basic numbers are still true, as well as its central premise, which is that diamonds is just about a notch below fertilizer as far as the quality of investment goes.

This is what they say, in short...

First, the diamonds are not actually all that rare. They were, before the beginning of the 20th century. But the since discovered reserves in Africa, Russia, and Australia are pretty big. What's more, the amount of diamonds that is held privately (extracted from these mines over the last 100 years) is astounding.

The diamond market is wholly owned by DeBeers. They fix the price by limiting production, marketing the diamonds as essentially a requirement for marriage in US and Japan (and thus attaching the "sentimental value" to them which is well beyond the real value).

Most importantly, they limit the liquidity of the market, which prevents the influx of second-hand diamonds which would certainly drop the price.

This is where it becomes really fun. How do you prevent the private diamonds from being sold? The scheme is brilliant: if you make a spread between retail and wholesale price huge, people - who paid the retail price when they acquired the stones - would find it very difficult to sell them, because when selling, they would be paying the wholesale price.

And this wholesale price is 2-3 times less than the retail.

In addition, DeBeers makes it hard for dealers to pay even the wholesale price: when dealers get their stones from DeBeers, they get them on consignment: they pay DeBeers when the diamonds are sold. There is no capital on their own involved. But if the dealer is buying the stone from the street, they have to put up their own capital.

The net result? That stone for which you paid $25000 at Costco? If you can get $7000 back for it, consider yourself lucky!

And based on the historical trends (http://minerals.usgs.gov/ds/2005/140/diamondindustrial.pdf), it will not be until the Universe collapses and is reborn in the next Big Bang that the prices will get to the levels where the investment will recoup itself :-).

Saturday, January 10, 2009

Malevich - a new code review system

We were using it in our team for the last week, with pretty good success. Over 30 code reviews went through it. A couple of bugs were exposed, and quickly fixed. Most importantly, the central premise held - making comments is easy, and people make a lot of them - many more than before!

So today I finally added the last thing I wanted before taking the project public - support for TFS. It seems to be working now, although I have not done a lot of testing with it.

The code is now on CodePlex!

You can read the history of the project here:

  http://1-800-magic.blogspot.com/2008/12/one-google-thing-i-miss-most.html

  http://1-800-magic.blogspot.com/2009/01/malevich-introduction.html


Or, you can jump in and install it from here:

  http://www.codeplex.com/Malevich.

Happy reviewing!

Thursday, January 8, 2009

Better than Vista!

http://xkcd.com/528/

:-)

[Proudly running Windows Server 2008 on my laptop :-)]

Wednesday, January 7, 2009

End of an era, part II

Within two month of the closing of print publication of the PC Magazine, the bell is now tolling for Dr. Dobb's...

http://ztrek.blogspot.com/2008/12/dr-dobbs-soon-to-become-monthly-section.html

Tuesday, January 6, 2009

Malevich, an introduction

For the last several weeks I was working on a code review system. I got sucked into it gradually: similar software that I used at Google took Guido van Rossum - a much better developer than me - almost a year to implement as his starter project. You can read about it here: http://code.google.com/p/rietveld/.

So I didn't expect that I would be able to do it, at least not in a reasonable time that is left from work.

But the team needed a code review system, and I was playing with ASP.NET. At some point, I was wondering how hard it is - really - to display a diff view of a file. Two hours later I had a working prototype. A localized success.

This got me thinking. Displaying the diff file must be about the hardest part of the system, isn't it? But what about entering the comments? It turned out that I did not completely forget the JavaScript yet, and an hour or so later I was able to click on my ASP.NET-generated page to enter the comments in a text box, and have them show permanently on a page after clicking a Submit button, or disappear on Cancel or Remove.



Ok, so far so good. Now I needed

  1. Free time.

  2. A database.

  3. A source control interface.



The first one was solved quickly - the team has finished our first milestone, a lot of people went on their Christmas vacations, and Seattle was thoroughly snowed in, all at the same time. So the evenings had become much longer. There was at a very minimum an opportunity to take the project further.

One big problem is that I am a database noob. I used SQL Server once before, and not in production environment, but rather for another pet project of mine - a status reporting application for the team. I did learn how to create tables, stored procedures, and security objects back then, but I have no idea how good (or, rather, how bad) my SQL really is.

Luckily, I did discover the database extension for Visual Studio 2008 in my previous run-in with SQL Server. It is called Visual Studio 2008 Database Edition GDR (German Democratic Republic? I have no idea what GDR is), and you can download it from here: http://www.microsoft.com/downloads/details.aspx?FamilyID=bb3ad767-5f69-4db9-b1c9-8f55759846ed&displaylang=en.

The really nice feature of this GDR thing is that it makes updating the database really, really easy: you modify T-SQL that creates the database objects, and hit Deploy, after which it figures out what NEEDS to be changed, and changes the database accordingly, without destroying the database contents. It even does data conversions automagically!

The downside of editing database code inside GDR vs. SQL Server Management Studio is that Intellisense does not work.

The database took some time - mostly T-SQL in the stored procedures. But eventually it worked, and I needed to start storing real data in it.

Most Microsoft teams use Source Depot - a Perforce derivative - as their source control system. I don't know if Perforce has an API today. Source Depot was snapped from Perforce a while ago, and the API available for it is beyond horrific. Really, truly terrible. A command is issued by calling a function with p4 command line, and you get back semi-parsed data. Except some data gets dumped into standard output anyway.

So I ended up just running the command line parameter, capturing the output and parsing it.

By the way, before I forget, here's a way to capture a process output in C# that actually works (just reading standard handles blocks forever if there is a lot of output on both stdout and stderr channels):

private delegate string StringDelegate();

public static string ReadProcessOutput(Process proc,
bool eatFirstLine, ref string errorMessage)
{
StringDelegate outputStreamAsyncReader =
new StringDelegate(proc.StandardOutput.ReadToEnd);
StringDelegate errorStreamAsyncReader =
new StringDelegate(proc.StandardError.ReadToEnd);
IAsyncResult outAsyncResult =
outputStreamAsyncReader.BeginInvoke(null, null);
IAsyncResult errAsyncResult =
errorStreamAsyncReader.BeginInvoke(null, null);

// WaitHandle.WaitAll does not work in STA.
if (Thread.CurrentThread.GetApartmentState() ==
ApartmentState.STA)
{
while (!(outAsyncResult.IsCompleted &&
errAsyncResult.IsCompleted))
Thread.Sleep(500);
}
else
{
WaitHandle[] handles = {
outAsyncResult.AsyncWaitHandle,
errAsyncResult.AsyncWaitHandle
};
if (!WaitHandle.WaitAll(handles))
{
Console.WriteLine("Execution aborted!");
return null;
}
}

string results = outputStreamAsyncReader.EndInvoke(
outAsyncResult);
errorMessage = errorStreamAsyncReader.EndInvoke(
errAsyncResult);

proc.WaitForExit();

return results;
}
Anyway, dealing with the source control turned out to be the nastiest part of the whole project. But in the end, it finally worked, and I could parse the change descriptions, get file versions, and so on.


At this point, I was very, very close. Now I needed a name.

Google code review system was called Mondrian - for Piet Mondrian, an early XX century Dutch painter (http://en.wikipedia.org/wiki/Piet_Mondrian). So Malevich (http://en.wikipedia.org/wiki/Kazimir_Malevich) - a Russian painter from approximately the same era was a logical choice. It also had an added benefit: his most famous painting was the "Black Square". Here it is:


I love "Black Square" because, being really, really, really bad at anything that involves any sort of art, and doubly bad with painting, images, and graphical design (as you can see by looking at the screen shots) this is the one image that even I can reproduce. So if this software project will ever need an icon, or a logo, or any sort of graphical representation, I revel in knowing that I will be able to do it :-).

Anyway, after the name was picked, two other things remained - a program to upload files, and finishing the web site.

I've got to say - I LOVE ASP.NET. I have no idea how good it is in terms of performance, but man it is easy to develop on! Very logical API, easy to learn and use, and Intellisense absolutely rocks! Without much previous experience, I was able to put out roughly 2000 lines of code that implemented the web site and web service to talk to JavaScript code in the browser in perhaps a couple of weekends.



So where are we now? The code is deployed, I demoed it to the team, and a few reviews had flown through it. A couple of bugs found and fixed within minutes, complete with redeployment of the app. I changed the database to accomodate - in the future - the addition of TFS. I've added perforce support, this took less than one evening.



All in all, I've spent less than 80 hours working on it, which I think is not bad. I can say that Visual Studio, SQL, LINQ and ASP.NET mattered a lot - there's no way in hell I could produce anything even close to it even in twice the time without these tools. Windows is not free, but man it makes developers productive :-).

I've uploaded Malevich to CodePlex (http://www.codeplex.com), but have not published the project yet - I think I will add TFS support before I do. The rule on CodePlex is that it must be published within a month, so by the 1st of February it has to be up, or it will get deleted. Check back before then for the link...

Update: It's been published now: http://www.codeplex.com/Malevich. And we have successfully used it for a months and a half now on my team for more than 200 reviews, 2000 files, and 3000 comments. Give it a whirl!

Monday, January 5, 2009

A slideshow

Today we were having a review of M0 results with our product unit manager - feature teams talking about the design work they did over the last couple of months that resulted in current implementation plans.

Cool stuff.

For a change we get the devs and testers, as opposed to PMs, to talk - to make sure they have high-level view of how parts of the overall project fits together.

Big room, about 30-40 people showed up. Overhead projector, all the standard meeting/team review attributes.

I am giving the introduction: where we are at the macro level, what went well, what didn't go so well, the schedule, the resource allocations.

Connect my laptop, fire the Powerpoint, and start talking. All in all, about 15 minutes, maybe a bit less. Not so many slides. But slides nevertheless.

The next presenter is a dev on one of our features. He is to use my laptop, so I am handing the microphone over and heading to my seat when he tells me that he needs the projection.

Turns out I was showing slides with the projector off. For 15 minutes. Lost, I look around. "Did you not see my slides?" -- "You had slides???" Homeric laughter and general hilarity ensues...

True story.

Sunday, January 4, 2009

More Michael Lewis

"A lot has been said and written, for instance, about the corrupting effects on Wall Street of gigantic bonuses. What happened inside the major Wall Street firms, though, was more deeply unsettling than greedy people lusting for big checks: leaders of public corporations, especially financial corporations, are as good as required to lead for the short term.

Richard Fuld, the former chief executive of Lehman Brothers, E. Stanley O’Neal, the former chief executive of Merrill Lynch, and Charles O. Prince III, Citigroup’s chief executive, may have paid themselves humongous sums of money at the end of each year, as a result of the bond market bonanza. But if any one of them had set himself up as a whistleblower — had stood up and said “this business is irresponsible and we are not going to participate in it” — he would probably have been fired. Not immediately, perhaps. But a few quarters of earnings that lagged behind those of every other Wall Street firm would invite outrage from subordinates, who would flee for other, less responsible firms, and from shareholders, who would call for his resignation. Eventually he’d be replaced by someone willing to make money from the credit bubble."

http://www.nytimes.com/2009/01/04/opinion/04lewiseinhorn.html?_r=1&pagewanted=1

Saturday, January 3, 2009

Your taxes at work, part II

http://www.liveleak.com/view?i=3b3_1230864719

I hate it when the mass media censors so called "disturbing" videos and photographs of casualties resulting from from military attacks. People who are disturbed by the actions of the army which they support sent by the president they elected should not be allowed to vote...