What I learned from interviewing my friends

Posted on 25 March 2014 by Joseph

When I set out to interview some friends about Silvi, I knew it would be useful. It's the Right Thing to Do for a lean startup, maximizing information gain. What I didn't know that it would be as useful in as many different ways as it was. First, let me provide some context.

When I first considered building Silvi, I wanted to do all the things I failed to do my first time around in the startup game - build customer-first, learning about their unique needs and generalizing to produce a robust product. But before I did any of that, I needed to see if there was a real need. Software developers are a notoriously picky group of people. Were people feeling the same pain around their documentation that I had felt myself?

Instead of spending a week trying to find people to talk to and another week or more waiting for them to pencil me in, I reached out to a great resource: my group of friends. Within one week, I had spoken with seven or eight different people, from tiny startups to big companies. Each had their own personal perspective and a unique perspective based on the company they worked for, and each was more than happy to chat with me for a little while. This made for a very friendly environment, rather than the bristly, sales-like environment some market interviews have.

Crucially, this allowed me to validate my idea in a minimal amount of time. When you are considering several startup ideas, minimizing your time to decision is very important. More than that, though, conducting these interviews in a conversational and relaxed environment taught me several things, one of which I wasn't expecting.

Market existence

On the surface, the sole purpose of these interviews is to evaluate whether there was a market need. And it certainly does, though the urgency of the need must be taken with a grain of salt, as one's friends may be building up their responses based on what they think you want to hear. As a result, it is important to continue conducting interviews as your idea evolves. Though I mentioned earlier that the relaxed environment of the interviews was a plus, you should also be able to convince more wary potential clients that you have an idea worth talking about, and they should be your goal in the next phase of your interviews. After all, unless you have a lot of friends you will be selling to these people eventually.

With your friend across the table, you should also try to identify who in the organization would make the purchasing decision, as this is the person you'll want to talk to next. In several of my interviews, the software engineers themselves made the decisions, but in others a manager or even a separate IT department buys software for the teams to use. Getting this information early can help you craft your market alignment.

Technical requirements

Talking with people about the problems they have with existing solutions, both personally and within their company as a whole, exposes technical features to consider for your product. After all, you are trying to fill the gaps in the market for at least a portion of your customer base. Interestingly, these gaps can be completely different depending on who you are talking to and their situation.

One of my friends working for a smallish but successful startup claims that they maintain their documentation entirely in Google Docs, and furthermore that they have never had any problem with it. Another friend at a more established company inherited several generations of documentation, each tied to the source control tools they were using at the time. His whole team had trouble finding anything in that quagmire.

Stories like this will help you solidify your vision for your product. You can't be everything to everyone, but you should be something amazing for the group you are trying to address.

WILDCARD! Marketing language

This one threw me for a loop. As I conducted more interviews, a common language emerged that people were using to describe their frustrations. "There's a complete lack of centralization." "It's almost impossible to find things." "I don't trust our documentation." Phrases like these can become the cornerstone of your marketing language. When you use the phrases your customers expect, you resonate with them; you are familiar.

When you find phrases like this, you should test them out against your other interviewees. When I talked about lack of trust in documentation, I would see eyes light up and heads nod. This is exactly the type of response your marketing copy should have, especially at an early stage. Agreement and resonance help build the so-called "sales environment" - the setting which maximizes your opportunity to connect with your customers.


TL;DR: Before you do anything else, interview your friends or an otherwise friendly group. They are easy to connect with, and you can gain a ton of useful information that will help flesh out your idea. Wait, can you do a TL;DR at the end?

Who needs a database?

Posted on 20 March 2014 by Joseph

Isn't it funny how all of the boring, extraneous parts of a project often add up to take as much time as the central parts? Like, not only do you have to solve your problem, but you also have to handle accounts, and send emails, and write a privacy statement, and a million other things like that. It's pretty rare that you get to have fun doing these less exciting parts, so when I got the chance to, I jumped.

Last week I read this article, and I thought that the basic idea was great: eliminate a lot of complexity, boilerplate code, and repetition by being a little more clever. Specifically, the article outlined a method for handling events like password resets, in which you typically send a URL containing a hash to the client, that didn't require the use of a database. Intriguing? Let me summarize.

