As a Freelancer, Not Every Day is Great

I wrote the other day about not conflating your sense of self-worth with your work. That is particularly important on the days where your work isn’t going as well as you think it should.

When I started freelancing, I thought I would never have a bad day of work again. I’d be in control of my own destiny and, since I was going to do my best work without fail, every day was going to be more awesome than the last.

I don’t think this could be any further from the truth. Over the last five years of freelancing, I’ve definitely had more good days than bad, but I’ve had some days that were really, really terrible.

Most of the time, the bad days came because of something I couldn’t control. Maybe there was a storm and our power was knocked out, or my motherboard on my computer had a corrupted BIOS and wouldn’t boot, or a client was late getting me feedback but absolutely had to launch tomorrow because of some reason they hadn’t told me about.

There’s not much to do in these situations but buck up, do the best you can, and try to have a positive outcome to the day. Accomplish one thing and you’ll set yourself on the path to success tomorrow.

If you’re getting into freelancing and think everything will always be awesome, you’re wrong. Accept that fact, prepare to deal with it, and you’ll be better for it.

Testimonials: Why and How

Testimonials have been an extremely important part of my business. I’m pretty sure, based on traffic analysis, that my testimonials drive more of my business than anything else.

Why are Testimonials so Important?

The obvious reason is that they’re a form of social proof. Other people running businesses have trusted their projects with you and had success so you’re probably a smart bet. You have obviously shown the ability to get stuff done in the past, so you’re likely to do so in the future.

How about another reason? For me, testimonials have been a life-saver when I’m working on a challenging project. Being able to look back and see the positive outcomes I’ve had on other projects and being reminded of how highly people regard my work gets me excited to do my best all over again. Here’s one of my favorite testimonials:

Nick was incredibly fast and efficient. He went above and beyond our expectations and was very pleasant to work with. We have yet to encounter a truer professional. We were also extremely impressed by his huge guns.

Aaron Gibraltar – Urtak, Inc.

How could I read that and not get pumped about my work?

How to Ask for a Testimonial

First, do your absolute best work at all times. Provide real value to your clients and they’ll be more willing to talk about your services.

Second, ask for a testimonial as soon as you finish a project. You want your client’s experience with you to be as fresh in their mind as possible. I try to ask for a testimonial in my final delivery message.

Next, make sure the client knows what you’re looking for. Specifically, ask for a sentence or two and nothing more. Writing a sentence takes so little effort that oftentimes people will just do it right away. Any larger commitment will cause your request to be put on the back burner.

You’ll probably need to follow up once or twice. If the client doesn’t get back to you immediately, send them a note via email in a week saying something like:

Joe, I really enjoyed working with you and I feel like we had a really successful project together. If you thought the same, I’d love to have a sentence or two from you about your experiences with me to post on my testimonials page.

As you can see, the request mentions your existing testimonials. There are two reasons for this:

  1. The client will see that you’re not asking for something out of the ordinary because others have already gone through the process of giving you a testimonial
  2. They will understand that you’re not looking for much more than a sentence or two and are more likely to follow through

Finally, move on quietly if the client can’t (because of corporate policy) or won’t write you a testimonial. It isn’t the end of the world as there will always be new clients you can try to impress.

Have you found that testimonials are an important part of your business? How do you go about getting them from clients?

Remember: You Are Not Your Work

Freelancers (myself definitely included) often feel a strong emotional attachment to their work. This attachment has some notable benefits:

  • An intrinsic desire to produce the best work possible
  • A sense of purpose when you’re working through a project, especially when you’re working alone
  • Pride in the end result of all your work

The danger comes when you take that emotional attachment too far and start conflating your sense of self with the success of your projects. This might be fine if every single thing you work on is successful and proceeds smoothly.

Realistically, that just isn’t an appropriate expectation. You are going to have at least a few bad projects, even if it is because of things that are outside of your control.

This is something I’ve been dealing with for the last month or so. A project went off the rails and, while I’ve been working as hard as possible to finish it in a satisfactory way, it just hasn’t gotten any better. Because of this project, I’ve been depressed, angry, and not a ton of fun to be around.

I’m trying to take a step back, now, and see my work as separate from myself. Things are slowly getting better, but it is still a struggle for me to not conflate my sense of self worth with my work success.

If you are experiencing the same or similar feelings, take this opportunity to realize you are not your work. Remind yourself every day – that’s what I’ve done and it is helping.

If you’ve got other ways to deal with this, I’d love to hear about them in the comments.

Building a WordPress Plugin: Multiple Featured Images

I worked on a WordPress site recently where the client needed to associate two different images with each piece of content. I couldn’t use a single image with a custom size because the images’ aspect ratios were so different.

I knew that I could use WordPress’s Featured Image functionality for the first image, but I wasn’t quite sure what to do for the second. I needed it to be as easy as possible to use because my client was non-technical. I looked for an existing solution by searching the WordPress plugin repository. I found a plugin that seemed promising, but Multiple Featured Images hasn’t been updated since May 2012 and didn’t support the media picker interface introduced in WordPress 3.5.

Because I knew this was something I would use on future projects, I decided to build my own plugin.

Building the Plugin

The plugin provides an interface that a developer can use to add multiple named image pickers to any type of content and access the images within the site’s theme. It uses the new media picker interface introduced in 3.5.

Laying the Foundation

Every WordPress plugin I build starts from the same basic plugin skeleton. If you’re interested in using my skeleton, feel free to download it from GitHub.

