Community

I am pretty selfish. I would like to think I can be wholly altruistic, but I cannot remember a deed I did for someone else where I was not rewarded by a general feeling of well-being. Perhaps this is normal and we kid ourselves that true altruism exists because, well, it feels good to believe that. Recently, I realised it is because of this feeling that I volunteer as part of the local developer community.

I have been involved in the Ann Arbor area developer community for just under five years. A couple of colleagues had suggested I attend an Ann Arbor .NET Developer (AADND) meeting, but oddly, a woodworking class is what led me there. In that class, I met fellow developer Steve Meagher, we became friends, and he eventually persuaded me to tag along with him to a .NET meeting. Like many within the developer community, I avoided user groups and other community events for fear of not fitting in or some other perceived discomfort. At that first meeting, I met David Giard as he was the speaker that evening. Meeting David turned out to be a gateway into the wider community and volunteering. At the time, he was the president of the Great Lakes Area .NET group (GANG) and he invited me to attend a meeting there the following week. Just as with Steve at woodworking class, another connection was made and so it was that my adventures in the developer community continued. Through the friends I made attending the local groups, I ventured to far off places like CodeMash and Kalamazoo X. Through the friends I made attending those far off places, I ventured to electronic wonderlands like Twitter, StackOverflow, and my own blog. And eventually, through the encouragement I received from this amazingly supportive community, my family, and my friends, I found the courage to look inward, to seek help for the demons that fostered my low self-esteem, and to grow.

I have volunteered on the board of AADND, as a participant and team leader at Give Camp, and as a speaker at CodeMash; having thoroughly enjoyed every second, I can tell you that volunteering is 100% pure fun.

OK, that is utter bollocks; volunteering is hard. There is no pleasure in finding content for newsletters and slide decks, no joy in the conflicts a team faces when you have less than a day to get a database migrated, no comfort in preparing and rehearsing a talk1. Volunteering is often stressful, sometimes boring, and always built upon a foundation of compromise and sacrifice. If those things were the rewards of volunteering, I cannot imagine anyone who would do it. Every year, Michael Eaton tells a tale of how he declares that this Kalamazoo X will be his last. That it is too much work. Too much worry. Too much sacrifice.

Thankfully, the hard work leads to gratitude: the emotional words of a non-profit director overwhelmed by the generosity of local developers; a room of people applauding at the end of a talk; or a simple "thank you". Regardless of its delivery, seeing or hearing that someone is grateful makes all the effort worthwhile. It feels good. For community volunteers like Michael Eaton it is the gratitude shown by attendees, speakers, and co-organizers that ultimately leads to more events (like just one more Kalamazoo X).

So, next time you enjoy something that someone volunteered to do, show your gratitude. And if the opportunity arises, try volunteering; you have no idea who might be grateful and how good that might feel.

  1. or a last minute Pecha Kucha that your friends then make sure will get heard while you are busy searching for that lost sleep []

#kalx15: Back to Basics

This weekend a couple of friends stopped by the house at around 5:45 on Saturday morning. Normally, this would not be welcomed, but it was time for our annual road trip out to attend the Kalamazoo X conference. This year marked my third year of attending this fantastic one-day, single-track, soft-skills conference. Though often referred to as a non-tech conference for techies, Kalamazoo X is a really accessible event and as such, this was the second year that my wife, Chrissy, also joined us.

This year's conference was held at Loft 310. I found the new venue — with attendees sat around tables similar to how people would be seated at a wedding or party — to be an improvement over last year. Though fantastic, last year's academic venue, larger attendance, and expanded speaker schedule lost much of the intimacy and community that made my first year at KalX a memorable and somewhat life adjusting event. This year was a return to that more intimate experience of two years ago, feeling much more like a gathering of friends and family than a conference of professionals.

