I <3 Steve McConnell
Coding Horror
programming and human factors
by Jeff Atwood


3 posts from April 2010

April 30, 2010

What's Wrong With CSS

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:

serverfault.com screenshot superuser.com screenshot
meta.stackoverflow screenshot stackoverflow.com screenshot

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:

  • Vertical alignment is a giant, hacky PITA. (Tables work great for this though!)
  • Lack of variables so we have to repeat colors all over the place.
  • Lack of nesting so we have to repeat huge blocks of CSS all over the place.

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.

Posted by Jeff Atwood    89 Comments

April 21, 2010

So You'd Like to Send Some Email (Through Code)

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.

  1. _domainkey.example.com
    "o=~\; r=contact@example.com"
  2. selector._domainkey.example.com
    "k=rsa\; p={public-key-base64-string-here}"

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:

  1. Test emails sent to a GMail account.

    Use the "show original" menu on the arriving email to see the raw message content as seen by the email server. You want to verify that the headers definitely contain the following:

    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.

  2. Test emails sent to the Port25 email verifier

    Port25 offers a really nifty public service -- you can send email to check-auth@verifier.port25.com and it will reply to the from: address with an extensive diagnostic! Here's an example summary result from a test email I just sent to it:

    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.

Posted by Jeff Atwood    66 Comments

April 4, 2010

Three Monitors For Every User

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:

  1. Buy an exotic video card that has more than 2 monitor connections.
  2. 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:

Radeon-eyefinity-video-card-outputs

It contains:

  • two DVI outputs
  • one HDMI output
  • one DisplayPort output

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:

  1. a passive, analog DisplayPort to DVI conversion cable for ~$30 that supports up to 1920x1200
  2. an active, digital DisplayPort to DVI converter for $110 that supports all resolutions

I ended up going with the active converter, which has mixed reviews, but it's worked well for me over the last few weeks.

Accell-ultraav-displayport-to-dvi-adapter

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.

Posted by Jeff Atwood    98 Comments
Content (c) 2009 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved.