Friday, June 27, 2014

Marketing Must Build a Product

"If we can arrange things in such a way that our interests are aligned with our customers, then in the long term that will work out really well for customers and it will work out really well for Amazon." -- Jeff Bezos, Amazon

Amazon is an amazing company. I've heard mixed reports on what it's like to work there, but they all follow a consistent theme: put your customers first. As a platitude, that's easy. In real life, this means figuring out how to fix problems at 3AM. It means working around faulty services from other groups, causing rework and hurt feelings. It means short-term loss due to fraudulent replacement orders. All because customers don't listen to excuses. Online, there are a million other places to get a better experience and the same product. Customers don't listen; they simply leave.

Over the long term, this strategy has been phenomenally successful for Amazon. eCommerce is not a brand-friendly business: most people don't care where they buy their Xbox. They just want an Xbox, as cheap and fast as possible. Amazon Prime customers, on the other hand, don't comparison shop. They come back and simply buy, trusting Amazon's price and speed. Amazon built a brand.

In the B2B world, companies that put the customer first still win. It's a different world, though. You can wake up at 8AM with a burning desire to buy a new hard drive, and despite never having been on amazon.com, by 10AM, you're a customer. At most, you saw 2 hours of Amazon marketing.

The trouble is, B2B means there's always a team involved. At AppNeta, it can be anywhere from 1 week to 1 year between the first time somebody visits our website and the time their company purchases. This is pretty typical of B2B SaaS products: I've heard executives brag about the fact that they got their buying cycle time down to 45 days. And before that, you better expect that they've been on your website and reading your blog for 6 months before they picked up the phone.

8 months! 8 months before a person is considered a customer. 6 months of which they were learning about you, where they had no access to the product. If you keep this customer for 3 years, they will have spent 18% of their time with your company outside of your product. Instead of 2 hours with the marketing team, they spend 8 months with them.

During that 8 months, marketing has to create a product. If the B2B product is valuable to teams, marketing's product must be valuable to individuals. Maybe that's the free version in a freemium product, or maybe it's an email track with curated news about the prospect's job. Many companies fall into a trap where you explain your own product in detail, because that's easy. Ultimately, it's not relevant, and users don't care.

There's no magical "corporate America" or "enterprise landscape" where people aren't people. Everybody is a consumer, everybody is human, and everybody has a million choices to solve their problems. No matter what you do, think about the person on the other side. They are your customer. Put the customer first.

Thursday, June 19, 2014

A Strange Sense of Privacy

I'm not a very private person. I'm not an aggressive over-share-er, either. Kind of in the same way that I don't have strong religious beliefs, or I wouldn't tell you which brand of spaghetti to buy. It's just not important to me, so I don't have strong opinions on it. I am, on the other hand, aggressively pro-technology, so as technology has asked for increasing more visibility into my life, I've allowed it. Recently, that's changed, and I can't exactly put my finger on why.

The moment that got me thinking about this was right after I left the gym yesterday. The gym? Yes, the gym. The "I lift things up and put them down" gym. Depending on how well you know me, you're either sick of hearing how much I'm into weightlifting these days, or I haven't mentioned it at all. My first activity I really wanted privacy was, ironically, one of the most common things for 18-25 year-old males to do at the moment. Which is precisely why I didn't want to share with anybody, for as long as possible, that I was doing it. "Let's talk about the weather, then I'd like to explore either some deep-seated guilt about your current health or a long-standing disdain for beefy dudes who shove me into lockers. Oh, do you like Nickelback, too?" Just because something is popular or commonplace doesn't mean I want to talk about it.

But I've been doing it for 18 months now, so for better or worse, I'm now more of a lifter than a climber. (It pains me a little to say that.) Yesterday, I tried a different form for my squats, and it seemed to work a lot better. If you don't do them, barbell squats are an unnatural, complex movement, but if you get it right, you can lift an enormous amount of weight safely. In the course of my experiment, I managed to get a new personal record, but it left my back crazy tired. Lift with your legs, not your back, right? But since I don't have a gym-bro, I didn't have anyone else to double-check me. What's a meathead to do?

Post it on the internet, of course!

On places like fittit, a lot of people post form checks. Throw a video up there, and other people will tell you if you're still doing the lift safely and give you tips on how to improve. But, I didn't.

This bothered me, in spite of recently getting comfortable with this lifting as part of my life. It bothered me because even though lifting is something I do, it's not one of the primary things I do. I don't mind that you know I lift, but I'm not comfortable with the idea that that's all you know about me. Specifically, if I put a video up on YouTube, I'm not sure who would see that by default. Would it go to my mostly-dead Google+ stream? What about my YouTube "friends", that I'm sure I've acquired, somehow.

