October 2006 Blog Posts
Jeff Atwood points out several problems with using blacklists (specifically Akismet) to prevent comment spam. He makes the following point:
The core problem is relying on a single method of defense against spam.
Absolutely. Subtext employs several measures against comment spam, mostly of a heuristic nature. The latest release adds Akismet support as well as Visible and Invisible CAPTCHA.
The funny thing about CAPTCHA and especially Invisible CAPTCHA is the number of people who claim it won’t work and is broken. As Jeff points out, this may be true among researchers, but it is not the case in the wild. However let me back up his post with some numbers.
For the past four days, I have not emptied my blog spam folder, just to see what gets put in there. So far, in that time, my blog has received 1441 comments, trackbacks, etc... Of those, 1407 have been flagged as spam by Akismet or marked as spam by me. Of those, only one was a comment. The rest were trackbacks/pingbacks.
So as far as I am concerned, Invisible CAPTCHA is working well so far. And it has the benefit of being usable, assuming you can do simple math.
So assuming that CAPTCHA, for now, is the best approach to fighting comment spam, we need to deal with its critical weakness.
The real problem is how do we enable CAPTCHA for trackbacks?
I wrote about this problem before when I discussed my qualms about CAPTCHA.
The reason I didn’t mention CAPTCHA is that it would be ineffective for me. Much of my spam comes in via automated means such as a trackback/pingback . The whole point of a trackback is to allow another computer to post a comment on my site. So telling a computer apart from a human in that situation is pointless.
I mentioned this to Atwood who pointed out that trackbacks and pingbacks are indeed automated, but they are left on behalf of a user. This is true. When I write a blog post, Subtext will look at all the links in my post and attempt to trackback each one for me.
Unfortunately, the trackback and pingback APIs have no facility for dealing with CAPTCHA. Unless there were a community effort to revise these specs (I would be happy to join in), CAPTCHA for trackbacks and pingbacks are not gonna happen.
Even with such a community effort, implementing CAPTCHA for trackbacks is going to be a lot of work for blog implementers. In part, this is indicative of a usability issue with CAPTCHA based approaches. CAPTCHA requires human intervention. This makes integrating CAPTCHA with something like trackbacks hard work, whereas if someone comes up with a better automated filter, integrating that is easy.
So for the time being, we have two choices.
- Abandon Trackbacks/Pingbacks
- Find better ways to filter trackbacks and pingbacks.
I know many have decided to simply abandon trackbacks. I understand this choice, but I personally am not ready to throw in the towel just yet. Trackbacks can and do add a lot of value to discussions that occur via blogs. So far, Akismet has allowed me to reclaim trackbacks.
What is the next step? Well I agree with Jeff:
Akismet is a fine addition to our anti-spamming toolkit. But that doesn't mean it's a good idea to outsource your entire anti-spam effort to a single website, either. Anti-spam security starts at home. For best results, use defense in depth and combine local anti-spam measures, such as CAPTCHA, with Akismet as a backup.
Though I think we need to start working on some better non-CAPTCHA filters to combine with Akismet.
I love Halloween because it makes for great parties. What makes a party great? Costumes! This past weekend I drove up from Los Angeles to San Jose to stay with my friend Kyle and his wife Cara. That evening we drove up to San Francisco to attend a house party in a large four story house in the hills. It was about the perfect size for the 100+ people in attendance, and they hade a few top-notch local DJs spinning.
Unfortunately, I didn’t bring my camera so I had to rely on “donated pics”. I usually am a total shutterfly. Of course, not having my camera gave me more time to enjoy the party. Click on a pic for a larger version.
My buddy Kyle went as Noam Chomsky, which was a hit of a costume, as simple as it was. It’s rather freaky to see a black and white 2-D face on a normal body. The face looked almost disembodied. The first pic with Hunter S. Thompson just cracks me up. What a literary powerhouse! I went as the thoroughly unimaginative “Kung-Fu guy”. The necklace I’m wearing is made up of miniature skulls of my enemies. Yes, I fight those crazy Lilliputians.
The DEA agent was probably one of my favorite costumes, just for the chill he sent down the spine of some partygoers.
As expected, someone with showed up in a Crocodile Hunter costume, though I don’t understand the red wig. We also had Cartman and his favorite snack in attendance.
Finally, the governator showed up, but he was inappropriately dressed. I had to black out that portion of his costume. Let’s just say, it took a pretty big circle to black that portion out. Good going Guv’nor!
The beaker costume above was one of my favorites. She and her boyfriend both were beakers. Apparently the sight of them making out was disturbing. That’s my friend Jason aka Count Pimpula on the right with his girl Rocel.
Steve Harman points out another bug in the upgrade process. I feel really terrible that this one slipped through, though as far as I can tell so far, it may be mostly cosmetic in its effect. It apparently converts some comments to trackbacks. While this is not desirable behavior, since we show both comments and trackbacks in the comment section, it might not have a huge negative effect.
Unfortunately, I am in San Jose right now, so I can’t update the release with a fix yet. Hopefully Steve can handle it.
Actually yes, I would like a little cheese with that whine.
In the essay entitled Hold the Mayo, 37signals points out the obvious fact that most surveys ask users what features they want added to a product. They rarely ask what features they want removed.
I have in the past asked users for permission to remove features, but I've never taken the extra step of asking users, which features would they like removed. So here I go.
Which feature(s) would you like to see removed from Subtext?
I think a natural response I will receive is the question, Why would you ever want to remove a feature?
Features have many hidden costs. Even relatively simple features. I am going to tell a short story about one such occurrence that happened in Subtext. I won't name names and this is not meant to call anyone out for public embarrasment or chastisement. I have probably been more guilty of this than anyone.
Not too long ago, someone checked in a control into Subtext that displays recent comments, just as we were close to preparing a release. I was a little miffed at the time because I was not expecting anyone to add features at the time.
However this was a pet feature and some of the devs really wanted it in the system. Besides, it's such a small feature, what could go wrong? So I let it in not wanting to be a hard-ass about it.
The recent comments control is a pretty simple control in concept. When added to the main skin template, it simply displays recent comments left on various blog posts. Sounds simple, no?
However, as with any feature, the devil is in the details. Several problems immediately became apparent as we tested the release, and one problem affected me in a later release.
- The control let you truncate comments after a certain number of characters. However it didn't strip HTML out, so if it truncated a comment in the middle of a tag, it would completely mess up the whole page.
- The control bypassed our provider model and made stored proc calls directly.
- The control displayed all feedback for a blog, even ones that were left via the Contact Page, thus potentially displaying private messages.
- Recently, I tried to append a little HTML comment after messages that had been processed by Akismet to make it easy to see that Akismet was indeed working by viewing source. Comments aren't being stripped by the Recent Comments control, so the site was broken in a way that I had not anticipated. It took a long time before I realized what was happening.
So for a relatively small feature, a lot of development time and effort was used up in supporting it. I am glad we have the feature now, I really like it and plan to add it to my own blog at some point.
But the main point still stands, every feature is like an iceberg. When scoping it out in your head, you typically only think of the top part that sticks out above the water. However, the real effort is in the part under water that supports the whole thing.
So if there is a feature in your product that provides very little bang for the support buck, consider getting rid of it.
So again, Which feature(s) would you like to see removed from Subtext?
Now that 37signals have put their book Getting Real online for free, I’ve finally gotten around to start reading it. And so far, I love it. I think there are a lot of great lessons, reminders, ideas in here that will help me make products I work on that much better.
I have a premonition, even before writing this, that a lot of people will tell me that the book is crap because they don’t believe in functional specifications and even Joel believes in functional specifications. As the authors point out in their Caveats, disclaimers, and other preemptive strikes page, their techniques don’t apply to everyone (though probably to more people than one would think). Also, their ideas are not an all or nothing affair. Gotta have your functional specs, then by all means keep doing it. Don’t throw out the baby with the bathwater.
Open Source projects are probably in the best position to make use of their guidelines, though I am sure many more projects would be made much better by getting real. Subtext will most certainly gain from many of these approaches. My hope is to use Subtext as a testing ground for the principles in Getting Real, and then using its success to show my clients that they don’t need to be so rigid about product development. We’ll see.
As you might guess, many of my upcoming blog posts will focus on some of these topics.
As I mentioned in my last post, someone reported a bug with deleting posts in the admin tool. That posts also describes a quick and dirty workaround.
However, I fixed the bug and updated our current 1.9.2 release at SourceForge. The existing URL to download the release is still valid. The full version number for this release is 18.104.22.168.
To find out which version of Subtext you are running, just log into your admin and look in the bottom left corner. You should see the version there.
If you downloaded Subtext 1.9.2 before I applied the update, then you will probably see version 22.214.171.124 in there. If so, you have two options. You can either apply the quick and dirty patch mentioned in my last post, or you can download the latest build and update the
Subtext.Framework.dll file in the
However, this latest update also includes some CSS fixes for the KeyWest skin in IE 7 designed (and fixed) by Robb Allen. So if you are using that skin, you should download and apply this update.
Someone reported that they cannot delete posts in the just released Subtext 1.9.2. I am mortified that we do do not have a unit test for this function! To our defense, we did start with 0% code coverage in unit tests and have now reached 37.9% and rising!
I have a quick fix if this problem affects you. I am also currently building a more permanent fix which I will release soon.
Run the following query in Query Analyzer (don’t forget to hit
CTRL+SHIFT+M to replace the template parameters before executing this).
, @BlogID int = NULL
DELETE FROM [<dbUser,varchar,dbo>].[subtext_Links]
WHERE PostID = @ID
DELETE FROM [<dbUser,varchar,dbo>].[subtext_EntryViewCount]
WHERE EntryID = @ID
DELETE FROM [<dbUser,varchar,dbo>].[subtext_Referrals]
WHERE EntryID = @ID
DELETE FROM [<dbUser,varchar,dbo>].[subtext_Feedback]
WHERE EntryId = @ID
DELETE FROM [<dbUser,varchar,dbo>].[subtext_Content]
WHERE [ID] = @ID
SET QUOTED_IDENTIFIER OFF
SET ANSI_NULLS ON
GRANT EXECUTE ON
Sorry for those that this affects. Like I said, we’ll have a bug fix out soon.
Well if you’re the punk malcontent who found my wallet yesterday, you go on a shopping spree...at Rite Aid, Vons, and Ralphs (a convenience store and two grocery stores).
Really, if you’re going to try and spend my hard-earned credit, at least go buy something decent like a stereo system. There’s a Best Buy just down the street that isn’t much further away than the local Vons.
I mean who spends $103 at a Rite-Aid?! Who!? I really mean it. Who? I’d like to know because I want my wallet back. And to the store clerks there, doesn’t it even slightly ring the suspicion bell in your head when someone tries to buy $103 worth of anything at Rite-Aid? I didn’t even know you could buy that much stuff at a Rite-Aid. I figured buying out everything in the store would lighten your wallet by about $70, tops. Maybe he bought the cash register too.
Did you even look at the guy and compare his picture to the Id, my Id, he presented? There’s not a lot of hapas running around L.A. so I doubt he looked that much like me, assuming it was a "he".
Anyways, yesterday on a short walk, I dropped my wallet. A really comical mistake like you see in the cartoons. I put my wallet in my side cargo-short pocket, which unbeknownst to me, had a huge gaping hole in it. I returned back to the area literally 10 to 15 minutes later, and it was gone. And then the charges started showing up in my account as I frantically called to close my cards.
Anyways, that’s why I rushed out a new release of Subtext last night. I figure follow something bad with something good. Oh, and I bought a lottery ticket. I know, the chances are pretty much nill. But what were the chances I’d put a wallet in a pocket with no bottom? This is a chance for the world to get back to even terms with me.
Having trouble sleeping lately. Thought I'd start an intermittent blog series about the questions that keep me up at night.
For example, this question popped in my head tonight and would not let me rest.
What the hell is a Karma Chameleon?
Ponder amongst yourselves. I bid you adieu.
UPDATE: A bug was reported that blog posts could not be deleted. We have updated the release files with a fixed version. There’s also a quick and dirty workaround.
Reading over my last blog post, I realized I can be a bit long winded when describing the latest release of Subtext. Can you blame me? I pour my blood, sweat, and tears (minus the blood) into this project. Doesn’t that give me some leeway to be a total blowhard?
This post is for those who just want the meat. Subtext 1.9.2 has the goal of making the world safe for trackbacks and comments. It adds significant comment spam blocking support. Here are the key take-aways for upgrading.
- As always, backup your database and site first before upgrading. We implemented a major schema change which requires that the upgrade process move some data to a new table.
- If you are upgrading from Subtext 1.5 or earlier, read this.
- Instructions for upgrading.
- Instructions for a clean installation. This is easier than upgrading.
- When upgrading, make sure to merge your customizations to web.config into the new web.config.
- If you use Akismet, make sure not to use ReverseDOS until we resolve some issues.
- After upgrading, login to the admin and select the correct timezone that you are located in.
Download it here.
Making the world safe for trackbacks again!
UPDATE: A bug was reported that blog posts could not be deleted. We have updated the release files with a fixed version. There’s also a quick and dirty workaround. You only need to apply the fix if you downloaded and installed Subtext before this update message was posted. See here for details.
Well it took me a little longer than anticipated, but I finally teased out the remaining show stopper bugs and put the finishing touches on Subtext 1.9.2. If you plan on upgrading to Subtext 1.9.2, please consider reading this entire post carefully. If not, at least remember to backup your database and site first before upgrading!
What happened to 1.9.1? Long story for another time. We are skipping from 1.9 to 1.9.2. Here is a list of releases so far just in case you were curious. UPDATE: I had only listed the versions that required an automatic upgrade. Here is the complete list with the ones in bold required an automatic database update:
- Subtext 1.0
- Subtext 1.5
- Subtext 1.5.1
- Subtext 1.5.2
- Subtext 1.9
- Subtext 1.9.2
As you can see, our version numbering has been a bit less than consistent. But starting with 2.0, we should stay a bit more consistent. With the launch of IE 7, don’t be surprised if we come out with a 1.9.3 version that just includes fixes to the skins. Volunteers for that effort are welcome.
The reason we call this the “Shields Up” edition is that the focus has been on dealing with Comment Spam. I had few interesting ideas I could not implement in time, but I did implement three key features that have really made a huge difference in regards to comment spam, and may not necessitate my other ideas.
Subtext now has full integrated support for Akismet spam filtering. Not too long ago, I released an Akismet API so that others can use it for spam filtering.
Scott Hanselman Members of the DasBlog team implemented it for DasBlog (in their source control tree. Not yet released.) and has nothing but praise for it, saying it has completely eliminated comment spam for him.
Now let me explain what I mean by full integrated support. When you enable Akismet and are looking at approved comments in the feedback section, if you notice something that should have been filtered as spam, you can check it and click the Spam button. That will then report a false negative to Akismet, indicating that they failed to mark this item as spam and then move the item to the new Trash folder.
Likewise, if you notice an item that is flagged as spam, but should not have been, you can check the item and click Approve which reports a false positive to Akismet. By doing so, you will be training Akismet to become more adept at filtering spam.
Note, to enable Akismet, you must sign up for an Akismet API key and supply that key to Subtext via the admin section. In order to get this key, you have to register for a Wordpress.com user account, whether or not you plan to use a Wordpress blog.
Another note, Akismet may not work in Medium Trust scenarios. So if you host your site with a hosting provider such as GoDaddy, WebHost4Life, etc... who run their sites in Medium trust, Akismet might not work for you. I wrote about the problem here.
In the comments to the aforementioned post, Scott Watermasysk points out a promising approach. Have your hosting provider set up a proxy which can be used to make requests. To that end, I did add some
Web.config appSettings for enabling proxy support:
ProxyHost, ProxyPort, ProxyUsername, ProxyPassword.
If specified, all web requests for subtext will use this proxy.
Not too long ago, I released a lightweight invisible CAPTCHA validator control. As far as I can tell, it has worked pretty well at keeping out automated comment spam. But as I also warned, it did nothing to stop Trackback/Pingback spam. Hence the need for Akismet support.
Invisible CAPTCHA is probably not necessary given the Akismet support, but since Akismet is not enabled by default and Invisible CAPTCHA is, it will provide some relief until you get your Akismet API key, if you so choose.
If Invisible CAPTCHA causes problems for you for some reason, you can turn it off for the entire site (no per-blog setting) via
Some of you twisted my arm, so I am complying. I love my users so I gotta keep them happy. We now have support for the standard visible CAPTCHA you know and love.
Time Zone Fix
This topic gets its own special treatment because it is both a bug fix and a new feature. Not too long ago I mentioned some work I did for DasBlog regarding daylight savings time.
Long story short, there was no way in the .NET framework to convert from one arbitrary timezone to another. The way this manifested in Subtext is that users who had their blog hosted in another timezone always had incorrect timestamps on their blog posts. This fix resolves that issue.
Previously, Subtext only stored a timezone offset for each timezone. For example, the offset for Pacific Standard Time (PST) is -8. However that is not accurate enough to be correct. For example, at the time of this blog post, the offset is actually -7 because of Daylight Savings Time.
Likewise, choosing -7 for a timezone is not accurate because Arizona does not observe daylight savings, but Mountain Time does. The only way to really get this accurate is to store the actual timezone and not just the offset. So that is what Subtext now does.
We now list every timezone (at least every one I could extract from the Windows XP registry). There is no natural integer identifier for a timezone, so based on a suggestion by someone at Microsoft, I used the hash code of the timezone’s Standard Name. This is the only guaranteed consistent unique identifier for a timezone. So when you upgrade to 1.9.2, we try and update your offset to the most likely timezone id. Obviously, we can’t be perfect about this, so after you upgrade, you probably want to login and configure your blog with the correct timezone if we chose unwisely.
Other Release Note Items
- Feature  Support referencing External CSS
- Feature  Flag All, Destroy All options
- Fixed  Disabling trackbacks didn’t disable trackbacks properly.
Upgrade Instructions and Warnings
If you are upgrading from Subtext 1.5 or below, then please read this important note on upgrading. Subtext 1.9.2 runs on ASP.NET 2.0, so upgrading from 1.5 and below(which ran on ASP.NET 1.1) takes a few additional steps than normal. As always, don’t forget to merge your
web.config customizations into the new
Also, for all people upgrading to 1.9.2. The Subtext 1.9.2 upgrade process performs a major database schema change, moving all comments and trackbacks into a new
subtext_Feedback table. We’ve tested this over and over again working out all the kinks we could find, but we can’t guarantee that it will be 100% perfect. Thus backup your database first before upgrading!
Also, as I mentioned in the previous section, after you upgrade, please check the timezone setting in the admin section.
Lastly, in order to improve the commenting experience, we’ve added a tiny dash of Ajax using the MagicAjax control for leaving comments on a blog. However, this does not work well with the excellent ReverseDOS because ReverseDOS acts before the request is passed on the our code. If you plan on using Akismet, it is recommended that you turn off ReverseDOS support in the
Web.config. In fact, the web.config file that comes with 1.9.2 disables ReverseDOS. I’ve been in contact with the ReverseDOS creator, Michael Campbell, about these issues and he has plans to work together to address them. But like all things, life comes first.
Even though Akismet does a great job with comment spam, I still think ReverseDOS is worthwhile and a nice complement to Akismet. However, we need tighter control of when ReverseDOS is triggered in the request pipeline in order to integrate it into Subtext’s existing spam filtering functionality.
Ok, enough talk already, where do I download this sucker? The download is hosted by SourceForge here: [DOWNLOAD]
Dan Appleman takes the .NET focused custom search engine idea one step further by grabbing a great domain name for his search engine. Now why didn’t I think of that! Sometimes that’s all it takes between a search engine that will get used alot (his) and one that won't (mine). I'd be happy to throw my support over to his, though I’m keeping my baby. The one key difference is I plan to leave my search engine open for others to contribute sites. Perhaps the two will complement each other.
And no, the domain name is not the only reason. This guy *is* Dan Appleman after all! He wrote the Visual Basic Programmer's Guide to the Win32 API which allowed me to feel like a real programmer despite using VB back in the day. For this, I owe him a debt of gratitude as I was thus equipped to stand up to the C++ snobs.
Now we just need someone to help spruce up the logo.
Tags: Custom Search, Search, .NET, Dan Appleman
In response to question about integrating my custom search engine with the browser, Oran left a comment with a link to a post on how to implement searching your FogBugz database in your browser via the OpenSearch provider, which is supported by Firefox 2.0 and IE7.
So I went ahead and used this as a guide to implementing OpenSearch for my custom search engine on my blog. When you visit my blog, you should notice that the the search icon in the top left corner of your browser is highlighted (screenshot from Firefox 2).
Click on the down arrow and you will see my own search engine Haack Attack in the list of search providers.
Now you can search using Haack Attack via your browser. Implementing this required two easy steps. First I created an
OpenSearch.xml file and dropped it in the root of my website. Here is my file with some of the gunk removed from the url.
<?xml version="1.0" encoding="UTF-8" ?>
<Tags>Software Development C# ASP.NET</Tags>
Search the web for relevant
.NET and software development content.
Remember to make sure to use
& for query string ampersands, as this is an XML file. Also, if you are using your own Google Custom search engine, the actual template value looks something like:
3BLH%3A23%3BCX%3AHaack%2520 Attack%2520The%2520Web%3BVLC% 3A%23663399%3BLW%3A100%3BDIV%3A%23336699%3BFORID%3A0%3BT%3A%23000000
%3BALC%3A%23660000%3BLC %3A%23660000%3BS%3Ahttp%3A%2F%2Fhaacked%2Ecom %2F%3BL%3Ahttp%3A%2F%2Fhaacked%2Ecom%2Fskins%2Fhaacked%2Fimages%2F Header%2Ejpg%3BGIMP%3A%23000000%3BLP%3A1%3BBGC%3A%23FFFFFF%3BAH%3Aleft& client=pub-7694059317326582
So be sure to change it appropriate to your own search engine.
The second step is to add auto-discovery. I added the following <link /> tag to my blog. The bolded sections you would obviously want to customize for your own needs.
<link title="Haack Attack"
So give it a try and let me know what you think. Be sure to add sites you think are relevant to this searh engine.
Google just launched a neat build-your-own-search-engine feature. You can choose sites that should be emphasized in the search results, or even restrict search results to that list.
It offers a lot of customization, but I seemed to have run into a problem in which it doesn’t seem to be saving the images for my profile for some reason.
One particularly neat feature is the ability to allow others to collaborate on the search engine. For example, check out my search engine called Haack Attack the Web. I entered a few .NET and software development focused websites to the list of sites to search, restricting results to just those sites.
Feel free to add your favorite websites to the search engine. I think I’ll actually find this as a useful first stop for finding .NET content.
The next step is to integrate this into the search features for Firefox and IE7. Anyone want to help?
Last night I went to the “World Famous Viper Room”. Gotta respect that their website makes sure to mention the World Famous part. I suppose you’d have to have a real club inferiority complex to promote your club as The In This Neighborhood Sorta Famous Viper Room.
Anyways, the purpose of my visit was to see my soccer (sorry, football) teammate and team captain Pete perform in the acoustic lounge, an intimate (read tiny) lounge downstairs from the main room.
Pete’s the one from Glasgow with a heavy Scottish brogue. We can hardly understand him most of the time, though I'm getting better at it. Usually I just nod my head in agreement.
After our last game we asked Pete if he’d get us on the guest list. “Not after that performance I won't.” was his reply...I think. He was referring to the 12 to 1 drubbing received at the hands of Hollywood United. This is the team fielding 9 internationally capped players and two World Cup players, one of whom was on the winning squad of the 1998 champions.
In contrast, we are fielding one internationally capped player who played for the Cayman Islands. We should be making some acquisitions for the next season that should help. Our goal for the next season is to keep them in the single digits. Incremental improvements, ya know?
Here’s a little trick that I didn't know about till Steve told me about it. So that’s what those other tabs in the Remote Destkop dialog are for!
Mapping local drives to a Remote Desktop connection.
This is a handy tip to go with these others:
When I was a bright eyed bushy tailed senior in college, I remember wading through pages and pages of job ads in Jobtrak (which has since been absorbed into Monster.com).
Most of the ads held my attention in the same way reading a phone book does. The bulk of them had something like the following format.
Design and develop data-driven internet based applications that meet functional and technical specifications. Use [technology X] and [technology Y] following the [methodology du jour] set of best practices. Create documentation, review code, and perform testing.
Required Skills and Experience:
Must have X years in language [now outdated language]. Must be proficient in BLAH, BLAH, and BLAH. Ability to work in a team environment. Able to work in a fast-paced [meaning we’ll work your ass off] environment.
I know what you’re thinking. Where do I sign up!
Yaaaaawn. Keep in mind, this was in 1997 just as the job market was starting to reach the stratosphere. Competition was tight back then. Do a search on Dice.com right now and you’ll still see a lot of the same.
I’m sorry, but your job posting is not the place to spew forth a list of Must have this and Must have that and a list of responsibilities so plain and vanilla that...that... I just don’t have a good analogy for it. Sorry.
These type of ads are attempting to filter out candidates who do not meet some laundry list of requirements. But this is not the goal of a good job ad. A good job ad should not explain what hoops the candidate must jump through to join your company, it should explain why the candidate should even want to jump through those hoops in the first place.
This of course assumes you are attempting to hire some star developers away from their current jobs rather than resume padders who have spent most of their careers in training classes so they can place your laundry list of technology TLAs underneath their name on their resume.
Certainly, a job posting should explain briefly the type of work and experience desired to fill the role. No point in having a person who only has experience in sales and marketing applying for your senior DBA position (true story). But first and foremost, you want to catch the attention of a great candidate. Boring job ads that read like the one above do not capture the imagination of good developers.
Back to my story. As I said, most of the ads fit this mold, but there were a few here and there that popped off the screen. I wish I had saved the one that really caught my attention. It was from a small company named Sequoia Softworks (which is still around but now named Solien) My memory of it is vague, so I’ll just make something up that resembles the spirit of the ad. All I remember is that it started off by asking questions.
Are you a fast learner and good problem solver? Are you interested in solving interesting business problems using the latest web technologies and building dynamic data driven web applications?
We’re a small web development company in Seal Beach (right by the beach in fact!) looking for bright developers. We have a fun casual environment (we sometimes wear shorts to work) with some seriously interesting software projects.
Experience in Perl, VBScript, and SQL is helpful, but being a quick learner is even more important. If you’ve got what it takes, give us a call.
I ended up working there for six years, moving up the ranks to end up as the Manager of Software Development (never once writing a line of PERL). They did several things right in their ad, as I recall.
Challenge the reader and demand an answer!
Do you have two years experience in C#? is not a challenge to the reader. This is not a question that captures my attention nor draws me in demanding an answer.
Do you know C# like Paris Hilton knows manafacturing fame? Now that is a challenge to my intellect! Hell yeah I know C# like nobody’s business. That kind of question demands an answer. And a good candidate is more likely to drive over to your headquarters and give it to you.
Appeal to vanity
Not every appeal to vanity is a bad thing. It doesn’t always amount to sucking up. This point is closely related to the last point in that an appeal to vanity is also a challenge to a candidate to show just how great they are. Asking someone if they are a good problem solver, for example, conjures up a desire to prove it.
Show some personality
Sure, many corporations seem like soulless cubicle farms in which workers are seen as mindless drones. But surely not your company, right? So why does your job posting have a tombstone all over it?
Who wants to be another cog in a machine performing mundane tasks for god knows what reason? Your ad should express a bit of your company’s personality and culture. It should also indicate to the reader that people who come to work for you are going to work with people. Interesting people. And they are going to work on interesting projects.
I write all this because of an article I read about business schools. It was a throw-away quote in which some employer mentioned how his new employee fresh out of business school helped rewrite some job postings and they were able to quickly fill some positions with high quality candidates they had been struggling to fill.
A well written job posting makes a difference.
I mentioned before that I am participating in the HiddenNetwork job board network because I really believe in the power of blogs to connect good people with good jobs. It’s in its nascent stages so I really don’t know how well it is fulfilling that mission yet, but I believe that it will do well.
If you do post a job via my blog (yes, I get a little something something if you do), be sure to make it a good posting that really captures the imagination of good developers (as all my readers are! See. Appeal to vanity.). It’s even more of a challenge given how few words you have at your disposal for these ads.
Jeff Atwood writes a great post about The Last Responsible Moment. Take a second to read it and come back here. I’ll wait.
In the comments, someone named Steve makes this comment:
This is madness. Today’s minds have been overly affected by short attention span music videos, video games, film edits that skip around every .4 seconds, etc.
People are no longer able to focus and hold a thought, hence their "requirements" never settle down, hence "agile development", extreme coding, etc.
I wonder what methodology the space shuttle folks use.
You shouldn’t humor this stuff, it’s a serious disease.
Ahhhh yes. The Space Shuttle. The paragon of software development. Nevermind the fact that 99.999% of developers are not developing software for the Space Shuttle, we should all write code like the Shuttle developers. Let’s delve into that idea a bit, shall we?
Invoking the Space Shuttle is common when arguing against iterative or agile methodologies to building software. Do people really think hey, if agile won’t work for the Space Shuttle, how the hell is it going to work for my client’s widget tracking application? Gee, your widget app is doomed.
The Space Shuttle is a different beast entirely from what most developers deal with day in and day out. This is not to say that there aren’t lessons to be learned from how the Shuttle software is built, there certainly are. Good lessons. No, great lessons! But in order to make good use of the lessons, you must understand how your client is very different from the client to the Shuttle developers.
One reason that the requirements for the Space Shuttle can be more formally specified beforehand and up front is because the requirements have very little need to chang once the project is underway. When was the last time the laws of gravity changed? The Shuttle code is mostly an autonomous system, which means the “users” of the code is the Shuttle itself as well as the various electronic and mechanical systems that it must coordinate. These are well specified systems that do not need to change often, if at all, in the course of a project.
Contrast this to business processes which are constantly evolving and heavily people centric. Many times, the users of a system aren’t even sure about how to solve the business problem they are trying to solve with software. This is partly why they don’t exactly know what they want until they have the solution in hand. We can wave activity diagrams, list of requirements, user stories and use cases in front of them all day long, but these are rough approximations of what the final system will do and look like. It’s showing the user a pencil sketch of a stick figure and hoping they see the Mona Lisa.
Later in the comments, the same guy Steve responds with what we should do with users to focuse them.
1) what do you want it to do?
2) understand the business as much as you can
3) draw a line in the sand for that which you can safely deliver pretty soon
4) build a system that is extensible, something that can be added on too fairly easily, because changes are coming (that is agile-ness)
5) charge for changes
And I agree. One of the common misperceptions of agile approaches is that you never draw a line in the sand. This is flat out wrong. You do draw a line in the sand, but you do it every iteration.
Unlike the BDUF Waterfall approach which requires that you force the user to spew requirements until he or she is blue in the face, with iterative approaches you gather a list of requirements and prioritize them according to iterations. This helps a long way to avoiding poor requirements due to design fatique. The user can change any requirements for later iterations, but once an iteration has commenced, the line in the sand is drawn for that iteration.
To me, this sounds like the last responsible moment for deciding on a set of requirements to implement. You don’t have to decide on the entire system at the beginning. You get some leeway to trade requirements for later iterations. You only are forced to decide for the current iteration.
Dave (a Subtext developer) just blogged about a project template he created for creating Atlas applications using the Web Application Project (WAP). This is wicked useful for those of us who prefer Web Application Projects over Web Site Projects.
In a recent post I ranted about how ASP.NET denies
WebPermission in Medium Trust. I also mentioned that there may be some legitimate reasons to deny this permission based on this hosting guide.
Then Cathal (thanks!) emailed me and pointed out that the
originUrl does not take wildcards, it takes a regular expression.
So I updated the <
trust /> element of
web.config like so:
<trust level="Medium" originUrl=".*" />
Lo and Behold, it works! Akismet works. Trackbacks work. All in Medium Trust.
Of course, a hosting provider can easily override this as Scott Guthrie points out in my comments. I need to stop blogging while sleep deprived. I have a tendency to say stupid things.
Now a smart hosting company can probably create a custom medium trust policy in order to make sure this doesn’t work, but as far as I can tell, this completely works around the whole idea of denying
WebPermission in Medium Trust.
If I can simply add a regular expression to allow all web requests, what’s the point of denying WebPermission?
Tyler, an old friend and an outstanding contractor for VelocIT recently wrote a post suggesting one would receive better performance by passing in an array of objects to the Microsoft Data Application Block methods rather than passing in an array of SqlParameter instances. He cited this article.
The article suggests that instead of this:
public void GetWithSqlParams(SystemUser aUser)
SqlParameter parameters = new SqlParameter
new SqlParameter("@id", aUser.Id)
, new SqlParameter("@name", aUser.Name)
, new SqlParameter("@name", aUser.Email)
, new SqlParameter("@name", aUser.LastLogin)
, new SqlParameter("@name", aUser.LastLogOut)
You should do something like this for performance reasons:
public void GetWithSqlParams(SystemUser aUser)
Naturally, when given such advice, I fall back to the first rule of good performance from the perf guru himself, Rico Mariani. Rule #1 Is to Measure. So I mentioned to Tyler that I’d love to see metrics on both approaches. He posted the result on his blog.
Calling the methods included in a previous post, 5000 times each,
With parameters took 1203.125 milliseconds.
With objects took 1250 milliseconds.
Objects took -46.875 milliseconds less.
20000 times each:
With parameters took 4859.375 milliseconds.
With objects took 5015.625 milliseconds.
Objects took -156.25 milliseconds less.
The results show that the performance difference is negligible. However, even before seeing the performance results, I would agree with the article to choose the second approach, but for different reasons. It results in a lot less code. As I have said before, Less code is better code.
I tend to prefer optimizing for productivity all the time, but only optimizing for performance after carefully measuring for bottlenecks.
There’s also a basic economics question hidden in this story. The first approach does seem to eke out slightly better performance, but at what cost? That’s a lot more code to write to eke out 47 milliseconds worth of performance out of 5000 method calls. Is it really worth it?
This particular example may not be the best example of this principle of wasting time optimizing at the expense of productivity because there is one redeeming factor worth mentioning with the first approach.
By explicitly specifying the parameters, the parameters can be listed in any order, whereas the second approach requires that the parameters be listed in the same order that they are specified in the stored procedure. Based on that, some may find the first approach preferable. Me, I prefer the second approach because it is cleaner, easier to read, and I don’t see keeping the order intact much more work than getting the parameter names correct.
But that’s just me.
This is a bit of rant born out of some frustrations I have with ASP.NET. When setting the trust level of an ASP.NET site, you have the following options:
Full, High, Medium, Low, Minimal
It turns out that many web hosting companies have chosen to congregate around Medium trust as a sweet spot in terms of tightened security while still allowing decent functionality. Only natural as it is the one in the middle.
For the most part, I am sure there are very good reasons for which permissions make it into Medium trust and which ones are not allowed. But some decisions seem rather arbitrary. For example,
WebPermission. Why couldn’t that be a part of the default Medium trust configuration? I mean really? Why not? (Ok, there are really good reasons, but remember, this is a rant, not careful analysis. Bear with me. Let me get it off my chest.)
Web applications have very good reason to make web requests (ever hear of something called a web service. They may take off someday) and how damaging is that going to be to a hosting environment. I mean, put a throttle on it if you are that concerned, but don’t rule it out entirely!
I really do want to be a good ASP.NET citizen and support Medium Trust with applications such as Subtext, but what a huge pain it is when some of the best features do not work under Medium Trust. For example, Akismet.
Akismet makes a web request in order to check incoming comments for spam. I tried messing around with wildcards for the
originUrl attribute of the
<trust /> element, but they don’t work. In fact, I only found a single blog post that said it would work, but no documentation that backed that claim up.
Instead, you need access to the
machine.config file (as the previously linked blog post describes), which no self respecting host is going to just give you willy nilly. Nope. In order to get Akismet to work under medium trust, I have to tell Subtext users that they must beg, canoodle, and plead with their host provider to update the
machine.config file to allow unrestricted access to the
WebPermission. Good luck with that.
If they don’t give unrestricted access, then they need to add an
originURl entry for each URL you wish to request. Hopefully
machine.config entries do allow wildcards because the URL for an Akismet request includes the Akismet API code. Otherwise running an Akismet enabled multiple user blog in a custom Medium Trust environment would be a royal pain.
Hopefully you can see the reason behind all my bitching and moaning. A major goal for Subtext is to provide a really simple and easy installation experience. At least as easy as possible for installing a web application. Having an installation step that requires groveling does not make for a good user experience. But then again, security and usability have always created tension between them.
Scott Watermasysk points out a great guide to enabling
WebPermission in Medium Trust for hosters. So if you’re going to be groveling, at least you have a helpful guide to give them. The guide also points out the security risks in involved with Medium Trust.
Steve Harman finds this gem of a site that generates interesting statistics and graphs for an open source website. Just point it to your code repository and let it go!
Check out the Subtext project stats.
For a project I’m working on, we have an automated build server running CruiseControl.NET hosted in a virtual machine. We do the same thing for Subtext.
Some of you may have multiple virtual servers running on the same machine. Typically in such a setup (at least typically for me), each virtual server won’t have its own public IP Address, instead sharing the public IP of the host computer.
This makes it a tad bit difficult to manage the virtual servers since using Remote Desktop to connect to the public IP will connect to the host computer and not the virtual machine. The same thing applies to multiple real computers behind a firewall.
One solution (and the one I use) is set up each virtual server to run Terminal Services, but each one listens on a different port. Then set up port-forwarding on your firewall to forward requests for the respective ports to the correct virtual machine.
Configuring the Server
The setting for the Terminal Services port lives in the following registry key:
Open up Regedit, find this key, and look for the the PortNumber value.
Double click on the PortNumber setting and enter in the port number you wish to use. Unless you think in hex (pat yourself on the back if you do), you might want to click on decimal before entering your new port number.
Or, you can use my creatively named VelocIT Terminal Services Port Changer application, which is available with source. This is a simple five minute application that does one thing and one thing only. It allows you to change the port number that Terminal Services listens on.
Remember, all the usual caveats apply about tinkering with the registry. You do so at your own risk.
Connecting via Remote Desktop to the non-standard Port
Now that you have the server all set up, you need to connect to it. This is pretty easy. Suppose you change the port for the virtual machine to listen in on port 3900. You simply append 3900 to the server name (or IP) when connecting via Remote Desktop.
Keep In Mind
Keep in mind that the user you attempt to connect with must have the logon interactively right as well as permissions to logon to the terminal services session. For more on that, check out this extremely helpful article with its trouble shooting section.
That’s pretty easy, no? Now you should have no problem managing your legions of virtual servers.
Just upgraded my blog to the latest version of Subtext in the Subversion 1.9 branch, not that you needed to know that. I just appreciate you letting me know if you run into problems leaving a comment and such by using my Contact page.
Before I release 1.9.2 (long story why that's ends with a 2 and not 1), I need to update the Contact page so that spam filters also apply to the contact page.
Weird how coding on Subtext relaxes me. For the past couple days I’ve been feeling a bit under the weather and getting worse. The weird part is that anytime I try and eat something, there’s a terrible after-taste. And no, it’s not my breathe. I couldn’t finish my pizza tonight. Pizza!
Anyways, I couldn’t sleep tonight so I figured hacking on some Subtext code might relax me. I fixed some bugs and implemented FeedBurner support in Subtext, using the DasBlog code as a guide, though the way we implement feeds is much different. It’ll come out in the next edition of Subtext.
Unfortunately, it may take me longer to release because although coding on Subtext feels good when I’m sick, QA testing doesn’t.
Recently I wrote a .NET based Akismet API component for Subtext. In attempting to make as clean as interface as possible, I made the the type of the property to store the commenter’s IP address of type
This sort of falls in line with the Framework Design Guidelines, which mention using the
Uri class in your public interface rather than a string to represent an URL. I figured this advice equally applied to IP Addresses as well.
To obtain the user’s IP Address, I simply used the
UserHostAddress property of the
HttpRequest object like so.
UserHostAddress property is simply a wrapper around the
REMOTE_ADDR server variable which can be accessed like so.
For users behind a proxy (or router), this returns only one IP Address, the IP Address of the proxy (or router). After some more digging, I learned that many large proxy servers will append their IP Address to a list maintained within another HTTP Header,
For example, if you make a request from a country outside of the U.S., your proxy server might add the header
HTTP_X_FORWARDED_FOR and put in your real IP and append its own IP Address to the end. If your request then goes through yet another proxy server, it may append its IP Address to the end. Note that not all proxy servers follow this convention, the notable exception being anonymizing proxies.
Thus to get the real IP address for the user, it makes sense to check the value of this first:
If that value is empty or null, then check the
So what does this mean for my Akismet implementation? I could simply change that property to be a string and return the entire list of IP addresses. That’s probably the best choice, but I am not sure whether or not Akismet accepts multiple IPs. Not only that, I’m really tired and lazy, and this change would require that I change the Subtext schema since we store the commenter’s IP in a field just large enough to hold a single IP address.
So unless smart slap me upside the head and call me crazy for this approach, I plan to look at the
HTTP_X_FORWARDED_FOR header first and take the first IP address in the list if there are any. Otherwise I will grab the value of
UserHostAddress. As far as I am concerned, it’s really not that important that I am 100% accurate in identifying the remote IP, I just need something consistent to pass to Akismet.
Quick tip for you if you need to remotely connect to a server with VMWare Server installed in order to manage the virtual server.
VMWare Server Console doesn’t work correctly if you Remote Desktop or Terminal in. You have to physically be at the machine or Remote Desktop into the Console session.
The symptoms I ran into was that I could not open a virtual machine, and when I tried to create a new one, I got an “Invalid Handle” error.
UPDATE: I’ve since supplemented this with another approach.
Jeremy Miller asks the question, “How do you organize your NUnit test code?”. My answer? I don’t, I organize my MbUnit test code.
Bad jokes aside, I do understand that his question is more focused on the structure of unit testing code and not the structure of any particular unit testing framework.
I pretty much follow the same structure that Jeremy does in that I have a test fixture per class (sometimes more than one per class for special cases). I experimented with having a test fixture per method, but gave up on that as it became a maintenance headache. Too many files!
One convention I use is to prefix my unit test projects with "UnitTest". Thus the unit tests for Subtext are in the project UnitTests.Subtext.dll. The main reason for this, besides the obvious fact that it’s a sensible name for a project that contains unit tests, is that for most projects, the unit test assembly would show up on bottom in the Solution Explorer because of alphabetic ordering.
So then I co-found a company whose name starts with the letter V. Doh!
UPDATE: I neglected to point out (as David Hayden did) that with VS.NET 2005 I can use Solution Folders to group tests. We actually use Solution Folders within Subtext. Unfortunately, many of my company work is still using VS.NET 2003, which does not boast such a nice feature.
One thing I don’t do is separate my unit tests and integration tests into two separate assemblies. Currently I don’t separate those tests at all, though I have plans to start.
Even when I do start separating tests, one issue with having unit tests in two separate assemblies is that I don’t know how to produce NCover reports that merge the results of coverage from two separate assemblies.
One solution I proposed in the comments to Jeremy’s post is to use a single assembly for tests, but have UnitTests and Integration Tests live in two separate top level namespaces. Thus in MbUnit or in TD.NET, you can simply run the tests for one namespace or another.
In the root of a unit test project, I tend to have a few helper classes such as
UnitTestHelper, which contains static methods useful for unit tests. I also have a
ReflectionHelper class, just in case I need to “cheat” a little. Any other classes I might find useful typically go in the root, such as my
SimulatedHttpRequest class as well.
Jeff Atwood writes a great rebuttal to Steve Yegge’s rant on Agile methodologies. I won’t expound on it too much except to point out this quote which should be an instant classic, emphasis mine:
Steve talks about “staying lightweight” as if it’s the easiest thing in the world, like it’s some natural state of grace that developers and organizations are born into. Telling developers they should stay lightweight is akin to telling depressed people they should cheer up.
Heh heh. Jeff moves from a Coding Horror to a Coding Hero.
Now while I agree much of it is religion, I like to think that the goal is to remove as much religion out of software development as possible. A key step, as Jeff points out, is recognizing which aspects are religion.
...the only truly dangerous people are the religious nuts who don’t realize they are religious nuts.
It’s like alcoholism. You have to first accept you are an alcoholic. But then, once recognizing that, you strive to make changes.
For example, Java vs .NET is a religious issue insofar as one attempts to make an absolute claim that one is superior to the other.
However it is less a religious issue to say that I prefer .NET over Java for reasons X, Y, and Z based on my experience with both, or even to say that in situation X, Java is a preferred solution.
Likewise, just because double-blind tests are nearly impossible to conduct does not mean that we cannot increase the body of knowledge of software engineering.
For the most part, we turn to the techniques of social scientists and economists by poring over historical data and looking at trends to extrapolate what information we can, with appropriate margins of error.
Thus we can state, with a fair degree of certainty, that:
Design is a complex, iterative process. Initial design solutions are usually wrong and certainly not optimal.
That is fact 28 of Facts and Fallacies of Software Engineering by Robert L. Glass, who is by no means an agile zealot. Yet this fact does show a weakness in waterfall methodologies that is addressed by agile methodologies, a differentiation that owes more to the scientific method than pure religion.
Ever prolific Jon Galloway has released another tool on our tools site. When we started the tools site, I talked some trash to spur some friendly competition between the two of us. Let’s just say Jon is kicking my arse so hard my relatives in Korea can’t sit down.
His latest RegmonToRegfile tool works with yet another SysInternals tool, Regmon.
Regmon is essentially a Tivo for your registry, allowing you to record and play back changes to the registry.
Regmon lacks the ability to export to a registry (.reg) file, which is where Jon’s tool comes to play. It can parse the Regmon log files and translate them to .reg files.
Here is a link to Jon’s blog post on this tool.
Personal matters (good stuff) and work has been keeping me really busy lately, but every free moment I get I plod along, coding a bit here and there, getting Subtext 1.9.1 “Shields Up” ready for action.
There were a couple of innovations I wanted to include in this version as well as a TimeZone handling fix, but recent comment spam shit storms have created a sense of urgency to get what I have done out the door ASAP.
In retrospect, as soon as I finished the Akismet support, I should have released.
I have a working build that I am going to test on my own site tonight. If it works out fine, I will deploy a beta to SourceForge. This will be the first Subtext release that we label Beta. I think it will be just as stable as any other release, but there's a significant schema change involved and I want to test it more before I announce a full release.
Please note, there is a significant schema change in which data gets moved around, so backup your database and all applicable warnings apply. Upgrade at your own risk. I am going to copy my database over and upgrade offline to test it out before deploying.
Shields up edition will contain Akismet support and CAPTCHA. The Akismet support required adding comment “folders” to allow the user to report false positives and false negatives.
For the most part, the Disk Defragmenter application (located at %SystemRoot%\system32\dfrg.msc) that comes with Windows XP does a decent enough job of defragmenting a hard drive for most users.
But if you’re a developer, you are not like most users, often dealing with very large files and installing and uninstalling applications like there’s no tomorrow. For you, there are a couple of other free utilities you should have in your utility belt.
Recently I noticed my hard drive grinding a lot. After defragmenting my drive, I clicked on the View Report button this time (I normally never do this out of hurriedness).
This brings up a little report dialog.
And in the bottom, there is a list of files that Disk Defragmenter could not defragment. In this case, I think the file was simply too large for the poor utility. So I reached into my utility belt and whipped out Contig.
Contig is a command line utility from SysInternals that can report on the fragmentation of individual files and defrag an individual file.
I opened up a console window, changed directory to the Backup directory, and ran the command:
Which defragmented every file ending with the tib extension (in this case just one). This took a good while to complete working against a 29 Gig file, but successfully reduced the fragmens from four to two, which made a huge difference. I may try again to see if it can bring it down to a single fragment.
I ran Disk Defragmenter again and here are the results.
Keep in mind that the disk usage before this pass with the defragger was the usage after running Disk Defragmenter once. After using contig and then defragging again, I received much better results.
Another limitation of Disk Defragmenter is that it cannot defragment files open for exclusive access, such as the Page File. Again, reaching into my utility belt I pull yet another tool from Sysinternals (those guys rock!), PageDefrag.
Running PageDefrag brings up a list of page files, event log files, registry files along with how many clusters and fragments make up those files.
This utility allows you to specify which files to defrag and either defragment them on the next reboot, or have them defragmented at every boot. As you can see in the screenshot, there was only one fragmentted file, so the need for this tool is not great at the moment. But it is good to have it there when I need it.
With these tools in hand, you are ready to be a defragmenting ninja.
Right now, there is no easy way to convert a time from one arbitrary timezone to another arbitrary timezone in .NET. Certainly you can convert from UTC to the local system time, or from the local system time to UTC. But how do you convert from PST to EST?
Well Scott Hanselman recently pointed me to some ingenious code in DasBlog originally written by Clemens Vasters that does this. I recently submitted a patch to DasBlog so that this code properly handles daylight savings and I had planned to blog about it in more detail later. Unfortunately, we recently found out that changes in Vista may break this particular approach.
It turns out that the Orcas release introduces a new
TimeZone2 class. This class will finally allow conversions between arbitrary timezones.
Krzysztof Cwalina (who wins the award for Microsoft blogger with the highest consonants to vowel ration in a first name) points out that many people are not thrilled with the "2" suffix and provides context on the naming choice.
Kathy Kam of the BCL team points out some other proposed names for the new
TimeZone2 class and the problems with each.
I’m fine with
asks the question
in a recent post if writing your own blog software is a form of procrastination (no, blogging is).
I remember reading something where someone equated rolling your own blog engine is the modern day equivalent of the Hello World program. I wish I could remember where I heard that so I can give proper credit. UPDATE: Kent Sharkey reminds me that I read it on his blog. It was a quote from Scott Wigart. Thanks for the memory refresh Kent!
Obviously, as an Open Source project founder building a blog engine, I have a biased opinion on this topic (I can own up to that). My feeling is that for most cases (not all) rolling your own blog engine is a waste of time given that there are several good open source blog engines such as Dasblog, SUB, and Subtext.
It isn’t so much that writing a rudimentary blog engine is hard. It isn’t. To get a basic blog engine up and running is quite easy. The challenge lies in going beyond that basic engine.
The common complaint with these existing solutions (and motivation for rolling your own) is that they contain more features than a person needs. Agreed. There’s no way a blog engine designed for mass consumption is going to only have the features needed by any given individual.
However, there are a lot of features these blog engines support that you wouldn’t realize you want or need till you get your own engine up and running. And in implementing these common features, a developer can spend a lot of time playing catch-up by reinventing the kitchen sink. Who has that kind of time?
Why reinvent the sink, when the sink is there for the taking?
For example, let’s look at fighting comment spam.
Implementing comments on a blog is quite easy. But then you go live with your blog and suddenly you’re overwhelmed with insurance offers. Implementing comments is easy, implementing it well takes more time.
If you are going to roll your own blog engine, at least “steal” the Subtext Akismet API library in our Subversion repository. Dasblog did. However, even with that library, you still ought to build a UI for reporting false positives and false negatives back to Akismet etc... Again, not difficult, but it is time consuming and it has already been done before.
Some other features that modern blog engines provide that you might not have thought about (not all are supported by Subtext yet, but by at least one of the blogs I mentioned):
- RFC3229 with Feeds
- BlogML - So you can get your posts in there.
- Email to Weblog
- Multiple Blog Support (more useful than you think)
- Timezone Handling (for servers in other timezone)
- Windows Live Writer support
- Metablog API
- Easy Installation and Upgrade
- XHTML Compliance
- Live Comment Preview
My point isn’t necessarily to dissuade developers from rolling their own blog engine. It’s fun code to write, I admit. My point is really this (actually two points):
1. If you plan to write your own blog engine, take a good hard look at the code for existing Open Source blog engines and ask yourself if your needs wouldn’t be better served by contributing to one of these projects. They could use your help and it gets you a lot of features for free. Just don’t use the ones you don’t need.
2. If you still want to write your own, at least take a look at the code contained in these projects and try to avail yourself of the gems contained therein. It’ll help you keep your wheel reinventions to a minimum.
That’s all I’m trying to say. Help us... help you.
This is a pretty sweet video that demonstrates a system for sketching on a whiteboard using mimio-like projection system. The instructor draws objects, adds a gravity vector, and then animates his drawings to see the result.
Another interesting take on user interfaces for industrial design.
A friend of mine sent me an interesting report that Brad Greenspan, the founder of eUniverse (now Intermix Media) that created and owned MySpace.com issued an online report that the sale of MySpace intentionally defrauded shareholders out of multiple billions of dollars because they hid MySpace revenues from shareholders.
Disclosure: Technically, I used to work for Intermix Media as they owned my last employer, SkillJam, before SkillJam was sold to Fun technologies.
The most surprising bit to me is that (according to the report)
Shareholders were not aware that Myspace’s revenue was growing at a 1200 percent annualized rate and increasing.
I wonder how much of this is true. If it is true, what happens next? Gotta love the smell of scandal in the morning.
Alex Papadimoulis, the man behind the ever entertaining (and depressing) TheDailyWTF announces a new business venture to help connect employers fielding good jobs with good developers interested in a career change. At least that’s the hope.
The Hidden Network is the name of the venture and the plan is to pay bloggers $5.00 per thousand ad views (cha-ching!) for hosting pre-screened job postings in small ad blocks that look very similar to the standard Google AdSense ad. In the interest of full disclosure, I will be taking part as a blogger hosting these job postings.
I’ve written before about how hiring is challenging and that blogs are a great means to connecting with and hiring good developers. In fact, that’s how I recruited Jon Galloway to join my company, brought in Steve Harman (a Subtext Developer) as a contractor, and almost landed another well known blogger, until his own company grabbed his leg and dragged him kicking and screaming back into the fold giving him anything he wanted (I kid).
My hope is that somehow, my blog helps connect a good employer with a good developer. It’s worked for my company, it may work for yours. Connecting good developers with good jobs is a win win experience for all. My fear is that the ads will turn into a bunch of phising expeditions by headhunders looking to collect resumes. It will be imperative that Hidden Network work hard at trying to filter out all but the high quality job postings.
As Alex states in the comments of that post, blogs are the new Trade Publications, so paying $5.00 per CPM is quite sustainable and viable. It will be interesting to see if his grand experiment works out.
Keyvan Nayyeri has a great tip for how to control the display of a type in the various debugger windows using a
DebuggerTypeProxy attribute. His post includes screenshots with this in use.
This is an attribute you can apply to a class, assembly, or struct to specify another class to examine within a debugger window.
You still get access to the raw view of the original type, just in case some other developer plays a practical joke on you by specifying a
DebuggerTypeProxy that displays the value of every field as being 42.
Great show on .NET Rocks today featuring Rob Conery, architect of the Commerce Starter Kit and SubSonic.
However, being the self-centered person that I am, the only thing I remember hearing is my last name being mispronounced.
For the record, my last name is “Haack” which is pronounced Hack, as in,
Yeah, I took a look at the new code Phil put into Subtext. Talk about an ugly Haack!
On the show Rob pronounced it Hock which is only okay if you are British, which Rob is not. I already gave Rob crap about it, so we’re cool.
I recently wrote about a lightweight invisible CAPTCHA validator control I built as a defensive measure against comment spam. I wanted the control to work in as many situations as possible, so it doesn’t rely on
Session since some users of the control may want to turn those things off.
Of course this begs the question, how do I know the answer submitted in the form is the answer to the question I asked? Remember, never trust your inputs, even form submissions can easily be tampered with.
Well one way is to give the client the answer in some manner that it can’t be read and can’t be tampered with. Encryption to the rescue!
Using a few new objects from the
System.Security.Cryptography namespace in .NET 2.0, I quickly put together code that would encrypt the answer along with the current system time into a base 64 encoded string. That string would then be placed in a hidden input field.
When the form is submitted, I made sure that the encrypted value contained the answer and that the date inside was not too old, thus defeating replay attacks.
The first change was to initialize the encryption algorithm via a static constructor.
The code can be hard to read in a browser, so I did include the source code in the download link at the end of this post.
static SymmetricAlgorithm encryptionAlgorithm
static SymmetricAlgorithm InitializeEncryptionAlgorithm()
SymmetricAlgorithm rijaendel = RijndaelManaged.Create();
With that in place, I added a couple static methods to the control.
static SymmetricAlgorithm InitializeEncryptionAlgorithm()
SymmetricAlgorithm rijaendel = RijndaelManaged.Create();
public static string EncryptString(string clearText)
byte clearTextBytes = Encoding.UTF8.GetBytes(clearText);
byte encrypted = encryptionAlgorithm.CreateEncryptor()
In the PreRender method I simply took the answer, appended the date using a pipe character as a separator, encrypted the whole stew, and the slapped it in a hidden form field.
//Inside of OnPreRender
string EncryptAnswer(string answer)
+ DateTime.Now.ToString("yyyy/MM/dd HH:mm"));
Now with all that in place, when the user submits the form, I can determine if the answer is valid by grabbing the value from the form field, calling decrypt on it, splitting it using the pipe character as a delimiter, and examining the result.
protected override bool EvaluateIsValid()
string answer = GetClientSpecifiedAnswer();
string encryptedAnswerFromForm =
string decryptedAnswer = DecryptString(encryptedAnswerFromForm);
string answerParts = decryptedAnswer.Split('|');
if(answerParts.Length < 2)
string expectedAnswer = answerParts;
DateTime date = DateTime.ParseExact(answerParts
, "yyyy/MM/dd HH:mm", CultureInfo.InvariantCulture);
if ((DateTime.Now - date).Minutes > 30)
this.ErrorMessage = "Sorry, but this form has expired.
Please submit again.";
&& answer == expectedAnswer;
// Gets the answer from the client, whether entered by
private string GetClientSpecifiedAnswer()
string answer = Page.Request.Form[this.HiddenAnswerFieldName];
answer = Page.Request.Form[this.VisibleAnswerFieldName];
This technique could work particularly well for a visible CAPTCHA control as well. The request for a CAPTCHA image is an asynchronous request and the code that renders that image has to know which CAPTCHA image to render. Implementations I’ve seen simply store an image in the CACHE using a GUID as a key when rendering the control. Thus when the asynchronous request to grab the CAPTCHA image arrives, the CAPTCHA image rendering
HttpHandler looks up the image using the GUID and renders that baby out.
Using encryption, the URL for the CAPTCHA image could embed the answer (aka the word to render).
If you are interested, you can download an updated binary and source code for the Invisible CAPTCHA control which now includes the symmetric encryption from here.
UPDATE: I could not slip the subtle beg for an MSDN subscription I surreptitiously embedded in this post past my astute readers. Many thanks to James Avery for contributing an MSDN subscription to this grateful developer. Now that I have my MSDN subscription, I say this whole VPC licensing thing is a non-issue and quit whining about it. (I joke, I joke!).
In a recent post I declared that Virtual PC is a suitable answer to the lack of backwards compatibility support for Visual Studio.NET 2003. In the comments to that post Ryan Smith asks a great question surrounding the licensing issues involved.
Is Microsoft going to let me use my OEM license key from an ancient machine so that I can run Windows XP in a virtual machine on Vista to test and debug in VS 2003?
I think as developers, we take for granted that we are going to have MSDN subscriptions (I used to but I don’t right now) and plenty of OS licenses for development purposes. But suppose I sell my old machine and purchase a new machine with Vista installed. How can I apply the suggested workaround of installing Virtual PC with Windows XP if I don't have a license to XP?
Ryan wrote Microsoft with this question and received a response that indicated that Microsoft hasn’t quite figured this out. Does this mean that developers need to shell out another $189 or so in order to develop with Visual Studio.NET 2003 in a Virtual PC running Windows XP on Vista?
Jon Galloway combines Timesnapper (Tivo for geeks) with the OCR abilities of Microsoft Office Document Imaging (OCR you didn’t realize you owned) and never forgets anything ever again.
I read this article recently that describes the mind frying complexity of the Windows development process. With Vista sporting around 50 million lines of code, it’s no wonder Vista suffers from delays. Quick, what does line #37,920,117 say?
Microsoft has acknowledged the need to release more often (as in sometime this millenia), but that agility is difficult to achieve with the current codebase due to its immense complexity as well as Microsoft’s (stubbornly?) heroic efforts to maintain backward compatibilty. The author of the article labels this the Curse of Backward Compatibility.
I don’t think anyone doubts that maintaining backwards compatibility can be a Herculean effort because it goes beyond supporting legacy specification (which is challenging enough). Just look at how Microsoft supports old code that broke the rules. Additionally, the fact that old code poses a security threat requires even more code to patch those security threats. Ideally alot of that code would be removed outright, but it is challenging to remove or rewrite any of it in fear of breaking too many applications.
Of course there are very good business reasons for Microsoft to maintain this religious adherence to backwards compatibility (starts with an m ends with a y and has one in the middle). The primary one being they have a huge user base when compared to Apple, which does not give Microsoft the luxury of a “Do Over” as Apple has done with OSX.
A different article (same magazine) points to virtualization technology as the answer. This article talks suggests a virtualization layer that is core to the operating system. I think we are already seeing hints of this in play with Microsoft’s answer to developers angry that Vista is not going to support Visual Studio.NET 2003.
The big technical challenge is with enabling scenarios like advanced debugging. Debuggers are incredibly invasive in a process, and so changes in how an OS handles memory layout can have big impacts on it. Vista did a lot of work in this release to tighten security and lock down process/memory usage - which is what is affecting both the VS debugger, as well as every other debugger out there. Since the VS debugger is particularly rich (multi-language, managed/native interop, COM + Jscript integration, etc) - it will need additional work to fully support all scenarios on Vista. That is also the reason we are releasing a special servicing release after VS 2005 SP1 specific to Vista - to make sure everything (and especially debugging and profiling) work in all scenarios. It is actually several man-months of work (we’ve had a team working on this for quite awhile). Note that the .NET 1.1 (and ASP.NET 1.1) is fully supported at runtime on Vista. VS 2003 will mostly work on Vista. What we are saying, though, is that there will be some scenarios where VS 2003 doesn’t work (or work well) on Vista - hence the reason it isn’t a supported scenario. Instead, we recommend using a VPC/VM image for VS 2003 development to ensure 100% compat.
This answer did not satisfy everyone (which answer does?), many seeing it as a copout as it pretty much states that to maintain backward compatibility, use Virtual PC.
Keep in mind that this particular scenario is not going to affect the average user. Instead, it affects developers, who are notorious for being early adopters and, one would think, would be more amenable to adopting virtualization as an answer, because hey! It’s cool new technology!
Personally I am satisfied by this answer because I have no plans to upgrade to Vista any time soon (my very own copout). Sure, it’s not the best answer I would’ve hoped for if I was planning an impending upgrade. But given a choice between a more secure Vista released sooner, or a several months delay to make sure that developers with advanced debugging needs on VS.NET 2003 are happy, I’m going to have to say go ahead and break with backward compatibility. But at the same time, push out the .NET 2.0 Framework as a required update to Windows XP.
With Windows XP, Microsoft finally released a consumer operating system that was good enough. Many users will not need to upgrade to Vista for a looong time. I think it is probably a good time to start looking at cleaning up and modularizing that 50 million line rambling historical record they call a codebase.
If my DOS app circa 1986 stops working on Vista, so be it. If I’m still running DOS apps, am I really upgrading to Vista? Using a virtual operating system may not be the best answer we could hope for, but I think it’s good enough and should hopefully free Microsoft up to really take Windows to the next level. It may cause some difficulties, but there’s no easy path to paying off the immense design debt that Microsoft has accrued with Windows.
UPDATE: I think a good measure of a blog is the intelligence and quality of the comments. This comments in response to this post makes my blog look good (not all do).
As several commenters pointed out, the function returns a local DateTime adjusted from the specified UTC date. By calling
ToUniversalTime() on the result, I get the behavior I am looking for. That’s why I ask you smart people before making an ass of myself on the bug report site.
Before I post this as a bug, can anyone tell me why this test fails when I think it should pass?
public void ParseUsingAssumingUniversalReturnsDateTimeKindUtc()
IFormatProvider culture = new CultureInfo("en-US", true);
DateTime utcDate = DateTime.Parse("10/01/2006 19:30", culture,
"Expected AssumeUniversal would return a UTC date.");
What is going on here is I am calling the method
DateTime.Parse passing in a
DateTimeStyle.AssumeUniversal as an argument. My understanding is that it should indicate to the
Parse method that the passed in string denotes a Coordinated Univeral Time (aka UTC).
But when I check the
Kind property of the resulting
DateTime instance, it returns
DatTimeKind.Local rather than
The unit test demonstrates what I think should happen. Either this really is a bug, or I am wrong in my assumptions, in which case I would like to know, how are you supposed to parse a string representing a date/time in the UTC timezone?