My plugin skeleton is ridiculously simple – it is a class with exclusively static methods. The class acts as a pseudo namespace that encapsulates the functionality of my plugin and ensures there are no function naming collisions. There are a million different ways to structure a WordPress plugin, but my skeleton has worked well for me over the past few years.

After forking my skeleton for this project, I renamed the plugin class to something appropriate (MFI_Reloaded) and changed all other strings as necessary.

Defining the Public Interface

Since this plugin is going to be used by other developers, the first thing I wanted to do is define a well thought out interface. The following is what I came up with:

function mfi_reloaded_add_image_picker($name, $args = array());

function mfi_reloaded_has_image($name, $post_id = null);

function mfi_reloaded_get_image_id($name, $post_id = null);

function mfi_reloaded_get_image($name, $size = 'thumbnail', $post_id = null, $attributes = array());
function mfi_reloaded_the_image($name, $size = 'thumbnail', $post_id = null, $attributes = array());

This interface accomplishes both goals I had for the plugin. Developers can register new named image pickers using mfi_reloaded_add_image_picker and access saved information with the rest.

With this interface, I consciously tried to stay close to the analogous functions provided by the WordPress post thumbnail template tags.

Implementing the Public Interface

Now that there is an interface for telling the plugin what image pickers to display, I need a way to store that data in the plugin. For the sake of simplicity, I decided to use an associative array which I added as a private static field on our plugin class.

At the top of my plugin class I added the following:

private static $image_pickers = array();

Now, I needed to actually make the template tags defined earlier do something. When I build template tags, I tend to make all “getters” and “setters” simply delegate to public static methods on the class. That’s what I did here.

At the bottom of my plugin class I added the following:

public static function add_image_picker($name, $args) {
	return false;
}

public static function get_image_id($name, $post_id) {
	return false;
}

public static function get_image($name, $size, $post_id, $attributes) {
	return false;
}

I started here by implementing add_image_picker. This could have been as easy as adding $args as a new item on the previously defined $image_pickers field with $name as the array key. In the interest of making this plugin more robust, however, I went a little bit further:

  1. Return false immediately if the image picker name is not a string or if an image picker with that name has been previously registered
  2. Normalize the data passed in the $args parameter given the defaults specified in the template tag documentation
  3. Set $image_pickers[$name] = $normalized_args

Here is what this method ended up looking like:

public static function add_image_picker($name, $args) {
	if(!is_string($name) || isset(self::$image_pickers[$name])) {
		return false;
	}

	self::$image_pickers[$name] = self::_normalize_args($args);
}

I needed to normalize the arguments so I created the helper method named _normalize_args to do so. I usually prefix helper methods like this with an underscore (as you can see). That method reads as follows:

private static function _normalize_args($args) {
	$normalized_args = array();

	if(!isset($args['post_types'])) {
		$normalized_args['post_types'] = array('post', 'page');
	} else if(!is_array($args['post_types'])) {
		$normalized_args['post_types'] = array($args['post_types']);
	} else {
		$normalized_args['post_types'] = $args['post_types'];
	}

	$default_labels = array(
		'name' => __('Featured Image'),
		'set' => __('Set featured image'),
		'remove' => __('Remove featured image'),
		'popup_title' => __('Set Featured Image'),
		'popup_select' => __('Set featured image'),
	);

	if(!isset($args['labels']) || !is_array($args['labels'])) {
		$normalized_args['labels'] = $default_labels;
	} else {
		$normalized_args['labels'] = shortcode_atts($default_labels, $args['labels']);
	}

	return $normalized_args;
}

For now, that’s all I could really implement. I wasn’t storing any data yet so there was no way to return anything meaningful.

Adding the Meta Boxes

Once the public API was defined, the next step was to show the user an interface they could use. It won’t be fully functional at first, but it will look like correct.

To register our meta boxes, we first add a callback to the add_meta_boxes action. In my add_actions method (inside the is_admin conditional), I added the following code:

add_action('add_meta_boxes', array(__CLASS__, 'add_image_picker_meta_boxes'));

Then, I added the following public static method inside of my class:

public static function add_image_picker_meta_boxes($post_type) {
	foreach(self::$image_pickers as $image_picker_name => $image_picker_args) {
		if(in_array($post_type, $image_picker_args['post_types'])) {
			add_meta_box(
				'mfi-reloaded-' . sanitize_title_with_dashes($image_picker_name),
				$image_picker_args['labels']['name'],
				array(__CLASS__, 'display_image_picker_meta_box'),
				$post_type,
				'side',
				'default',
				compact('image_picker_name', 'image_picker_args')
			);
		}
	}
}

This method iterates over each registered image picker and checks to see if it is registered for the post type currently being edited. If it is, it adds a meta box by calling add_meta_box with the appropriate arguments.

In order to actually display the meta box, we need to implement the display_image_picker_meta_box method. In my plugin class, I added another public static method with that name that displays some output:

public static function display_image_picker_meta_box($post, $meta_box) {
	$image_picker_args = $meta_box['args']['image_picker_args'];
	$image_picker_name = $meta_box['args']['image_picker_name'];

	$image_id = mfi_reloaded_get_image_id($image_picker_name, $post->ID);
	$image = mfi_reloaded_get_image($image_picker_name, 'full', $post->ID);

	include('views/meta-boxes/image-picker.php');
}

We’re using the unimplemented public interface that we stubbed out earlier to grab the full size image for the picker in question (if it exists). We’ll be using this later. When printing HTML, I prefer to include a separate file (as you can see in the method above). The entirety of that file is as follows:

