Lighthouse (Taken with Instagram at Santa Cruz Harbor)
As they say in that commercial (you know the one I’m talking about): “life comes at you fast.” Less than 8 weeks ago, I had no idea what Pinterest was, let alone imagined that I’d be packing up the family and moving to California before the end of July. If you ask my friends, I’m sure they’ll tell you this was no surprise; moving to the bay area is something I’ve been talking about for years. But still, I can’t help but feel a little shocked that I’m writing this post from Palo Alto instead of Provo today.
If you’re a startup guy like me, you probably have a hard time imagining yourself at a larger more established company (even if that company is as cool as Facebook or Google). The term ‘startup’ gets thrown around a lot these days and young companies come in all shapes and sizes. In my case, I was looking for a startup that met a few specific requirements.
Early but not without funding
I’m prepared to take a risk, but being a parent means I’m not the only one taking the risk anymore. This may not apply to everyone, but for those with families, it’s a big deal. It reminds me of something Paul Graham wrote:
“Another way to decrease the risk is to join an existing startup instead of starting your own. Being one of the first employees of a startup is a lot like being a founder, in both the good ways and the bad.”
The earlier you join a startup, the more impact you’ll be able to have. I’ve only been at Pinterest for 3 weeks and I’ve already experienced this personally. One of my first tasks was to add a feature that makes it possible for users pin videos. We launched the feature last Monday and nearly 20,000 videos have been pinned so far. I’m quite certain that nothing I’ve ever done in my life has touched so many people in such a short amount of time.
Great team with a strong sense of direction
I can’t overstate how important this was to me. There were several good signs that helped me feel good about joining Pinterest. One that stands out to me was how careful the founders have been about publicity. In an era where so many startups seem to be overly focused on the fundraising game, it’s refreshing to meet a group of founders who haven’t forgotten their users. Pinterest is under-hyped for a reason (for now anyway).
Great product with adoring customers
I knew Pinterest was something special the moment I first saw it. Now that I’m on the team I realize that even more. We get fan mail pretty much every day. And not just simple ‘thank you’ notes, either; these are long gushing love letters. We have the best users ever.
Exciting technical challenges
This one caught me off guard. I knew that joining an early stage startup would present new technical challenges. What I didn’t realize was just how much I would learn and how quickly I would be thrust into the thick of it. I feel like I’m drinking from the firehose, but there’s honestly nothing I’d rather be doing at this point in my career. Pinterest is growing so quickly right now that it’s giving me opportunities to work on things I’d only ever dreamed of before.
As you can probably tell, I’m pretty excited about the opportunity to help turn Pinterest into something millions of people use and appreciate. I’ll do my best to post more as the site grows.
Oh, and if you’re looking for a great opportunity to work with amazing people on an exciting problem for the best users in the world, apply here. :-)
My second ‘visible’ contribution since joining Pinterest is now live (the first was the new “Pin It” button for websites)! Almost 15,000 videos have been pinned since the feature was launched on Monday. More details on the Pinterest blog:
Today, we’re excited to introduce a brand new feature at Pinterest: Video Pins! If you’ve ever wanted to pin a DIY tutorial, a cooking lesson, a music video, or just a Beluga whale enjoying a Mariachi Band, now you can.
Just push the very same Pin It button you have installed in your bookmarks bar whenever you see a YouTube video. We are hard at work adding support for other video sites.
Enjoy!
— http://twitter.com/#!/venturehacks/status/98164863951585280 http://venturehacks.com/articles/scale
I was a little surprised to discover that GitHub doesn’t allow you to fork your own projects as simply as you can fork someone else’s (if you don’t believe me, check out this post). Fortunately it’s pretty simple as long as you can come up with a different name for your new project.
Let’s assume we have an existing project called ‘foo’ and we want to create a fork called ‘bar’.
First, create a new project on GitHub called ‘bar’.
Next, clone foo using the name ‘bar’:
$ git clone git@github.com:YOURNAME/foo.git bar
Initialized empty Git repository in /projects/bar/.git/
remote: Counting objects: 3618, done.
remote: Compressing objects: 100% (1503/1503), done.
remote: Total 3618 (delta 2629), reused 2922 (delta 2075)
Receiving objects: 100% (3618/3618), 7.31 MiB | 1.23 MiB/s, done.
Resolving deltas: 100% (2629/2629), done.
Next, edit your Git config file and replace the origin URL with your new URL:
$ cd bar
$ vim .git/config
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = git@github.com:YOURNAME/bar.git #replace foo with bar
Optionally add your original repo as an upstream source:
$ git remote add upstream git@github.com:YOURNAME/foo.git
Finally, push your new repository up to GitHub:
$ git push -u origin master
Counting objects: 3618, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (949/949), done.
Writing objects: 100% (3618/3618), 7.31 MiB | 646 KiB/s, done.
Total 3618 (delta 2629), reused 3618 (delta 2629)
To git@github.com:YOURNAME/bar.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
Now you can push/pull from your new repo (bar) as expected. You should also be able to merge upstream changes using the following command (although I haven’t tried this yet):
$ git fetch upstream
$ git merge upstream/master
And that’s really all there is to it. :-)
Launching Banterly last week was a thrill. It was unbelievably fun meeting so many new people from all over the world through my own personal creation. Everyone was so friendly and supportive and I honestly loved every minute of it (even the parts where I was frantically fixing bugs). It was an experience I won’t soon forget and hope to repeat.
Not long after I launched, the guys at Embedly started a discussion suggesting that I check out their oEmbed service. The moment I saw it, I knew it was exactly what I had been looking for. The only annoying part was that I hadn’t discovered it several weeks ago when I first started working on embedded media in Banterly. So, for that reason, I decided to do a little demonstration and offer a little praise in hopes that others looking for a solution to their embedding woes might not waste time trying to roll their own (like I did).
Don’t DIY
Let’s start with what NOT to do. Don’t try to do media embedding yourself. For starters, there are simply too many different sites out there, each with slightly different methods of exposing their data (Embedly supports more than 160 providers for free or 185 for a reasonable subscription fee). Doing it myself, it took two days to get support for images, YouTube, and the most simple OpenGraph meta tags (which was extremely flimsy since no complete HTML parsers exist for Node yet).
Additionally, doing it yourself means downloading unknown content from unknown sources at the whim of your users—not exactly the kind of thing you want to expose yourself to when all you want is to embed content on your site. In fact, I almost cut the feature before launch because I was afraid malicious users might discover it and use it to abuse the system, but then I thought—‘why not?’ and left it in anyway.
embedly-node
You can use Embedly to embed media from both server side and client side (via jQuery). I chose to use the server side module (embedly-node) in order to reduce the number of requests I have to make to the Embedly API and to keep page load times fast for users. Using the embedly-node module on GitHub (use npm install embedly), I ended up with something similar to the following:
var Embedly = require("embedly");
var embedly = new Embedly.api({
//key: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // Embedly Pro Key
user_agent: 'Mozilla/5.0 (compatible; myapp/1.0; me@example.com)'
});
// Do initial comment text processing
// 1. Parse URLs from comment text
// 2. Render HTML comment by replacing parsed URLs with anchor tags
// 3. Save rendered comment HTML to the database
// 4. Send rendered comment HTML to connected clients (via WebSocket)
...
// 5. Try to get additional content from Embedly
if (urls) {
// Using Embedly oEmbed API
embedly.oembed({
params: {
urls: urls // Can send single URL also.
},
complete: function(e, objs) {
for (var i in objs) {
// Returned if the URL content is not supported on free tier
if (objs[i].type == "error") continue;
// Some embed types don't contain a URL
objs[i].url = objs[i].url || urls[i];
// Render and replace the previously rendered link
html = html.replace(getLink(urls[i]), render(objs[i]));
}
// Only save and send to clients if the HTML has changed
if (comment.html != html) {
// Update the rendered HTML in the database
comment.html = html;
comment.save(function() {
// Send updated comment HTML to connected clients (via WebSocket)
...
});
}
});
}
Sending the initial HTML to connected clients immediately (rather than waiting for Embedly) helps keep the UI fast and responsive while users are chatting. Once Embedly returns, new HTML is sent out to clients to replace the initial comment markup. In my tests, I found Embedly to be quite fast at returning embed data, but its speed will always be limited by how fast the URL host can return its own content, so it’s much more reasonable to keep the updates separate.
A simplified version of my render function looks like this (would be admittedly much cooler with a template):
function render(obj) {
if (obj.type == "photo") {
// Just an image (only works with pro account)
// image dimensions are available as obj.height and obj.width
var html = "<img src='"+obj.url+"' width='500'/>";
return html;
} else if (obj.type == "video") {
var html = "";
// Add thumbnail
if (obj.thumbnail_url)
html += "<a href='"+obj.url+"' target='_blank'><img src='"+obj.thumbnail_url+"' width='100'/></a>";
// Add title link
if (obj.title)
html += "<a href='"+obj.url+"' target='_blank'><h6>"+obj.title+"</h6></a>";
// Add an explicit link
html += "<a href='"+obj.url+"' target='_blank'>"+obj.url+"</a>";
// Add description
if (obj.description)
html += "<p>"+obj.description+"</p>";
// Wrap it up
return "<div class='oembed'>"+html+"</div>";
}
// Just a normal link
return "<a href='"+obj.url+"' target='_blank'>"+obj.url+"</a>";
}
In addition to oEmbed, there are also ‘preview’ and ‘objectify’ which I have yet to explore (maybe I’ll follow up if I do) but are only available for Pro Embedly users.
Thanks guys!
I also think it’s worth mentioning how helpful the folks at Embedly were during the process (especially Bob Corsaro aka @doki_pen). Throughout the experience, they were extremely available (chatting on Banterly and exchanging emails late into the night). They even fixed a bug I found within minutes of reporting it!