The speaker schedule was also condensed this year and all the better for it. The roster included a welcome return of some KalX veterans like Jeff Blankenburg (@jeffblankenburg), H. Alan Stevens (@alanstevens), Jim Holmes (@aJimHolmes), and Elizabeth Naramore (@ElizabethN), as well as some newcomers like Jay Harris (@jayharris), Cori Drew (@coridrew), and Dawn Kuczwara (@digitaldawn). Though each topic was different, they were bound by the common year-on-year KalX themes of learning, mentoring, and growing.

Upon reflection, the talks that I remember most vividly were those where the speaker opened up, let down barriers, and gave honestly to the audience. Alan Stevens was, as always, a joyous speaker to experience — his command of space and time when delivering a talk is really exemplary, yet it was his candidness in discussing his struggle with depression with which I connected. While Cory House (@housecor) spoke on breaking the monotony in life and stepping outside of our comfort zones, it was when he opened up about his social anxieties and personal journey to overcome them that I took notice. And though Jay Harris delivered as polished a presentation as he ever has, it was his willingness to share his broken dreams of baseball and airplanes, open up about personal challenges, and be as raw with the attendees as he is with his friends that took his talk from good to great.

Though I enjoyed all the talks, it was Jay's talk, #conviction, that stood out most for me. Jay's message felt like the third part to a trilogy that started with Jeff Blankenburg's talk, "Be A Beginner", and was fleshed out by Alan Stevens' talk on "Values Driven Development". Jay judiciously spent every second of his time with a well thought out rebuttal to the too often repeated adages "follow your passion" and "hard work pays off". It is easy to ignore the privileges we have that we more commonly refer to as "talents", to let humility lessen their importance, but it is talent coupled with conviction that leads to success1.

Of course, it was not just about the guys. There was not only a more diverse audience than one might expect, but three of the eight speakers were women2. A favourite talk of the day for me was "Give Up!" from KalX newcomer, Dawn Kuczwara. Through personal anecdotes and a wonderful, personable delivery, Dawn explained the importance of letting go of control, of allowing people the opportunity to fail and learn, and of making sure not to stifle the growth of yourself or your team by micromanaging and "helping". To me, this talk was the second part to another trilogy that was started by Cori Drew and her impassioned (though perhaps a tad too long) talk that related her experiences mentoring her daughter from curious kid to seasoned speaker (at age 11), and closed with Elizabeth Naramore explaining why it is always OK to follow your passions in your leisure time, regardless of talent.

Though it was a long, tiring day (I drove, drank far too much caffeine, and stayed up way too late), Kalamazoo X was a day well spent. I am grateful to Michael Eaton (@mjeaton), Matt Davis (@mattsonlyattack), and all their minions, speakers, and tolerant friends and family for the time and patience spent in organising and delivering a terrific conference. Once more, after CodeMash had refreshed my curiosity, Kalamazoo X reset my spirit.

 

  1. having a passion for singing does not mean you can sing and no amount of hard work will change that []
  2. This shouldn't be a point of note, but in an industry traditionally dominated by men, it is []

Debugging in LINQPad

If you have been reading my blog over the last few months you will no doubt be aware that I am a regular user of LINQPad. I do not have any commercial involvement with LINQPad nor its creators, I just really like it. Recently, I decided to try out the latest release, which adds integrated debugging to the already feature rich tool. This amazingly powerful new feature adds yet another reason why this application should be in every developer's arsenal, regardless of experience and ability (it is a great learning tool for students). Here is a brief overview of this new feature, which is available with the premium license (currently on sale for $85 at time of writing; it may not be the case as you are reading this).

When running the latest LINQPad, the debugging feature adds some new buttons to the familiar toolbar. All the debugging features are available for both statement and program-based queries in C#, VB, and F# (not expressions or SQL languages). The first new button is the `Pause` button, also known as `Break`. This works as you might expect, pausing the current code execution. The other two are to specify how exceptions should be handled, informing  the debugger to break on unhandled exceptions and when exceptions are thrown. Breakpoints can be added by clicking in the margin to the left of the code or pressing `F9` when the caret is on the desired line.  When a breakpoint is active on a line, it is indicated as a large red circle. For those who regularly use Visual Studio, the breakpoint and general debugger experience will be familiar.

