Information Technology Dark Side

Struggles of a Self-Taught Coder

Information Technology Dark Side header image 1

This guy hated the crazy view logic all over his rails app. You won’t believe the simple trick he used to fix it!

August 22nd, 2014 · No Comments

Sorry, I couldn’t help it. This style of headline is all over the place. It’s tacky, so this is me mocking it a little bit.

Yesterday I blogged about a distinction regarding DRY and posited an example of view code that is better with repetitive code than DRY code. I won’t rehash that, so here’s a link.

In the end of the post I mention that I want to be able to create views that are named like this:
- menu.html.haml (default)
- menu.bsa_troop.html.haml (alternate view for Boy Scout of America Troops)
- menu.sa_group.html.haml (alternate view for Scouts Australia Groups)

Yesterday I converted a few really ugly views to use this approach, just to test out the idea that the view code would be easier to read and work on. I was really satisfied with how it turned out, but I still had to do add some logic to render the right view based on the troop type.

In this post I’m going to show you how I created a file system resolver to get rid of any sort of if logic like this:

if @troop.is_bsa_troop?
  render 'menu.bsa_troop'
elsif @troop.is_au_group?
  render 'menu.sa_group'
else
  render 'menu'
end

There are two things I needed to do to make this work:
1) Create a special resolver to find the views based on the troop type
2) Tell the resolver what troop type we are using

The code isn’t fancy or hard to understand, so I’m just going to show it to you.

class ApplicationController < ActionController::Base
  include ApplicationHelper

  prepend_view_path UnitTypeResolver.new
  ... 
  around_filter :set_unit_type
  ...
  
  def set_unit_type
    Thread.current[:template_type] = @troop.try(:template_type)
    yield
  ensure
    Thread.current[:template_type] = nil
  end
  ...
end
class UnitTypeResolver < ActionView::FileSystemResolver
  def initialize
    super("app/views")
  end

  def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
    unit_type = Thread.current[:template_type]
    if unit_type
      key = key.to_s + unit_type
      name = "#{name}.#{unit_type}"
    end
    super(name, prefix, partial, details, key, locals)
  end
end

Bam. menu.bsa_troop.html.haml gets rendered automatically if it exists and the troop is a Boy Scouts of America troop. If it doesn’t exist, it just renders menu.html.haml. This makes it super easy to create a totally different view of a feature for a specific type of troop, which makes my view code a lot easier to read and work on.

Thanks, @kofno, for helping me figure this out.

→ No CommentsTags: Uncategorized

Functionally repetitive code is best served DRY. Coincidentally repetitive code is best served simple.

August 20th, 2014 · 2 Comments

I’m starting to realize there is a difference between code that is functionally repetitive and code that is coincidentally repetitive.

Here’s an example of some functionally repetitive code:

class Admin::CoursesController < Admin::AdminController

  def index
    @courses = Course.all
  end

  def show
    @course = Course.find(params[:id])
  end

  def engagement
    @course = Course.find(params[:id])
  end

  def new
    @course = Course.new
  end
  ...
end

Functionally repetitive code is repetitive because of the functional nature of the activity being coded – for instance, finding a resource on a controller in all the RUD actions. That’s functionally repetitive. “Find the course” always means the same thing here.

Which is why that controller is better like this:

class Admin::CoursesController < Admin::AdminController
  before_filter :find_course
  
  def index
    @courses = Course.all
  end

  def show

  end

  def engagement

  end

  def new
    @course = Course.new
  end

  ...
  protected

  def find_course
    unless params[:id].blank?
      @course = Course.find(params[:id])
    end
  end
end

Drying this code up makes things simpler only because “find the course” always means the same thing at a functional level.

Not all repetitive code is functionally repetitive though. This is especially true in view code for me in TroopTrack where I have lots of conditional code based on the type of organization the user is in. I end up with lots of code like this haml partial:

- if @troop.is_tlu_troop?
  %li= link_to 'Advancement Instructions', '/assets/Advancement_Instructions_20140801.pdf', target: '_blank'
%li= link_to "#{achieve_title} Overview", achieve_root_path
- if @can_edit_achievement_records || @can_view_achievement_records
  - unless @troop.is_troop? || @troop.is_venture_crew?
    %li= link_to 'Record Individual Progress', [:new, :achieve, :individual_progress]
  - if @troop.is_tlu_troop?
    %li= link_to 'Record Progress (Bulk)', [:new, :achieve, :bulk_achievement]
  - else
    %li= link_to "Record Progress (Bulk)", record_progress_achieve_workflows_path

  - if @troop.bsa?
    %li= link_to "Print Advancement Report", advancement_report_achieve_workflows_path
  - if @troop.bsa? && @can_use_turbonet
    %li= link_to "TurboNET Advancement Report", [:new, :achieve, :e_advancement_report], {'data-no-turbolink' => true}
    %li= link_to "TurboNET Roster Import", [:new, :achieve, :turbonet_import], {'data-no-turbolink' => true}
  - if @troop.is_ahg_troop? 
    %li= link_to "TurboNET Advancement Report", [:new, :achieve, :ahg_advancement_report]
    %li= link_to "TurboNET Hugs Reports", [:achieve, :hugs_reports]
  - if @troop.is_troop?
    %li= link_to "Print Blue Cards", blue_cards_achieve_workflows_path
  - if @troop.is_troop?
    %li= link_to 'Board of Review Worksheets', bulk_board_of_review_achieve_users_path
  - unless @troop.aidmatrix?
    - if @troop.is_tlu_troop?
      %li= link_to 'Purchase Completed Awards', [:new, :achieve, :award_order]
      %li= link_to 'Past Award Orders', [:achieve, :award_orders]
    - else
      %li= link_to 'Shopping List', shopping_list_achieve_workflows_path
    %li= link_to 'Print Award Cards', award_cards_achieve_workflows_path
    %li= link_to "Print Agenda by #{scout_title}", agenda_by_scout_achieve_workflows_path(:format => :pdf), :target => '_blank'
    %li= link_to "Print Agenda by #{subunit_name}", agenda_by_level_achieve_workflows_path(:format => :pdf), :target => '_blank'
    %li= link_to "Present Awards", present_awards_achieve_workflows_path

  - if @troop.is_troop?
    %li= link_to 'Hours, Nights, & Miles', trackables_achieve_users_path

