Joining LeanKit
I am excited to announce that I’m joining LeanKit as Senior Software Developer and Technical Evangelist. Chris Hefley, co-founder and CEO, is a long-time friend I have had the pleasure of working with a couple of times in my career, which makes this move extra-special. Chris started LeanKit in 2009 with co-founders Stephen Franklin, Daniel Norton, and Jon Terry. Today LeanKit is a rapidly growing startup, and poised to be the premier online tool for Kanban.
What is Kanban? The short answer is it’s a way to visualize work and the flow of work from start to finish. This is typically done using sticky notes or index cards representing the work to be done, placed on a board that has columns for each stage that work has to go through, such as “to-do,” “doing,” and “done.” Although there is much more to Kanban, simply visualizing work and the progression of work through stages can bring incredible insight to teams and stake-holders.
Since part of my role at LeanKit will be technical evangelist, I’m looking forward to it being my job to get more involved in the developer community, both online and at various events. If you have any questions about LeanKit, Kanban, or developing custom LeanKit solutions for your organization, please feel free to contact me here, Twitter, or on LinkedIn.
Read MoreIntro to RavenDB on Channel 9
I recently gave my Intro to RavenDB presentation at the virtual aspConf. The presentation was recorded and is now up on Channel 9.
Read MoreIntro to RavenDB Presentation
I’m excited to announce that my “Introduction to RavenDB: NoSQL is Rapping at Your Door” presentation that I gave at CodeMash 2012 was recorded by InfoQ is now available.
This was my first year to CodeMash, and it was an absolute blast for me AND my family. Here are just two reasons to go next year: indoor water park and bacon bar. Need I say more? CodeMash 2012 sold out in just 20 minutes, so you better start getting ready for 2013.
I’ll being giving a similar (but much improved) version of this talk at CodepaLOUsa 2012 in Louisville, KY, March 15-17. Hope to see you there!
Read MoreWindows Phone 7: One Year Retrospective
One year ago, I retired my iPhone 3G and grabbed the new Samsung Focus Windows Phone 7. After a year of use, here’s my take on the WP7 experience.
The Good
- Hardware: The hardware is fantastic. Beautiful screen, fast, thin, good battery life, and a great camera. I can also say the device is very durable.
- Linked Email: The 7.5 “Mango” update brought some great refinements, including the ability to combine multiple email accounts into a unified inbox. The advantage of WP7′s linked inbox feature is you can have more than one group of mailboxes, such as having single inbox for work, one linked inbox group for personal email accounts, and another linked inbox group for business accounts. Microsoft got email right.
- Metro UI: For applications that can take full advantage of the Metro UI experience, such as side-swiping the screen left or right, a much richer, intuitive experience can be achieved.
- Try-Before-You-Buy: Every app has a built-in trial version, so you can try it out before you buy it. You don’t have to search for a free version of an app.
The Bad
- Streaming: Even on a good WiFi connection, most streaming experiences are very frustrating with stuttering and stops. The Zune subscription service could easily be my favorite WP7 feature. Instead, I never use it. It just boggles my mind that its developers didn’t do a better job of buffering. YouTube is unusable.
- Scrolling: Scrolling through a long list of items, such as Facebook or Twitter statuses, leaves a lot to be desired. Scrolling isn’t always smooth. There’s a limit to how fast you can scroll. On the iPhone, for example, if you rapidly flick the screen multiple times, scrolling gets faster and faster, allowing you to quickly get to the top or bottom of a list. Also, while trying to scroll vertically, you can swipe a little too much to the left or right, causing the app to switch views.
The Ugly
- Back/Home/Search Buttons: The three buttons along the bottom of every WP7 device serve a very useful purpose. However, because they are not physical buttons, I end up accidentally touching them all the time. Extremely frustrating.
- Apps: Except for a few games, every application I’ve downloaded from the Marketplace has had some kind of user experience failure. Microsoft can claim their Marketplace is growing, but there have been many applications I’ve tried that were just downright horrible.
Summary
For a long time I was very forgiving of WP7′s flaws, knowing this was a new platform and it would take a while for the features and apps to mature. What has been most disheartening is to see bugs and bad user experience in apps that simply never get fixed. Over and over I found myself thinking, “This kind of experience would never be tolerated in an iPhone version of this app.” In some cases, such as the Facebook app, apps have gone from bad to worse. You would think, for the most popular apps that users download, Microsoft would choose to step in and say, “These apps are critical to our success. Let us help you build the very best and solid applications on the market.”
I honestly want to see WP7 succeed. Good competition is always in the best interest of consumers.I think some of it’s features are (or could be) better than iOS and Android. Unfortunately, my patience has run out.
Read MoreFresh Brewed Code
There’s not a word yet, for old friends who just met. — Gonzo
I’m excited to announce that I’m part of the blogger community FreshBrewedCode.com. It’s certainly an honor to be named among peers that I highly admire and respect. This blog will continue to be a mixed bag of geeky, personal, and philosophical, but you may notice that my more technical posts will be cross-posted to FreshBrewedCode. If you’re so inclined, I believe FreshBrewedCode will be a site worth keeping your eye on.
Read MoreScale Windows Services with RabbitMQ
(Cross-posted to FreshBrewedCode.com)
When I joined my current company, we had a mixed bag of Windows Services and scheduled tasks that processed images and video from various sources. For example, we had a service that polled mailboxes for emails, another that polled Facebook accounts, one that sent video off to be encoded, and so forth. Although there were shared libraries, there remained a lot of duplicated code. Most of these services were single-threaded, and were not designed to scale beyond a single instance. Since our platform must be able to respond to huge spikes of activity whenever there is breaking news or weather events, we needed to figure out a better mouse trap.
Having attended a couple of Nashville .NET User Group lectures earlier in the year that touched on distributed architecture[1], I had a good start on where to begin. A message queue-based system seemed to be the obvious answer.
Competing Consumers Message Pattern
The “competing consumers” pattern comes from Enterprise Integration Patterns, and describes a message-based system where multiple consumers listen to a single message queue, and only one consumer is allowed to process any given message. The beauty of using a message queue is it makes no difference if these consumers are multiple instances of an application (or threads) on the same machine, or spread across multiple physical machines. Leveraging this pattern with a message queue platform makes it almost trivial to create a system that provides load balancing, scalability, and redundancy. You remain focused on implementing the business logic required to process a single message.
Basic Pattern Implementation with RabbitMQ and C#
If you don’t already have the RabbitMQ Server installed, go to the RabbitMQ Downloads page and click on the appropriate installation guide for your platform. If you are running Windows, you’ll need to download and installErlang first. I also recommend you install the management plugin, which will give you a web-based UI for monitoring and managing RabbitMQ Server. You can easily add the latest RabbitMQ.Client .NET library to your Visual Studio project using NuGet. Or, see “Further Reading and Resources” below to download the compiled library and source code.
Let’s dive into some code that demonstrates publishing and consuming messages. Here is the code for the server/publisher:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // Set up the RabbitMQ connection and channel var connectionFactory = new ConnectionFactory { HostName = "localhost", Port = 5672, UserName = "guest", Password = "guest", Protocol = Protocols.AMQP_0_9_1, RequestedFrameMax = UInt32.MaxValue, RequestedHeartbeat = UInt16.MaxValue }; using (var connection = connectionFactory.CreateConnection()) using (var channel = connection.CreateModel()) { // Create a new, durable exchange channel.ExchangeDeclare("sample-ex", ExchangeType.Direct, true, false, null); // Create a new, durable queue channel.QueueDeclare("sample-queue", true, false, false, null); // Bind the queue to the exchange channel.QueueBind("sample-queue", "sample-ex", "optional-routing-key"); // Set up message properties var properties = channel.CreateBasicProperties(); properties.DeliveryMode = 2; // Messages are persistent and will survive a server restart // Ready to start publishing // The message to publish can be anything that can be serialized to a byte array, // such as a serializable object, an ID for an entity, or simply a string var encoding = new UTF8Encoding(); for (var i = 0; i < 10; i++) { var msg = string.Format("This is message #{0}?", i+1); var msgBytes = encoding.GetBytes(msg); channel.BasicPublish("sample-ex", "optional-routing-key", false, false, properties, msgBytes); } channel.Close(); } Console.WriteLine("Messages published"); Console.ReadKey(true); |
And here is the code for the client/consumer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // Set up the RabbitMQ connection and channel var connectionFactory = new ConnectionFactory { HostName = "localhost", Port = 5672, UserName = "guest", Password = "guest", Protocol = Protocols.AMQP_0_9_1, RequestedFrameMax = UInt32.MaxValue, RequestedHeartbeat = UInt16.MaxValue }; using (var connection = connectionFactory.CreateConnection()) using (var channel = connection.CreateModel()) { // This instructs the channel not to prefetch more than one message channel.BasicQos(0, 1, false); // Create a new, durable exchange channel.ExchangeDeclare("sample-ex", ExchangeType.Direct, true, false, null); // Create a new, durable queue channel.QueueDeclare("sample-queue", true, false, false, null); // Bind the queue to the exchange channel.QueueBind("sample-queue", "sample-ex", "optional-routing-key"); using (var subscription = new Subscription(channel, "sample-queue", false)) { Console.WriteLine("Waiting for messages..."); var encoding = new UTF8Encoding(); while (channel.IsOpen) { BasicDeliverEventArgs eventArgs; var success = subscription.Next(2000, out eventArgs); if (success == false) continue; var msgBytes = eventArgs.Body; var message = encoding.GetString(msgBytes); Console.WriteLine(message); channel.BasicAck(eventArgs.DeliveryTag, false); } } } |
The first key step to implementing the pattern is to declare a direct exchange, which will publish messages to a single message queue based on routing information. The commonly-used fanout exchange broadcasts messages to every message queue bound to the exchange.
The second key step is configuring the consumer channel’s BasicQos setting so that it only fetches one message off the queue at a time. If this is not set, then a single consumer could essentially put a hold on all the messages currently waiting in the queue so that none of the other consumers can access them. The messages will remain in a “consumed” but unacknowledged state until they are processed by the one consumer, totally defeating the purpose of implementing this pattern! There’s not a lot of documentation available on configuring BasicQos, so I had to figure out this requirement the hard way.
The last step is to acknowledge that the message has been processed, allowing the RabbitMQ server to delete the message from the queue, and the consumer to pick up the next available message.
Running the Sample Code
- Download the sample project.
- Load and build the solution.
- Launch two or more command prompts to be used as consumers.
- Change the current directory of each command prompt to
[your-project-root]\CompetingConsumers.Consumer\bin\Debugand launchCompetingConsumers.Consumer.exe - In Visual Studio, press F5 to launch CompetingConsumers.Publisher.
Further Reading and Resources
Hopefully this brief introduction has wet your appetite for further exploration into messaging and RabbitMQ. Here are a few links to get you down the road a bit further.
- The official RabbitMQ .NET library and documentation.
- Don’t let the title scare you. “AMQP 0-9-1 Model Explained” is a concise introduction to the messaging protocol that RabbitMQ is based upon, and essential reading for understanding the fundamentals of RabbitMQ.
- The official RabbitMQ .NET Getting Started tutorial code.
- RabbitMQ in Action by Alvaro Videla and Jason J.W. Williams. You can download chapter 1 for free, which provides a great introduction to the history of messaging and some of the advantages of RabbitMQ over other messaging platforms.
[1] The two Nashville .NET User Group lectures I had attended were Bryan Hunter’s talk on Command Query Responsibility Segregation (CQRS), and Alex Robson and Jim Cowart’s Introduction to Symbiote. I could extol the benefits of being involved in your local developer community, but that could be its own post for another day.
Read MoreBrand Yourself
Last night at the Nashville .NET User Group we ended the evening with a terrific discussion on careers. We had a great panel of developers, entrepreneurs, and recruiters. It was nice to hear the transparency between developers and recruiters. Here are some quick highlights:
- Create a LinkedIn profile.
- Publish an open source project.
- Publish a blog.
- Create a StackOverflow profile and build some points.
- Create a Careers profile at StackOverflow.
- Use social media, such as Twitter.
- Build relationships with recruiters, and interview them.
- Attend user groups and regional developer conferences.
- Find a mentor.
- Be a mentor.
In my opinion, your most important career goal should be to establish a personal brand. Your brand is anything that gives others an impression about you. Open source projects, blogs, social media, and networking are all opportunities where you can establish credibility and a good reputation. You also can’t underestimate the impact of your attitude, the way you treat others, or even the way you dress. There’s something to be said for personal hygiene, too…
Your brand can open doors, regardless of how many years experience you may have. Whether you are fresh out of school, or looking to change careers, getting a job in today’s market with little on-the-job experience can be very difficult. The recruiters on the panel agreed three years is the minimum most employers are looking for. However, I have seen first-hand how a person’s passionate pursuit of learning and establishing a brand led to great opportunities.
- Demonstrate your expertise.
- Demonstrate your passion for learning.
- Demonstrate. You. Are. Awesome.
Your reputation is your most valuable asset. The kind of employer (or client) you really want to work with will care enough to seek and discover your reputation. Supply the world with plenty of evidence.
Choose a good reputation over great riches; being held in high esteem is better than silver or gold. – Proverbs 22:1 (NLT)
Last, for those that desire to start their own business:
- Learn how to talk to non-geeks.
- Learn all you can about every aspect of running a business.
- Utilize local resources such as EntrepreneurCenter and JumpStart Foundry.
- Read Quitter, Delivering Happiness, Start with Why, and a ton of other great books.
Stupid SQL Tricks – Using Computed Columns for Code Generation
I recently had a need to drop and recreate all the default constraints on some tables so I could convert the data types of from varchar to nvarchar. The constraints were originally created allowing SQL Server to automatically name them. So, they looked something like: DF__TableName__Colu__2BE6BFCF. Maybe this doesn’t bother you. To me, it’s an abomination.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | SET NOCOUNT ON; DECLARE @SQL nvarchar(MAX), @executeScriptsImmediately bit, @id INT; SET @executeScriptsImmediately = 0; -- Set this to 1 to immediately drop and recreate constraints DECLARE @tbl TABLE ( Id INT IDENTITY(1,1), TableName nvarchar(128) NOT NULL, ColumnName nvarchar(128) NOT NULL, DefaultName nvarchar(128) NOT NULL, DefaultDefinition nvarchar(MAX), NewDefaultName AS ( N'DF_' + TableName + N'_' + ColumnName ), DropText AS (N'IF EXISTS ( SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N''[' + DefaultName + N']'') AND type = ''D'' ) BEGIN ALTER TABLE [dbo].[' + TableName + N'] DROP CONSTRAINT [' + DefaultName + N'] END'), CreateText AS (N'IF NOT EXISTS ( SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N''[DF_' + TableName + N'_' + ColumnName + N']'') AND type = ''D'' ) BEGIN ALTER TABLE [dbo].[' + TableName + N'] ADD CONSTRAINT [DF_' + TableName + N'_' + ColumnName + N'] DEFAULT ' + DefaultDefinition + N' FOR [' + ColumnName + N'] END'), OrigCreateText AS (N'IF NOT EXISTS ( SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N''[' + DefaultName + N']'') AND type = ''D'' ) BEGIN ALTER TABLE [dbo].[' + TableName + N'] ADD CONSTRAINT [' + DefaultName + N'] DEFAULT ' + DefaultDefinition + N' FOR [' + ColumnName + N'] END') ); INSERT INTO @tbl ( TableName, ColumnName, DefaultName, DefaultDefinition ) SELECT t.name, c.name, dc.name, dc.definition FROM sys.COLUMNS c JOIN sys.TABLES t ON t.object_id = c.object_id JOIN sys.objects d ON d.object_id = c.default_object_id JOIN sys.types tp ON tp.system_type_id = c.system_type_id JOIN sys.default_constraints dc ON dc.object_id = d.object_id WHERE d.TYPE = N'd' -- This filters query to only defaults on varchar data types -- AND tp.name = N'varchar' -- This filters query to only defaults that do not match the naming convention AND dc.name <> N'DF_' + t.name + N'_' + c.name ORDER BY t.name, c.name PRINT N'-------------------------------------------'; PRINT N'-- Backup of original default contraints'; PRINT N'-------------------------------------------'; PRINT N'/*'; SET @id = ( SELECT TOP 1 Id FROM @tbl ORDER BY Id ); WHILE ( @id IS NOT NULL ) BEGIN SELECT @SQL = OrigCreateText FROM @tbl WHERE Id = @id; PRINT @SQL; SET @id = ( SELECT TOP 1 Id FROM @tbl WHERE Id > @id ORDER BY Id ); END PRINT N'*/'; PRINT N''; PRINT N'-------------------------------------------'; PRINT N'-- Drop default contraints'; PRINT N'-------------------------------------------'; SET @id = ( SELECT TOP 1 Id FROM @tbl ORDER BY Id ); WHILE ( @id IS NOT NULL ) BEGIN SELECT @SQL = DropText FROM @tbl WHERE Id = @id; PRINT @SQL; IF (@executeScriptsImmediately = 1) EXEC sp_executesql @SQL; SET @id = ( SELECT TOP 1 Id FROM @tbl WHERE Id > @id ORDER BY Id ); END PRINT N''; PRINT N'-------------------------------------------'; PRINT N'-- Create new default contraints'; PRINT N'-------------------------------------------'; SET @id = ( SELECT TOP 1 Id FROM @tbl ORDER BY Id ); WHILE ( @id IS NOT NULL ) BEGIN SELECT @SQL = CreateText FROM @tbl WHERE Id = @id; PRINT @SQL; IF (@executeScriptsImmediately = 1) EXEC sp_executesql @SQL; SET @id = ( SELECT TOP 1 Id FROM @tbl WHERE Id > @id ORDER BY Id ); END PRINT N''; |
View and download this script from GitHub.
Granted, you could accomplish the same thing by moving the “templates” to the SELECT statement that inserts rows into the the table variable. I just felt especially nerdy using computed columns. I like being especially nerdy.
Read MoreRestless
Being “good enough” is not good enough.
Switchfoot recently released an album named Vice Verses that has struck a chord with my life (pardon the pun). Here’s the chorus from one of the album’s songs.
No, I’m not alright
I know that I’m not right
A steering wheel doesn’t mean you can drive
A warm body doesn’t mean I’m aliveNo, I’m not alright
I know that I’m not right
Feel like I travel but I never arrive
I wanna thrive not just survive– Switchfoot, Thrive
There’s a scene in the movie Courageous where one character says, “You’ve been a good enough father.” To which the second replies, “I don’t want to be a ‘good enough’ father.” Later, that same character says,
“I don’t feel like I started well. I want to finish well.”
There are many areas of my life where I don’t want to settle for “good enough.”
- I don’t want to be a “good enough” husband.
- I don’t want to be a “good enough” father.
- I don’t want to be a “good enough” friend.
- I don’t want to be a “good enough” boss.
- I don’t want to be a “good enough” employee.
- I don’t want to be a “good enough” software developer.
- I don’t want to be a “good enough” musician.
- I don’t want to be a “good enough” mentor.
- I want to thrive, not just survive.
Power of Persuasive Speech
I am truly honored to be selected as a speaker at the CodeMash developer conference in January. Being a regional conference, CodeMash is not very large compared to something like the Microsoft Professional Developers Conference (PDC). However, it has become very popular. Most of the speakers are well-known and highly regarded in our industry. Last year, CodeMash sold out in 3 days. This year, it sold out in 20 minutes.
I have to admit, all these facts are making me a bit anxious. Obviously, I don’t want to make a fool of myself. I want to do an excellent job of informing my audience. And, to represent myself, my company, and Nashville well.
Alan Stevens recently spoke at the Nashville .NET User Group on distributed version control systems (DVCS) and Mercurial. If you haven’t had the pleasure of attending one of Alan’s talks, you should make a point to. You see, the thing about Alan is, he doesn’t just try to inform you. Neither can he settle for an attempt to persuade.
No. That’s not good enough.
Alan demands that you recognize the unseen war. He points to the shackles of bondage you unknowingly have grown comfortable wearing. Alan hands you the red pill and implores you to wake up from your stupor.
By the end of the talk, not only was I convinced that Mercurial was the best DVCS solution on the planet, I was ready and willing to lay down my life for the cause of distributed version control. I was reminded…
“…they make take our lives, but they’ll never take… OUR FREEDOM!” — William Wallace, Braveheart
That’s the kind of speaker I want to be.
Watch Alan Stevens’ DVCS presentation.
Read More
I am a father, geek, musician, and software developer living in the Nashville, TN area. I'm currently a Developer Advocate for 