<div class="mfi-reloaded-image-picker"
		data-mfi-reloaded-image-id="<?php printf('%d', $image_id); ?>"
		data-mfi-reloaded-name="<?php esc_attr_e($image_picker_name); ?>"
		data-mfi-reloaded-select="<?php esc_attr_e($image_picker_args['labels']['popup_select']); ?>"
		data-mfi-reloaded-title="<?php esc_attr_e($image_picker_args['labels']['popup_title']); ?>">

	<div class="mfi-reloaded-image-picker-preview"></div>

	<a class="mfi-reloaded-image-picker-remove" href="#"><?php esc_html_e($image_picker_args['labels']['remove']); ?></a>
	<a class="mfi-reloaded-image-picker-set" href="#"><?php esc_html_e($image_picker_args['labels']['set']); ?></a>
</div>

This code outputs a container for the thumbnail preview and two links that allow the user to take action. Of course, nothing happens yet.

Adding the Image Picking Functionality

The media picker is controlled via JavaScript and styled with CSS so I needed to enqueue the appropriate scripts and styles in the administrative panel on the editing pages. To do so, I hooked into admin_enqueue_scripts by adding the following line inside of my add_actions method (in the is_admin conditional):

add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_administrative_resources'));

The enqueue_administrative_resources callback is responsible for checking if the post editing screen is being viewed and, if so, enqueueing the appropriate scripts.

public static function enqueue_administrative_resources() {
	$screen = get_current_screen();

	if('post' === $screen->base) {
		wp_enqueue_media();

		wp_enqueue_script('mfi-reloaded', plugins_url('resources/backend/mfi-reloaded.js', __FILE__), array('jquery'), self::VERSION, true);
		wp_enqueue_style('mfi-reloaded', plugins_url('resources/backend/mfi-reloaded.css', __FILE__), array(), self::VERSION);
	}
}

The stylesheet is pretty simple as it just prevents the preview image from flowing outside the bounds of its container:

.mfi-reloaded-image-picker-preview img {
	height: auto;
	max-width: 100%;
}

The JavaScript file is a little more complex, but I’ve added comments liberally where I think there could be confusion. I encourage you to read the source if you’re interested in what is going on:

jQuery(document).ready(function($) {
	// Register a click event handler on the remove links
	$('.mfi-reloaded-image-picker-remove').click(function(event) {
		event.preventDefault();

		var $remove = $(this),
			$container = $remove.parents('.mfi-reloaded-image-picker'),
			$preview = $remove.siblings('.mfi-reloaded-image-picker-preview'),
			$set = $remove.siblings('.mfi-reloaded-image-picker-set'),
			_name = $container.data('mfi-reloaded-name');

		// Initiate an AJAX request to remove the image id for this image picker
		$.post(
			ajaxurl,
			{
				action: 'mfi_reloaded_set_image_id',
				image_id: 0,
				name: _name,
				post_id: $('#post_ID').val()
			},
			function(data, status) { }
		);

		// Hide the link that allows a user to remove the image
		$remove.hide();

		// Remove the preview thumbnail because it is no longer valid
		$preview.empty();

		// Show the link that allows a user to set the image
		$set.show();
	});

	// Register a click event handler in order to show the media picker
	$('.mfi-reloaded-image-picker-set').click(function(event) {
		event.preventDefault();

		var $set = $(this),
			$container = $set.parents('.mfi-reloaded-image-picker'),
			$preview = $set.siblings('.mfi-reloaded-image-picker-preview'),
			$remove = $set.siblings('.mfi-reloaded-image-picker-remove'),
			_name = $container.data('mfi-reloaded-name'),
			_select = $container.data('mfi-reloaded-select'),
			_title = $container.data('mfi-reloaded-title');

		// Set up the media picker frame
		var mfi_reloaded_frame = wp.media({
			// Open the media picker in select mode only
			frame: 'select',

			// Only allow a single image to be chosen
			multiple: false,

			// Set the popup title from the HTML markup we output for the active picker
			title: _title,

			// Only allow the user to choose form images
			library: { type: 'image' },

			button: {
				// Set the button text from the HTML markup we output for the active picker
				text:  _select
			}
		});

		mfi_reloaded_frame.on('select', function(){
			var media_attachment = mfi_reloaded_frame.state().get('selection').first().toJSON();

			// Initiate an AJAX request to set the image id for this image picker
			$.post(
				ajaxurl,
				{
					action: 'mfi_reloaded_set_image_id',
					image_id: media_attachment.id,
					name: _name,
					post_id: $('#post_ID').val()
				},
				function(data, status) { }
			);

			// Add the image to the preview container
			$preview.append($('<img />').attr('src', media_attachment.sizes.full.url).attr('alt', media_attachment.title));
		});

		// Show the remove link
		$remove.show();

		// Hide the set link
		$set.hide();

		mfi_reloaded_frame.open();
	});

	$('.mfi-reloaded-image-picker').each(function(index, element) {
		var $container = $(element),
			$preview = $container.children('.mfi-reloaded-image-picker-preview'),
			$remove = $container.children('.mfi-reloaded-image-picker-remove'),
			$set = $container.children('.mfi-reloaded-image-picker-set');

		if(0 === $preview.children().size()) {
			$remove.hide();
		} else {
			$set.hide();
		}
	});
});

Now that the JavaScript was in place and working, I need to make sure that the user’s choices were persisted.

Saving and Loading Data

When I’m saving data for a post, I generally like to create two small wrapper methods that get and set the data. That’s exactly what I did for this plugin:

private static function _get_meta($post_id, $meta_key = null) {
	$post_id = empty($post_id) && in_the_loop() ? get_the_ID() : $post_id;

	$meta = get_post_meta($post_id, 'rfi-reloaded-images', true);
	
	if(!is_array($meta)) {
		$meta = array();
	}

	return is_null($meta_key) ? $meta : (isset($meta[$meta_key]) ? $meta[$meta_key] : false);
}

private static function _set_meta($post_id, $meta) {
	$post_id = empty($post_id) && in_the_loop() ? get_the_ID() : $post_id;

	update_post_meta($post_id, 'rfi-reloaded-images', $meta);

	return $meta;
}

Now that these were in place, I could address the AJAX call that was currently being unhandled. To start, I added an action in my add_actions method for the ajax call:

add_action('wp_ajax_mfi_reloaded_set_image_id', array(__CLASS__, 'ajax_mfi_reloaded_set_image_id'));

Then, I created the callback and made sure it checked permissions for the post being modified and then persisted data appropriately.

public static function ajax_mfi_reloaded_set_image_id() {
	$data = stripslashes_deep($_REQUEST);

	$image_id = $data['image_id'];
	$name = $data['name'];
	$post_id = $data['post_id'];

	if($post_id && current_user_can('edit_post', $post_id)) {
		$images = self::_get_meta($post_id);

		if(empty($image_id)) {
			unset($images[$name]);
		} else {
			$images[$name] = $image_id;
		}

		self::_set_meta($post_id, $images);
	}

	exit;
}

Finishing the Public Interface

Now that we’ve started saving data, we can finally implement the rest of the public interface. I started with the get_image_id method in the plugin class.

public static function get_image_id($name, $post_id) {
	return self::_get_meta($post_id, $name);
}

I just used the wrapper function I created earlier to pull out the appropriate information from the serialized data and returned it.

Next, I implemented the get_image method, which was straightforward enough now that I had the ID.

public static function get_image($name, $size, $post_id, $attributes) {
	$image_id = self::get_image_id($name, $post_id);

	if($image_id) {
		return wp_get_attachment_image($image_id, $size, false, $attributes);
	}

	return false;
}

And that was it, everything was working like I wanted it to.

Using the Multiple Image Picker

Now that the plugin is done, using it is easy. For testing, I put the following code into a file in /wp-content/mu-plugins/

<?php

add_action('init', function() {
	if(function_exists('mfi_reloaded_add_image_picker')) {
		mfi_reloaded_add_image_picker('hero-image', array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Hero Image'),
				'set' => __('Set hero image'),
				'remove' => __('Remove hero image'),
				'popup_title' => __('Set Hero Image'),
				'popup_select' => __('Set hero image'),
			),
		));

		mfi_reloaded_add_image_picker('sidekick-image', array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Sidekick Image'),
				'set' => __('Set sidekick image'),
				'remove' => __('Remove sidekick image'),
				'popup_title' => __('Set Sidekick Image'),
				'popup_select' => __('Set sidekick image'),
			),
		));
	}
});

This registers two image pickers for use on posts only. I then used the mfi_reloaded_the_image to display the images for posts in the loop.

If you’re interested in the plugin I built, download it from GitHub. If you’ve got suggestions for improvements, I’d love to see a pull request for patch. Let me know how you plan on using it in the comments!

Fixed Price Contracts and Freelancing

I’ve been using fixed price contracts almost exclusively since I started freelancing in 2008. If you’re not familiar with the term, a fixed price contract is a contract where the amount of payment depends on producing some deliverable and does not take into account the time taken or resources expended to produce that deliverable. This is in contrast to a time-based contract where the client pays for every unit of time spent on their project, or a cost-plus contract where the client agrees to pay for the contractor’s costs plus some predefined profit margin.

Fixed price contracts appeal to prospective clients for a number of important reasons:

  1. Most of the risk of the project is shifted from the client to the contractor
  2. The client knows upfront exactly how much they will pay to receive what is specified
  3. There is very little administrative burden for the client – they don’t have to review time sheets or address itemized expenses

What’s In It for Freelancers?

While the benefits of fixed price contracts for freelancers are a bit more nuanced than the benefits for clients, I believe they are worth it.

More Money

You’ll make more money with fixed price contracts by taking advantage of value-based pricing. Value-based pricing means you charge the amount that your work is worth to your client, not the amount it costs you to produce it. This is a game-changer if implemented correctly.

An example is easy to come by. Recently, a prospect approached me and asked if I could fix some issues on their website for them. They obviously needed it done in short order and it was apparent that the issues were hampering their ability to run their business the way they wanted to.

I proposed an amount I thought would be acceptable to them based on their circumstances (it was in the low four figures), it was accepted, and I went ahead with the work. It took me a total of two hours to finish.

The client was super happy their problems had been resolved so quickly and I made a ton of money for my time. That’s the best possible outcome for any project.

Cash Flow

Fixed price contracts lead to a better understanding of your cash flow. This is a really good thing – one of the scariest parts of freelancing is the uncertainty of not knowing when money is going to come in.

For example, you set the price for a particular project at $5,000. Assuming a 40% deposit, you’ll receive $2,000 at the start of the project and $3,000 when it is over. If you stay on schedule for the project, you’ll have a really good idea of when you’ll receive that money. This knowledge allows you to schedule the rest of your work so that you have the money you need when you need it.

Motivation

One of the big dangers of freelancing is losing motivation to work on a project. In my experience, this is much more likely to happen with an hourly project where you’re unsure of the end then a well-specified fixed price contract where you know you’ve delivered the appropriate items and are going to get paid.

Things To Look Out For

Fixed price contracts aren’t always rosy. There’s a number of situations where they’re going to cause more trouble then they’re worth.

Ambiguous Project Definitions

This is an issue that has bit me (and other freelancers that I’ve talked to) more than any other. You start a project, build something you think the client is going to love based on the documentation you’ve received or produced, and then you deliver. Great! You’re done and getting ready to move on to the next project when your client sends you an email.

Wait, the client says, where is Feature X, Page Y, and instruction Z? You go back and try to explain that those things obviously weren’t part of the project (they’re going to take you 15 hours each, you’re thinking) until you look over your statement of work. That’s when you spot it – an ambiguous paragraph or sentence that could have led the client to believe that what they are asking for was included.

Now you’re stuck. You can either provide what they want, lose money and sleep, and get on with your life or you can put your foot down and insist that the requested items will have to be part of a separate work order. The first option makes things bad for you, the second makes things bad for the client, and this awesome project just turned into a cesspool.

If you’re going to work on fixed price contracts, you need to make sure the project is specified as well as it can be. There can’t be any questions about what is going to be built. That leads directly to the next danger.

Protracted Specification Period

If you’re going to specify a fixed price for a new project and you want to have a good experience, you need to know exactly what you’re building. This can sometimes lead to dozens of emails back and forth where you’re asking for clarifications on seemingly minute details.

While you can probably narrow down the price to a range during this process, it takes time and effort and you can’t be totally sure that your price is even going to be accepted by the prospect. Also, the prospect can get frustrated by all the back and forth and move on to another freelancer that will push forward with the project immediately.

You can mitigate this danger by charging for the specification process, but unless you have an existing point of contact or relationship with the prospect, that doesn’t always work.

Mid-Project Change Requests

Sometimes you’ll get into the middle of a project and a client changes their mind. They want to implement X instead of Y and change A to B. Sure, you say, we can do that, but I need you to specify exactly the changes you want, formalize it into a change request, and I can give you a quote on top of what we already specified.

These conversations are hard. They’re even harder when you have X half-built and the client can’t see it, but wants to swap it out for Y. It can be hard to explain that, while there isn’t a visible component of feature X, you spent time on it and will need to charge the complete price of switching to Y even though they never saw X.

I haven’t found a good way to deal with these things that makes everyone happy. If you’ve found something, I’d love to hear it.

What Do You Think?

I’ve tried to make the benefits of fixed price contracts clear in this post. I’ve had great success with them and I know others have as well. What about you? Do you use fixed price contracts and how have you dealt with some of the dangers involved? Have you encountered other things that make you shy away from fixed price projects? If you use another billing method, I’d love to hear about it in the comments below.

30 Days of Writing

I’m doing a little experiment this month. I’m going to write a moderately substantive post on this blog every single day. Some of these posts will be technical in nature, similar to my post about adding a setup fee to a new Stripe subscription, but most will cover things that require less upfront effort and discovery based on the myriad experiences I’ve had running my business.

This is a public commitment on my part. If you read this post and see me skip a day, please contact me via Twitter or Skype and tell me to get it done.

Why Am I Doing This?

Primarily, I want to be a better writer. I want to develop my ability to express my ideas concretely and concisely while making the text as compelling as possible. I figure the only way to do this is to practice it – that’s the reason for the emphasis I’m putting on production during the next 30 days. There’s other reasons as well, of course.

Developing a Public Voice

I write copiously as part of my business efforts: statements of work, technical documentation, delivery notes, and testing instructions are just some of the things I write. Add the actual code and you’re looking at a large number of words every single day.

There’s a difference between those things and what I’m trying to do here. All of the above pieces are distributed privately and aimed, almost without fail, at a single person or small group. The language I use in those documents is exceedingly explicit due to their very nature.

I feel that writing for public consumption is very different from the type of writing I usually do. Good public writing is interesting to read and does one of two things. It either teaches how to do something specific or expresses an editorialized opinion on some topic. Both of these things require a writing “voice” with some authority and style.

I Love to Teach

The feeling I get from helping someone with a problem they’re having is amazing. If I know something that can help another individual, I want to share that knowledge. I figure this blog is a great place to do that.

I was at Starbucks with my wife the other day and I noticed someone with a Macbook open and a sketch in front of them on notebook paper. It looked like they were building a website, so I decided to strike up a conversation with them. At first, I was looking to see if they were available to take on overflow work.

It turns out that the person was struggling with some schoolwork that involved building a website based on a Photoshop file. When I told him that I was a web developer, he asked if I would be willing to help him understand how to do what he needed to.

I was so happy to help this individual. We talked about how you break down the structure of a website logically, how to float things into place in order to achieve the desired layout, and what kind of elements made sense based on the content available. He was so grateful that I took a few minutes out of my day and I felt amazing afterwards because I made a real difference in someone’s life.

I want to experience that feeling more often. By posting tips about business and software development publicly, I’ll be able helping many more people than just one guy at Starbucks. That’s awesome!

Public Profile

I’ve come to realize I need to work on my public profile because I essentially don’t have one. Privately, I work with small, medium, and large clients on a variety of interesting projects and have very rarely worried about there not being enough work to do. An extensive referral base combined with existing clients keeps me more than busy enough.

Publicly, though, I might as well be a non-entity. No one has any idea who I am and that’s definitely not good for someone running a business based on personal relationships. If my connections dried up and my current clients ran out of things to do, I’d be pretty stuck.

Being perceived as an expert on the topics I know a lot about will help remedy this problem. I’ve heard there’s no better way to get people to think of you as an expert than to write about the topic you want them to think you’re an expert in, so that’s what I’m going to do.

How Am I Going to Do This?

I didn’t want to go into this process blind, so I’ve developed a very general plan to make sure I actually follow through.

First, I’ll be writing every day before I do anything else. I will spend up to one and a half hours working on content to publish.

Second, I’ll be brainstorming at least 20 topics every Sunday night for the upcoming week. I figure that I can probably get seven good posts of varying lengths out of 20 topics.

Finally, I’m making a commitment to hit the publish button. In the past, I always felt that something had to be perfect before I put it out there. With each passing day, I realize how untrue that is. I can always update or correct something later. For now, I just need to focus on getting things written and published.

Your Thoughts

Do you want to write more? If you do, please join me in this little experiment. I’m sure we can all learn a lot from each other. If you’ve got other comments, please drop me a line below!

Future Insights Live 2013

This past week I attended Future Insights Live 2013 in Las Vegas, NV. The conference is geared towards people in the tech industry who want to stay on top of the latest development, design and business practices.

Here’s the short review: I had an amazing time at the conference this year and, whether it is in Las Vegas next year or not, I will be attending again. The long version is slightly more nuanced, but results in the same conclusion.

Workshop

This year, the conference started with an optional full day workshop on one of several topics. Because the workshops were optional, they came with an additional charge on top of the conference ticket. The one I attended was hosted by Paolo Fragomeni and was focused on Node.js – what it is, why you might use it and how you would do so.

I loved the workshop I attended and feel like I got a lot out of it. Paolo was really energetic and full of passion for the topic and the format that he used for this particular workshop really resonated with me. In the morning, we went over the basic concepts used in Node, the philosophy behind node and some of the cool things you can (and people are) doing with it. In the afternoon, the workshop switched focus to more hands-on learning. We paired up with others in the workshop (I met an awesome dude named Gilber from Costa Rica and worked with him) and extended an attractive and functional (but kind of boring) chat client written with Node.js. Each pair was given a different task to work on – Gilber and I added several actions sourced from the IRC beginner’s guide to the chat application:

  • /me – starting your message with this command would replace it with your nickname when messages were broadcast to others
  • /nick – this command allowed you to change your nickname at any time after you had initially set it
  • /notice – this command allowed you to private message another user based on their nickname

We had a lot of fun implementing this and built it in such a way that we could pop in new commands as easily as writing a new callback that did what we wanted.

Keynotes

The conference keynotes (big group based sessions at the beginning and end of every day) were fairly good. In particular, I was really inspired by Carl Smith’s keynote “Your Money & Your Life: Designing a Business That Won’t Kill You”. It was amazing how Carl integrated his personal story into a wider narrative that, for me, was ridiculously inspiring.

Another highlight for me was Bruce Lawson’s “How to Destroy the Web” – a satirical look at all the things people have done and continue to do in a way that doesn’t open the web up to its full potential. There were a lot of laughs during that keynote and the phrase “Hippie Bullshit” was used multiple times with an accompanying picture of a neon-green bull with marijuana leaf tattoos.

Sessions

The sessions were a mixed bag and I think most other attendees agreed. Sometimes you would step into a session and it would totally knock your socks off. Other times, it would amount to a 50 minute product pitch. Of all the sessions I attended, I thought two were really fantastic and want to comment on them specifically. They both included a combination of speaker preparedness and the content itself.

Josh Cramer did a fantastic job with his session “How to Build Apps Better and Faster Using Lean Startup Principles.” As far as session names go, that is quite a mouthful and if you hadn’t attended it I wouldn’t fault you for thinking it was going to be one long bullshit pitch about the lean process and how you can only do development this one way. That is not what it turned out to be at all – it was a well-presented end-to-end discussion of how you can work with your clients and customers to create things that people will want to use. I loved this session and it was probably my favorite of the whole conference.

Meanwhile, in a completely different vein, John Bender presented a session called “Math Envy and CoffeeScript’s Foibles.” I think that most people walked into that room expecting to hear about using CoffeeScript for development or to be presented with an alternative. I know that’s why I was there. Instead, John spent 50 minutes entertaining us all with an in-depth look at programming language syntax vs. meaning and ways to analyse those things. It took me back to my days in college (which was awesome) and John did a great job explaining everything in detail all the while making the subject really entertaining.

Networking

One of the big problems I had with Future Insights Live last year was that I felt there wasn’t really a great way to randomly meet people. They addressed that this year by making the lunch tables bigger, setting up stand-up tables around the main conference location and getting sponsors (most notably modern.IE) to provide communal spaces. I met so many awesome people this year at the conference, and this was a major highlight for me. Here’s a brief overview of people I met:

  • A team of six developers and designers from Costa Rica who came up together to learn more about the web
  • The user experience director at the World Wildlife Fund
  • A pair of Canadian’s (brothers) who run a business doing location based service-business tracking
  • Someone from the Google Adwords marketing team who took an impromptu video of me giving what was essentially a testimonial about how I used Google Adwords to launch my freelance business
  • A teacher from Devry University who was looking to stay on the cutting edge
  • Several people who worked inside of either an agency or small business as part of the technology team
  • A Fort Wayne native who had transplanted to Houston – we bonded over a shared love of Chicago
  • Two awesome US Bank employees who somehow managed to resist going and laying by the pool in favor of learning about the web
  • Members of the Quicken Loans web team, including a dude who works remote from Florida but is originally from South Africa
  • Two keynote speakers, including Jeff Atwood of Coding Horror and StackOverflow fame

If you take a look at that list, you’ll see what a variety there was in the attendees at the conference. It was amazing to have the chance to just touch base with all these different people and find out how they were working, what they were working on and what they were working with.

Food

The food was pretty good overall – much better then typical conference fare. I especially appreciate the fact that fresh fruit was available at every break and that the coffee tasted good. I’d like to throw a special shout out again to modern.IE for providing an espresso cart every afternoon. The espresso cart baristos were super awesome and friendly and it was nice to chat with them for a bit every day.

If I enjoyed alcohol I would have been overjoyed on Tuesday because MediaTemple sponsored drinks after the closing keynote. I didn’t partake, but plenty of people seemed really excited about it.

Conclusions

Like I said at the beginning of this post, Future Insights Live 2013 was a great conference. I really enjoyed myself – I learned a lot, met a bunch of great people and came away inspired by some of the sessions. It was well worth the cost and I expect to attend again in 2014. Thank you to the Future Insights team for putting on a great event!

Adding a Setup Fee to a New Stripe Subscription

For those of you not familiar with Stripe, it is a payment processing service that makes it super easy to accept credit cards on your website without worrying about all the ugly PCI compliance bits that cost a lot of time and money. In addition to one time transactions, Stripe offers subscription billing and management in a way that makes a lot of sense. The only that that is missing from the subscription system is the notion of a setup fee. In my mind, it isn’t super obvious how you would create a setup fee with a Stripe subscription and I didn’t find a good explanation from anywhere else, so I thought I’d take a crack at it.

From my perusal of Stripe’s documentation, this is how you would create a setup fee for a subscription:

  1. Setup Stripe Checkout on your website to accept a user’s credit card details and receive a token
  2. Post that token to your web app
  3. Create a customer using the token that you received from Stripe
  4. Create an Invoice Item for your setup fee and associate it with your customer but do not pass anything as the invoice parameter
  5. Update the customer’s subscription to whatever subscription plan they signed up for

If you weren’t applying a setup fee to the user’s account, you could create the customer and assign them a subscription at the same time. However, because the initial subscription invoice is charged immediately upon creation, you need to make sure the Invoice Item for the setup fee already exists. That’s why you create the “dangling” Invoice Item – it will automatically get assigned to the next invoice. The next invoice just happens to be the one that starts the subscription for the user.

Microsoft Surface Review

I bought a Microsoft Surface RT on launch day last year and, after about 4 months of full time use, I feel like I’m qualified to write a fair and honest review about what the device does well and what its shortcomings are.

Hardware

The first thing I noticed about the Surface is how different it looks and feels compared to the other leading tablets on the market: the Apple iPad and Google Nexus 7. This may be a huge turnoff for some people, but I love it. The slightly angled edges look great. The smooth matte black case of the Surface make me feel like I’m using a device meant more for work than play.

In terms of weight, the Surface is definitely heavier then either of the previous two tablets I’ve used, but I feel like it is balanced better. I can hold the device in front of me for a substantial period of time without tiring.

The screen is decent. Colors are vibrant and the screen can get really bright in a dark room. I wish the resolution was higher so I could watch full HD video, but it is adequate for everything you would want to do on a tablet.

The best two aspects of the Surface hardware are definitely the kickstand and cover. They go hand in hand and really make the device stand out from other devices on the market. The kickstand flares out from the back of the device, angling it up when you’re doing things like document processing and email. When closed, the kickstand sits flush with the rest of the Surface’s case, making you forget it is there until you need it.

The cover (that you currently have to buy separately but should come bundled with the device) is awesome when used in conjunction with the kickstand. Whether you get the touch cover (my choice) or the type cover, it is great having a full keyboard to work on. This solved a serious problem I had when using my iPad – I could see the emails I was getting but hated responding to them because it was a chore to type on the digital keyboard. I’ve seen some speculation that you wouldn’t be able to seriously type on the cover, but my typing speeds are about 80-90% of what I normally get with a full desktop sized keyboard. One thing about the cover that I don’t understand is the tiny little trackpad under the spacebar. I feel like it has next to no use when you can just reach up and tap the screen. Maybe some people use it, but I know that I never will.

Software

I think the best way to discuss the Software on the Surface is to divide it into three main categories: the base operating system (Windows 8), 1st party apps (like Mail, People and Messaging) and 3rd party apps that you can get from the store.

Windows 8

Let’s just get this out of the way – I love Windows 8. I’ve been running it since the release candidate went out on all of my computers. It is, in my opinion, an excellent operating system with great performance, amazing hardware compatibility and a lot going for it. I think that the Windows UI start screen is awesome, especially for a tablet like the Surface.

For me, the best thing about Windows 8 on a tablet is the connectivity it provides to the rest of my home network. I can connect to my HomeGroup with ease, sharing documents, videos, pictures and more. I can Remote Desktop into my main development PC or my development laptop to do something that requires the processing power of a machine like that. I can share printers, storage and more the same way I can with any other machine on my network. It is wonderful.

Another great thing about Windows 8 is the consistency it provides in terms of UI for actions you’d want to perform. Swipe in from the left, right, top or bottom and you’ll get appropriate context specific actions. It feels like magic.

Finally, with Windows 8 you’re free to build your own machine and then install the operating system as an addition. I love building systems so this is a huge selling point for me.

Native Apps