I don’t like this code much at all. All those conditionals based on the type of troop we are looking at makes it hard to read and to think about, but the first alternative I think of involves creating a different partial for relevant type of troop. We support seven different types of troops in TroopTrack, so the idea of potentially having seven different partials with big swaths of them being the same made me cringe a bit at first. But the more I work on TroopTrack, which is now in its sixth year, the more palatable the idea has become. I now think it’s better than what you see above, especially if I introduce some conventions to make it easier, such as including the troop type in the partial file name, like this:
- achieve_menu.bsa_troop.html.haml
- achieve_menu.bsa_pack.html.haml
- achieve_menu.scouts_au.html.haml
- achieve_menu.ahg.html.haml

That makes the intent extremely clear, and I could use interpolation to render the right partial with a single line of code.

To me, that code may be repetitive, but it’s a lot easier to reason about and to work on. DRYing it up, at least in the way I originally did it, only makes it worse. I think the reason DRYing it up isn’t helpful is because this code is only repetitive by COINCIDENCE. By this, I mean that the features that are common are not common due to something functionally intrinsic about them, but just because they happen to share some common needs.

I mentioned this realization to @kofno and here is what he said. I like his perspective:

I think you’re having an important realization about the intent of DRY. It’ s not about repetitive code, but it’s about having a single canonical place for some piece of system information.
Whether that is how to render users, or what makes a valid course, etc.
Drying up things that change together is what’s important.
If you try to dry things that are orthogonal but only look similar, you’ll be miserable.
For removing repeating code patterns and getting rid of boilerplate, that’s what macros are for (provided your language supports them). Ruby kinda does w/ metaprogramming.

Like @kofno, you might have had a “well duh” reaction to all this. That’s cool. I’m learning…

→ 2 CommentsTags: Uncategorized

Coding without Caffeine

July 28th, 2014 · No Comments

Best Sleep EVER
I woke up this morning at 6:00 am feeling totally awesome. I slept like a log last night, better than I’ve slept anytime in the last six months. Shannon tells me kids came in and out of the room, talking, even snuggling, and I didn’t even roll over. It was a deep, dreamless sleep and I feel freaking great right now.

I quit caffeine 2 1/2 days ago.

Wait, why!?!?
I know right?

Caffeine is a huge part of the world’s culture, especially coffee, especially in the morning. Quitting caffeine is a very counter-culture thing to do, but that’s not why I did it.

I quit caffeine for four simple reasons:

1) It makes me grumpy. When I drink caffeine regularly I am less patient with others, especially my kids. I am more inclined to snap at them than when I am caffeine free.

2) It erodes my ability to focus. I wish I could cite the talk where I learned about this, but I can’t remember. I think it was at indy.rb. Maybe one of my four readers will remember. At any rate, he was talking about the importance of knowing when our focus is best and working on hard problems then. He also mentioned that we should avoid caffeine until after this period because it makes focusing more difficult. I experimented with this idea prior to quitting caffeine altogether and found I was better able to solve hard problems if I postponed my morning Dr. Pepper until after I had worked on them. Admittedly, this is hard to prove in a scientific way, but since it doesn’t harm me to not drink diet soda I don’t really care. Even if it’s just a Dumbo’s feather, I’m still flying.

3) It messes up my sleep. This is something that is hard to observe while I am drinking caffeine, but is obvious every time I quit (I’ve gone years without caffeine at various times in my life). I sleep much better without it, all other things being equal.

4) It’s addictive. I don’t like being addicted to things. It’s bad enough that I am a food addict, but being a diet soda junkie also makes me feel a little depressed.

There are other reasons too. Drinking a lot of diet soda dulls my taste buds. It’s also generally pretty bad for you and, counter-intuitively, increases feelings of hunger. Caffeine also aggravates acid reflux, which sucks.

Withdrawal Symptoms
What’s it like to quit caffeine cold turkey?

The previous time I quit caffeine I had a horrible migraine for two days. During that time I had to interview a job candidate and in the middle of the interview I asked for a five minute break so I could go throw up. The migraine was that bad.

After two days I had a dull ache in the front of my brain for about three days.

It sucked.

This time I didn’t have a single migraine, but I quit differently. For one thing, I planned it better. I had my last diet soda at noon on Friday and immediately took some ibuprofen. I continued to take ibuprofen regularly on Saturday and Sunday.

I also paired my de-caffeination with a 3-day fast. During this time I drank only water (mostly). I started it on Thursday night and didn’t eat again until yesterday afternoon. I did this mostly because my parents and my little brother did it, based on research my oldest brother did (I have 4 brothers), and I can’t cite any research personally. But they did it and said it made them feel pretty great and changed the way they felt about food in good ways.

I was worried it would aggravate the withdrawal symptoms, but I think it had the opposite effect. I say this because at the time I quit I was drinking twice as much Diet Dr. Pepper as the time quitting rendered me useless for two days.

Coding without Caffeine
So, does it make my code any better? It’s hard to say, this time around, since I only quit 3 days ago. I don’t see how it can make my code any worse.

Heh. That made it sound like my code is as bad as it could possibly be. You’ll have to ask @kofno if that’s the case, I don’t think it is. I just don’t see how not being impaired by a stimulant could have a negative impact on anything once the withdrawal is over.

In the past I have found that I think more clearly and am distracted less easily without caffeine. I’m also less irritable and more inclined to be helpful to others.

About the 3-day Fast
I was 290 pounds when I started it and I ended my fast at 282 pounds. I plan to repeat the fast once a month for three days forever, based on my experience this weekend. According to my family members who have tried it, there are medically validated benefits from a regular fast, including improved immune system and lifespan. As I said before, I have no idea, but I belong to a smart family so I’m just going with it. Here are a few things I observed during and after my fast:

1) I craved fruit. We went grocery shopping during the fast and I was surprised at what made me salivate. It wasn’t candy, chocolate, or ice cream. I really wanted to eat the fruit.

2) Rich foods make me feel sick. I had a slice of cheesecake for my daughter’s birthday and couldn’t finish it. This is unheard of – I could normally eat three or four slices.

3) I needed salt. Next time I will take salt tablets. This time I just added salt to my water. Ick.

4) My stomach seems to have shrunk. I ate a smaller than-normal dinner on Sunday at the end of my fast and didn’t binge. I felt full.

5) Food tastes better. I think this is partly caused by my cessation of diet soda, which messes with my taste buds.

6) It changed the way I felt about food. I found myself imagining really good meals. Not fast food, or restaurant food, but homemade food from the cookbooks I have collected but rarely used. It made me want to cook that food.

7) It changed the way I feel about the poor. Starving isn’t easy. With enough water, it’s not terrible, but it’s hard to go without food for three days. This made me more sympathetic towards those people for whom the source of their next meal is not a certainty.

Your mileage may vary
I’m not saying you should try either of these things. I’m not a doctor and, as I have mentioned, I didn’t even bother to research any of this before embarking on my own experiment. You may find that none of this applies to you.

→ No CommentsTags: Uncategorized

Interns at TroopTrack

July 7th, 2014 · No Comments

Hiring IFL (Intern for Live)
Until recently, we really struggled to provide consistent customer service. Sometimes we were awesome – customers would put in a ticket on a weekend in the wee hours and would get a response right away. Other times… not so great. It was hard to provide consistent customers service when it was just Shannon and I and TroopTrack was growing so rapidly that it was clear we couldn’t do it ourselves. Things got worse when Shannon had to take a leave of absence for a family crisis. We soon found ourselves staring at more than a thousand open help desk tickets. It had gone from kind of bad to horrible.

During this time we experimented with hiring free-lancers to help with customer service. These were typically people with experience in scouting who thought they had extra time. We would train them on TroopTrack basics, then expect them to answer questions. This sometimes worked okay, but never great. The biggest problem was consistency. No one had a set schedule, so we never knew when someone would work. People always seemed to be busier than they thought.

We were feeling pretty desperate, and then one day I thought of my nephew Spencer who was due to return home from a two-year sabbatical in Brazil and would have five months at home before he went back to school. He is smart, technical, and needed a job to pay for his return to school. By smart, I mean super smart – he has a full-tuition academic scholarship to an excellent university.

I hired Spencer for $10/hour, forty hours a week and put him to work on the help desk. I gave him a Macbook Air, set up a local environment so he could run TT locally, pointed him at the queue of questions in the help desk and said “Figure it out”.

And he did. He occasionally asked us questions, but for the most part he used his brains and curiosity to become an expert user in a very short time. Every day, the first thing he does is answer every new help desk ticket. As a result, our customers get a response within a business day. This appears to have affected our conversion rate from trial customers to paying customers in a good way:

TroopTrack_Help_Desk

Two months of data is hardly conclusive, but the early indications are good that we are achieving a more consistent conversion rate than we have had in the past.

Our second intern, Coleman, starts tomorrow. Spencer is going to provide most of his training.

We call Spencer IFL, short for Intern for Life, and pronounced ‘Eifel’.

Here are some details about our internship program that I think may be helpful to other bootstrappers who are considering using interns.

Payment
Our interns all start at $10/hour. The most they can earn as an intern is $15/hour. I give them a $1 raise ever 4 – 8 weeks if they perform well until they hit the max. Once they hit the max, they are stuck there until they become an Apprentice.

An apprentice can earn up to $25/hour. To become an apprentice, an intern completes a special project that demonstrates an aptitude and passion for programming as well as a basic understanding of important programmer topics like SCM, TDD, etc. Once they complete their project they present it to the three C’s (CEO, CTO, and COO) and we decide collectively to promote them to apprentice. We don’t have any apprentices yet, but Spencer is working diligently on his project. I mention this to illustrate two things 1) it’s theory and 2) we have a plan.

An apprentice can eventually become a programmer. To become a programmer the apprentice has to achieve competency in our technology stack and be able to work relatively independently to solve problems and create features. A programmer can earn up to $50/hour.

A TroopTrack employee can progress through all of these stages as a part-time employee, which means that our interns can continue to work for TT after the summer is over and they start back in school.

The gist of all this is that an internship at TroopTrack is a good opportunity. It is totally possible that a sophomore or junior in college could start as an intern at TroopTrack and be a programmer by the time they graduate, finding themselves in a position where their earning potential is considerably higher than their peers.

Schedule
Interns are young. Spencer is very smart and mature for his age, but he’s still a kid. It’s important to establish a set schedule and expect interns to keep it. They need to understand that consistency and reliability are important and that their continued earning growth is dependent on demonstrating those qualities. I have been very frank about this with IFL and will have this very conversation with IFL #2 tomorrow.

Assigning Tasks
Spencer’s primary responsibility is the help desk. But he also answers the phone, changes the trash, vacuums the office, and hauls stuff from my truck up the stairs to our office when I need him to. We also expect him to advance his programming knowledge, fix bugs, and add new features to TroopTrack that are within his wheelhouse. We give him pretty wide control over what he works on in the same way that my parents did: “You can play with your friends when your chores are done.”

This means simply that he needs to get our helpdesk back down to inbox 0 every morning before he does anything else. After that, he can learn ruby, fix bugs, or work on his apprenticeship project.

Establishing these priorities has been important – they mirror the priorities of our company and they create a healthy incentive to do what matters most NOW before moving on to activities with a more speculative value.

Hiring
There are two types of luck:
1) Sheer luck, like winning the lottery or dying in a plane crash.
2) Exploiting a chance opportunity for which you have prepared.

Spencer is awesome, and I don’t deny that he is a lucky hire for us. But it is luck #2, not luck #1. Our experiments with freelancers, our discussions about career progression, and our attempts to manage the helpdesk ourselves put us in a position to fully take advantage of the fact that a very smart kid with nothing to do was within arms reach.

There was still some luck involved and I shouldn’t pretend that I know a lot about hiring interns. There are a few things I think are important.

1) You need to present a learning opportunity. Interns are students – their career is learning. If they aren’t interested in a learning opportunity you have dodged a bullet by not hiring them. If you don’t have a learning opportunity to offer you won’t get the best candidates.
2) Hire someone who wants to learn to program. This is not the same thing as hiring a CS major. People choose majors for a rainbow of reasons – don’t assume that someone who is studying CS wants to learn to program or that someone who is study Ancient Roman Literature doesn’t.
3) Hire someone you know if you can. I have known Spencer his entire life. I know his pros and his cons. I knew before I hired him that he would need phone coaching. I also knew that he would soak up programming concepts like a sponge.

Learning
We hired Spencer and handed him two books and a bunch of web sites. The books were Learn to Program and Agile Web Development with Rails 4. I told him to work through the books every day AFTER he finished the helpdesk. Within a week I could tell that Spencer had an aptitude for programming. The web sites we handed him were about git and github.

→ No CommentsTags: Uncategorized

The Bootstrapping Thing I Suck Most At

June 13th, 2014 · No Comments

Benjamin Franklin said

So convenient a thing it is to be a reasonable creature, since it enables one to find or make a
reason for anything one has a mind to do

I’m not being a reasonable creature, and it’s darned inconvenient

Last night I stayed up playing Civ 5 until 2:00 am. I didn’t enjoy it as much as I should have because I felt guilty that I wasn’t working on my bootstrapped business.

Playing is healthy and we need to do it. I shouldn’t feel guilty about it. I know this is true in a academic sort of way, but I can’t seem to integrate that into my nature.

This is in spite of tremendous evidence in my business that doesn’t even include the experiences of other bootstrappers who have successfully built product businesses without killing themselves. And yet, when I “only” work 50 hours in a week I feel guilty. This has got to change.

The Case Against My Work Guilt
Let’s recap the evidence proving that
1) I don’t need to work 80+ hours a week anymore (if I ever did)
2) That I don’t need to feel guilty about goofing around

  1. In 2009 the partnership that started TroopTrack fell apart and I spent several months dealing with the conflict – first I bought out a partner, then I dissolved and re-formed the company, then I spent a month wallowing in depression and emotionally regrouping, all the while ignoring the day-to-day operations of the business. Our subscriber base didn’t really grow, but hey, we’re still here.
  2. In late 2010 I hurt my back very badly. I struggled to walk for a while and spent six+ months walking with a cane. I made almost no changes to TroopTrack during this time and ignored all but the most critical customer requests. Several customers called me to ask if I was okay. I think they worried that I had died. Here’s the rub: Our Subscriber base continued to grow
  3. In late 2013 my sister-in-law moved in with us. She had terminal cancer and Shannon became her full-time caregiver. Shannon had previously been operating the help desk and had been rocking it. With her gone and TroopTrack growing at an unprecedented rate, we fell far behind and didn’t catch up for more than six months. Guess what? Our subscriber base continued to grow

The Verdict: Guilty of Stupid Guilt
Great. Now I have to feel guilty about feeling guilty when it happens.

Just kidding. Hopefully putting this down in the open like this will make me feel better about it.

The worst part about this guilt is that it’s totally unreasonable. There is no need for me to work that hard anymore. I have Ryan, Spencer, and Shannon backing me up. We are paying the bills and growing like a California wildfire. It’s stupid to feel guilty about not doing something I don’t need to do.

Disclaimer
The fact that TroopTrack’s customer service/support has been on life support a couple of times without negatively impacting the long-term viability of the business should not be construed as a belief on my part that I can just ignore the business and it will be fine. That’s stupid. The point here is that there is absolutely no reason for me to think that I have to work 24/7 just to keep the business growing. There is way too much evidence for that belief to persist any longer in my consciousness. It’s time for it to go.

Be free Dave.

→ No CommentsTags: Uncategorized

Bootstrapping sucks. Everyone should do it.

May 9th, 2014 · No Comments

Bootstrapping Sucks
Winston Churchill famously said the following after losing power following World War 2:

Democracy is the worst form of government, except for all those other forms that have been tried from time to time.

After six long years of bootstrapping, I feel much the same way. Bootstrapping sucks. It’s freaking hard, at least the way I did it, and it’s full of low-points that require you to learn important lessons the hard way.

The hardest thing I’ve had to learn is how to be the CEO. Six years ago I didn’t know Jack about running a business. In 2008 I literally believed that if I built a web-app for managing Boy Scout Troops I would sell 10,000 subscriptions immediately and be rich overnight.

Pfft.

It took four years to get 100 subscriptions. Four long years of working a real job with real demands, then working another 20-40 hours on nights and weekends. For FOUR YEARS. Do the math on the return on that investment real quick. Actually, don’t. It’s too depressing. 100 subscriptions is only about $9000 in revenue. There’s no way that’s a good investment.

That’s the first thing that sucks about bootstrapping:

Reality sucks.

Every bootstrapper has a moment where they realize that this is going to be hard, and lots and lots of bootstrappers don’t make it past that moment.

I remember that moment explicitly. I was sitting in a Qdoba in Indiana in August 2009 talking with some guys about TroopTrack and I suddenly realized the truth. This was an endurance race, and it was a race no one has to finish and no one ever wins. You only lose. All you have to do is stop running and you lose. But there’s no finish line. IT NEVER FREAKING ENDS.

It’s easy to see how that could be a depressing moment. I almost threw in the towel. My product was crap. My business wasn’t growing. My partners were hostile and wanting out. My day job was pushing me hard. My wife was sick and I had gained a crap-ton of weight.

Shannon wouldn’t let me quit, and she helped me see a simple truth:

All we have to do is never quit

That subtle viewpoint shift was important. Instead of seeing bootstrapping as an endless race that I could never “win”, I learned a simple truth: I didn’t need to win. All I had to do was never quit.

Here’s the next thing that sucks about bootstrapping a web-based business:

The Toobs NEVER Sleep
People use TroopTrack at all hours of the day, 7 days a week, 365 days a year. And they have problems. And questions. And feature requests. They expect me to help them, to answer their questions, to fix bugs, and to add the features they ask for. I have customers who, if I don’t respond to a help desk ticket within an hour on a Sunday afternoon, will post their question to my Facebook page, send me an email, and call my cell phone.

Okay, I’m exaggerating a little, but it sometimes feels that way.

TroopTrack has been growing 250% per year since 2009. That’s not a big deal when you start with 2, at least for the first four years. For a long time I was able to keep up with everything and excellent customer service became the cornerstone of our reputation.

Then I broke my body. Well, at least that’s how it felt. I slipped a disk. It was horrible. I could barely walk, even with a cane. I couldn’t even sit up to work, and laying on my back to code was only marginally workable because of the leg spasms that attacked me every few minutes. I was barely able to keep my day-job commitments, much less my TroopTrack commitments. It was months before I was healthy enough to do my day job well and to work on TroopTrack.

In the meantime, TroopTrack hadn’t stopped growing and the requests for help kept coming in. I started to get emails asking if I was still in business. Long time customers called me to see if I was still alive. I had no choice but to tell them the truth and beg for patience. It was humiliating and, sadly, not the last time it would happen.

It happened again last year, when my sister-in-law and her family moved into our home for the final stages of her battle with cancer.

During that time TroopTrack was growing more than ever, and the help desk tickets weren’t slowing down. We went from handling it to completely failing at customer support overnight. By the time the family crisis was over we had 1700 open help desk tickets.

Here’s the third thing that really sucks about bootstrapping, and the last one I’m gonna mention today:

Your family will hate it
Bootstrapping a business is like having a mistress, but without the extramarital sex or intrigue. Or, at least I think it is. I’m not that kind of guy. At any rate, it certainly comes with the guilt and jealousy that I imagine cheaters feel.

Shannon used to describe TroopTrack as the other woman. She was committed to it, she believed in it, and when I wanted to give up she was the rock that kept me going, but she also resented it, and so did my kids. Frankly, so did I. I didn’t want to work all the time. I managed to survive that way for the first five years, but by 2013 I was burned out. I wanted to play. I wanted to go to the movies without worrying about the ****ed distribution list server falling over. That thing goes down and I get irate people within an hour. I hate email. I wanted to go on vacation and not think about TroopTrack for two weeks a year.

All of this was completely impossible, and the guilt was horrible.

Dad, you never play with me anymore.
Jake, age 5

Boom. Cold knife in the heart.

All My Problems Were Solvable
I already talked about solving the first problem (Reality sucks), but the other two problems had one solution: Hire Shannon.

She was looking for a way to earn some money on the side and was talking about getting a part-time job and one day the obvious hit me hard enough for me to recognize it. So I hired Shannon to be the COO. I paid her crap (and frankly still do!) and asked her to get the help desk under control. She was happy to get the money and to have the freedom of a job she can do from home on her own schedule (the toobs never sleep!).

The best part of all is that TroopTrack stopped being the other woman. SHARING MY TROOPTRACK TROUBLES WITH MY SPOUSE WAS THE BEST BUSINESS DECISION I EVER MADE. Almost overnight this:

The whole family is sitting around watching a movie and you have to be on your laptop the whole time!?!?
Shannon, Pre-COO

became this:

You’ve seen this movie before and there are five help desk tickets I need your help with, hint hint.
Shannon, TroopTrack COO

I can’t tell you just how awesome and gratifying it is to have Shannon engaged in our business. Talking about TroopTrack used to be boring to her. She would listen, but not really engage, and the subject would get changed at the first opportunity. Not anymore. Now she schedules business lunches with me. She is in the details of the business. She’s excited about where it’s going, and she’s making it better.

This is a big freaking deal Mr. Bootstrapper.

Everything Else Sucks More
Sure, the last six years have been hard. But look at this:

TroopTrack_Help_Desk

You’ll have to click it if you want to be able to read it.

That’s pretty awesome, IMO. Sure, it took six years to do it, but I have enough subscribers now that I could live completely off TroopTrack. And the growth curve is promising – if we don’t stop running and keep solving problems as we encounter them, we will someday have 10,000 subscriptions. And hey, that’s a crap-ton of money.

But what does this have to do with everything else sucking?

It’s simple. I want you to understand what I currently have because I bootstrapped. That chart is MINE (and Shannon’s). We own TroopTrack outright. Even if TroopTrack leveled off today and stayed level, we would still have the financial equivalent of a winning lottery ticket that pays out $100k every single year.

At this very moment we are a financially independent family. We can’t be fired, laid off, downsized, whatever. We control our own destiny.

So let’s talk about what the other options would have given us and whether or not I would have avoided the three things that suck most about bootstrapping.

VC Backing
First of all, VCs expect you to work like a bootstrapper. So, right off the bat, I would still have the problems associated with working all the time, but with a nasty new twist: if I became temporarily incapacitated because of health or crisis they would leverage that to take more of the company away from me.

There are all sorts of unhealthy dynamics associated with venture capitalism that are well documented elsewhere, so I’ll skip those. Worst of all, I wouldn’t be able to chart my own course. I’d have to deal with VCs and their opinions. No thanks.

Secondly, having VC money doesn’t make the transition from fantasy to reality any easier. In fact, the presence of other people’s money can postpone that transition, all the way up to the day you shut down. When you still have a pile of cash you don’t have to face the facts.

Finally, the toobs still never sleep. If I had piles of money I could have just hired people to worry about the customers. That detachment early in a product company from the customer is not good. That’s how you learn – talking with customers is a critical part of my learning experience. Sure, there are ways you can structure support so that you aren’t totally detached from it, but doing so would have brought other troubles and problems to solve that I didnt’ have to go through as a bootstrapper.

So what if I had taken VC money to start TroopTrack, where would I be?

Out of business. I guarantee it. There have been numerous attempts to do this and every single one ended the same way. Dead. And I think I understand why.

It’s because this market is a slow mover. It doesn’t matter how awesome your product is – it takes years to get this market to change. The purchase cycle of scouting software is at least a month, and the effort for a scouting unit to switch from one package to another is huge. Even if the technology makes switching easy, the social costs of switching are super high. First you have to convince the committee. Then you have to win over the parents. It’s hard. People don’t want to go through it unless they have to, and they don’t want to mess around with software that might not be around tomorrow. They take the process very seriously – we frequently get emails from customers that read like a million dollar RFP. For a $99 product.

There’s no VC in the world that has the patience for that. Even with a million dollars it would have taken six years to get 1000 subscriptions.

The Other Option
The other option I want to discuss is doing nothing. Sure, I would have had an easier six years, probably. I might still be at Liberty Mutual, slogging away at putting a percentage of my blood sweat and tears into a 401k that might someday be able to pay out $100k a year when I am old.

Meh.

The One Thing I would Change
This year we made a big change in the way we bootstrap. I quit my job and started consulting. I landed enough contract work to also hire Ryan Bell. We structured our contracts so that Ryan only has to consult for 30 hours each week and he has time to work on TroopTrack. I still work a lot, but not as much as in the past.

I wish I’d done this sooner. I don’t think I could have made it work prior to 2011 or so because I still had a crap-ton of things to learn about Ruby on Rails development (I was a project manager who hadn’t programmed in years when I started TroopTrack). It’s really hard to hold down a real programming job and launch a product company on the side.

So, if you can, start a consulting company along with your bootstrapped product company. Then structure your contracts so you can live off of 20 – 30 hours per week of consulting. Working as hard as I have done sucks big time and I don’t recommend it.

Everyone Should Bootstrap
I’m so glad I built TroopTrack this way. It feels great to still have complete control of the product when it finally starts to get traction. It’s been super hard and I have had to think long and hard about the answer to THE question:

If you knew in 2008 what you know now, would you still launch TroopTrack as a bootstrapped company?
Everyone I Know

Heck yeah I would. And you should too.

If you want to start a business, bootstrap it. If you want to start a business that can’t be bootstrapped, START A DIFFERENT BUSINESS. It took me four years of evaluating different business ideas before I settled on mine. If you can’t bootstrap your idea, toss it until you find one you can. There are tons of market gaps out there that can be invaded by small businesses. If you look long enough you will find one that is right for you.

Do it.

Gratuitous Acknowledgements
PS. This may sound gratuitous, but I don’t care. TroopTrack was inspired by the success of 37Signals and is an outright attempt to emulate and imitate them. Even though I’ve never met a single one of them, I owe them my thanks. So here it is: Thanks a Crap Ton. I love you guys.

PPS. Also, thanks to you Shannon and my kids. I love you more than TroopTrack. :)

PPPS. I’d be screwed without @kofno. Thanks man.

Other Bootstrapping Posts

Here are a few of the hard lessons I’ve learned over the years:

→ No CommentsTags: Uncategorized

Finally! An excuse to override method missing!

December 2nd, 2013 · 1 Comment

I load a lot of data into TroopTrack from CSV files by mapping fields in the CSV file to active record. Most of the time I am importing data from my competitors using CSV files they provide (and control the format of). In the past I have tended to write pretty sucky code for doing this where I build a hash of values that I then use to update active record, like this:

CSV.parse(@file_to_import, { :col_sep => "t" }) do |row|
  household_hash_1 = {
          :name => row[25],
          :line1 => row[27],
          :line2 => row[28],
          :city => row[29],
          :state => row[30],
          :zip_code => row[31]
        }
end

This code seemed okay when I wrote it, but then I learned that at least one of my competitors would add a new field to the CSV file every couple of months, which meant I had to redo the mapping of the attributes. PFFT.

Last week I started a project loading data into ActiveRecord from Microsoft SQL Server. There are about a hundred different tables that I need to transform and load into TroopTrack. Needless to say I wasn’t feeling very thrilled about using the approach mentioned above. The mere thought of THOUSANDS of lines of code mapping a hash to a CSV file makes me want to vomit.

Method Missing to the Rescue
If you don’t know about method missing you’re probably not reading my blog, so I’m not going to explain. If you wandered here somehow with no ruby or rails experience and still care, just google “override ruby method missing”.

All the code below does is let me treat a row from a CSV file more like an active record entry, so that I can write mapping code that is more readable and avoid thousands of lines of that crap above.

In other words, given a row from a csv file with a header that includes “last_name”, I can write code like this:

row.get_last_name

That’s pretty handy.

class DataGenius
  def initialize(filename)
    @data_items = []
    rows = CSV.read([Rails.root, 'db', 'data', 'csv', filename].join('/'))
    rows.each_with_index do |row, i|
      if i == 1
        @header = Hash[*row.each_with_index.map{|val, idx| [val, idx]}.flatten]
      elsif i > 1
        @data_items << DataItem.new(row, self)
      end
    end
  end

  def header 
    @header
  end

  def data_items
    @data_items
  end

  class DataItem
    def initialize(row, data_genius)
      @row = row
      @data_genius = data_genius
    end

    def row
      @row
    end

    def method_missing(meth, *args, &block)
      if meth.to_s =~ /^get_(.+)$/
        p $1
        @row[@data_genius.header[$1]]
      else
        super # You *must* call super if you don't handle the
              # method, otherwise you'll mess up Ruby's method
              # lookup.
      end
    end
  end
end

→ 1 CommentTags: Uncategorized

Conventional Rails

June 27th, 2013 · No Comments

Otherwise known as…
This post could also be called “Stuff I’ve figured out about Rails but didn’t want to blog about because I suspect everyone else already knows it and will figure out that I’m a moron”.

A code picture is worth a thousand words
Here’s some code. I’ve got some routes in there to set the stage, followed by pairs of roughly equivalent code demonstrating some conventions I’ve learned about and have grown to appreciate. I’ve not seen these conventions documented anywhere, but that doesn’t mean they aren’t. It just means I learned them the hard way.

# Routes
resources :users do
  resources :addresses do
    resources :phone_numbers #NEVER
  end
end

# users
# users/6
# users/6/edit
# users/new
# users/6/addresses
# users/6/addresses/5/edit
# users/6/addresses/5
# users/6/addresses/5/phone_numbers/11
# etc...

resource :tricks
namespace :magic do
  resources :tricks do
    get :secret, on: :member
  end
end

# magic/tricks
# magic/tricks/new
# magic/tricks/6
# magic/tricks/6/edit
# etc...

######### Example 1 - in a view #########
= render 'users/user', user: @user
= render partial: 'users/user', locals: {user: @user}
# Partial in ./_user.html.haml

# is roughly equivalent to...
= render @user
# Partial in users/_user.html.haml

######### Example 2 #########
= render partial 'address', collection: @user.addresses, as: address
# ./_address.html.haml

= render @user.addresses
# addresses/_address.html.haml

######### Example 3 - controller redirects #########
redirect_to show_user_path(@user)
# OR how about this?
redirect_to @user

redirect_to users_path
# OR how about this?
redirect_to :users

######### Example 4 - It works in links too #########
# In a view
link_to :users

# Some equivalents...
link_to edit_user_path(@user)
# Is the same as
link_to [:edit, @user]

link_to edit_user_address_path(@user, @address)
# Is the same as
link_to [:edit, @user, @address]

# Namespaced index view
link_to magic_tricks_path
# Is the same as
link_to [:magic, :tricks]

# Regular edit action in a namespace 
link_to edit_magic_trick_path(@trick)
# Is the same as
link_to [:edit, :magic, @trick]

# Custom action in a namespace
link_to secret_magic_trick_path(@trick)
# Is the same as
link_to [:secret, :magic, @trick]

######### Example 5 - rendering partials with a namespace #########
# In a view
= render [:magic, @tricks]
# Partial in views/magic/tricks/_trick.html.haml

= render @tricks
# Partial in views/tricks/_trick.html.haml

= render [:user, current_user.tricks]
# Partial in views/user/tricks/_trick.html.haml

######### Example 6 - the challenge #########
# What does this redirect to?
class AddressesController < ApplicationController
  def destroy
    @user = User.find(params[:user_id])
    @address = @user.find(params[:id])
    @address.destroy
    redirect_to user_path(@user)
  end
end

# Whatever you said is probably wrong. It actually redirects to a the user destroy action. WAT!?! Or, at least I think that's what I observed... 

# I THINK THIS IS A RAILS BUG!!!!

# Which brings up a point... how do you create a path to delete stuff using this convention?
link_to [:magic, @trick], method: :delete, confirm: "Are you sure?"

README
These conventions are all about two things for me: readability and the principle of least surprise. I don’t think the readability comment needs to be explained, but maybe the principle of least surprise does. When you render partials in this way, there is no question about where the partial lives in the file structure, even when you are rendering an object in a view outside of its normal view folder.

I also like the way this forces you to think about all those partials you create as you build an app. Every time you find yourself in a situation where you need to render an object and you can’t just do ‘render @thingy’ in your view, you should take a minute and think. Are you really doing the right thing? Do you really need to render a thing six different ways? This isn’t just about clean, dry code – it’s also about good UI design. The choice to create a new partial should be a deliberate one.

But, hey, you can have your cake and eat it too. Sometimes. For example, on TroopTrack I have 5 major functional areas: plan, manage, communicate, achieve, and share. Three of these functional areas have different renderings of users, but I can still follow this convention using namespaces, like this:

# In a view

= render [:manage, @user]
# partial: app/views/manage/users/_user.html.haml

= render [:achieve, @user]
# partial: app/views/achieve/users/_user.html.haml

= render [:communicate, @user]
# partial: app/views/communicate/users/_user.html.haml

Happy, happy, happy
I really lourve this convention for rendering. It changes my approach to the user interface in ways that encourages goodness and drives consistency, but that’s not the only reason. When the time comes to start worrying about performance, it makes russian doll cacheing a lot easier.

Ugh. I can’t seem to wrap this post up, so I’m just gonna stop typing.

→ No CommentsTags: Uncategorized

Adding Events to Various Calendars (Google, Live, Yahoo)

March 6th, 2013 · 1 Comment

Everything’s a nail
In the past I’ve used a jquery plugin for generating links to add events to a calendar, but it recently broke when I upgraded jquery on TroopTrack. So I started banging around trying to get it to work for about an hour before I realized I was being stupid. The jquery library was 1) building a drop down menu and 2) generating links.

You don’t need to use jquery to do that. It’s pretty easy to make drop down menus (twitter bootstrap) and generate links (helper). In fact, since my event details were all being stored in active record, using javascript made it harder than it needs to be.

Anyway, in the event you ever find yourself in my shoes, here’s what I did:

Build the urls

  def google_calendar_url(event)
    # https://www.google.com/calendar/render?action=TEMPLATE&text=Joe's+40th+Birthday&details=Joe+turns+40+just+this+once&dates=20111212T190000/20111212T200000&location=Gillette+Stadium&sf=true&output=xml
    "http://www.google.com/calendar/event?action=TEMPLATE&trp=false" +
    "&text=" + event.title + 
    "&dates=" + event.activity_at.to_s(:ics) + 
    "/" + event.end_at.to_s(:ics) +
    "&location=" + event.location +
    "&details=" + event.description 
  end

  def live_calendar_url(event)
    "http://calendar.live.com/calendar/calendar.aspx?rru=addevent" +
    "&dtstart=" + event.activity_at.to_s(:ics) +
    "&dtend=" + event.end_at.to_s(:ics) +
    "&summary=" + event.title + 
    "&location=" + event.location
  end

  def yahoo_calendar_url(event)
    "http://calendar.yahoo.com/?v=60" + 
    "&TITLE=" + event.title + 
    "&ST=" + event.activity_at.to_s(:ics) +  
    "&in_loc=" + event.location +
    "&DESC=" + event.description +
    "&URL=" + plan_event_url(event) + 
    "&DUR=" + event.duration 
  end

With a little help from some friends
The time formats matter, so I created a time formatter in an intializer:

[Time].map do |klass|
  klass::DATE_FORMATS[:ics] = lambda { |date| date.strftime("%Y%m%dT%H%M%S")}
end

and Yahoo expects a duration in the HHMM format, which I did by adding a method on my event class:

def duration
  "%02d" % ((end_at - activity_at)/3600.floor) + "%02d" % (((end_at - activity_at)/60).modulo(60))
end

Boom shaka laka
At this point adding an event to a calendar is as easy as pie. Here’s my Twitter Bootstrap worthy HAML

%ul.nav.pull-left
  %li.dropdown
    = link_to icon('icon-calendar'), '#', {'data-toggle' => "dropdown", :class => 'dropdown-toggle'}
    %ul.dropdown-menu{'role' => "menu"}
      %li= link_to 'Google', google_calendar_url(@event), :target => '_blank'
      %li= link_to 'Live', live_calendar_url(@event), :target => '_blank'
      %li= link_to 'Yahoo', yahoo_calendar_url(@event), :target => '_blank'
      %li= link_to 'iCal', plan_event_path(@event, :format => :ics)

The last link is to a downloadable .ics file, which is a topic for another day.

→ 1 CommentTags: Uncategorized

Useful Way to Automagically Add Controller/Action Selectors to an App

February 4th, 2013 · No Comments

Not Invented Here
I swiped this idea from the work Craig Ulliott did on bodyshopbids.com. Thanks Craig!

Don’t hard-code selectors for action and controller specific CSS and Javascript
Sometimes you need a bit of CSS or javascript that only applies to a specific action, or maybe to every action on a controller. So, you go in and add a class or an id to every view that controller supports.

Then you sigh.

You can do something like this instead:

  %body{:id => body_id, :class => body_class}

where body_id and body_class are helper methods. For TroopTrack, these methods look like this:

def body_id
    body_id = controller.class.name.sub(/Controller$/, '')
      .sub(/::/, '').underscore+'_page'
    body_id.sub!(/^devise_/, "devise_#{controller.scope_name}_") 
     if body_id.match(/^devise_/)
    
    return body_id
  end

  def body_class
    if @body_class.nil?
      @body_class = ""
    end
    
    @body_class << " #{controller.action_name} "
    
    @body_class << " #{controller.class.name.split("::").first.downcase}" 
      unless controller.class.name.index("::").nil?

    @body_class << " unit_type_#{@troop.unit_type_id}" if @troop

    return @body_class
  end

I think TroopTrack’s helpers are a bit overly complicated, but that’s because many of them exist in a namespace and I want a class for the namespace as well as the controller. I also need to adjust my CSS based on the type of unit they are, so that’s in there too. Here’s an example body tag from TroopTrack:

<body class=' index  share unit_type_3' id='share_share_page'>

Index is the action. Share is the name space. Unit_type_3 means it’s a cub scout pack. The id, share_share_page, means it’s on the share controller in the share namespace.

Sometimes when I write a blog post and share my code I see opportunities to dry things up or refactor them. This is one of those moments.

A more reasonable place to start would be something like this:

  def body_id
    controller.class.name.sub(/Controller$/, '').sub(/::/, '').underscore
  end

  def body_class
    controller.action_name
  end

As usual, I’m suspicious that there is a ruby or rails idiom out there that can make that code better. I’m all ears.

With this in place, it’s very easy to override CSS or write javascript that is specific to a given page. If you are following the Rails 3 convention for assets, you could just add this scope to the beginning of every controller-specific CSS and javascript file so that you are forcing the behaviors in those files to only apply to the controllers they relate to. I’m not a big fan of this per se, but I don’t condemn it as a practice either.

Don’t Overdo It!
It’s easy to let this get out of control and start putting every new bit of CSS you write inside a selector so that you don’t accidentally mess up something else. Please don’t fall into this trap. It only makes your CSS worse and will eventually lead to inconsistency in your site styling. Instead, if you can, take the time to make sure you are building a clean and consistent set of styles for your site and use the selectors only when you truly need to override something.

→ No CommentsTags: Uncategorized