On a recent airplane flight, I happened to catch the movie Groundhog Day. Again.
If you aren't familiar with this classic film, the premise is simple: Bill Murray, somehow, gets stuck reliving the same day over and over.
It's been at least 5 years since I've seen Groundhog Day. I don't know if it's my advanced age, or what, but it really struck me on this particular viewing: this is no comedy. There's a veneer of broad comedy, yes, but lurking just under that veneer is a deep, dark existential conundrum.
It might be amusing to relive the same day a few times, maybe even a few dozen times. But an entire year of the same day -- an entire decade of the same day -- everything happening in precisely, exactly the same way? My back of the envelope calculation easily ran to a decade. But I was wrong. The director, Harold Ramis thinks it was actually 30 or 40 years.
I think the 10-year estimate is too short. It takes at least 10 years to get good at anything, and alloting for the down time and misguided years [Phil] spent, it had to be more like 30 or 40 years [spent reliving the same day].
We only see bits and pieces of the full experience in the movie, but this time my mind began filling in the gaps. Repeating the same day for decades plays to our secret collective fear that our lives are irrelevant and ultimately pointless. None of our actions -- even suicide, in endless grisly permutations -- ever change anything. What's the point? Why bother? How many of us are trapped in here, and how can we escape?
This is some dark, scary stuff when you really think about it.
You want a prediction about the weather, you're asking the wrong Phil.I'll give you a winter prediction.
It's gonna be cold,
it's gonna be gray,
and it's gonna last you for the rest of your life.
Comedy, my ass. I wanted to cry.
But there is a way out: redemption through repetition. If you have to watch Groundhog Day a few times to appreciate it, you're not alone. Indeed, that seems to be the whole point. Just ask Roger Ebert:
"Groundhog Day" is a film that finds its note and purpose so precisely that its genius may not be immediately noticeable. It unfolds so inevitably, is so entertaining, so apparently effortless, that you have to stand back and slap yourself before you see how good it really is.Certainly I underrated it in my original review; I enjoyed it so easily that I was seduced into cheerful moderation. But there are a few films, and this is one of them, that burrow into our memories and become reference points. When you find yourself needing the phrase This is like "Groundhog Day" to explain how you feel, a movie has accomplished something.
There's something delightfully Ouroboros about the epiphanies and layered revelations in repeated viewings of a movie that is itself about (nearly) endless repetition.
Which, naturally, brings me to A/B testing. That's what Phil spends most of those thirty years doing. He spends it pursuing a woman, technically, but it's how he does it that is interesting:
Rita: This whole day has just been one long setup.Phil: It hasn't.
Rita: And I hate fudge!
Phil: [making a mental list] No white chocolate. No fudge.
Rita: What are you doing? Are you making some kind of list? Did you call my friends and ask what I like and what I don't like? Is this what love is for you?
Phil: This is real. This is love.
Rita: Stop saying that! You must be crazy.
Phil doesn't just go on one date with Rita, he goes on thousands of dates. During each date, he makes note of what she likes and responds to, and drops everything she doesn't. At the end he arrives at -- quite literally -- the perfect date. Everything that happens is the most ideal, most desirable version of all possible outcomes on that date on that particular day. Such are the luxuries afforded to a man repeating the same day forever.
This is the purest form of A/B testing imaginable. Given two choices, pick the one that "wins", and keep repeating this ad infinitum until you arrive at the ultimate, most scientifically desirable choice. Your marketing weasels would probably collapse in an ecstatic, religious fervor if they could achieve anything even remotely close to the level of perfect A/B testing depicted in Groundhog Day.
But at the end of this perfect date, something impossible happens: Rita rejects Phil.
Phil wasn't making these choices because he honestly believed in them. He was making these choices because he wanted a specific outcome -- winning over Rita -- and the experimental data told him which path he should take. Although the date was technically perfect, it didn't ring true to Rita, and that made all the difference.
That's the problem with A/B testing. It's empty. It has no feeling, no empathy, and at worst, it's dishonest. As my friend Nathan Bowers said:
A/B testing is like sandpaper. You can use it to smooth out details, but you can't actually create anything with it.
The next time you reach for A/B testing tools, remember what happened to Phil. You can achieve a shallow local maximum with A/B testing -- but you'll never win hearts and minds. If you, or anyone on your team, is still having trouble figuring that out, well, the solution is simple.
Just watch Groundhog Day again.
| [advertisement] JIRA Studio - Hosted software development suite. Build better software. Faster. Free trial » |
Remember that Scene in Star Trek IV where Scotty tried to use a Mac Plus?
Using a mouse or keyboard to control a computer? Don't be silly. In the future, clearly there's only one way computers will be controlled: by speaking to them.
There's only one teeny-tiny problem with this magical future world of computers we control with our voices.
It doesn't work.
Despite ridiculous, order of magnitude increases in computing power over the last decade, we can't figure out how to get speech recognition accuracy above 80% -- when the baseline human voice transcription accuracy rate is anywhere from 96% to 98%!
In 2001 recognition accuracy topped out at 80%, far short of HAL-like levels of comprehension. Adding data or computing power made no difference. Researchers at Carnegie Mellon University checked again in 2006 and found the situation unchanged. With human discrimination as high as 98%, the unclosed gap left little basis for conversation. But sticking to a few topics, like numbers, helped. Saying “one” into the phone works about as well as pressing a button, approaching 100% accuracy. But loosen the vocabulary constraint and recognition begins to drift, turning to vertigo in the wide-open vastness of linguistic space.
As Robert Fortner explained in Rest in Peas: The Unrecognized Death of Speech Recognition, after all these years, we're desperately far away from any sort of universal speech recognition that's useful or practical.
Now, we do have to clarify that we're talking about universal recognition: saying anything to a computer, and having it reliably convert that into a valid, accurate text representation. When you constrain the voice input to a more limited vocabulary -- say, just numbers, or only the names that happen to be in your telephone's address book -- it's not unreasonable to expect a high level of accuracy. I tend to think of this as "voice control" rather than "voice recognition".
Still, I think we're avoiding the real question: is voice control, even hypothetically perfect voice control, more effective than the lower tech alternatives? In my experience, speech is one of the least effective, inefficient forms of communicating with other human beings. By that, I mean ...
I am by no means against talking with my fellow human beings. I have a very deep respect for those rare few who are great communicators in the challenging medium of conversational speech. Though we've all been trained literally from birth how to use our voices to communicate, voice communication remains filled with pitfalls and misunderstandings. Even in the best of conditions.
So why in the world -- outside of a disability -- would I want to extend the creaky, rickety old bridge of voice communication to controlling my computer? Isn't there a better way?
Robert's post contains some examples in the comments from voice control enthusiasts:
in addition to extremely accurate voice dictation, there are those really cool commands, like being able to say something like "search Google for Balloon Boy" or something like that and having it automatically open up your browser and enter the search term -- something like this is accomplished many times faster than a human could do it. Or, being able to total up a column of numbers in Microsoft Excel by saying simply "total this column" and seeing the results in a blink of an eye, literally.
That's funny, because I just fired up the Google app on my iPhone, said "balloon boy" into it, and got .. a search for "blue boy". I am not making this up. As for the Excel example, total which column? Let's assume you've dealt with the tricky problem of selecting what column you're talking about with only your voice. (I'm sorry, was it D5? B5?) Wouldn't it be many times faster to click the toolbar icon with your mouse, or press the keyboard command equivalent, to sum the column -- rather than methodically and tediously saying the words "sum this column" out loud?
I'm also trying to imagine a room full of people controlling their computers or phones using their voices. It's difficult enough to get work done in today's chatty work environments without the added burden of a floor full of people saying "zoom ... enhance" to their computers all day long. Wouldn't we all end up hoarse and deaf?
Let's look at another practical example -- YouTube's automatic speech recognition feature. I clicked through to the first UC Berkeley video with this feature, clicked the CC (closed caption) icon, and immediately got .. this.
"Light exerts force on matter". But according to Google's automatic speech recognition, it's "like the search for some matter". Unsurprisingly, it does not get better from there. You'd be way more confused than educated if you had to learn this lecture from the automatic transcription.
Back when Joel Spolsky and I had a podcast together, a helpful listener suggested using speech recognition to get a basic podcast transcript going. Everything I knew about voice recognition told me this wouldn't help, but harm. What's worse: transcribing everything by hand, from scratch -- or correcting every third or fourth word in an auto-generated machine transcript? Maybe it's just me, but the friction of the huge error rate inherent in the machine transcript seems far more intimidating than a blank slate human transcription. The humans may not be particularly efficient, but they all add value along the way -- collective human judgment can editorially improve the transcript, by removing all the duplication, repetition, and "ums" of a literal, by-the-book transcription.
In 2004, Mike Bliss composed a poem about voice recognition. He then read it to voice recognition software on his PC, and rewrote it as recognized.
|
a poem by Mike Bliss like a baby, it listens it can't discriminate it tries to understand it reflects what it thinks you say it gets it wrong... sometimes sometimes it gets it right. One day it will grow up, like a baby, it has potential will it go to work? will it turn to crime? you look at it indulgently. you can't help loving it, can you? |
a poem by like myth like a baby, it nuisance it can't discriminate it tries to oven it reflects lot it things you say it gets it run sometimes sometimes it gets it right won't day it will grow bop Ninth a baby, it has provincial will it both to look? will it the two crime? you move at it inevitably you can't help loving it, cannot you? |
The real punchline here is that Mike re-ran the experiment in 2008, and after 5 minutes of voice training, the voice recognition got all but 2 words of the original poem correct!
I suspect that's still not good enough in the face of the existing simpler alternatives. Remember handwriting recognition? It was all the rage in the era of the Apple Newton.
It wasn't as bad as Doonesbury made it out to be. I learned Palm's Graffiti handwriting recognition language and got fairly proficient with it. More than ten years later, you'd expect to see massively improved handwriting recognition of some sort in today's iPads and iPhones and iOthers, right? Well, maybe, if by "massively improved" you mean "nonexistent".
While it still surely has its niche uses, I personally don't miss handwriting recognition. Not even a little. And I can't help wondering if voice recognition will go the same way.
| [advertisement] JIRA 4 - Simplify bug tracking for everyone involved. Get started from $10 for 10 users » |
After we created Stack Overflow, some people were convinced we had built a marginally better mousetrap for asking and answering questions. The inevitable speculation began: can we use your engine to build a Q&A site about {topic}? Our answer was Stack Exchange. Pay us $129 a month (and up), and you too can create a hosted Q&A community on our engine -- for whatever topic you like!
Well, I have a confession to make: my heart was never in Stack Exchange. It was a parallel effort in a parallel universe only tangentially related to my own. There's a whole host of reasons why, but if I had to summarize it in a sentence, I'd say that money is poisonous to communities. That $129/month doesn't sound like much -- and it isn't -- but the commercial nature of the enterprise permeated and distorted everything from the get-go.
(fortunately, the model is changing with Stack Exchange 2.0, but that's a topic for another blog post.)
Yes, Stack Overflow Internet Services Incorporated©®™ is technically a business, even a venture capital backed business now -- but I didn't co-found it because I wanted to make money. I co-founded it because I wanted to build something cool that made the internet better. Yes, selfishly for myself, of course, but also in conjunction with all of my fellow programmers, because I know none of us is as dumb as all of us.
Nobody is participating in Stack Overflow to make money. We're participating in Stack Overflow because …
I don't care how much you pay me, you'll never be able to recreate the incredibly satisfying feeling I get when demonstrating mastery within my community of peers. That's what we do on Stack Overflow: have fun, while making the internet one infinitesimally tiny bit better every day.
So is it any wonder that some claim Stack Overflow is more satisfying than their real jobs? Not to me.
If this all seems like a bunch of communist hippie bullcrap to you, I understand. It's hard to explain. But there is quite a bit of science documenting these strange motivations. Let's start with Dan Pink's 2009 TED talk.
Dan's talk centers on the candle problem. Given the following three items …
… how can you attach the candle to the wall?
It's not a very interesting problem on its own -- that is, until you try to incentivize teams to solve it:
Now I want to tell you about an experiment using the candle problem by a scientist from Princeton named Sam Glucksberg. Here's what he did.To the first group, he said, "I'm going to time you to establish norms, averages for how long it typically takes someone to solve this sort of problem."
To the second group, he said, "If you're in the top 25 percent of the fastest times you get five dollars. If you're the fastest of everyone we're testing here today you get 20 dollars." (This was many years ago. Adjusted for inflation, it's a decent sum of money for a few minutes of work.)
Question: How much faster did this group solve the problem?
Answer: It took them, on average, three and a half minutes longer. Three and a half minutes longer. Now this makes no sense, right? I mean, I'm an American. I believe in free markets. That's not how it's supposed to work. If you want people to perform better, you reward them. Give them bonuses, commissions, their own reality show. Incentivize them. That's how business works. But that's not happening here. You've got a monetary incentive designed to sharpen thinking and accelerate creativity -- and it does just the opposite. It dulls thinking and blocks creativity.
It turns out that traditional carrot-and-stick incentives are only useful for repetitive, mechanical tasks. The minute you have to do anything even slightly complex that requires even a little problem solving without a clear solution or rules -- those incentives not only don't work, they make things worse!
Pink eventually wrote a book about this, Drive: The Surprising Truth About What Motivates Us.
There's no need to read the book; this clever ten minute whiteboard animation will walk you through the main points. If you view only one video today, view this one.
The concept of intrinsic motivation may not be a new one, but I find that very few companies are brave enough to actually implement them.
I've tried mightily to live up to the ideals that Stack Overflow was founded on when building out my team. I don't care when you come to work or what your schedule is. I don't care where in the world you live (provided you have a great internet connection). I don't care how you do the work. I'm not going to micromanage you and assign you a queue of task items. There's no need.
If you want to build a ship, don't drum up the men to gather wood, divide the work and give orders. Instead, teach them to yearn for the vast and endless sea.
– Antoine de Saint Exupéry
Because I know you yearn for the vast and endless sea, just like we do.
| [advertisement] JIRA 4 - Simplify bug tracking for everyone involved. Get started from $10 for 10 users » |
When I first chose my own adventure, I didn't know what working remotely from home was going to be like. I had never done it before. As programmers go, I'm fairly social. Which still means I'm a borderline sociopath by normal standards. All the same, I was worried that I'd go stir-crazy with no division between my work life and my home life.
Well, I haven't gone stir-crazy yet. I think. But in building Stack Overflow, I have learned a few things about what it means to work remotely -- at least when it comes to programming. Our current team encompasses 5 people, distributed all over the USA, along with the team in NYC.
My first mistake was attempting to program alone. I had weekly calls with my business partner, Joel Spolsky, which were quite productive in terms of figuring out what it was we were trying to do together -- but he wasn't writing code. I was coding alone. Really alone. One guy working all by yourself alone. This didn't work at all for me. I was unmoored, directionless, suffering from analysis paralysis, and barely able to get motivated enough to write even a few lines of code. I rapidly realized that I'd made a huge mistake in not having a coding buddy to work with.
That situation rectified itself soon enough, as I was fortunate enough to find one of my favorite old coding buddies was available. Even though Jarrod was in North Carolina and I was in California, the shared source code was the mutual glue that stuck us together, motivated us, and kept us moving forward. To be fair, we also had the considerable advantage of prior history, because we had worked together at a previous job. But the minimum bar to work remotely is to find someone who loves code as much as you do. It's … enough. Anything else on top of that -- old friendships, new friendships, a good working relationship -- is icing that makes working together all the sweeter. I eventually expanded the team in the same way by adding another old coding buddy, Geoff, who lives in Oregon. And again by adding Kevin, who I didn't know, but had built amazing stuff for us without even being asked to, from Texas. And again by adding Robert, in Florida, who I also didn't know, but spent so much time on every single part of our sites that I felt he had been running alongside our team the whole way, there all along.
The reason remote development worked for us, in retrospect, wasn't just shared love of code. I picked developers who I knew -- I had incontrovertible proof -- were amazing programmers. I'm not saying they're perfect, far from it, merely that they were top programmers by any metric you'd care to measure. That's why they were able to work remotely. Newbie programmers, or competent programmers who are phoning it in, are absolutely not going to have the moxie necessary to get things done remotely -- at least, not without a pointy haired manager, or grumpy old team lead, breathing down their neck. Don't even think about working remotely with anyone who doesn't freakin' bleed ones and zeros, and has a proven track record of getting things done.
While Joel certainly had a lot of high level input into what Stack Overflow eventually became, I only talked to him once a week, at best (these calls were the genesis of our weekly podcast series). I had a strong, clear vision of what I wanted Stack Overflow to be, and how I wanted it to work. Whenever there was a question about functionality or implementation, my team was able to rally around me and collectively make decisions we liked, and that I personally felt were in tune with this vision. And if you know me at all, you know I'm not shy about saying no, either. We were able to build exactly what we wanted, exactly how we wanted.
Bottom line, we were on a mission from God. And we still are.
So, there are a few basic ground rules for remote development, at least as I've seen it work:
This is all well and good when you have a remote team size of three, as we did for the bulk of Stack Overflow development. And all in the same country. Now we need to grow the company, and I'd like to grow it in distributed fashion, by hiring other amazing developers from around the world, many of whom I have met through Stack Overflow itself.
But how do you scale remote development? Joel had some deep seated concerns about this, so I tapped one of my heroes, Miguel de Icaza -- who I'm proud to note is on our all-star board of advisors -- and he was generous enough to give us some personal advice based on his experience running the Mono project, which has dozens of developers distributed all over the world.
At the risk of summarizing mercilessly (and perhaps too much), I'll boil down Miguel's advice the best I can. There are three tools you'll need in place if you plan to grow a large-ish and still functional remote team:
We're currently experimenting with Campfire, but whatever floats your boat and you can get your team to consistently use, will work. Chat is the most essential and omnipresent form of communication you have when working remotely, so you need to make absolutely sure it's functioning before going any further.
We're using Google Groups and although it's old school in spades, it works plenty well for this. You can get the emails as they arrive, or view the archived list via the web interface. One word of caution, however. Every time you see something arrive in your inbox from the mailing list you better believe, in your heart of hearts, that it contains useful information. The minute the mailing list becomes just another "whenever I have time to read that stuff", noise engine, or distraction from work … you've let someone cry wolf too much, and ruined it. So be very careful. Noisy, argumentative, or useless things posted to the mailing list should be punishable by death. Or noogies.
As much as I love ASCII, sometimes faceless ASCII characters just aren't enough to capture the full intentions and feelings of the human being behind them. When you find yourself sending kilobytes of ASCII back and forth, and still are unsatisfied that you're communicating, you should instill a reflexive habit of "going voice" on your team.
Never underestimate the power of actually talking to another human being. I know, I know, the whole reason we got into this programming thing was to avoid talking to other people, but bear with me here. You can't be face to face on a remote team without flying 6 plus hours, and who the heck has that kind of time? I've got work I need to get done! Well, the next best thing to hopping on a plane is to fire up Skype and have a little voice chat. Easy peasy. All that human nuance which is totally lost in faceless ASCII characters (yes, even with our old pal *<:-)) will come roaring back if you regularly schedule voice chats. I recommend at least once a week at an absolute minimum; they don't have to be long meetings, but it sure helps in understanding the human being behind all those awesome checkins.
Nobody hates meetings and process claptrap more than I do, but there is a certain amount of process you'll need to keep a bunch of loosely connected remote teams and developers in sync.
Every Monday, as in somebody's-got-a-case-of-the, each team should produce a brief, summarized rundown of:
This doesn't have to be (and in fact shouldn't be) a long report. The briefer the better, but do try to capture all the useful highlights. Mail this to the mailing list every Monday like clockwork. Now, how many "teams" you have is up to you; I don't think this needs to be done at the individual developer level, but you could.
Any time you conduct what you would consider to be a "meeting" with someone else, take minutes! That is, write down what happened in bullet point form, so those remote team members who couldn't be there can benefit from -- or at least hear about -- whatever happened.
Again, this doesn't have to be long, and if you find taking meeting minutes onerous then you're probably doing it wrong. A simple bulleted list of sentences should suffice. We don't need to know every little detail, just the big picture stuff: who was there? What topics were discussed? What decisions were made? What are the next steps?
Both of the above should, of course, be mailed out to the mailing list as they are completed so everyone can be notified. You do have a mailing list, right? Of course you do!
If this seems like a lot of jibba-jabba, well, that's because remote development is hard. It takes discipline to make it all work, certainly more discipline than piling a bunch of programmers into the same cubicle farm. But when you imagine what this kind of intellectual work -- not just programming, but anything where you're working in mostly thought-stuff -- will be like in ten, twenty, even thirty years … don't you think it will look a lot like what happens every day right now on Stack Overflow? That is, a programmer in Brazil helping a programmer in New Jersey solve a problem?
If I have learned anything from Stack Overflow it is that the world of programming is truly global. I am honored to meet these brilliant programmers from every corner of the world, even if only in a small way through a website. Nothing is more exciting for me than the prospect of adding international members to the Stack Overflow team. The development of Stack Overflow should be reflective of what Stack Overflow is: an international effort of like-minded -- and dare I say totally awesome -- programmers. I wish I could hire each and every one of you. OK, maybe I'm a little biased. But to me, that's how awesome the Stack Overflow community is.
I believe remote development represents the future of work. If we have to spend a little time figuring out how this stuff works, and maybe even make some mistakes along the way, it's worth it. As far as I'm concerned, the future is now. Why wait?
| [advertisement] Atlassian Starter Day - Learn how startups like Boxee and Pandora broke through. Sign-up today » |
We're currently in the midst of a CSS Zen Garden type excerise on our family of Q&A websites, which I affectionately refer to as "the Trilogy":
(In case you were wondering, yes, meta is the Star Wars Holiday Special.)
These sites all run the same core engine, but the logo, domain, and CSS "skin" that lies over the HTML skeleton is different in each case:
|
|
|
|
|
|
They are not terribly different looking, it's true, but we also want them to be recognizable as a family of sites.
We're working with two amazing designers, Jin Yang and Nathan Bowers, who are helping us whip the CSS and HTML into shape so they can produce a set of about 10 different Zen Garden designs. As new sites in our network get democracied into being, these designs will be used as a palette for the community to choose from. (And, later, the community will decide on a domain name and logo as well.)
Anyway, I bring this up not because my pokemans, let me show you them, but because I have to personally maintain four different CSS files. And that number is only going to get larger. Much larger. That scares me a little.
Most of all, what I've learned from this exercise in site theming is that CSS is kind of painful. I fully support CSS as a (mostly) functional user interface Model-View-Controller. But even if you have extreme HTML hygiene and Austrian levels of discipline, CSS has some serious limitations in practice.
Things in particular that bite us a lot:
In short, CSS violates the living crap out of the DRY principle. You are constantly and unavoidably repeating yourself.
That's why I'm so intrigued by two Ruby gems that attempt to directly address the deficiencies of CSS.
1. Less CSS
/* CSS */
#header {
-moz-border-radius: 5;
-webkit-border-radius: 5;
border-radius: 5;
}
#footer {
-moz-border-radius: 10;
-webkit-border-radius: 10;
border-radius: 10;
}
|
// LessCSS
.rounded_corners (@radius: 5px) {
-moz-border-radius: @radius;
-webkit-border-radius: @radius;
border-radius: @radius;
}
#header {
.rounded_corners;
}
#footer {
.rounded_corners(10px);
}
|
2. SASS
/* CSS */
.content_navigation {
border-color: #3bbfce;
color: #2aaebd;
}
.border {
padding: 8px;
margin: 8px;
border-color: #3bbfce;
}
|
// Sass !blue = #3bbfce !margin = 16px .content_navigation border-color = !blue color = !blue - #111 .border padding = !margin / 2 margin = !margin / 2 border-color = !blue |
As you can see, in both cases we're transmogrifying CSS into a bit more of a programming language, rather than the static set of layout rules it currently exists as. Behind the scenes, we're generating plain vanilla CSS using these little dynamic languages. This could be done at project build time, or even dynamically on every page load if you have a good caching strategy.
I'm not sure how many of these improvements CSS3 will bring, never mind when the bulk of browsers in the world will support it. But I definitely feel that the core changes identified in both Less CSS and SASS address very real pain points in practical CSS use. It's worth checking them out to understand why they exist, what they bring to the table, and how you could possibly adopt some of these strategies in your own CSS and your favorite programming language.
| [advertisement] Atlassian Starter Day - Learn how startups like Boxee and Pandora broke through. Sign-up today » |
I have what I would charitably describe as a hate-hate relationship with email. I desperately try to avoid sending email, not just for myself, but also in the code I write.
Despite my misgivings, email is the cockroach of communication mediums: you just can't kill it. Email is the one method of online contact that almost everyone -- at least for that subset of "everyone" which includes people who can bear to touch a computer at all -- is guaranteed to have, and use. Yes, you can make a fairly compelling case that email is for old stupid people, but let's table that discussion for now.
So, reluctantly, we come to the issue of sending email through code. It's easy! Let's send some email through oh, I don't know, let's say ... Ruby, courtesy of some sample code I found while browsing the Ruby tag on Stack Overflow.
require 'net/smtp'
def send_email(to, subject = "", body = "")
from = "my@email.com"
body= "From: #{from}\r\nTo: #{to}\r\nSubject: #{subject}\r\n\r\n#{body}\r\n"
Net::SMTP.start('192.168.10.213', 25, '192.168.0.218') do |smtp|
smtp.send_message body, from, to
end
end
send_email "foo@example.com", "title", "body goes here"
There's a bug in this code, though. Do you see it?
Just because you send an email doesn't mean it will arrive. Not by a long shot. Bear in mind this is email we're talking about. It was never designed to survive a bitter onslaught of criminals and spam, not to mention the explosive, exponential growth it has seen over the last twenty years. Email is a well that has been truly and thoroughly poisoned -- the digital equivalent of a superfund cleanup site. The ecosystem around email is a dank miasma of half-implemented, incompletely supported anti-spam hacks and workarounds.
Which means the odds of that random email your code just sent getting to its specific destination is .. spotty. At best.
If you want email your code sends to actually arrive in someone's AOL mailbox, to the dulcet tones of "You've Got Mail!", there are a few things you must do first. And most of them are only peripherally related to writing code.
1. Make sure the computer sending the email has a Reverse PTR record
What's a reverse PTR record? It's something your ISP has to configure for you -- a way of verifying that the email you send from a particular IP address actually belongs to the domain it is purportedly from.
Not every IP address has a corresponding PTR record. In fact, if you took a random sampling of addresses your firewall blocked because they were up to no good, you'd probably find most have no PTR record - a dig -x gets you no information. That's also apt to be true for mail spammers, or their PTR doesn't match up: if you do a dig -x on their IP you get a result, but if you look up that result you might not get the same IP you started with.That's why PTR records have become important. Originally, PTR records were just intended as a convenience, and perhaps as a way to be neat and complete. There still are no requirements that you have a PTR record or that it be accurate, but because of the abuse of the internet by spammers, certain conventions have grown up. For example, you may not be able to send email to some sites if you don't have a valid PTR record, or if your pointer is "generic".
How do you get a PTR record? You might think that this is done by your domain registrar - after all, they point your domain to an IP address. Or you might think whoever handles your DNS would do this. But the PTR record isn't up to them, it's up to the ISP that "owns" the IP block it came from. They are the ones who need to create the PTR record.
A reverse PTR record is critical. How critical? Don't even bother reading any further until you've verified that your ISP has correctly configured the reverse PTR record for the server that will be sending email. It is absolutely the most common check done by mail servers these days. Fail the reverse PTR check, and I guarantee that a huge percentage of the emails you send will end up in the great bit bucket in the sky -- and not in the email inboxes you intended.
2. Configure DomainKeys Identified Mail in your DNS and code
What's DomainKeys Identified Mail? With DKIM, you "sign" every email you send with your private key, a key only you could possibly know. And this can be verified by attempting to decrypt the email using the public key stored in your public DNS records. It's really quite clever!
The first thing you need to do is generate some public-private key pairs (one for every domain you want to send email from) via OpenSSL. I used a win32 version I found. Issue these commands to produce the keys in the below files:
$ openssl genrsa -out rsa.private 1024 $ openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM
These public and private keys are just big ol' Base64 encoded strings, so plop them in your code as configuration string resources that you can retrieve later.
Next, add some DNS records. You'll need two new TXT records.
The first TXT DNS record is the global DomainKeys policy and contact email.
The second TXT DNS record is the public base64 key you generated earlier, as one giant unbroken string. Note that the "selector" part of this record can be anything you want; it's basically just a disambiguating string.
Almost done. One last thing -- we need to sign our emails before sending them. In any rational world this would be handled by an email library of some kind. We use Mailbee.NET which makes this fairly painless:
smtp.Message = dk.Sign(smtp.Message,
null, AppSettings.Email.DomainKeyPrivate, false, "selector");
3. Set up a SenderID record in your DNS
To be honest, SenderID is a bit of a "nice to have" compared to the above two. But if you've gone this far, you might as well go the distance. SenderID, while a little antiquated and kind of.. Microsoft/Hotmail centric.. doesn't take much additional effort.
SenderID isn't complicated. It's another TXT DNS record at the root of, say, example.com, which contains a specially formatted string documenting all the allowed IP addresses that mail can be expected to come from. Here's an example:
"v=spf1 a mx ip4:10.0.0.1 ip4:10.0.0.2 ~all"
You can use the Sender ID SPF Record Wizard to generate one of these for each domain you send email from.
That sucked. How do I know all this junk is working?
I agree, it sucked. Email sucks; what did you expect? I used two methods to verify that all the above was working:
Received-SPF: pass Authentication-Results: ... spf=pass ... dkim=pass
If you see that, then the Reverse PTR and DKIM signing you set up is working. Google provides excellent diagnostic feedback in their email server headers, so if something isn't working, you can usually discover enough of a hint there to figure out why.
SPF check: pass DomainKeys check: fail DKIM check: pass Sender-ID check: pass SpamAssassin check: ham
You want to pass SPF, DKIM, and Sender-ID. Don't worry about the DomainKeys failure, as I believe it is spurious -- DKIM is the "newer" version of that same protocol.
Yes, the above three steps are quite a bit of work just to send a lousy email. But I don't send email lightly. By the time I've reached the point where I am forced to write code to send out email, I really, really want those damn emails to arrive. By any means necessary.
And for those who are the unfortunate recipients of these emails: my condolences.
| [advertisement] JIRA Studio - SVN hosting, issue tracking, CI and Google Apps integration. Free trial » |
As far as I'm concerned, you can never be too rich, too thin, or have too much screen space. By "screen", I mean not just large monitors, but multiple large monitors. I've been evangelizing multiple monitors since the dark days of Windows Millennium Edition:
If you're a long time reader you're probably sick of hearing about this stuff by now, but something rather wonderful has happened since I last wrote about it:
If you're only using one monitor, you are cheating yourself out of potential productivity. Two monitors is a no-brainer. It's so fundamental that I included it as a part of the Programmer's Bill of Rights.But you can do better.
As good as two monitors is, three monitors is even better. With three monitors, there's a "center" to focus on. And 50% more display area. While there's certainly a point of diminishing returns for additional monitors, I think three is the sweet spot. Even Edward Tufte, in the class I recently attended, explicitly mentioned multiple monitors. I don't care how large a single display can be; you can never have enough desktop space.
Normally, to achieve three monitors, you have to either:
- Buy an exotic video card that has more than 2 monitor connections.
- Install a second video card.
Fortunately, that is no longer true. I was excited to learn that the latest ATI video cards have gone from two to three video outputs. Which means you can now achieve triple monitors with a single video card upgrade! They call this "eyefinity", but it's really just shorthand for "raising the standard from two display outputs to three".
But, there is a (small) catch. The PC ecosystem is in the middle of shifting display output standards. For evidence of this, you need look no further than the back panel of one of these newfangled triple display capable ATI video cards:
It contains:
I suspect part of this odd connector layout is due to space restrictions (DVI is awfully chunky), but I've always understood DisplayPort to be the new, improved DVI connector for computer monitors, and HDMI to be the new, improved s-video/component connector for televisions. Of course these worlds are blurring, as modern high-definition TVs make surprisingly effective computer monitors, too.
Anyway, since all my monitors have only DVI inputs, I wasn't sure what to do with the other output. So I asked on Super User. The helpful answers led me to discover that, as I suspected, the third output has to be DisplayPort. So to connect my third monitor, I needed to convert DisplayPort to DVI, and there are two ways:
I ended up going with the active converter, which has mixed reviews, but it's worked well for me over the last few weeks.
Note that this adapter requires USB power, and given the spotty results others have had with it, some theorize that it needs quite a bit of juice to work reliably. I plugged it into my system's nearby rear USB ports which do tend to deliver more power (they're closer to the power supply, and have short cable paths). Now, I have gotten the occasional very momentary black screen with it, but nothing severe enough to be a problem or frequent enough to become a pattern. If you have DisplayPort compatible monitors, of course, this whole conversion conundrum is a complete non-issue. But DisplayPort is fairly new, and even my new-ish LCD monitors don't support it yet.
The cool thing about this upgrade, besides feeding my video card addiction, is that I was able to simplify my hardware configuration. That's always good. I went from two video cards to one, which means less power consumption, simpler system configuration, and fewer overall driver oddities. Basically, it makes triple monitors -- dare I say it -- almost a mainstream desktop configuration. How could I not be excited about that?
I was also hoping that Nvidia would follow ATI's lead here and make three display outputs the standard for all their new video cards, too, but sadly that's not the case. It turns out their new GTX 480 fails in other ways, in that it's basically the Pentium 4 of video cards -- generating ridiculous amounts of heat for very little performance gain. Based on those two facts, I am comfortable endorsing ATI wholeheartedly at this point. But, do be careful, because not all ATI cards support triple display outputs (aka "eyefinity"). These are the ones that I know do:
Unless you're a gamer, there's no reason to care about anything other than the least expensive model here, which will handily crush any 2D or 3D desktop GUI acceleration needs you might have. As an addict, of course I bought the high end model and it absolutely did not disappoint -- more than doubling my framerates in the excellent game Battlefield: Bad Company 2 over the GTX 280 I had before.
I'm excited that a triple monitor setup is now, thanks to ATI, so easily attainable for desktop users -- as long as you're aware of the DisplayPort caveat I discussed above. I'd encourage anyone who is even remotely interested in the (many) productivity benefits of a triple monitor setup to seriously consider an ATI video card upgrade.
| [advertisement] JIRA Studio - Hosted software development suite. Build better software. Faster. Free trial » |
Writing code? That's the easy part. Getting your application in the hands of users, and creating applications that people actually want to use -- now that's the hard stuff.
I've been a long time fan of Krug's book Don't Make Me Think. Not just because it's a quick, easy read (and it is!) -- but because it's the most concise and most approachable book I've ever found to teach the fundamental importance of usability. As far as I'm concerned, if you want to help us make the software industry a saner place, the first step is getting Don't Make Me Think in the hands of as many of your coworkers as you can. If you don't have people that care about usability on your project, your project is doomed.
Beyond getting people over the hurdle of at least paging through the Krug book, and perhaps begrudgingly conceding that this usability stuff matters, the next challenge is figuring out how to integrate usability testing into your project. It's easy to say "Usability is Important!", but you have to walk the walk, too. I touched on some low friction ways to get started in Low-Fi Usability Testing. That rough outline is now available in handy, more complete book form -- Rocket Surgery Made Easy: The Do-It-Yourself Guide to Finding and Fixing Usability Problems.
Don't worry, Krug's book is just as usable as his advice. It's yet another quick, easy read. Take it from the man himself:
If you're wondering what the beginner's "how do I boil water?" recipe for software project usability is, stop reading this post and get a copy of Rocket Surgery Made Easy. Now.
One of the holy grails of usability testing is eyetracking -- measuring where people's eyes look as they use software and web pages. Yes, there are clever JavaScript tools that can measure where users move their pointers, but that's only a small part of the story. Where the eye wanders, the pointer may not, and vice-versa. But, who has the time and equipment necessary to conduct an actual eyetracking study? Almost nobody.
That's where Eyetracking Web Usability comes in.
Eyetracking Web Usability is chock full of incredibly detailed eyetracking data for dozens of websites. Even though you (probably) can't afford to do real eyetracking, you can certainly use this book as a reference. There is enough variety in UI and data that you can map the results, observations, and explanations found here to what your project is doing.
This particular book is rather eyetracking specific, but it's just the latest entry in a whole series on usability, and I recommend them all highly. These books are a fount of worthwhile data for anyone who works on software and cares about usability, from one of the most preeminent usability experts on the web.
Usability isn't really cheap or easy. It's an endless war, with innumerable battlegrounds, stretching all the way back to the dawn of computing. But these books, at least, are cheap and easy in the sense that they give you some basic training in fighting the good (usability) fight. That's the best I can do, and it's all I'd ask from anyone else I work with.
| [advertisement] JIRA Studio - Hosted software development suite. Build better software. Faster. Free trial » |
If you've ever wrangled a user interface, you've probably heard of Fitts' Law. It's pretty simple -- the larger an item is, and the closer it is to your cursor, the easier it is to click on. Kevin Hale put together a great visual summary of Fitts' Law, so rather than over-explain it, I'll refer you there.
The short version of Fitts' law, to save you all that tedious reading, is this:
I know, it's very simple, almost too simple, but humor me by following along with some thought exercises. Imagine yourself trying to click on ...
Fitts' Law is mostly common sense, and enjoys enough currency with UI designers that they're likely to know about it even if they don't follow it as religiously as they should. Unfortunately, I've found that designers are much less likely to consider the opposite of Fitts' Law, which is arguably just as important.
If we should make UI elements we want users to click on large, and ideally place them at corners or edges for maximum clickability -- what should we do with UI elements we don't want users to click on? Like, say, the "delete all my work" button?
Alan Cooper, in About Face 3, calls this the ejector seat lever.
In the cockpit of every jet fighter is a brightly painted lever that, when pulled, fires a small rocket engine underneath the pilot's seat, blowing the pilot, still in his seat, out of the aircraft to parachute safely to earth. Ejector seat levers can only be used once, and their consequences are significant and irreversible.Applications must have ejector seat levers so that users can—occasionally—move persistent objects in the interface, or dramatically (sometimes irreversibly) alter the function or behavior of the application. The one thing that must never happen is accidental deployment of the ejector seat.
![]()
The interface design must assure that a user can never inadvertently fire the ejector seat when all he wants to do is make some minor adjustment to the program.
I can think of a half-dozen applications I regularly use where the ejector seat button is inexplicably placed right next to the cabin lights button. Let's take a look at our old friend GMail, for example:
I can tell what you're thinking. Did he click Send or Save Now? Well, to tell you the truth, in all the excitement of composing that angry email, I kind of lost track myself. Good thing we can easily undo a sent mail! Oh wait, we totally can't. Consider my seat, or at least that particular rash email, ejected.
It's even worse when I'm archiving emails.
While there were at least 10 pixels between the buttons in the previous example, here there are all of ... three. Every few days I accidentally click Report Spam when I really meant to click Archive. Now, to Google's credit, they do offer a simple, obvious undo path for these accidental clicks. But I can't help wondering why it is, exactly, that these two buttons with such radically different functionality just have to be right next to each other.
Undo is powerful stuff, but wouldn't it be better still if I wasn't pulling the darn ejector seat lever all the time? Wouldn't it make more sense to put that risky ejector seat lever in a different location, and make it smaller? Consider the WordPress post editor.
Here, the common Update operation is large and obviously a button -- it's easy to see and easy to click on. The less common Move to Trash operation is smaller, presented as a vanilla hyperlink, and placed well away from Update.
The next time you're constructing a user interface, you should absolutely follow Fitts' law. It just makes sense. But don't forget to follow the opposite of Fitts' law, too -- uncommon or dangerous UI items should be difficult to click on!
| [advertisement] JIRA 4 - Simplify issue tracking for everyone involved. Get started from $10 for 10 users. |
While I may have mixed emotions toward LINQ to SQL, we've had great success with it on Stack Overflow. That's why I was surprised to read the following:
If you are building an ASP.NET web application that's going to get thousands of hits per hour, the execution overhead of Linq queries is going to consume too much CPU and make your site slow. There’s a runtime cost associated with each and every Linq Query you write. The queries are parsed and converted to a nice SQL Statement on every hit. It’s not done at compile time because there’s no way to figure out what you might be sending as the parameters in the queries during runtime.So, if you have common Linq to Sql statements like the following one ..
var query = from widget in dc.Widgets where widget.ID == id && widget.PageID == pageId select widget; var widget = query.SingleOrDefault();.. throughout your growing web application, you are soon going to have scalability nightmares.
J.D. Conley goes further:
So I dug into the call graph a bit and found out the code causing by far the most damage was the creation of the LINQ query object for every call! The actual round trip to the database paled in comparison.
I must admit, these results seem ... unbelievable. Querying the database is so slow (relative to typical code execution) that if you have to ask how long it will take, you can't afford it. I have a very hard time accepting the idea that dynamically parsing a Linq query would take longer than round-tripping to the database. Pretend I'm from Missouri: show me. Because I am unconvinced.
All of this is very curious, because Stack Overflow uses naive, uncompiled Linq queries on every page, and we are a top 1,000 website on the public internet by most accounts these days. We get a considerable amount of traffic; the last time I checked it was about 1.5 million pageviews per day. We go to great pains to make sure everything is as fast as we can. We're not as fast as we'd like to be yet, but I think we're doing a reasonable job so far. The journey is still very much underway -- we realize that overnight success takes years.
Anyway, Stack Overflow has dozens to hundreds of plain vanilla uncompiled Linq to SQL queries on every page. What we don't have is "scalability nightmares". CPU usage has been one of our least relevant constraints over the last two years as the site has grown. We've also heard from other development teams, multiple times, that Linq to SQL is "slow". But we've never been able to reproduce this even when armed with a profiler.
Quite the mystery.
Now, it's absolutely true that Linq to SQL has the performance peculiarity both posters are describing. We know that's true because Rico tells us so, and Rico ... well, Rico's the man.
In short the problem is that the basic Linq construction (we don’t really have to reach for a complex query to illustrate) results in repeated evaluations of the query if you ran the query more than once.Each execution builds the expression tree, and then builds the required SQL. In many cases all that will be different from one invocation to another is a single integer filtering parameter. Furthermore, any databinding code that we must emit via lightweight reflection will have to be jitted each time the query runs. Implicit caching of these objects seems problematic because we could never know what good policy is for such a cache -- only the user has the necessary knowledge.
It's fascinating stuff; you should read the whole series.
What's unfortunate about Linq in this scenario is that you're intentionally sacrificing something that any old and busted SQL database gives you for free. When you send a garden variety parameterized SQL query through to a traditional SQL database, it's hashed, then matched against existing cached query plans. The computational cost of pre-processing a given query is only paid the first time the database sees the new query. All subsequent runs of that same query use the cached query plan and skip the query evaluation. Not so in Linq to SQL. As Rico said, every single run of the Linq query is fully parsed every time it happens.
Now, there is a way to compile your Linq queries, but I personally find the syntax kind of ... ugly and contorted. You tell me:
Func<Northwinds, IQueryable<Orders>, int> q =
CompiledQuery.Compile<Northwinds, int, IQueryable<Orders>>
((Northwinds nw, int orderid) =>
from o in nw.Orders
where o.OrderId == orderid
select o );
Northwinds nw = new Northwinds(conn);
foreach (Orders o in q(nw, orderid))
{
}
Anyway, that's neither here nor there; we can confirm the performance penalty of failing to compile our queries ourselves. We recently wrote a one time conversion job against a simple 3 column table containing about 500,000 records. The meat of it looked like this:
db.PostTags.Where(t => t.PostId == this.Id).ToList();
Then we compared it with the SQL variant; note that this is also being auto-cast down to the handy PostTag object as well, so the only difference is whether or not the query itself is SQL.
db.ExecuteQuery( "select * from PostTags where PostId={0}", this.Id).ToList();
On an Intel Core 2 Quad running at 2.83 GHz, the former took 422 seconds while the latter took 275 seconds.
The penalty for failing to compile this query, across 500k iterations, was 147 seconds. Wow! That's 1.5 times slower! Man, only a BASIC programmer would be dumb enough to skip compiling all their Linq queries. But wait a second, no, wait 147 seconds. Let's do the math, even though I suck at it. Each uncompiled run of the query took less than one third of a millisecond longer.
At first I was worried that every Stack Overflow page was 1.5 times slower than it should be. But then I realized it's probably more realistic to make sure that any page we generate isn't doing 500 freakin' thousand queries! Have we found ourselves in the sad tragedy of micro-optimization theater ... again? I think we might have. Now I'm just depressed.
While it's arguably correct to say that every compiled Linq query (or for that matter, any compiled anything) will be faster, your decisions should be a bit more nuanced than compiled or bust. How much benefit you get out of compilation depends how many times you're doing it. Rico would be the first to point this out, and in fact he already has:
Testing 1 batches of 5000 selectsuncompiled 543.48 selects/sec compiled 925.75 selects/sec
Testing 5000 batches of 1 selects
uncompiled 546.03 selects/sec compiled 461.89 selects/sec
Have I mentioned that Rico is the man? Do you see the inversion here? Either you're doing 1 batch of 5000 queries, or 5000 batches of 1 query. One is dramatically faster when compiled; the other is actually a big honking net negative if you consider the developer time spent converting all those beautifully, wonderfully simple Linq queries to the contorted syntax necessary for compilation. Not to mention the implied code maintenance.
I'm a big fan of compiled languages. Even Facebook will tell you that PHP is about as half as fast as it should be on a good day with a tailwind. But compilation alone is not the entire performance story. Not even close. If you're compiling something -- whether it's PHP, a regular expression, or a Linq query, don't expect a silver bullet, or you may end up disappointed.
| [advertisement] JIRA 4 - Simplify issue tracking for everyone involved. Get started from $10 for 10 users. |
We launched three new Stack Exchange sites this week!
We’ll have three more for you next week, too.
Need to hire a really great programmer? Want a job that doesn't drive you crazy? Visit the Joel on Software Job Board: Great software jobs, great people.
“We decided that individually-branded sites felt more authentic and trustworthy. We thought that letting every Stack Exchange site have its own domain name, visual identity, logo, and brand would help the community feel more coherent. After all, nobody wants to say that they live in Housing Block 2938TC. They want to live in Colonial Manor. Never mind the connotation of, well, colonies.”
Need to hire a really great programmer? Want a job that doesn't drive you crazy? Visit the Joel on Software Job Board: Great software jobs, great people.
Want to know how to export mail from Gmail? Or delete your Facebook account? Or send giant files via email?
Well, the new Web Applications Stack Exchange is for you. It’s a part of the Stack Exchange network, so it has the clean, elegant design that made Stack Overflow a phenomenal success.
The newest member of the Stack Exchange Network is the first one to go through the community site-creation process called Area 51. There are more great sites in the pipeline, but they have to demonstrate that they can reach critical mass or we won’t create them.
Need to hire a really great programmer? Want a job that doesn't drive you crazy? Visit the Joel on Software Job Board: Great software jobs, great people.
Neil has posted a video of Don Norman (most famous for his book The Design of Everyday Things) speaking at the Business of Software conference last year in San Francisco.
“Imagine you’re on the first slide of your powerpoint presentation and want to move to the next slide. Your remote control has two buttons. They are unmarked, but one button points up and one button points down.
“Which button do you press?”
It turns out half the people press up, half the people press down, and everybody thinks their choice is obvious. It’s a great talk.
The early bird discount for the Business of Software 2010 (Boston, October 4-6) saves you $400, but it expires this week, so this is the right time to sign up for my favorite conference.
Need to hire a really great programmer? Want a job that doesn't drive you crazy? Visit the Joel on Software Job Board: Great software jobs, great people.
Area 51 is now in beta. This is the promised place where the community comes together to invent new Stack Exchange sites.
Benofsky from Hacker News writes:
Seems overly complicated, I have no idea what's going on when I visit Area 51, I guess this is their strategy for turning away uncommitted users.
Also, how are they going to make money?
I’m glad you asked, benofsky! The answer is simple. Volume.
Well, I’m not one to take Internet chat board comments seriously. After all, the Anonymous Nostradamus’s over at Code Project reacted thus when Stack Overflow itself launched:
I think the UI sucks. I can't imagine this site being around in a year.
We all know how stunningly accurate that prediction was:
Benofsky is onto something, though. Area 51 is not for everyone. If you don’t know what it’s for, or why it’s going to work, or you can’t figure it out, it’s not, actually for you.
Need to hire a really great programmer? Want a job that doesn't drive you crazy? Visit the Joel on Software Job Board: Great software jobs, great people.
My friend Noam at Harvard Business School does annual surveys of executive compensation in tech companies. The surveys are a great way to figure out how much to pay that VP of Sales you were thinking of hiring, or whether your equity grant as CTO is fair.
You can buy the studies for $999, or you can participate in the survey and get free access to the results when they’re published.
Need to hire a really great programmer? Want a job that doesn't drive you crazy? Visit the Joel on Software Job Board: Great software jobs, great people.
Paul Kenny is a regular speaker at the Business of Software. He’s an expert on sales, especially high tech sales, and both Red Gate and Fog Creek hire him all the time to train our sales teams and build our sales organization.
Here’s a video of his talk at last year’s conference:
The next Business of Software conference will be in Boston, October 4th-6th.
Need to hire a really great programmer? Want a job that doesn't drive you crazy? Visit the Joel on Software Job Board: Great software jobs, great people.
Creating a Twitter client is a really interesting exercise in application development because, amongst many reasons, it's taking input from effectively an infinite number of people and places. Never trust user input, right? Input to your application comes not only in the form of text, but also images. Writing a Twitter client is effectively writing a web browser that only browses one website. Getting a browser stable is hard.
Long Zheng, Raphael Rivera and the MetroTwit team (MetroTwit is a lovely new Twitter client) have hit an extremely interesting crashing bug. The input comes in the form of a corrupted JPG image from the web.
Here's the bad image. Looks like a picture some folks speaking on a panel. However, even though this image looks fine, this specific binary version of it has a corrupted Color Profile.
Sometimes folks don't realize that image formats contain lots of metadata that you can't see. Your JPGs may show what camera you used, what lens, what settings, possibly even the geo-coordinates of where you took the picture!
You can view all this extended information (EXIF) with a number of tools. A great free one is ExifTool by Phil Harvey at the command line, or a non-command line one like ExifPro. Windows Live Photo Gallery lets you view the data also.
Here's a snippet of some of the info in this pic:
Device Mfg Desc : IEC http://www.iec.ch
Device Model Desc : IEC 61966-2.1 Default RGB colour space - sRGB
Viewing Cond Desc : Reference Viewing Condition in IEC61966-2.1
Viewing Cond Illuminant : 19.6445 20.3718 16.8089
Viewing Cond Surround : 3.92889 4.07439 3.36179
Viewing Cond Illuminant Type : D50
Make : Leica Camera AG
Camera Model Name : M8 Digital Camera
Software : Aperture 3.0.2
Shutter Speed Value : 1/256
Exposure Compensation : 0
Max Aperture Value : 1.0
Metering Mode : Center-weighted average
Light Source : Flash
Focal Length : 0.0 mm
You can extract the image profile (ICC Profile) from an image like this with exiftool:
exiftool -icc_profile -b foo.jpg > profile.icc
If you're hardcore, you can get the Windows Imaging Component (WIC) Tools and run WICExplorer. WPF uses WIC to decode images. WICExplorer will report the error with this image as you load it.
When you're using WPF (Windows Presentation Foundation) to display an image on Windows, you might do something like this:
<Image Width="300" Height="300" ImageFailed="Image_ImageFailed">
<Image.Source>
<BitmapImage UriSource="http://hanselman.com/blog/images/JPGwithBadColorProfile.jpg"/>
</Image.Source>
</Image>
Except with this particular image, I'll get an exception the Color Profile (the image metadata) is corrupted. "ArgumentException: Value does not fall within the expected range." This is a corrupted file.
at System.Windows.Media.ColorContext.GetColorContextsHelper(GetColorContextsDelegate getColorContexts)
at System.Windows.Media.Imaging.BitmapFrameDecode.get_ColorContexts()
at System.Windows.Media.Imaging.BitmapImage.FinalizeCreation()
at System.Windows.Media.Imaging.BitmapImage.OnDownloadCompleted(Object sender, EventArgs e)
at System.Windows.Media.UniqueEventHelper.InvokeEvents(Object sender, EventArgs args)
at System.Windows.Media.Imaging.LateBoundBitmapDecoder.DownloadCallback(Object arg)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
If I get this exception, I can try to load the image again and ignore its color profile. Here's how I'd do that in XAML:
<Image Width="300" Height="300" ImageFailed="Image_ImageFailed" >
<Image.Source>
<BitmapImage CreateOptions="IgnoreColorProfile" UriSource="http://hanselman.com/blog/images/JPGwithBadColorProfile.jpg"/>
</Image.Source>
</Image>
If you're loading from code, you can ignore color profile information by adding the BitmapCreateOptions.IgnoreColorProfile flag to CreateOptions.
As an aside, Andrew Eichacker has a nice post on how to read all the BitmapMetadata in WPF. There's lots in there!
Here's loading the Bitmap into an image Control called "Foo."
var bi = new BitmapImage();
bi.BeginInit();
bi.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bi.UriSource = new Uri("http://hanselman.com/blog/images/JPGwithBadColorProfile.jpg");
bi.EndInit();
foo.Source = bi;
Knowing about possible corruption is important to be aware of, especially if you're loading arbitrary images from all over the place. If you don't care about color profiles, I'd just ignore them by default in your image loading code. If you are writing an image editor or you care about profiles, I'd catch the exception, let the user know, then load the image again without the profile.
Are you in King County/Seattle/Redmond/Bellevue Washington and surrounding areas? Are you a huge nerd? Perhaps a geek? No? Maybe a dork, dweeb or wonk. Maybe you're in town for an SDR (Software Design Review) or VSLive! Quite possibly you're just a normal person and a member of the general public.
We're holding a Nerd Dinner (it's like dinner, except with nerds) next week on Weds, Aug 4th at Crossroads Bellevue Mall Food Court at 6pm.
This one will be different from usual. Why?
It's a Daily Build Nerd Dinner!
We'll have Program Managers and Developers directly from Microsoft who'll bring their laptops with "Daily Builds" of stuff they are working on. Daily Builds mean literally stuff they've built that day that's cool. You'll give them feedback and they'll show you cool new stuff like the Razor View Engine and Visual Studio Tooling.
If you are a member of the public, be here!
Nerd Dinners are for us. All are welcome. Just come on by. We'll be near the Chess Board in the Food Court. Bring your co-workers and your social media friend!
Please spread the word! RSVP and tweet about this dinner at http://nrddnr.com/2770
Add to your calendar now...here's an iCal link for your Outlook or other calendar.
See you soon!
Disclaimer: I work for Microsoft in MSDN. I don't work for or with the Windows Phone 7 team in any capacity. I do know one guy over there, though. That said, I personally have an iPhone 3G (dead, the kids play Monkey Island on it), a iPhone 3GS (the wife won't use it, it's on a shelf) and an iPhone 4 (my non-work phone). I also have a work Samsung Blackjack (WinMo 6.5). I signed up and paid for a Windows Phone 7 developer account and I have ideas for 3 apps. No one has asked me to blog about the phone, my opinions are my own. Also, this is a developer prototype with whatever build they shipped it with.
A Windows Phone 7 developer phone showed up in the mail today. Inside the battery door it said "MS Asset" so it looks like I won't be able to keep it. Still, it's cool. I pulled the MicroSIM out of my iPhone 4 and shoved it, ungracefully, into the normal-sized SIM slot and while it's not kosher, it totally works. I'll go get an converter/adapter at some point.
Here's some things I was impressed with:
Some things I had trouble with:
It'll take a while to get used to "it just works" from Microsoft. All in all, I'm pleasantly surprised as everything has just worked.
The wife thought it was cool too, although she wants a hardware keyboard that flips out. Apparently Dell is making one like that. I keep forgetting that the software and the hardware are separate. I am looking forward to seeing what HTC does with this. Those guys are nuts.
I took a few moments and filmed some guerilla video of me exploring the phone. Again, this is just the build that was mailed to me today, not the final stuff.
Windows Phone 7 - June 29th - Walkthough of Developer Phone from Scott Hanselman on Vimeo.
In my spare time, I'm going to be working on BabySmash for WP7, as well as a Diabetes application and maybe a few others. You can get the free developer tools at http://developer.windowsphone.com and sign up to sell your apps as well. I'm optimistic. This is quite a bit cooler than I expected. Looking forward to what's next.
Related Links
As a reminder, if you're just turning in, here's our glossary: There have been two posts so far:
There were lots of great comments on both posts, and arguably there is more content in the comments than the posts! I want to give a big thank you to all the thoughtful people who have commented. You all know more than I do about this topic.
The point of this exercise is several-fold.
Here's some scores, some results, and some commentary.
This is the WEI my stock Lenovo W500 8Gb with an aftermarket OCZ Vertex 256gig SSD.

