blog

Loss Aversion and Tech Debt

Humans are loss-adverse. We place an irrationally high value on losing something over gaining an identical item. So for example, I’d be more upset about losing $10 than the happiness I’d feel by gaining $10. If I buy a meal and hate it, I’ll likely finish it anyway.

In general, people would rather gamble on a 50% chance of losing $1000 rather than giving up $500. Down $50 at the blackjack table? Odds are most people will want to try to win that back rather than take the loss. Curiously, most people would rather accept a guaranteed $500 rather than accept a 50% chance of making $1000. Irrational? Yup, but extremely predictable.

Loss Aversion is the fancy name for the phenomenon. People prefer avoiding losses to acquiring gains, which drives them to become more-risk tolerant in the face of loss. I think it can help explain how we build up and continue to live with technical debt in software development.

Tech debt is a useful metaphor describing the long term consequences inflicted upon a code base by deferring work. Similar to financial debt, we often choose to avoid doing the “right thing” now in favor of a faster path to the end result. The interest accrued by the debt adds up over time and can cause major problems.

There are lots of reasons that software engineers knowingly take on tech debt – deadlines, lack of knowledge or skills, too much work in progress – the list goes on. Sometimes it is unavoidable, sometimes not. Every project has some level of debt, though.

Paying off accumulated technical debt is where I see the ties into loss aversion. The time spent fixing a hastily implemented data access strategy, for example, is time not spent implementing a cool new feature. There is rarely any directly visible customer value delivered by paying off technical debt. In most people’s eyes, this is a loss of time, opportunity, and resources.

We are irrationally risk-tolerant in the face of this loss. Instead of spending $500 to pay off the debt, we’ll flip the coin, let the problems grow, and take the risk of losing $1000. Who knows, maybe the problems won’t surface for a long time. Maybe never. Maybe tomorrow, though.

So how do we fix this if the human mind is hardwired to avoid losses?

Shift the mindset of technical debt. Knowingly taking on technical debt is a loss, not a gain. We are losing the ability to easily respond to future requirements; we are not gaining a new feature in a shorter time frame. And existing tech debt should be seen as a sunk cost – it’s lost, and it’s better to forget the past.

If we accept the current state rather than treating tech debt as an incurred loss we will be less likely to gamble with the possibility of future losses. And hopefully our minds will start to blast warning sirens as we consider taking on new technical debt in the future.

Please, Stop Saying “I Can’t”

I’m as guilty as anyone when it comes to uttering the words “I can’t…”. That’s almost never actually true, though.

Here are some things I know that I can’t do:
I can’t fly by flapping my arms
I can’t speak Mandarin
I can’t eat my weight in Oreos

Here are some things I’ve claimed that I can’t do:
I can’t take a break to eat lunch
I can’t help you find a solution to problem XYZ
I can’t work on your project

The first list contains physical or scientific impossibilities. The second list contains choices. Unless you are locked in a room, we all can take a break to eat lunch. This represents a choice to temporarily stop doing one thing and start doing another.

There are influences that go into all of our choices and there are consequences to all of our choices. By joining a meeting we may be making a choice to skip a phone call or hold off on sending an email. There are plenty of reasons why one activity may be more important than another.

So instead of claiming that you cannot do something, start making your choices explicit. “I can’t take a break” instead becomes “I am choosing not to take a break because task XYZ is more important right now”. Recognize that you have a choice. Decisions are easier when we aren’t held hostage by what we claim we can and cannot do.

PS – The same goes for saying “I have to”. You don’t have to work late. You don’t have to send that email right now. Most of the things we have to do boil down to trade-offs.

Believe me, there is much that you “can’t” do or “have” to do 🙂

On Hiring Techies

I’ve had the good fortune to have worked on some incredible teams. The best teams I have been a part of each had a very structured and rigorous interview process. It took longer than average to hire new team members, but the tenure of team members was high and our attrition rates were unbelievably low. The teams gelled and that collaboration was reflected in the applications that we built.

Having an interview process like this leads to a lot of interviews over the course of my career – speaking with dozens, maybe hundreds, of potential candidates. I started writing this as a single blog post but it grew too large. Here’s what has worked for me, broken into a short blog series.

Evaluate Potential, Not Accomplishments
Coding Challenge
The Team Interview
Hire For Cultural Fit

Other Interviewing Inspiration

Johanna Rothman has some great books, articles and blogs on hiring technical people. She’s also an infinitely better writer than I, so I strongly suggest reading her work.

On Hiring Techies – Evaluate Potential, Not Accomplishments

This is a part of a series on Hiring Techies.

Evaluate Potential, Not Accomplishments

I don’t spend a ton of time reading resumes. Depending on the position we’re hiring for, I may look for a few critical skills but I’m mainly looking for themes that tell me that the candidate has a passion for technology. Do you contribute to an open source project? Do you blog? Do you have a history of attending or even speaking at conferences? Show me that technology is an important part of your life.

Don’t get me wrong. I do not want to read about your hobbies or what you do for fun. The fact that you have been the president of your local Pokemon club for 3 years or that you’re and avid White Sox fan is irrelevant. I want to get a sense of what you will bring to the team.

Does the candidate’s resume only highlight individual accomplishments? Are there any bullet points about past team’s successes? How about any experience introducing new technologies or organizing brown bag lunch sessions? I can typically spot a true team player based on how they highlight their successes and what they are proud of.

I want to work with a team of leaders. I want a team of mentors. There are millions of people who can write a web application. There are very few people who can write a web app and help me build a better team.

I’m looking for technological aptitude. A laundry list of impressive technical languages and tools is indeed impressive but it should never be the reason for hiring a candidate. Worse yet, it should never be the reason for passing on an interview opportunity or turning a candidate down. It took me a long time to realize that I don’t need to find someone with that fulfills every item on my wish list.

It’s important to remember that when we hire, we’re hiring a person and not a bag of skills.

Skills and experience are important, of course, but this should support your decision to hire someone rather than be the basis of your decision. This is probably the most frequent mistake I’ve seen folks make. Teams have missed out on great candidates because they were missing some specific tool in their belt. I’ve also seen very smart people hired who end up being culturally destructive.

I can teach an Eclipse user Visual Studio. I can teach a C# developer JavaScript. I cannot teach someone how to be passionate. I cannot teach someone how to fit into our culture.

Every position is different and the skills that are critical for the candidate to be successful in that position will vary. If your domain requires certain skills or experience then, absolutely, screen for that. I would expect this to be the exception rather than the rule, though.

On Hiring Techies – Coding Challenge

This is a part of a series on Hiring Techies.

Coding Challenge

Of course, if I’m evaluating a candidate to join my team I want to ensure that they know what they’re talking about. There are many ways to do this. Some people slam a candidate with technical questions for hours. Some people choose to do a whiteboard session solving a problem real time.

Like most, my preference is to go first through a short technical phone screen. If it’s obvious that the candidate has the critical skills we need, I’ll send out a coding challenge to be completed within the next few days. This is a short but non-trivial set of acceptance criteria – something that an average developer can complete in a few hours. Some folks balk at this request. This actually tells me a lot about the candidate and is an immediate red flag. Most candidates are happy to be given an opportunity to show what they can do and submit a solution within a day or two.

At this point the team reviews the submitted solution and decides whether or not to bring the candidate in for an interview. We make it clear that the solution that was crafted will be discussed during the interview.

As programmers, we spend considerably more time reading and discussing code than we do writing it. We need to be able to clearly explain our ideas and be willing to listen to feedback. The discussion I have with a candidate about their submitted solution will tell me more about their technical skills than a barrage of technical questions ever would. We’ll review design decisions, the application structure, SOLID principles and clean code, any technologies or frameworks used, and so on.

I also love reviewing the unit tests that were submitted (or having a discussion regarding the lack of tests). We’ll talk about TDD – why it was or wasn’t applied. It’s always fascinating to hear how the tests influenced the design.

I will very rarely challenge a candidate to solve some problem in person. The reason I don’t like to have the candidate code live is because the interview itself is already high pressure. Most of us have experienced this kind of test when interviewing. I want to see what they do when they’re at their best – not when they have a bunch of strangers staring at them. Programming isn’t about memorization. Anyone can find framework documentation online in seconds. Programming is about problem solving and I want to see how candidates think about problems.

Working code is one of the best ways I’ve found to evaluate a candidates technical skill level.

On Hiring Techies – The Team Interview

This is a part of a series on Hiring Techies.

The Team Interview

Who is involved in your interview process? Do a few managers speak to a candidate and then decide whether or not to hire them? There are few things more disruptive than having someone dropped into your team. If the day a new hire comes on board is the first day they meet their team then there is something wrong – not only in your interviewing process but I’d bet that there are probably major trust issues at the organizational level.

I probably spend nearly as much time with my teammates as I do my wife. If I’m spending that much of my life around someone then I need to know that we are (professionally) compatible. It’s not fair to the candidate or the rest of the team if they don’t have a chance to interview each other. I try to give as many people on the team as possible a chance to meet the candidate. After all, the folks on the team are much more qualified to decide who is right for the team than a few distant managers.

This advice goes for hiring individual contributors as well as managers. If your organization is looking externally for to fill a leadership role it’s extremely important for the people who will be reporting to that person to have a chance to interview them. Not only does this help evaluate the candidate from multiple points of view, but it will also establish a rapport early on.

The team must be completely bought in to the decision of who is hired into their team. Assigning a new person to a team can lead to distrust and skepticism. A healthy team knows what they need and will welcome the chance to be involved in the process. And believe me, the candidate will also appreciate being able to meet with as many potential future coworkers as possible.

These interviews can be conducted as a series of 1-on-1 discussions or as a larger group discussion. I’d limit this to at most 3 people at the same time, though. Panel interviews are intense for the candidate. They can also be challenging for the interviewers because inevitably someone will be more outspoken. If you do run a panel interview, practice first. Know who will be asking which questions and make sure that each interviewer has enough time to get through their most important questions. And always account for buffer time needed for the candidate to ask questions.

Once all interviews are complete the decision on who to hire has to be unanimous. If even one person does not agree on the hire then have a team discussion about the concerns. If consensus cannot be reached, pass on the candidate. Again, this shouldn’t be due to some missing technical skill – those can be taught. Find the right person, then worry about getting the right skills in place.

My teams may have missed out on some great people with this approach, but I’m confident that it was the right decision for the team. It is a risk, for sure, but the effect of a bad hire far outweighs it.

On Hiring Techies – Hire For Cultural Fit

This is a part of a series on Hiring Techies.

Hire For Cultural Fit

I said it earlier, but it bears repeating: I can teach someone a new technology. I cannot teach someone how to fit into our culture.

A lot of technical interviewers are laser focused on a candidate’s skill set. I’ve been asked to explain how clustered indexes work in SQL server and I’ve analyzed algorithm run times. Heck, I’ve been asked to provide a high level system design for a hypothetical application. Maybe it’s due to our industry’s disproportionally high percentage of introverts (myself included), but I’ve personally been through many interviews where I haven’t been asked a single question about how I work with others or what kind of environment I thrive in.

It is unfortunately rare that any behavioral questions are included when interviewing techies. This is a big problem that leads to poor performing teams and high turnover. Professional interview training would undoubtedly help. But in the absence of that, my advice is to ask questions that force the candidate to reveal how they’ve handled specific situations in the past. Get a feel for the teams and organizations where they have been most and least successful.

Maybe you know that your team can get trapped in design debates. Maybe your team rotates through presenters in bi-weekly brown bag sessions. You know what makes your team special and you know what challenges your team faces. You know the personalities and quirks of your teammates. Ask questions that reveal how the candidate fits in.

Avoid questions with binary responses. You know, dead end questions like “Do you like the new thingamabob framework?”. Focus on questions that invite the candidate to illustrate a past situation that they’ve been a part of. Ask them to describe a situation that they are likely to encounter at your organization. Ask clarifying questions. Listen to responses and build your subsequent questions upon each other. Circle back to an earlier part of the interview and dig deeper. Again, you need make sure that the candidate that you are interviewing is going to fit in with your team and with the larger organization.

All this culture and team chemistry talk may lead you to believe that the candidate’s tech skills shouldn’t matter. They do, of course, but I’d rather hire a good developer who is a great cultural fit than a brilliant developer who clashes with our culture. If the candidate has some experience that other team members do not then I want to make sure that knowledge will be spread.

The right hire will improve your team, your product and your organization. The right mix of interview questions you should have a feeling whether or not this candidate will do so. Spend the time to develop your interview process. You’ll be glad you did.

Booked Feedback

One of the things I love most about working on open source software is the engagement of the community. Booked has a very active community with a huge repository of information.

An increasingly common problem I’m hearing is that Booked has a steep learning curve. Being in the application every day, I have blinders on that hide these types of issues from me.

How can we clean up the workflow and make Booked a pleasure to use? I want to know what’s difficult. I want to hear your thoughts on how to improve the initial application experience.

My goal is to get to a point where new users do not need a help page or a tutorial. I want you to go from install to booking a reservation in minutes without needing help from the community.

Submit your feature requests and improvement suggestions!

I can’t promise that every suggestion will be implemented, but I can promise to consider each suggestion and use your feedback to improve Booked.

TDD: Back to Hand Rolled Stubs

I’m unashamedly an Agile practitioner and self-proclaimed enthusiast. It’s not a perfect way to build software, but I haven’t found anything better. And until I do, this is the approach I’m taking.

Building Quality In

One of the core principles of Agile is the focus of building quality in from the start, not asserting quality after the product is built. At first this is counter-intuitive, right? How can you ensure the quality of something which is in the process of being built? This is where TDD saves the day. Write your tests first – set the expectations of what the code is supposed to do – then write the production code.

When I rewrote Booked for the 2.0 release I did it all test-first. In fact, there are thousands of assertions covering the majority of the code base. This is super important for a dynamic language like PHP where it is very easy to shoot yourself in the foot. Lots of tests = high confidence that my change didn’t break something. Of course, unit tests in a statically typed language are also critical.

The Unit Testing Approach

I use PHPUnit in Booked and it works great. Aside from providing your typical unit testing functionality, PHPUnit also includes test doubles. Mocks and stubs are great for substituting behavior and isolating components in your tests. It’s a technique I rely on heavily and it leads to loosely coupled code and more focused tests.

Martin Fowler has a great writeup comparing mocks, stubs and other substitution patterns.

Death By Over-Specification

When I first starting practicing TDD there weren’t a lot of mocking frameworks or libraries available. We wrote our stubs and mocks by hand and used a plain assertion framework (nUnit in the .NET space). It takes some time to write this code – you’re building new objects to stand in for existing objects. The interfaces need to match, the return types need to match, and in some cases the return object needs to be set up in a certain way. It can be a decent amount of work. My laziness is what led me to mocking frameworks.

Mocking libraries gained a lot of traction around 2006/2007. Mocks, and mocking frameworks in particular, work by setting up expectations and then returning appropriate responses when you exercise the code. This awesome because you can quickly substitute behavior of specific methods without building up a whole object.

Great! Look how fast I’m going!

So Super Fast

One problem with mocks is that they want to know everything about your code. For example, given you pass parameters x, y, z to this function then return this specific result. This is great when you care about how you execute a function – and there are valid reasons to care about that. This is not great when all you want to do is return a canned response and move on. One of the most frequent arguments I hear against unit testing and TDD is that the tests are fragile and difficult to maintain. Over-specification is generally the real cause of these problems.

An Example

Here’s a test pulled straight from the Booked test suite.