The native apps are kind of a mixed bag. I think that IE10 running in Windows UI mode is the best web browser on the market right now. There’s no chrome to get in the way of your browsing experience and you can do everything you’d expect to be able to do when surfing the web.

Other than IE, the highlights probably include Calendar, Messaging and Music. Music is a really interesting one, especially if you sign up for the Xbox Music Pass ($100 a year) which basically lets you stream and/or download any song in existence to any connected device.

Unfortunately, the rest of the native apps don’t really shine. It is admirable that the People app attempts to collect all your contacts in one place, but it is slow and somewhat buggy when you’re just trying to check out the latest tweets from your stream. The mail interface could use an overhaul (although it is adequate for light email management) and the Video app is basically a marketplace to sell you movies and TV shows.

The worst example of a native app, though, has to be the Store app. It is almost unbelievable in my mind that Store is so bad because you’d think it would be a main selling point of the operating system as a whole. Here are some things that I’ve experienced:

  • Store tells me there is no internet connection even when I know there is
  • I’m told there are updates for installed apps and when I tap them, there’s nothing there
  • Search is ridiculously slow at times for seemingly no reason
  • I select to install or update something and then go back to the app and am greeted with a blank screen

I don’t know what is wrong with the Store app but sometimes it is awesome and sometimes it is awful and it seems like there is no rhyme or reason to which will happen.

3rd Party Apps

To be quite honest, I don’t use a bunch of 3rd party apps. The ones that I do use are really well crafted and provide an integrated experience with the rest of the operating system. The two best examples are probably the ESPN app (which is absolutely fantastic) and Maximum PC for Windows 8. Both are great, follow the Windows UI design patterns and let me get straight to my content without any excessive chrome.

Just a little side commentary – I’m not sure why people get so caught up in the number of apps available for a platform. When I used an iPad, I primarily used 4 things: Safari, Mail, Twitter and Facebook. I also read some magazines on there. I don’t really feel like I’m missing anything on Windows 8.

Performance

This is where I’m not quite as happy with the Surface RT as I think I could be. The performance is just not up to par with what I expect given everything else that is great about the device. A lot of things that are super snappy on my desktop or laptop Windows 8 device perform really poorly on the tablet. I just don’t think the device’s processor is as fast as it needs to be to drive the thing. I’ve heard this is very specific to the Surface RT and that the Surface Pro is awesome in terms of performance. That kind of makes me wish I had waited for it.

Conclusions

I’m really happy, overall, with the Microsoft Surface. I’ve never been tempted to go back to the iPad for daily use since I picked it up. Do I think everyone would like the Surface as much as I do? No, I really don’t. I think people who enjoy the UI paradigm pushed by Apple for the last few years would hate it because it would be so different to them.

If you’re already running Windows PCs in the rest of your house, though, and you want to watch movies, play games and do productivity stuff in full versions of the Microsoft Office suite, the Surface might be perfect for you. I encourage you to go to your local Best Buy or Microsoft Store and check it out, at least. I think you’ll be pleasantly surprised.

One caveat: I highly recommend getting the Surface Pro instead of RT for performance reasons. If there was a trade in program, I’d go tomorrow to my local Microsoft store and take advantage of it without any questions.

Download All the TekPub Videos from Your Account’s RSS Feed (using Windows PowerShell)

I just subscribed to TekPub after watching the previews for a bunch of videos and realizing how awesome they are. I subscribed to the Annual plan (for $179) so that I could download the videos to my local device and watch them at my leisure.

After signing up and signing in, you’re presented with a variety of RSS feeds to choose from, including ones tailored for iTunes and Zune. I don’t have iTunes installed on my development PC and after upgrading to Windows 8, I don’t have the Zune app installed anymore either. I didn’t want to right-click each video link to download the videos individually, so I wrote up the following PowerShell script to run during the day while I worked. I was heavily inspired by Scott Hanselman’s post on downloading with PowerShell.

$base = "B:\Video\TekPub"
$chars = [System.IO.Path]::GetInvalidFileNameChars()

$xml = ([xml](New-Object System.Net.Webclient).DownloadString("put your url containing your token here"))

$xml.rss.channel.item | foreach
{
    $url = $_.enclosure.url
    $url_parts = $url.Split('/')

    $folder = $url_parts[4]

    $filename = $_.title;
    foreach($char in $chars)
    {
        $filename = $file.Replace($char, '')
    }

    $filepath = $base + "\" + $folder + "\" + $filename

    $client = (New-Object System.Net.WebClient)
    $client.Headers["User-Agent"] = "TekPub Video Downloader - Nick Ohrn (nohrn@ohrnventures.com)"

    if(!(test-path $file))
    {
        try
        {
            $client.DownloadFile($url, $filepath)
        }
        catch
        {
            Write-Host $error[0]
        }
    }
}

There’s a couple of important things to notice here. First, I explicitly declared the base location that I wanted the videos to be saved to. Second, I’m replacing all characters that would lead to an invalid Windows filename with an underscore to create a valid filename from the production title.

Finally, you’ll notice the custom User-Agent header that I added. I noticed that if you don’t supply the User-Agent, you’ll get a 406 Rejected error thrown back at you. In addition, it is just courteous to let people know what is requesting their stuff.

There are a few interesting things you could probably do to improve this script:

  • Find common prefixes for video titles and then put those videos into subdirectories named by the series
  • Number videos based on their position in the feed (like 001 - Full Throttle - Azure - Azure Deployment or something)

Anyways, if you’re looking to download a bunch of videos from TekPub (or any RSS feed, really) feel free to grab the above script.