Using Visual Studio 2010 and building the trunk of the NerdDinner project (a smallish sized ASP.NET app) is about 3 to 4 seconds. On my new PC, building NerdDinner from scratch happens before I can say "one one-thousand." I could try it from MSBuild and the command line...
Here's a better way to measure. Taking Chris Tavares' vsvars32.ps2, changed slightly for Visual Studio 2010 and run under PowerShell x86, I add this to my local machine's Microsoft.PowerShell_profile.ps1. (As well as running "set-executionpolicy unrestricted" as admin. Or set it to whatever you like).
function Get-Batchfile ($file) {
$cmd = "`"$file`" & set"
cmd /c $cmd | Foreach-Object {
$p, $v = $_.split('=')
Set-Item -path env:$p -value $v
}
}
function VsVars32($version = "10.0")
{
$key = "HKLM:SOFTWARE\Microsoft\VisualStudio\" + $version
$VsKey = get-ItemProperty $key
$VsInstallPath = [System.IO.Path]::GetDirectoryName($VsKey.InstallDir)
$VsToolsDir = [System.IO.Path]::GetDirectoryName($VsInstallPath)
$VsToolsDir = [System.IO.Path]::Combine($VsToolsDir, "Tools")
$BatchFile = [System.IO.Path]::Combine($VsToolsDir, "vsvars32.bat")
Get-Batchfile $BatchFile
[System.Console]::Title = "Visual Studio " + $version + " Windows Powershell"
}
Thanks to Chris, now I can type "VsVars32" at any PowerShell prompt and I'm immediately setup with a Visual Studio Command Line environment. Just as an FYI, while MSBuild does report timings, since I'm using PowerShell, I can surround any command with "measure-command" as in.
measure-command { msbuild /t:rebuild .\NerdDinner.sln }
In this case from the command line, NerdDinner takes 1900ms on my laptop and 900ms on my desktop, so this isn't a good test as the app is far too small.
Of course, I can also run msbuild with the /m switch and get parallel builds on multi-core machines. However, since NerdDinner only has two projects, I only get a 10% performance increase. Let's try a more significant project.
Here's the results, building NHibernate 3.0 Alpha using NHibernate.Everything.sln:
| Times are in seconds | Lenovo W500 w/ SSD | Ultimate PC 1.0 | Ultimate PC 2.0 |
|---|---|---|---|
| MSBuild /t:rebuild | 36.05 | 35.52 | 16.12 |
| MSBuild /t:rebuild /m | 24.98 | 25.57 | 12.53 |
| MSBuild | 6.17 | 6.99 | 3.11 |
| MSBuild /m | 5.91 | 6.66 | 2.72 |
Interesting that last year's laptop is very competitive with the original "Ultimate PC" from 2007 (still a beefy Quad Core) due to the laptop's new SSD.
Still, against the best numbers the new PC is effectively twice as fast or more at everything. Yesterday I was encoding a large HD video for Channel 9 and while the Lenovo took 3 hours, the new Ultimate PC 2.0 took 15 minutes. In some cases, it'll save hours on computationally intensive tasks, I believe. Additionally, for computationally intensive multi-tasking that have caused me to use multiple computers in the past, this machine has handled easily. For example, while I was encoding that video, I was also streaming live 720p HD video over Office Communicator. This takes at least 2 procs working full and slowed my Ultimate PC 1.0 down to the point of uselessness. The aggregate CPU on the new machine never went above 12% while just streaming video.
While I did go overboard on the Video Card, I'm extremely happy with the computer so far. I threw in an NVidia GTX275 I had laying around to run another 22" monitor while the GTX480 is running the 30" and a 24". The GTX480 does run hot. For you, dear reader, a GTX275 or any decent $100 card would do you just fine and you'd save a lot of money.
Here's the final parts list along with some caveats. Remember, building your own PC does come with risks, as you're sometimes putting together different companies parts and things don't always work perfectly. You can get a great 7.x WEI machine for $1000 from just about anyone.
Also, Shay Erlichmen wrote some Google Apps Javascript and put up a LIVE Google Spreadsheet that will scrape NewEgg's site and put up the current total of all the components. Thanks to Shay!
I built the case over two 4 hour sessions and broadcasted live to 80 of my close internet friends on UStream. A lot of really smart people helped me out on this build and offered a lot of interesting points.
The first day build went bad because of the power supply issue so we got it built, then spent hours thinking it was a motherboard issue. It was the power supply.
After we assembled the machine and before we installed any drivers, here was the first WEI score. The 1.0s happened because the standard VGA driver loaded.

After installing the Intel Chipset drivers for the Motherboard and the latest NVidia drivers, we were at 7.8 for video and 7.4 all up with the SSD holding us back.

I moved the SSD to another SATA port as this motherboard has multiple SATA chipsets (Intel, Marvel) and installed the latest drivers. In one case, setting the BIOS to IDE instead of AHCI seemed to stick me at 5.9 for disk.

Once I moved onto the Marvel SATA 3 controller with 6 GB/s I got 7.9 on disk. Interestingly the same drive on the Intel SATA 2 controller gets 7.7. The Marvel gets 310MB/s out of the Crucial SSD while the Intel controller gets 270MB/s.
At this point, running everything stock and air-cooled, I have a nice 7.8 machine. I suspect I won't be able to get the Processor to 7.9 without overclocking to 4Ghz. As it is, the machine is running lovely. I wouldn't mind overclocking but I have decided against water cooling as I'm just too old and I need a machine to work 100% of the time. Pete Brown will be water cooling his, he says, so I'll be following his build. That said, if anyone wants to sit with me and chat about if it's possible to get this machine to 4Ghz using air-cooling, give me a holler. As for the graphics, I don't see how I can get to 7.9 without something drastic.
All in all, a great time was had by all. Thanks so much to everyone who helped and to all the wonderful (and waspish) commenters who made this a better experience.
Related Links:
Woohoo!
.Phil and friends released ASP.NET MVC 3 Preview 1 today. I snuck into the office of Phil Haack and Morgan the Canadian Intern to talk about the release of ASP.NET MVC 3 Preview 1 and some of the cool "futures" stuff that Morgan (and our fleet of interns) is working on. This video isn't only about MVC as Morgan's working on some cool CSS Sprites stuff that works nicely in WebForms that you should check out as well.
Also, my two-hundred-and-twenty-fourth podcast is up and I talk more in depth with, yes, you guessed, it, Phil Haack. More detail in a less shaky-camera format.
Note that installing ASP.NET MVC 3 won't mess up your ASP.NET MVC 2 applications.
Telerik is our sponsor for this show.
Hanselminutes podcasts listeners can get $50 off any Telerik product this summer. All interested listeners should drop an email to podcast@telerik.com and mention the Hanselminutes promo and their sales team will reply with the special $50-off coupon code.
Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface and developer tools, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET AJAX, MVC, Silverlight,Windows Forms and WPF. Enjoy developer tools like .NET Reporting,ORM, Automated Testing Tools, Agile Project Management Tools, and Content Management Solution. And now you can increase your productivity with JustCode, Telerik’s new productivity tool for code analysis and refactoring. Visit www.telerik.com.
Enjoy!
My two-hundred-and-twenty-second podcast is up. Scott sits down with designer Jin Yang (@jzy) to talk about the fundamental differences between developers and designers. Are we a totally different breed? How should designers and developers work together? Should designers code their own sites?
NOTE: If you want to download our complete archives as a feed - that's all 219 shows, subscribe to the Complete MP3 Feed here.
Download: MP3 Full Show
Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.
Telerik is our sponsor for this show.
Hanselminutes podcasts listeners can get $50 off any Telerik product this summer. All interested listeners should drop an email to podcast@telerik.com and mention the Hanselminutes promo and their sales team will reply with the special $50-off coupon code.
Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface and developer tools, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET AJAX,MVC,Silverlight,Windows Formsand WPF. Enjoy developer tools like .NET reporting,ORM,Automated Testing Tools, TFS, and Content Management Solution. And now you can increase your productivity with JustCode, Telerik’s new productivity tool for code analysis and refactoring. Visitwww.telerik.com.
As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)
Enjoy. Who knows what'll happen in the next show?
My two-hundred-and-twenty-first podcast is up. This week it's a very sick Dan Fernandez from Channel 9 who joins me on the show for a random Hanselminutiae. We talk iPads, Windows Phone 7, Hulu Plus, TimeSvr, innovation and more. Dan is the co-host of This Week on Channel 9, a random and cool weekly video show on Channel 9 that you can subscribe to here!
NOTE: If you want to download our complete archives as a feed - that's all 219 shows, subscribe to the Complete MP3 Feed here.
Download: MP3 Full Show
Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.
Telerik is our sponsor for this show.
Hanselminutes podcasts listeners can get $50 off any Telerik product this summer. All interested listeners should drop an email to podcast@telerik.com and mention the Hanselminutes promo and their sales team will reply with the special $50-off coupon code.
Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface and developer tools, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET AJAX,MVC,Silverlight,Windows Formsand WPF. Enjoy developer tools like .NET reporting,ORM,Automated Testing Tools, TFS, and Content Management Solution. And now you can increase your productivity with JustCode, Telerik’s new productivity tool for code analysis and refactoring. Visitwww.telerik.com.
As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)
Enjoy. Who knows what'll happen in the next show?
Microsoft's been releasing a number of right-sized LEGO pieces lately. In case you missed it, Betas have been announced for:
More details to come on all this. Howver, on the tooling side, I did get a chance to talk to Damian Edwards, a developer working on some of this stuff and I put video up on Channel 9 yesterday.
There's lots of cool pieces that are packaged up with WebMatrix initially, but these pieces are interesting for pro developers as well.
Still, something's missing.
In my mind, it's been too hard to talk to databases. I like LINQ to SQL and used it on the first NerdDinner version, but since EF4 sucks so much less is way better than earlier versions of EF, Jon and I updated NerdDinner to use EF. It was easy, but I would have liked to code first, and code only if I could.
Microsoft announced a new Entity Framework CTP today. It has the romantic and wonderful name "Microsoft ADO.NET Entity Framework Feature CTP4" which is lame. You can say "EF Feature CTP4" but I like "EF Magic Unicorn Edition" but that's just me. We're getting the tech better at Microsoft but still can't get naming right. Whadayagonnado? Still, it makes EF a pleasure.
It's got a lot of interesting features and choices, and while it's still a CTP, you should take a minute and check it out.
To get a more detailed version of this walkthrough plus downloadable sample code, check out the ADO team's excellent blog post.
After you install it (it won't mess up your system if you do), go and create a new whatever project. For my little example, I'll make a new ASP.NET MVC Website. It works for me better than a console app to illustrate a point.
Add a reference to Microsoft.Data.Entity.CTP.dll.
Make a new class, maybe in the Models folder, and name it something like Book. Add some code like this. Notice it's just code. Nothing derives from anything.
public class Book
{
[Key]
public int ISBN { get; set; }
[Required]
public string Title { get; set; }
public DateTime FirstPublished { get; set; }
public bool IsFiction { get; set; }
}
Notice I've put [Key] and [Required] on this class, but if that bothers me, I could put these kinds of declarations in a more fluent way in my database context class, in OnModelCreating.
builder.Entity<Book>().HasKey(b => b.ISBN);
builder.Entity<Book>().Property(b => b.Title).IsRequired();
To access my data, Here's a SimpleBookCatalog...
public class SimpleBookCatalog : DbContext
{
public DbSet<Book> Books { get; set; }
}
Next, I'll make a new Controller, via right|click Add Controller. I'll make a BookController.
public class BookController : Controller
{
SimpleBookCatalog db = new SimpleBookCatalog();
public ActionResult Index()
{
var books = db.Books;
return View(books);
}
...
}
I'll right click on the Index method and make an Index view. Then I'll run my app.
No data. What if I look in my SQL Management Studio? I got a Database created for me with a convention-based name since I didn't supply one.
If I specified a different connection string, that DB could be anywhere.
However, if use a different database provider, like say, a SQL 4 Compact Edition one, setting it as the default in my Application_Start:
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
Then when I run my app and look in my App_Data folder:
So I got a file based database without doing anything and I don't need SQL Server. (Yes, I can change the name, location, etc.) If I do nothing, I get a reasonable convention.
Next, I'll add two Create methods, one for a GET and one for a POST. In Create, I'll add my new book and save the changes:
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Book book)
{
db.Books.Add(book);
db.SaveChanges();
return RedirectToAction("Index");
}
I'll right click, Add View, and make a Create View. Run my app, look at the empty list, then click Create.
Click Create, and I'm redirected back to the Index page:
Back on the Index page, I can change the link to Details to use our primary key:
<%: Html.ActionLink("Details", "Details", new { id=item.ISBN })%> |
Create a Details View and add a Details method:
public ActionResult Details(int id)
{
var book = db.Books.Find(id);
return View(book);
}
See that Find method? That's there automatically. I can certainly use al the LINQy goodness as well, but as you can see, CRUD is simple. I can hook up Edit and Delete in a few minutes as well.
Here's the whole thing:
public class BooksController : Controller
{
SimpleBookCatalog db = new SimpleBookCatalog();
public ActionResult Index()
{
var books = db.Books;
return View(books);
}
// GET: /Books/Details/5
public ActionResult Details(int id)
{
var book = db.Books.Find(id);
return View(book);
}
// GET: /Books/Create
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Book book)
{
db.Books.Add(book);
db.SaveChanges();
return RedirectToAction("Index");
}
// GET: /Books/Edit/5
public ActionResult Edit(int id)
{
return View(db.Books.Find(id));
}
// POST: /Books/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var book = db.Books.Find(id);
UpdateModel(book);
db.SaveChanges();
return RedirectToAction("Index");
}
// GET: /Books/Delete/5
public ActionResult Delete(int id)
{
var book = db.Books.Find(id);
return View(book);
}
// POST: /Books/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
db.Books.Remove(db.Books.Find(id));
db.SaveChanges();
return RedirectToAction("Index");
}
}
So that's a nice simple controller that uses a model that was written in just code. The database and its schema was created for me. The DbContext is LINQable with stuff like Add, Find, and Remove all just there. Plus, it's all EF under the hood, so if you need more complex stuff, you can do it.
For example, here's a more complex Code First Model with Collections, and more attributes. I show some fluent wiring up of relationships later on, although there are conventions that can assign bi-directionality based on naming.
public class Book
{
[Key]
public int ISBN { get; set; }
[Required]
public string Title { get; set; }
[Required]
public DateTime FirstPublished { get; set; }
[Required]
public bool IsFiction { get; set; }
public virtual Publisher Publisher { get; set; }
[RelatedTo(RelatedProperty="Author")]
public virtual Author Author { get; set; }
}
public class Person
{
[ScaffoldColumn(false)]
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Author : Person
{
[ScaffoldColumn(false)]
public int AuthorId { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class Publisher
{
[ScaffoldColumn(false)]
public int PublisherId { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class SimpleBookCatalog : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<Author> Authors { get; set; }
public DbSet<Publisher> Publishers { get; set; }
}
Also, "Magic Unicorn EF" supports DataAnnotations (or validation via Fluent interfaces), so those [Required] and [StringLength] stuff from before? Those apply not only in JavaScript, but also at the Server-side and Database persistence layers.
You can make your own strategies for creating databases, based on what's going on with the model, if it's changed, etc. Here's some built-in examples. Yours can do whatever you like. Here the SimpleBookCatalog is the DbContext from before.
//This is the default strategy. It creates the DB only if it doesn't exist
Database.SetInitializer(new CreateDatabaseOnlyIfNotExists<SimpleBookCatalog>());
//Recreates the DB if the model changes but doesn't insert seed data.
Database.SetInitializer(new RecreateDatabaseIfModelChanges<SimpleBookCatalog>());
//Strategy for always recreating the DB every time the app is run.
Database.SetInitializer(new AlwaysRecreateDatabase<SimpleBookCatalog>());
Connection Strings for the SQL 4 CE provider are simpler (like, they are possible to memorize, which is amazing, considering how hard they are now). For example:
<add name="SimpleBookCatalog" connectionString="Data Source=C:\FooFoo\MyBooks.sdf" providerName="System.Data.SqlServerCe.4.0"/>
Here's some examples of fluent mappings and setting up relationships to give you an idea of the kinds of things you can do, while avoiding looking at any visual designers. It all depends on how clean you need your POCO (Plain Old CLR Objects) to be.
modelBuilder.Entity<Book>().HasKey(b => b.ISBN);
modelBuilder.Entity<Book>().HasRequired(b => b.Title);
modelBuilder.Entity<Book>().HasRequired(b => b.Author).WithMany(a => a.Books);
modelBuilder.Entity<Publisher>().Property(p => p.Name).MaxLength = 50;
modelBuilder.Entity<Author>().HasMany(a => a.Books).WithRequired();
These configurations can be batched up into a class that handles configuration for a specific entity so you can reuse them and more easily config like this.
builder.Configurations.Add(new BookConfiguration());
All this is a really basic example as a means of introduction and for my own learning, but so far I like it. It takes just a few minutes to get a lot done without much code. Since this is all Entity Framework, I can put an OData service on top of my model really quickly and then start consuming those services from iPhones or whatever.
It'll be interesting to take a sample like Nerd Dinner or MVC Music Store and change them to use Code First EF and the Razor View Engine and see if they are more readable and how many fewer lines of code they are.
Related Links
As a reminder, if you're just turning in, here's our glossary:
There were lots of great comments in the original post. Here's some of my answers to those comments, as well as some on Twitter. The fun thing about doing hardware builds is that EVERYONE has an opinion. Also, since I'm a software guy (although I could totally rock a 486-SX build) your opinion, Dear Reader, is better than mine. I can only explain why I (and Pete, who is doing a similar build with me) made certain decisions.
We hosted a great podcast last week with some actual members of the WEI team and asked their ideas on how we could create the Ultimate Developer PC. You can listen to Hanselminutes Podcast 220 on "Creating the Ultimate Developer Machine 2.0 - How can we get a 7.9 WEI Score for under $3k?" here.
There's some guiding principles for this build.
So here's what I purchased and what I was thinking when I did it.
I have likely (and will likely) make mistakes here, but that's part of the fun of building your own machine. Remember, Jedis build their own lightsabers, so you should build your own computer at least one!
I'll be building this in the next week or two.
QUESTION: Is there any interest in a "live build" where we stream the build and the viewers make suggestions, ask questions, etc?
Your thoughts?
ScottGu announced Microsoft WebMatrix Beta today.
It's a small (15 megs if you have .NET 4, 50megs if you don't) lightweight IDE for making ASP.NET or PHP websites. I've said before that Microsoft is just now starting to get the "right-sized LEGO pieces." It the past some stuff was too little or too big, but now as an example, here's five separate and right-sized LEGOs (ya, I know it's singular) that are helpful for the professional and the folks just getting starter, or those who don't want to learn a bunch of stuff to setup and/or modify a website.
Visual Studio 2010 looks like a dashboard. It's big and powerful and overwhelming. I tried to get my wife, then later my 10 year old nephew to make a website and it was a non-starter. It worked later with WebMatrix. This isn't for professionals, even though it has components that professionals will use. Pros will like a free file-based SQL Server, and a non-service version of IIS that's more compatible than the Visual Web Developer, and pros might like using Razor as an alternative View Engine for their ASP.NET MVC sites.
However, folks that are starting out, or hobbyists, or maybe just installing a blog or forum will use WebMatrix to get stuff done.
Here's how you'd get WebMatrix, sign up for a new host, and deploy your first site.
Go to the WebMatrix site and click Download Now. It'll install the Web Platform Install (about 2 megs) and then you click Install. The WebMatrix download for me was 8megs, then SQL Compact was 2.5megs, and then some deployment dependencies brought my total download to 29 megs.
You can install a site from the Gallery, like ScottGu did when he chose BlogEngine.NET. These are regular ASP.NET and PHP apps that you've probably used before. There's lots of Open Source apps like Blogs (like DasBlog, that runs this site!), Content Management Systems (like Umbraco, that powers http://asp.net!) and other apps that you can start working with immediately.
However, since we're learning, my wife and I are going to select Site from Template and pick the Bakery App. This is a basic store-type application that we can deploy in just a few minutes. We don't need to look at code if we don't want to.
Hit OK and I'm in WebMatrix looking a new screen.
At this point I can click Run to see my app.
It's running locally on IIS Express along with some others Apps I was looking at earlier. This is effectively FULL IIS (not Visual Web Developer) but installed as a User App...when I stop the app, it's gone. It's not an auto-startup service, but it is IIS, which means your apps will run the same locally as they do deployed.
I can run it locally, but how do I get it up to a host?
Things usually get complicated when you go to deploy and app. It's pretty easy to get an application running locally, but it's sometimes a challenge to get that app up on a site. People can sign up for a Gmail or Hotmail account and get that running, so why can't they get a website up that they coded themselves?
I've blogged about WebDeployment before and shown examples in my Mix Video "If you're using XCopy you're doing it wrong." WebDeploy is part of WebMatrix also.
I'll click on Publish and "Find Web Hosting" and I get a list of hosts I can choose from. I'll select Applied Innovations and click Learn More. I do prefer hosts like AdHost that give you both a .NET 2 and .NET 4 Application Pools to work in, which means I can use apps from the Gallery and apps that use the new Razor Syntax at the same time.
I go to Applied Innovations, and fill out the form. I'll get free hosting until next year with 1 GB of Disk Space and 100 GB monthly transfer. I get .NET 3.5 and 4, but also PHP. I can use SQL Compact, but also mySQL.
I fill out a form - no credit card needed - and they send me an email. Click a link and they send me back an email with all the details I need. This is the same experience you'll get with all the hosters.
My Opinion: Some hosts make you switch your AppPools between .NET 2 and .NET 4 and that's too hard for beginners, I think. Hopefully all the hosters will remove this step and offer two app pools, or make switching between them a top level button in their Control Panels. AdHost does this. I hope others will also.
To switch to .NET 4, go to the Control Panel URL in your welcome email. Click WebSites, Extensions and select ASP.NET 4 from the dropdown.
Other hosts to choose from during this Beta are:
I fill out the Publish Form in WebMatrix with the details direct from my new host.
Notice I don't need to do anything special for my database, as it's a file-based SQL database, runs in Medium Trust and requires no configuration.
Click Publish, and I'll get a list of files that'll be copied up. This is a differential copy, so if you change one file later, only that file will go up. Be sure to click the checkbox to deploy your database.
The publish happens in the background...
Then it completes, and I can now visit my new Bakery site online at my host, AppliedI:
Of course, if I wanted to, I could change the site, maybe modify the Products page:
@{
LayoutPage = "~/_Layout.cshtml";
PageData["Title"] = "Product";
var db = Database.Open("bakery");
}
<h2>Available Products:</h2>
<div class="products group">
@foreach (var p in db.Query("SELECT * FROM PRODUCTS")) {
<div class="product">
<h3>@p.Name</h3>
<img src="@Href("~/Images/"+ p.ImageName)" alt="@p.Name"/>
<p>@p.Description</p>
<ul class="group">
<li class="price">$@string.Format("{0:f}", p.Price)</li>
<li class="order">
<form action="Order" method="post">
<input type="hidden" name="ProductId" value="@p.Id"/>
<input type="submit" value="Order Now"/>
</form>
</li>
<ul>
</div>
}
</div>
There's lots of nice helpers for Google Analytics and Twitter integration:
<div class="sidebar">I could of course, also edit my data:
@Twitter.Search("#bakery", caption: "#bakery")
</div>
All in a reasonably not scary-dashboard looking UI. It's all ASP.NET underneath, so I can graduate to ASP.NET MVC and move my logic into controllers, and I've already got Views written in the "Razor" syntax, which is the new default for ASP.NET MVC 3.
If you're reading this blog, and you're not my Wife, this tool probably isn't for you. (Hi, wife.)
However, the pieces that make up WebMatrix probably are. SQL Compact is pretty sweet. It's small, free, file-based and easy to upgrade to SQL Express Big Boys and Girls Edition. The new Razor syntax is a nice alternative to the WebForms Syntax (as are other alternative ViewEngines). IIS Express is hotness, especially if you've ever had an app that acted differently on IIS than on Visual Web Developer.
If you are learning web development or just want to get a site up, check out WebMatrix. Even if you don't edit the code, you can get and deploy and app to a host quickly, be it an ASP.NET app or a PHP app. If you do choose to write an app yourself, you can get started with WebMatrix and then move to Visual Studio (Free Express or $ Pro) if you outgrow it.
I'll report back and see what the WAF (Wife Acceptance Factor) is on WebMatrix.
Related Links
Bookmark and Share this page