public function testBindsDefaultScheduleByMonthWhenNothingSelected()
{
	$this->page = $this->getMock('ICalendarPage');
	$this->repository = $this->getMock('IReservationViewRepository');
	$this->scheduleRepository = $this->getMock('IScheduleRepository');
	$this->calendarFactory = $this->getMock('ICalendarFactory');
	$this->resourceService = $this->getMock('IResourceService');
	$this->subscriptionService = $this->getMock('ICalendarSubscriptionService');
	$this->privacyFilter = new FakePrivacyFilter();

	$this->presenter = new CalendarPresenter(
		$this->page,
		$this->calendarFactory,
		$this->repository,
		$this->scheduleRepository,
		$this->resourceService,
		$this->subscriptionService,
		$this->privacyFilter);

	$showInaccessible = true;
	$this->fakeConfig->SetSectionKey(ConfigSection::SCHEDULE, ConfigKeys::SCHEDULE_SHOW_INACCESSIBLE_RESOURCES, 'true');

	$userId = $this->fakeUser->UserId;
	$defaultScheduleId = 10;
	$userTimezone = "America/New_York";

	$calendarType = CalendarTypes::Month;

	$requestedDay = 4;
	$requestedMonth = 3;
	$requestedYear = 2011;

	$month = new CalendarMonth($requestedMonth, $requestedYear, $userTimezone);

	$startDate = Date::Parse('2011-01-01', 'UTC');
	$endDate = Date::Parse('2011-01-02', 'UTC');
	$summary = 'foo summary';
	$resourceId = 3;
	$fname = 'fname';
	$lname = 'lname';
	$referenceNumber = 'refnum';
	$resourceName = 'resource name';

	$res = new ReservationItemView($referenceNumber, $startDate, $endDate, 'resource name', $resourceId, 1, null, null, $summary, null, $fname, $lname, $userId);

	$r1 = new FakeBookableResource(1, 'dude1');
	$r2 = new FakeBookableResource($resourceId, $resourceName);

	$reservations = array($res);
	$resources = array($r1, $r2);
	/** @var Schedule[] $schedules */
	$schedules = array(new Schedule(1, null, false, 2, null), new Schedule($defaultScheduleId, null, true, 3, null),);

	$this->scheduleRepository
			->expects($this->atLeastOnce())
			->method('GetAll')
			->will($this->returnValue($schedules));

	$this->resourceService
			->expects($this->atLeastOnce())
			->method('GetAllResources')
			->with($this->equalTo($showInaccessible), $this->equalTo($this->fakeUser))
			->will($this->returnValue($resources));

	$this->resourceService
			->expects($this->atLeastOnce())
			->method('GetResourceGroups')
			->with($this->equalTo(null), $this->equalTo($this->fakeUser))
			->will($this->returnValue(new ResourceGroupTree()));

	$this->page
			->expects($this->atLeastOnce())
			->method('GetScheduleId')
			->will($this->returnValue(null));

	$this->page
			->expects($this->atLeastOnce())
			->method('GetResourceId')
			->will($this->returnValue(null));

	$this->repository
			->expects($this->atLeastOnce())
			->method('GetReservationList')
			->with($this->equalTo($month->FirstDay()),
				   $this->equalTo($month->LastDay()->AddDays(1)),
				   $this->equalTo(null), $this->equalTo(null),
				   $this->equalTo(null), $this->equalTo(null))
			->will($this->returnValue($reservations));

	$this->page
			->expects($this->atLeastOnce())
			->method('GetCalendarType')
			->will($this->returnValue($calendarType));

	$this->page
			->expects($this->atLeastOnce())
			->method('GetDay')
			->will($this->returnValue($requestedDay));

	$this->page
			->expects($this->atLeastOnce())
			->method('GetMonth')
			->will($this->returnValue($requestedMonth));

	$this->page
			->expects($this->atLeastOnce())
			->method('GetYear')
			->will($this->returnValue($requestedYear));

	$this->page
			->expects($this->atLeastOnce())
			->method('SetFirstDay')
			->with($this->equalTo($schedules[1]->GetWeekdayStart()));

	$this->calendarFactory
			->expects($this->atLeastOnce())
			->method('Create')
			->with($this->equalTo($calendarType),
				   $this->equalTo($requestedYear), $this->equalTo($requestedMonth), $this->equalTo($requestedDay),
				   $this->equalTo($userTimezone))
			->will($this->returnValue($month));

	$this->page->expects($this->atLeastOnce())->method('BindCalendar')->with($this->equalTo($month));

	$details = new CalendarSubscriptionDetails(true);
	$this->subscriptionService->expects($this->once())->method('ForSchedule')->with($this->equalTo($defaultScheduleId))->will($this->returnValue($details));

	$this->page->expects($this->atLeastOnce())->method('BindSubscription')->with($this->equalTo($details));

	$calendarFilters = new CalendarFilters($schedules, $resources, null, null, new ResourceGroupTree());
	$this->page->expects($this->atLeastOnce())->method('BindFilters')->with($this->equalTo($calendarFilters));

	$this->presenter->PageLoad($this->fakeUser, $userTimezone);

	$actualReservations = $month->Reservations();

	$expectedReservations = CalendarReservation::FromScheduleReservationList($reservations,
																			 $resources,
																			 $this->fakeUser,
																			 $this->privacyFilter);

	$this->assertEquals($expectedReservations, $actualReservations);
}

This is 75 lines of mock setup code! We’re expecting specific parameters and have mock objects being returned all over. Let’s take a look at this same test with stubs.

public function testBindsDefaultScheduleByMonthWhenNothingSelected()
{
	$this->page = new StubCalendarPage();
	$this->repository = new StubReservationViewRepository();
	$this->scheduleRepository = new StubScheduleRepository();
	$this->calendarFactory = new StubCalendarFactory();
	$this->resourceService = new StubResourceService();
	$this->subscriptionService = new StubCalendarSubscriptionService();
	$this->privacyFilter = new FakePrivacyFilter();

	$this->presenter = new CalendarPresenter(
		$this->page,
		$this->calendarFactory,
		$this->repository,
		$this->scheduleRepository,
		$this->resourceService,
		$this->subscriptionService,
		$this->privacyFilter);

	$showInaccessible = true;
	$this->fakeConfig->SetSectionKey(ConfigSection::SCHEDULE, ConfigKeys::SCHEDULE_SHOW_INACCESSIBLE_RESOURCES, 'true');

	$userId = $this->fakeUser->UserId;
	$defaultScheduleId = 10;
	$userTimezone = "America/New_York";

	$calendarType = CalendarTypes::Month;

	$requestedDay = 4;
	$requestedMonth = 3;
	$requestedYear = 2011;

	$month = new CalendarMonth($requestedMonth, $requestedYear, $userTimezone);

	$startDate = Date::Parse('2011-01-01', 'UTC');
	$endDate = Date::Parse('2011-01-02', 'UTC');
	$summary = 'foo summary';
	$resourceId = 3;
	$fname = 'fname';
	$lname = 'lname';
	$referenceNumber = 'refnum';
	$resourceName = 'resource name';

	$res = new ReservationItemView($referenceNumber, $startDate, $endDate, 'resource name', $resourceId, 1, null, null, $summary, null, $fname, $lname, $userId);

	$r1 = new FakeBookableResource(1, 'dude1');
	$r2 = new FakeBookableResource($resourceId, $resourceName);

	$reservations = array($res);
	$resources = array($r1, $r2);
	/** @var Schedule[] $schedules */
	$schedules = array(new Schedule(1, null, false, 2, null), new Schedule($defaultScheduleId, null, true, 3, null),);

	$this->scheduleRepository->SetSchedules($schedules);

	$this->resourceService->SetResources($resources);

	$this->page->SetScheduleId(null);

	$this->page->SetResourceId(null);

	$this->repository->SetReservaions($reservations);

	$this->page->SetCalendarType($calendarType);

	$this->page->SetDate($requestedDay, $requestedMonth, $requestedYear);

	$this->page->SetFirstDayStart($schedules[1]->GetWeekdayStart());

	$this->calendarFactory->SetCalendarMonth($month);

	$details = new CalendarSubscriptionDetails(true);
	$this->subscriptionService->SetDetails($details);

	$this->presenter->PageLoad($this->fakeUser, $userTimezone);

	$actualReservations = $month->Reservations();

	$expectedReservations = CalendarReservation::FromScheduleReservationList($reservations,
																			 $resources,
																			 $this->fakeUser,
																			 $this->privacyFilter);

	$this->assertEquals($expectedReservations, $actualReservations);
	$this->assertEquals($month->FirstDay(), $this->repository->_SearchStart);
	$this->assertEquals($month->LastDay()->AddDays(1), $this->repository->_SearchEnd);
	$this->assertEquals($calendarType, $this->calendarFactory->_CalendarType);
	$this->assertEquals($requestedYear, $this->calendarFactory->_CreateYear);
	$this->assertEquals($requestedMonth, $this->calendarFactory->_CreateMonth);
	$this->assertEquals($requestedDay, $this->calendarFactory->_CreateDay);
	$this->assertEquals($userTimezone, $this->calendarFactory->_CreateTimezone);
	$this->assertEquals($month, $this->page->_BoundMonth);
	$this->assertEquals($details, $this->page->_BoundSubscription);
	$calendarFilters = new CalendarFilters($schedules, $resources, null, null, new ResourceGroupTree());
	$this->assertEquals($calendarFilters, $this->page->_BoundFlters);
	$this->assertEquals($this->subscriptionService, $this->subscriptionService->_ScheduleId);
}