Pressing `F5` will run the query (or selected lines) as usual, but now, any breakpoints set on executing lines will cause the code to break. At this point, LINQPad will reveal some familar and not-so-familiar tools for debugging the code. General status information is displayed at the bottom of the LINQPad window, showing things like whether the code is executing or paused, whether the debugger is attached or not, and the process ID.

The next code statement to execute is highlighted in the code with a yellow arrow in the margin (in this case, overlaid on the breakpoint circle), and the code highlighted in yellow. In the lower left portion of the screen, we can see local variables and executing threads. We can also set up our own watches as necessary. Any objects in the `Locals` and `Watch` tabs can be expanded using the `+` glyph to reveal their constituent values. As in Visual Studio, these tabs allow the expansion of just-in-time LINQ queries so you can delve into the deep dark secrets of your code. However, you can also take advantage of LINQPad's fantastic dump feature and dump any value out to the `Results` tab on the right. If you want to control how far down the object graph a dump will go, you can modify the `Dump Depth` using the `+` and `-` controls in the column header.

The `Dump` output for the `range` variable
The `Dump` output for the `range` variable
Specifying the depth of the dump
Specifying the depth of the dump

For more information on LINQPad and its many features, check out the LINQPad website (http://linqpad.net). In my opinion, whether you use the free version or one of the paid upgrades, you will have one of the best coding utilities available for .NET.

Writing A Simple Slack Bot With Node slack-client

Last week, we held our first CareEvolution hackathon of 2015. The turn out was impressive and a wide variety of projects were undertaken, including 3D printed cups, Azure-based machine learning experiments, and Apple WatchKit prototypes. For my first hackathon project of the year, I decided to tinker with writing a bot for Slack. There are many ways to integrate custom functionality into Slack including an extensive API. I decided on writing a bot and working with the associated API because there was an existing NodeJS1 client wrapper, `slack-client`2. Using this client wrapper meant I could get straight to the functionality of my bot rather than getting intimate with the API and JSON payloads.

I ended up writing two bots. The first implemented the concept of `@here` that we had liked in HipChat and missed when we transitioned to Slack (they have `@channel`, but that includes offline people). The second implemented a way of querying our support server to get some basic details about our deployments without having to leave the current chat, something that I felt might be useful to our devops team. For this blog, I will concentrate on the simpler and less company-specific first bot, which I named `here-bot`.

The requirement for here-bot is simple:

When a message is sent to `@here` in a channel, notify only online members of the channel, excluding bots and the sender

In an ideal situation, this could be implemented like `@channel` and give users the ability to control how they get notified, but I could not identify an easy way to achieve that inside or outside of a bot (I raised a support request to get it added as a Slack feature). Instead, I felt there were two options:

  1. Tag users in a message back to the channel from `here-bot`
  2. Direct message the users from `here-bot` with links back to the channel

I decided on the first option as it was a little simpler.

To begin, I installed the client wrapper using `npm`:

npm install slack-client

The `slack-client` package provides a simple wrapper to the Slack API, making it easy to make a connection and get set up for handling messages. I used their sample code to guide me as I created the basic skeleton of `here-bot`.

var Slack = require('slack-client');

var token = 'MY SUPER SECRET BOT TOKEN';

var slack = new Slack(token, true, true);

slack.on('open', function () {
    var channels = Object.keys(slack.channels)
        .map(function (k) { return slack.channels[k]; })
        .filter(function (c) { return c.is_member; })
        .map(function (c) { return c.name; });

    var groups = Object.keys(slack.groups)
        .map(function (k) { return slack.groups[k]; })
        .filter(function (g) { return g.is_open && !g.is_archived; })
        .map(function (g) { return g.name; });

    console.log('Welcome to Slack. You are ' + slack.self.name + ' of ' + slack.team.name);

    if (channels.length > 0) {
        console.log('You are in: ' + channels.join(', '));
    }
    else {
        console.log('You are not in any channels.');
    }

    if (groups.length > 0) {
       console.log('As well as: ' + groups.join(', '));
    }
});

slack.login();

This code defines a connection to Slack using the token that is assigned to our bot by the bot integration setup on Slack's website. It then sets up a handler for the `open` event, where the groups and channels to which the bot belongs are output to the console. In Slack, I could see the bot reported as being online while the code executed and offline once I stopped execution. As bots go, it was not particularly impressive, but it was amazing how easy it was to get the bot online. The `slack-client` package made it easy to create a connection and iterate the bot's channels and groups, including querying whether the groups were open or archived.

For the next step, I needed to determine when my bot was messaged. It turns out that when a bot is the member of a channel (including direct message), it gets notified on each message entered in that channel. In our client code, we can get these messages using the `message` event.

slack.on('message', function(message) {
    var channel = slack.getChannelGroupOrDMByID(message.channel);
    var user = slack.getUserByID(message.user);

    if (message.type === 'message') {
        console.log(channel.name + ':' + user.name + ':' + message.text);
    }
});

Using the `slack-client`'s useful helper methods, I turned the message channel and user identifiers into channel and user objects. Then, if the message is a message (it turns out there are other types such as edits and deletions), I send the details of the message to the console.

With my bot now listening to messages, I wanted to determine if a message was written at the bot and should therefore alert the channel users. It turns out that when a message references a user, it actually embeds the user identifier in place of the displayed `@here` text. For example, a message that appears in the Slack message window as:

@here: Anyone know how to write a Slack bot?

Is sent to the `message` event as something like3:

<@U099999>: Anyone know how to write a Slack bot?

It turns out that this special code is how a link to a user or channel is embedded into a message. So, armed with this knowledge and knowing that I would want to mention users, I wrote a couple of helper methods: the first to generate a user mention embed code from a user identifier, the second to determine if a message was targeted at a specific user (i.e. that it began with a reference to that user).

var makeMention = function(userId) {
    return '<@' + userId + '>';
};

var isDirect = function(userId, messageText) {
    var userTag = makeMention(userId);
    return messageText &&
           messageText.length >= userTag.length &&
           messageText.substr(0, userTag.length) === userTag;
};

Using these helpers and the useful `slack.self` property, I could then update the `message` handler to only log messages that were sent directly to here-bot.

slack.on('message', function(message) {
    var channel = slack.getChannelGroupOrDMByID(message.channel);
    var user = slack.getUserByID(message.user);

    if (message.type === 'message' && isDirect(slack.self.id, message.text)) {
        console.log(channel.name + ':' + user.name + ':' + message.text);
    }
});

The final stage of the bot was to determine who was present in the channel and craft a message back to that channel mentioning those online users. This turned out to be a little trickier than I had anticipated. The `channel` object in `slack-client` provides an array of user identifiers for its members; `channel.members`. This array contains all users present in that channel, whether online or offline, bot or human. To determine details about each user, I would need the user object. However, the details for each Slack user are provided by the `slack.users` property. I needed to join the channel member identifiers with the Slack user details to get a collection of users for the channel. Through a little investigative debugging4, I learned that `slack.users` was not an array of user objects, but instead an object where each property name is a user identifier. At this point, I wrote a method to get the online human users for a channel.

var getOnlineHumansForChannel = function(channel) {
    if (!channel) return [];

    return (channel.members || [])
        .map(function(id) { return slack.users[id]; }
        .filter(function(u) { return !!u && !u.is_bot && u.presence === 'active'; });
};

Finally, I crafted a message and wrote that message to the channel. In this update of my `message` event handler, I have trimmed the bot's mention from the start of the message before creating an array of user mentions, excluding the user that sent the message. The last step calls `channel.send` to output a message in the channel that mentions all the online users for that channel and repeats the original message text.

slack.on('message', function(message) {
    var channel = slack.getChannelGroupOrDMByID(message.channel);
    var user = slack.getUserByID(message.user);

    if (message.type === 'message' && isDirect(slack.self.id, message.text)) {
        var trimmedMessage = message.text.substr(makeMention(slack.self.id).length).trim();
        
        var onlineUsers = getOnlineHumansForChannel(channel)
            .filter(function(u) { return u.id != user.id; })
            .map(function(u) { return makeMention(u.id); });
        
        channel.send(onlineUsers.join(', ') + '\r\n' + user.real_name + 'said: ' + trimmedMessage);
    }
});

Conclusion

Example

My `@here` bot is shown below in its entirety for those that are interested. It was incredibly easy to write thanks to the `slack-client` package, which left me with hackathon time to spare for a more complex bot. I will definitely be using `slack-client` again.

var Slack = require('slack-client');

var token = 'MY SUPER SECRET BOT TOKEN';

var slack = new Slack(token, true, true);

var makeMention = function(userId) {
    return '<@' + userId + '>';
};

var isDirect = function(userId, messageText) {
    var userTag = makeMention(userId);
    return messageText &&
           messageText.length >= userTag.length &&
           messageText.substr(0, userTag.length) === userTag;
};

var getOnlineHumansForChannel = function(channel) {
    if (!channel) return [];

    return (channel.members || [])
        .map(function(id) { return slack.users[id]; }
        .filter(function(u) { return !!u && !u.is_bot && u.presence === 'active'; });
};

slack.on('open', function () {
    var channels = Object.keys(slack.channels)
        .map(function (k) { return slack.channels[k]; })
        .filter(function (c) { return c.is_member; })
        .map(function (c) { return c.name; });

    var groups = Object.keys(slack.groups)
        .map(function (k) { return slack.groups[k]; })
        .filter(function (g) { return g.is_open && !g.is_archived; })
        .map(function (g) { return g.name; });

    console.log('Welcome to Slack. You are ' + slack.self.name + ' of ' + slack.team.name);

    if (channels.length > 0) {
        console.log('You are in: ' + channels.join(', '));
    }
    else {
        console.log('You are not in any channels.');
    }

    if (groups.length > 0) {
       console.log('As well as: ' + groups.join(', '));
    }
});

slack.on('message', function(message) {
    var channel = slack.getChannelGroupOrDMByID(message.channel);
    var user = slack.getUserByID(message.user);

    if (message.type === 'message' && isDirect(slack.self.id, message.text)) {
        var trimmedMessage = message.text.substr(makeMention(slack.self.id).length).trim();
        
        var onlineUsers = getOnlineHumansForChannel(channel)
            .filter(function(u) { return u.id != user.id; })
            .map(function(u) { return makeMention(u.id); });
        
        channel.send(onlineUsers.join(', ') + '\r\n' + user.real_name + 'said: ' + trimmedMessage);
    }
});

slack.login();

 

  1. or ioJS, if you would prefer []
  2. I find hackathons to be a bit like making a giant pile of sticks in the middle of a desert; it's an opportunity to get creative and build something where there seems to be nothing…using sticks…or in my case, a Node package and Slack []
  3. I totally made up the user identifier for this example []
  4. I used WebStorm 9 from JetBrains to debug my Node code, a surprisingly easy and pleasant experience []

Ten Years

This month marks ten years since I first set foot in the US. As I waited in line at immigration, tired from the flight and daunted by everything that might happen next, it was easy to forget everything that came before. Just three weeks earlier, my workplace had been tense with news that another wave of redundancies was sweeping through and I was unsure in what direction I was heading1. I was ready for a change, but did not want the uncertainty of finding a new job or the certainty of choosing to leave the one I had. I was living a step ahead of my means with little attention paid to the future. I was smoking. I was making dubious decisions or avoiding decisions entirely. I was feeling disenfranchised, misplaced, and numb.

One afternoon our manager called us all into a meeting room. There, he informed us of two positions available in the US and asked if any of us were interested. It felt like the silence lasted a long time although it was probably only a few seconds. No one was volunteering. I do not know the trigger  — my desire for a change, the allure of working in the US, or my need to get control of my life, but slowly, I raised my hand. I remember rationalizing it as no big deal, after all, I was only expressing interest, it was not like I would be whisked away to a plane immediately. With the raise of my hand, so began a series of small, easy decisions that led to the biggest self-directed change of my life so far.

Within the three weeks from when I raised my hand to when I stepped off the plane in Detroit, I packed, paused, and displaced my life. Boxes were filled, paperwork was filed, and farewells were planned. There was no time to stop and think about what I was doing, just lots of small decisions to make — accept or negotiate the contract, pack or throw away my things, take or leave my guitar, stay or go, sink or swim. All along the way, I kept telling myself it was not forever, it was no big deal. I was only going for a couple of months to meet the customer face-to-face; work (and a longer stay) was always going to be dependent on the acquisition of appropriate work visas. It was no big deal.

And so it was. Three weeks flew by. My sister, my parents, my new boss in the US, my old colleagues in the UK, my friends (including my housemate, who was seriously ill at the time), and many more all helped in some way. I am incredibly grateful to their support, it was amazing. During the whole experience, trepidation wrestled with excitement. Seconds after the taxi pulled away, leaving my parents and friends as they waved goodbye, excitement turned to panic.

What the hell am I doing?

I repeated that phrase in my head many times between London and Detroit. When the taxi left my old home. When the taxi left me at the airport. When I was pulled from the security line for "special screening". When I sat on the plane. When the plane took off. At least once per hour during the flight. When I landed. When I got into the immigration line. Over and over.

What the hell am I doing?

I am pretty sure I was terrified, but just like the small decisions that got me there, I focused on the immediate situation and did my best to ignore everything else. I think excitement and terror are pretty much the same thing but with different interpretations. As I accepted the situation as an adventure, the terror would subside and excitement returned.

Blimey, I'm actually going to America!

That was how my first few weeks in the US continued. A mixture of terror and excitement, depending on the situation and how I let myself accept it. It was the beginning of something new and ten years on, I cannot imagine doing any differently if it were to happen all over again. It was by far the best decision I ever made because I learned the value of making a decision instead of letting fate decide. I faced my own anxieties head on and made a decision to challenge my fear. The amazing sense of achievement that came from deciding for myself was life-affirming. While it took me another nine years to take that moment of control over my anxiety and begin learning how to harness it on a day-to-day basis, I still look back on that decision and the many ways it has changed my life. In a moment, I went from feeling disenfranchised, misplaced, and numb, to engaged, excited, and driven.

Of course, that first day in the US was merely the beginning, a lot has happened since and a lot more will happen yet. Though my move was certainly no panacea to my problems — there were many difficult challenges to over come, it was a catalyst for solutions, an opportunity to grow, and a clear example that fear alone could not stand in my way if I could find the courage to face it. It is a lesson that I have applied many times since; from winning the CodeMash Pecha Kucha contest, to marrying my amazing wife, so many achievements began in an otherwise unremarkable moment where I pushed my fear aside and made a decision to try.

So, whatever the next ten years hold for me, it is not fear, but small moments like raising my hand in that meeting room that will shape them. Can you say the same? Where will your decisions take you?

  1. Unlike in places such as Michigan, where employment is considered "at will" and can be terminated at any time, if a company in the UK wants to downsize, they must go through a process of making positions redundant. That means the position will no longer exist and as such, the organisation cannot hire someone else to perform that job. For a better explanation and more information, check out https://www.gov.uk/redundant-your-rights/overview []