Normally, you'd generate a random hash, throw it into the database along with some metadata like when it was generated, then tack that onto the URL you sent the user. This obviously requires a database table and multiple database queries, along with all the code required to manage those things. Yuck.

Instead, the author advocated digitally authenticating the payload of the message with a secret key and including the payload and key in the URL itself. When the user visits the URL, your app regenerates the key given the payload and checks for validity. If the keys match, you have a valid request. He even included a way to handle expiring the authenticated messages and making the URLs one-use.

OK, now do it with Python

Like I said earlier, I jumped at the opportunity. Let's build this system in Python using Flask. Flask is a particularly appropriate choice, since the author of the article above, Armin Ronacher founded the Flask project.

First, let's start with a minimal Flask boilerplate. (Feeling impatient? You can get all the code from this article here)

 1 from flask import Flask, request
 2 app = Flask(__name__)
 3 
 4 
 5 @app.route("/", methods=['GET', 'POST'])
 6 def passreset():
 7     if request.method == 'GET':
 8         return """
 9             <p>
10                 Forget your password?
11                 Enter your email below to reset it.
12             </p>
13             <form method='POST'>
14                 <input type='text' name='reset_email' />
15                 <button type='submit'>Reset your password</button>
16             </form>"""
17     else:
18         # Here is where you'd normally do all the nasty databse stuffs
19         return request.form.get('reset_email')
20 
21 
22 if __name__ == "__main__":
23     app.run(debug=True, host='0.0.0.0')

As it suggests, when the user submits the form, you'd normally generate the hash, collect the metadata, put all that in a database row, generate the URL and send the email. Not us though! Instead, what we'll do is create a payload, create an authentication code, and build the URL from that.

Build the payload

1 def create_payload(email):
2     payload = { 
3         "email": email
4     } 
5     return payload

Nothing fancy yet, we just want to know who we're resetting when the link is visited. Remember, we have no information about this request other than the payload, so include any information you will need!

Create the authentication code

1 import hmac
2 import json
3 
4 def get_auth_code(payload):
5     return hmac.new(app.config['SECRET_KEY'], json.dumps(payload)).hexdigest()

We're using a hash-based message authentication code (HMAC) as the authentication code. A HMAC is a strong way to combine a secret key (in this case, the Flask application secret key, though it doesn't need to be) with a payload. If you'd like to know more, you can read the Design Principles section of the Wikipedia article for more information on why it's strong and why you'd want that. Since the HMAC only hashes strings, we are dumping to a JSON string prior to computing the HMAC.

Form the URL

Now lets put the two functions together to create our URL. This function will return the GET string we need to include in our custom URL.

1 import urllib
2 
3 def get_url_keys(payload):
4     url_keys = { 
5         'auth_code': get_auth_code(payload)
6     } 
7     url_keys.update(payload)
8     return urllib.urlencode(url_keys)

With these functions in hand, let's rebuild the else clause of our route from the boilerplate earlier.

 1     else:  # From line number 17 in the boilerplate
 2         payload = create_payload(request.form['reset_email'])
 3         url = "/passreset?%s" % get_url_keys(payload)
 4         return """
 5             <p>
 6                 This should really be in an email!
 7                 Use the link below to reset your password.
 8             </p>
 9             <a href="%(url)s">
10                 %(url)s
11             </a>""" % {"url": url}

Obviously, as suggested by the page itself, don't do this in real life. Instead send the URL (complete with your domain! I have been burned by this in real emails) to your user via email.

Where do you wind up?

All that's left is to build a route for the reset URLs and parse them. Here's a function for checking the authenticity of the message.

1 class PassResetError(Exception):
2     pass
3 
4 
5 def check_passreset(payload, auth_code):
6     new_code = get_auth_code(payload)
7     if new_code != auth_code:
8         raise PassResetError("Invalid password reset request")

All it does is recompute the authentication code and compare it to the one it is given.