This is still a huge test (that’s a problem for another post). Here the stub setup code gets cut down to about 15 lines. What I also love is that I don’t have expectations set in the mock specification. Any assertions around parameters have been moved down with the rest of the assertions. This is wonderful for a few reasons.

1 – This follows the Arrange-Act-Assert model that helps us in writing clean tests.
2 – Most of the time I don’t care about what parameters I pass to a method.
3 – Mocks will typically not return anything if you don’t pass the right value to a mocked-out function. When you pass the wrong value null is returned, causing a null reference exception in your code. This is very frustrating and difficult to trace, especially when you changed the parameters intentionally.

I don’t care which method is called (most of the time). Often times I don’t care about the data that we’re feeding to a collaborating object, either. I just want to test this specific object’s functionality and assume that everything else is tested elsewhere and works as it should.

The Stub Library

One of the biggest ancillary benefits I’ve found to hand writing my stubs is that I’ve built up a pretty solid library of reusable objects. I have stub objects ready to go for different pages, for reservations, data access and so on. I can pull in an existing stub and continue on with my test. I don’t need to set up the same stub expectation or response over and over. I don’t even have to think about how this collaborating object works. And if I apply an automated refactor to a method signature I don’t have to worry about breaking a ton of tests – the refactoring would apply to the stub object, as well.

Looking Forward

I’ve come full circle on stubs since my early days of TDD. I used to view them as a productivity killer, but my opinion now is that they actually save me time. Sure, there’s a little upfront investment, but the return on that investment is high. Who knows, maybe I’ll venture back into the land of mocks again one day. Until then, I’m happy with my well-tested, easy to maintain production code base.

Twinkle Toes Software – Looking back on my first year

Wow, it’s been a year since I founded Twinkle Toes Software. I thought this would be a good time for me to stop and reflect a bit on how its going and what I’ve learned.

The Zero Entry Pool Approach

Am I a “zero-entry” entrepreneur? I’m sure there are countless ways to approach starting a business. For me, I decided against diving head first into it and instead opted for what I’m calling a “zero-entry” approach.

Have you been in a zero-entry pool?

Zero Entry Pool

This has a gradually declining entrance that allows you to enter the pool at your own pace. Compare this to a diving board, where you have very little control over the speed at which you enter – and there’s no turning back. I’ve taken the former approach to launching Twinkle Toes Software. At this point I’m about waist-deep 🙂 And this is what I’ve learned.

So… How’s It Going?

Thanks for asking! It’s going well. Interest in Booked Hosting is strong, as is the interest in custom development. Surprisingly, there hasn’t been much interest in our enterprise support services. Our community support is incredible, so I guess I shouldn’t be all that bewildered.

What I’ve Learned – Get Your Ducks In a Row

If you’re going to start a business, whether it’s a sole proprietorship (like Twinkle Toes), an LLC, S Corp, whatever, get those ducks in a row.

Ducks in a row