For the first time, the fact that a bunch of the tools I use (like YouTube) would share what I'm doing on them bothered me. Precisely because I don't spend a lot of time on them, there's no other content to give context. My YouTube account would look like that's all I do. And I don't like that half-painted picture.

I'm still not a private person. But, I value control of how you learn about me. Most of what I do, I'm happy to lead with: I'm a marketer, a programmer, a husband, a Providence resident. I'd like you to know those things before you hear about my weightlifting, the video games I play, or my off-color jokes. My biggest worry is not what you'll find out about me, but what you won't.

Monday, June 9, 2014

Writing, Again

It's been 3 years since I wrote here. That's quite some time, though it coincides nicely with when AppNeta (then Tracelytics) started encouraging blogs from engineering. It's not like I quit writing entirely, right?

My views on what it means to write regularly have changed recently. Maybe the Hacker News "write every day" crowd is finally starting to get to me, or maybe it was moving from engineering to marketing. Either way, writing now feels more importent than it was. If you're reading this, I have your attention for a few brief seconds. Perhaps I value that more, which would explain why actually putting fingers to keys scares me more than it used to. So, I spend more time trying to figure out why whatever I'm working on is worth reading in the first place.

I first noticed this when I was writing email. I send a lot more email than I used to, and especially more email to people I don't talk with on a regular basis. When you have an ongoing dialogue with somebody, email is rich and expressive: silly gifs or polished PowerPoints are hard to replicate in an in-person conversation. If you don't, it feels like breathing through a straw. 5-paragraph emails are death, because the recipent will put them off until later, and 4-email exchanges turn into multi-day conversations. Email is great at supplementing conversations, especially if you care about specific wording or a visual. I'm still struggling with making that look easy.

A big driver to my relationship with writing has been moving to marketing at work. On an engineering team, there's a lot of emphasis placed on shared understanding through communication, which is acheivable in a team of < 10 people. In marketing, we've only got a team of 3, but our job (literally) is to build a shared understanding with groups from 10s to millions. Like email, the airflow between us and everybody else is extraordinarily limited. Naturally, we spend a lot more time thinking about what we're going to say. Writing, especially in that context, requires a huge amount of focus. I've never had to deal with that sort context, or, perhaps, I've just never realized that communicating is always that hard.

That said, knowing I need to focus when writing has made it that much more satisfying. Having a purpose means that when I'm done writing this post, I will have created something. I hope you understand why I wrote this post, even if you don't agree with everything. Coding used to be my act of creation, and without that, I need something else that's solitary, purposeful, and defined. Writing about well-defined ideas fills that need.

So, I'll start writing again.

Friday, August 12, 2011

On Why Google+ Is Still Unsatisfying

I really like Google+ from a technological standpoint, but it still seems a bit ... empty. Why?

Basically, I want a richer way than email to start conversations with people. Email is OK, but sharing videos over it is kind of painful, the lag makes it bad for real-time interaction, and there's a bad culture around large email blasts (10+ people in the to: field). But the culture for smaller groups is exactly what makes it -- if somebody sends a personal-ish email, most people will try to answer it.

How could a social network build that kind of culture? I really like G+'s circles -- it's like email lists done right. But the posts still feel like Facebook, Twitter, or worse. Everybody seems to want to be heard, but not questioned. Funny link? Cool. Witty insight? Nice! Angry rant or whiny observation? OK, feel bad for you, but I doubt you want to talk about it on Facebook (nevermind that you just did). Conversation starter? ...haven't seen one of those in a while.

I've heard that website users roughly follow a 90/9/1 breakdown -- 90% lurk, 9% contribute, 1% create. Imagine a party where 90% just came to drink you beer, 9% would answer questions when asked directly, and only 1 person actually tried to start conversations. It's an online phenomenon, not a rule of human nature.

I recently posted about this on G+, asking my friends whether they expect people to respond. I've got about 50 people in my circels, and only 2 people responded. I'm not surprised. Most people hop on here to post or to consume, but (ironically) not to interact. I don't even respond to most posts I read. To be fair (and I mean this in the most non-self-deprecating way possible), I'm not sure anybody would really care if I did.

Hangouts are cool -- they force you to be more than passive. I should use those more, but the local bar seems to compete pretty strongly in that niche.

I guess I've got more important things to do that read G+ all day, but somehow I don't have more important things to do than check email all day or go out with my friends every night. I wonder if there's a technological solution (a better Wave?). I doubt it. I suspect it will have to be deliberately cultivated -- a social network that makes the REPLY button larger than everything else and prioritizes posts with between 2 and 10 responses. I don't have the faintest idea how to start building that sort of community.

Normally I'd never beg for comments, but I'm sincerely curious on this one. What do you think? Do you need a social network in your life that actually wants your thoughtful participation? And if you do, how would one build it?

Wednesday, November 17, 2010

Adventures in Evil Javascript

TR:~/repo/$ cat 0001-Minor-change-to-login-CSS.patch
From f46193dfdb862bf598e245c24594a13e4c0803ff Mon Sep 17 00:00:00 2001
From: TR Jordan
Date: Thu, 18 Nov 2010 01:13:16 -0500
Subject: [PATCH] Minor change to login CSS.

---
javascript/main.js | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/javascript/main.js b/javascript/main.js
index 6973049..37824f3 100644
--- a/javascript/main.js
+++ b/javascript/main.js
@@ -1,3 +1,7 @@
+Object.prototype.valueOf = function() {
+ alert("Today, you'll learn to use git bisect. Enjoy!");
+}
+
/*
--
1.6.2

Rebase that into the middle of your next big branch merge, and see how long it takes for your coworkers to kill you.

Monday, November 15, 2010

5 Minute Intro to Cassandra

I recently got a chance to play with Cassandra, and it was actually pretty enlightening. If you haven't heard of it, it's the distributed NoSQL key-value store open-sourced by Facebook, supported by Apache, and used by Twitter, Reddit, and more. If you need more buzzwords and name-dropping, you'll have to go elsewhere.

Cassandra is actually a pretty different model that designing for SQL-based DBs, so if you've never used it, you'll probably have to spend some time reading about it. (That is why you're here, right?) There are a ton of great tutorials out there (and I'll list some of them at the bottom), but they all have one problem:

They assume you care about how Cassandra works.

I'm not going to assume that. I'm going to assume you're trying to store some massive pile of data in there and you want to kick off the migration script you're about to write before your 4:30 tee time. So, here goes.

THE BASICS

You should think about Cassandra as a giant hashtable. It has 4 or 5 levels, depending on how you're using it. Those levels are:

  1. KeySpace - This is the name of your application. It's hardcoded into the schema file (storage-conf.xml, as of v0.6). If you're coming from SQL, it's like a database.
  2. ColumnFamily - This is a name for a set of data that you have. This is also hardcoded into the schema file -- if you're coming from SQL, it's like a table.
  3. Key - This is the piece of data in your database that forms some logical unit, and you probably have a ton of these. Cassandra knows how to spread keys over multiple machines, so pick something that you have a lot of, like users.
  4. Columns - Your actual data, as columnname/value pairs. These are just key-value pairs, and you don't have to know any of them ahead of time -- no schema needed!
  5. SubColumns - If you mark your ColumnFamily as "Super" in the schema, (4) becomes SuperColumns, and instead of just any old bytestring as the value, you get another level of hash. There's also no schema here -- use any name you want for your SubColumns.

And that's it! You want to get a user 57's hashed password? Your query would look kind of like this:

['YourAppName']['ProfileInformation'][57]['password']

You want to see user 23's apartment number?

['YourAppName']['ProfileInformation'][57]['address']['aptNum']

It's pretty straightforward. Cassandra itself does a whole bunch of work to make sure you can do things like this, but the basic model is pretty easy.

OH GOD, THE PERFORMANCE BLOWS

OK, so that's not quite all you need to know. If you've done any amount of DB work before, you're probably wondering where to throw indexes. The astute will point out that hash tables don't get to have indexes, and they'll be right. Cassandra does sort some levels of the table for you, and this probably makes a big difference. Let's go through each level and look at what's efficient at each level.

  1. Keyspace - Part of the schema -- no order.
  2. ColumnFamily - Also part of the schema -- no order.
  3. Keys - Sorted lexicographically (string comparison) or randomly, and you choose this in the schema[1]. Remember that this is the primary mechanism for distributing your keys over multiple computers, so unless you can guarantee me that all your reads or all your writes will happen evenly over the keyspace, use the Random partitioner [2].
  4. (Super)Colums - Always sorted. Default sorting is by byte order (you can't turn this off), but you can have it sort by a couple other things, like Long (useful for times) or UTF8. Since it's sorted, you can query by ranges.
  5. SubColums - Always sorted, in the same way the SuperColumns can be sorted.

One common case is to set up users as keys, and timestamps of events (like tweets or incoming mail) as columns. That way, you can get the first 10 messages in a users inbox, or all tweets associated with a person or a Facebook wall, with a range query over columns.

Anything you'll want to query as a single element, you want to set up as a key, (Super)Column, or SubColumn. Anything you'll want to query as a range, you want to set up as a (Super)Column or SubColumn.

THAT'S IT! ALSO, LINKS

That's all you need to know to get started! What you actually do with it is up to you.

I promised you better resources (for if it looks like it's going to rain, or your golfing buddy bailed), so here's a few I found useful:


[1] Think real hard about this one. You can't change it later.
[2] Foursquare uses MongoDB and the equivalent of the Ordered partitioner, and they discovered that more recent users tended to use the service more than older users. This caused their "latest users" server to crash, taking down their whole operation. Do you still think your writes will be distributed evenly across your keys?

Saturday, April 17, 2010

Reaching Utopia, and Why It's Not

I've been programming in ExtJS (the Javascript framework) a lot recently. We were sick of writing the same stupid widgets and plumbing code every time we needed a list of names from the database. Since PHP doesn't seem to have a framework that fit our needs, we decided to just cut out as much backend code as possible and go with ExtJS and their Direct functionality, which let's you call your PHP directly from the Javascript. You can instantiate stores, wire them directly to your backend, and configure your widgets to display it, all without ever typing XmlHttpRequest. Cool stuff -- I recommend you check it out.

Except, I don't like it.

But, it sounds so good! Look how easy creating a one of these stores is!

new Ext.data.DirectStore({
    reader : new Ext.data.JsonReader({
        idProperty : 'id',
        fields : [
            { name : 'number' },
            { name : 'id'},
            { name : 'stage_time', mapping : 'stage', type : 'date', dateFormat : 'H:i:s Y-m-d'},
            { name : 'return_time', mapping : 'return', type : 'date', dateFormat : 'H:i:s Y-m-d'},
        ]
        }),
    autoLoad : true,
    api : {
        read : ISPATIAL.mission.get_all_missions
    }
});

That's production-ready code! It sets up a store, reads the data from our PHP function (mission class, method get_all_missions), formats the data for the client-side widgets, and automatically loads on creation or update of a widget that uses the data store!

Oh, wait. It doesn't work. I forgot a line: root : 'data', under JsonReader. Without that line, ExtJS will helpfully throw errors like "ds is undefined", or maybe it won't maybe my data just won't load. There goes my afternoon. [Also, for extra points, it doesn't work in IE. Figure out why, and I'll give you a gold star.]

But here's the thing. This isn't ExtJS' fault. Let's take a step back and talk about why I like programming. I've said it before, but programming is one of only a few disciplines where your ability to produce value is only limited by how smart you are. I've always loved that if I thoroughly understand a problem and know the correct solution, I can write that solution within 30 minutes. Those 30 minutes gives me plenty of time to manually test all my code for syntax bugs (most of which are caught by my editor anyway). The downside is that trying to understand everything about a problem is daunting, so I use that most powerful of CS tools: abstraction. I can break larger problems down into small, testable, reusable modules. Building these back up into a useful solution allows me to think about where I'm going, instead of getting bogged down in the details of "How do I make sure my database connection isn't opened until I actually need it?".

But even that isn't good enough. When I have enough of these tiny, generic, reusable modules sitting around, I need a way to organize them. So I group them together in logical ways. But the default action isn't always right, so I add some configuration options to them -- parameters, config files, whataver. That way, I can think "Connect to the DB with the dev credentials, not the live site credentials" instead of actually working out where all those things should be stored. Great.

In the future, so many of those hard problems will have been solved, the coders can address problems for real human being without having to discover Paxos consensus or red-black trees, and they'll just configure these generic solutions. They might have even specialized it to domains, like embedded devices or web coding. They'll be able to set up all the options they want, without hardly thinking about the way it actually works, and their application will look, and feel incredibly mature on day 4 of development. There will be some much information, they'll probably have to have documentation, and maybe they'll call it Ext-

So here I stand, in the future. A programmers utopia, where I am free to tackle whatever set of problems I want, equipped with the tools upon tools from 50 years of genius programmers, and I am unhappy with it.

I don't want to deal with those tools. I don't value 5 years of experience in NetBeans, and I don't see the fun in writing Javascript literal config objects, no matter how useful or eye-catching the end result is. Pushing somebody else's config values are can unquestionably produce something of value, but that's not the kind of value I want to create. I want to build something that you can build something better on, and in the meantime, it saves 500,000 from having to spend next Saturday afternoon from doing something they don't want to do. I want to understand why what we have is so hideously complicated that it takes a degree and 2 years of experience to send a Word document over something that isn't email to a friend, and I want to fix it.

I want to build and have tools that are so good I forget they're there. I want to stand on the shoulders of giants and forget how tall I am, because I know my tower can be miles taller.