Here's the view for the new route.

 1 @app.route("/passreset")
 2 def newpass():
 3     # Need to check for both methods
 4     try:
 5         payload = {
 6             "email": str(request.args['email']),
 7         }
 8         auth_code = str(request.args['auth_code'])
 9         check_passreset(payload, auth_code)
10     except KeyError:
11         # Check for missing form elements
12         return "Invalid password reset request (form elements)"
13     except PassResetError, e:
14         return str(e)
15     # We're in the clear, give them the password reset form
16     return "Good to go! Here's where I'd render a new password form."

The view extracts the keys from the GET string in the url (request.args) and handles missing values. It then calls the verification function above and either renders a password reset form (ahem) or returns an error. You can try it yourself by changing a character or two of the auth_code or email in the URL in your location bar.

From here you should be able to integrate this into your own models and endpoints. Like I mentioned above, you can find all the code from this article here. Though we built this in the context of a password reset form, this same concept extends to any hashed URL operation, like email verifications.

Next time we'll take a look at two extensions: expiring requests and one-use requests.

The Failure of Language as Description

Posted on 24 February 2013 by Joseph

Last night, I sat down to play yet another round of my favorite board game, Settlers of Catan. As is pretty common, there was a newcomer who had never played before, and a couple players who needed a quick refresher. For some reason, it's usually my job to explain how the game is played, and I dutifully sat with a copy of the instructions (so as not to miss anything), explaining resource production, turn structure, various special cases, a little strategy, and other such topics.

As my description became longer and longer, I found myself apologizing for the ostensible complexity of the game. I assured the newcomer that the actual game itself, once play began, was much simpler and easier to follow than the instructions themselves.

This struck me as an interesting point, and as something that can be formulated in general: for some (classes of) things, the natural language description of the thing is significantly more complex than the thing itself.

On the one hand, this seems to be a failure of language. If language is purpose-built as a descriptive mechanism, then an inability to describe something effectively is by definition a swing and a miss. On the other hand, this can be seen as a triumph of the usability and power of the thing being described. If its purpose is so obvious as to not require description, yet so powerful that it defies description, then the thing has met its purpose beautifully. (Sidenote: I would definitely say this applies to Settlers).

I think that the above is also very pertinent to software and software documentation. The best software is intuitive and easy to use; documentation for the same software tends to be a long-winded, laborious combinatorial explosion of the different use cases and features. Since, as above, the software itself may already be a triumph, perhaps the best way to achieve parity with the documentation is to improve the descriptive mechanism.

Screenshots and videos are one way to improve the language of software documentation. Another is adding a layer of structure atop the description in order to make it more targeted and accessible. In a nutshell, this is one of the goals of Silvi: to elevate documentation to the level of what is being described, reducing the confusion of newcomers and the frustration of reference-seekers.

Information As Choice Architecture

Posted on 10 February 2013 by Joseph

As explained by Richard Thaler and Cass Sunstein in their influential work Nudge: Improving Decisions about Health, Wealth, and Happiness, structure is a critical element of good choice architecture, and becomes increasingly important as the choices become more complex. As an example, imagine choosing a paint color from the thousands of choices offered by paint companies if the colors were only offered in alphabetical order.

This is the same type of problem a newcomer faces when approaching existing documentation: they have a concept of what they are interested in (e.g. beige wall paint), but lack the vocabulary to find it, particularly as the volume information grows large (e.g. Benjamin Moore's color Kansas Grain). The choice in the instance of an information seeker is more abstract, of course. The decision they face is where to focus their attention to answer their question in the shortest amount of time.

As with a fan deck for paint samples, by introducing and enforcing a level of structure atop the information, an information seeker can be quickly directed to the concept they are looking for. By keeping headings and labels short and keeping information concise, a content creator can enable a newcomer to easily evaluate subtopics within a concept to retrieve their answer.

Of course, this is exactly what Silvi strives to do. Organizing information categorically allows for quick 'pruning' of unrelated choices (blues are not browns, dark browns are not beiges). By nudging content creators to keep titles succinct and content short, subtopics are easy to evaluate and absorb. By offering effortless rearranging of content, documentation can be kept up to date, and important concepts can be promoted. Silvi offers information seekers a better choice architecture for directing their focus.