The Legal Stuff

First of all, I’m not a lawyer and this isn’t legal advice, so consult an attorney for all legal matters. Every state and business type has their own set of rules. Find out what yours are and follow them. Better yet, hire an attorney to help you or use a site like LegalZoom (I don’t use LegalZoom and have no affiliation with them). There are different advantages and rules that go into different business registration types. Find the one that makes sense for you.

The Small Business Association here in the US has a lot of great advice and tools for launching your business. Definitely check that out.

Get your company registered with your state, city and county municipalities. Get an EIN and keep your social security number out of all records. Keep your personal and professional identities separate – your business is one thing and your personal life is another. The more you keep these isolated from one another, the better.

Make sure you have the right insurance and make sure you have enough of it. If you keep your business legally separate it can be difficult to jeopardize your personal finances in the event that legal action is taken against you. That said, you want to ensure you have insurance that offers the right amount of protection for your situation.

Set Up a Professional Web Presence

If you have a website that looks like it was built by a kid you will miss out on business. Really, would you give your money to a business that does not present themselves in a professional way?

Unless you’re a web designer, don’t waste your time building your own web site. Joomla and WordPress are readily available and there are thousands of themes available, most at a nominal cost. Set it up and spend your time focusing on content and marketing rather than design.

Ongoing Operations

Keep Track of Every Dollar Coming In and Going Out

Keep business expenses and income separate from your personal accounts. Set up a separate bank account if you want to be safe. Taxes are not fun. Taxes are even less fun when you’re reporting for your business, too. Hire an accountant if you are pulling in enough money to make it worth the cost. Otherwise, use an accounting package. I’ve heard good things about QuickBooks, but I’ve never used the software.

I do use Wave and would highly recommend them. The software is web-based, so it is available anywhere. This is great when you’re not in front of your primary computer or need to capture transactions on the go. They provide quoting, invoicing, accounting, credit card payment processing and more. Amazingly, it’s also free (aside from the credit card processing, which runs at a reasonable 3%).

Different business types have different accounting rules. Know what those rules are and follow them. If you’re going all in and will be self-employed make sure you know when and how to pay your taxes. Actually, just hire an accountant. My guess is that you’re not a tax or finance expert so find someone that is.

Analyze Your Web Traffic

The more you understand your website’s audience, the more effectively you can tune your website. Google Analytics makes it trivial to capture and analyze your web traffic. See where people are coming from, their navigation path, how long they stick around, etc. This is crucial information to help keep an eye on the effectiveness of any marketing campaigns or call to action sections of your website.

It’s Hard Work

Take That Estimate and Double It

I don’t know anyone who started their own business thinking it would be easy. I believed it would be a lot of work, but I was wrong. I underestimated how much work it is. Be prepared to be working long days. Set these expectations with your family. You absolutely need their support if you’re going to be successful.

Also, set realistic timeline expectations with clients. Don’t promise to turn something around in a few days when you know you need weeks with your current workload. You can attract a lot of clients by promising quick turnarounds, but you’re going to lose them and your reputation will be tarnished if you can’t keep your promises. In my experience most clients want things delivered immediately, but understand and accept longer estimates. Be honest and deliver and you’ll build a loyal client base.

You Will Probably Lose Money

There are very few business that launch and immediately turn a profit. Be prepared to lose money for months, maybe even years. You may never make money. Something like half of all new businesses fail in the first few years.

What does this mean?

If you’re quitting your full-time gig for this then have enough in your savings to live off of for at least a year. If you’re doing this part-time then be ready to invest a lot of your free time and money. Find investors if necessary.

Booked was in the marketplace for 10 years and had a reputation as a successful open source product. Even with that history, I’m barely turning a profit. I lost money during the first 6-7 months after the Twinkle Toes launch.

But It’s Fun

I’m having a fantastic time running Twinkle Toes. Yes, it’s a lot of work but it’s work that I am passionate about. Booked Scheduler is very close to my heart and being able to dedicate time to it while making a little bit of money feels great.

What Did I Miss?

Anyone have any other tips? Let’s hear about your experiences launching a business or product!