Microsoft, I forgive you!


Microsoft is different now. The evil masterminds are all retired. No more Ballmer jumping around screaming for developers, developers, developers. No more Gates plotting monopoly schemes for world domination. They’re both finally out, and the new guy in charge isn’t making us wince.

The company seems to be finding its groove again. Seems being the operative word. Lots of cool stuff that’s actually shipping, but whether it’ll make a dent in the world remains to be seen. Yet at least there’s some promise and optimism back. The ghosts of “cutting off the air supply” and IE6 have finally been vanquished.

Just look at the latest Microsoft Event recap. This is not Ballmer’s bouncy castle anymore. This is good. This is fun. This is promising.

Besides, the company just isn’t scary any more. Or tone-setting. Or, in many areas of past glory, even relevant. And perversely enough, that’s exactly what is setting it free to try harder, try again, and make an impression with a crowd that’s no longer dispositioned to reject the effort because Microsoft is the big bad wolf.

There’s no more wolf any more. It’s more like a puppy. Or a Golden Retriever at best. Nobody is afraid of Microsoft. So since they can’t rule the roost through fear, they’re trying the long route of — let’s not get ahead of ourselves and kid about LOVE — intrigue. And frankly some hope.

The two titans of integrated hardware and software are Apple and Google. And they’ve divided the spoils of the world just a little too neatly between them. It’s bizarre, but I actually want to see a resurgent, strong Microsoft. If for nothing else, to take the smug confidence of both titans down a peg.

I’m hoping real hard that either the Surface or the Hololens or SOMETHING out of Redmond is an honest-to-god big hit with the general public (outside of gaming). Not another cool demo or R&D plaything. But something that ships and in quantity.

I’ll be honest and say I held a grudge. For a long time. And that axe wasn’t getting buried until that hopping, jumping, dancing buffoon was out the door. But now that he is, and Gates is busy spending his loot curing the planet, do you know what? I can forgive.

Yes, I know they’ve been pining for it, so here it is: Microsoft, I forgive you. And I’m actually excited for the work you’re doing. ✌️


Would you believe that we actually have a Windows app for Basecamp 3? Yes, hell indeed did freeze over and Ballmer started flying.

New in Basecamp 3: Chatbots!

We’ve been naughty at Basecamp. Since before the launch of version 3, almost a year ago, we’ve had an internal API for chatbots. This allowed us to pipe in GitHub commits and control our operations infrastructure from Campfire. It was a quick, hackish version, but it was enough to make it work — well, work for us.

Finally it’s time to atone and bring chatbots to everyone! You get chatbot, you get a chatbot, YOU GET A CHATBOT!!! Actually, you get the invitation to build a chatbot, I should say. Because we’re just publishing the API. You still have to make, bring, or adopt your own bot. But it’s really silly-easy-peasy.

There are two types of chatbots in Basecamp 3:

Interactive chatbots

These are bots that people can talk to and get a response from. If you register a chatbot with the name of Tally, you can do !tally image donkey in any Campfire on the account, and the command URL registered for Tally will receive a JSON payload that has a key of command containing “image donkey”. Tally can then simply return a text/html 200 OK response to that POST, and the result will immediately be piped into Campfire.

If the command requires longer processing, you can grab the command_url from the payload, and send the response later (it’d be kind if you give some feedback, like “Processing command for shits and giggles” while you’re computing that donkey image, though!).

People can even start ping sessions with these bots:


Reporting chatbots

These are bots that just send information to Campfire on their own without user interaction. They’re a great fit for things like continuous integration, statistical information, nagios alerts, and so on. These reporting bots do not have a command URL. When you add a reporting bot, you’ll be able to see a special tokenized URL that you can use to post to the Campfire as that bot. Each Campfire has their own URL.

Here’s Nagios reporting some hoopla and giving options to resolve:

ChatOPS!

Of course, an interactive chatbot can also act as a reporting chatbot and send information unprovoked.

You can read about the payload format and how to add chatbots programmatically in our chatbots API documentation.

But you can also manage the chatbots through the web UI. Here’s how:

Click the Settings wheel on a Basecamp front page

This is the index for chatbots. Only administrators are allowed to add/change chatbots (but anyone can talk to an interactive chatbot).

All chatbots need a name, SEBASTIAN!! But it should be compatible with calling it as a command, so only single words.

The GitHub special!

Basecamp 3 ships with one, special built-in chatbot for GitHub. This bot makes it super easy to start piping in commits and other events from GitHub. Without creating your own code and operating your own bot.

You configure it on a per-Basecamp basis by clicking the GitHub link at the bottom of the Chatbots configuration screen, which takes you to this setup guide:


That’s it. Although I strongly encourage you to mind the chatbot noise. Don’t pipe a million reporting bots into a room that everyone is expected to pay attention to all the time. It’s just too much and it’ll make you sweat.

At Basecamp, we’ve actually set up entirely separate Basecamps for the robots most of the time. It’s very easy for chatbots to otherwise completely drown out humans in a busy room. And then skynet has pretty much won already, right? JOIN THE RESISTANCE!

It’s always your fault


The easiest thing in the world is to blame others when things go wrong. Analyzing every misstep, pointing out every flaw. There’s value to such analysis, but it’s incomplete. To round the circle, you have to do the harder work: Figure out how this is actually your fault.

This is obviously most pertinent if you’re responsible for others. “The buck stops here” has been a popular phrase for half a century because its a concept that needs reminding. It’s too easy otherwise for those at the top to lay blame upon those at the bottom. (Just look at the current Wells Fargo fraud!).

But it’s not just bosses who need a lesson in introspection. Everyone could do well to take one. If you’re part of a team or a process and something went wrong, of course it’s also your fault. You could have looked harder. You could have raised your doubts. You could have double checked.

There’s a system in place that caused this to happen, and you’re part of that system. Shit never happens in a vacuum. The vast majority of it is a predictable consequence of the way things are. Even if it was “just somebody’s fault”, others put or kept that person there.

The goal is to change the system, and to change the system, you have to change its parts. Have the courage to start with yourself. Absorb as much blame and responsibility you can for what happened, and hopefully some of that introspection will rub off on the other parts of the system. But even if it doesn’t, you’ve still done your bit to improve matters.

It’s your fault. Say it.


We’ve made many mistakes at Basecamp. Technical mistakes, people mistakes, product mistakes. I’ve always learnt the most from those when I assume I had the power to fix the system. Even if I didn’t know of it (I clearly should have!), even if I didn’t foresee it (I clearly could have!). Everything is at least partly my fault, a lot of it is probably mostly my fault, and some of it is obviously just my fault. I’d like to think that accepting all this is part reason why we’re still here.

Program to where the performance puck is going to be, not where it has been

In the year of our lord, 2013, the thought of ARM-powered phones running the full web experience with JavaScript as fast as x86-powered desktops was a laughable pipe dream. Way back in those olden days, some three years ago, the iPhone 5 was off by about a factor of 10. It seemed impossible that this would change any time soon.

But it did. The new iPhone 7 runs JavaScript, as measured by the JetStream benchmark, FASTER than the fastest of the (non-pro/air) MacBooks you can buy today. The best 5K iMac with a 4Ghz i7 processor is now less than twice as fast as the iPhone 7 on the same test. The ARM CPUs are improving at an absolutely insane pace. Moore might have taken a seat with desktop CPUs, but he’s sprinting faster than ever on mobile.


The pace of progress have embarrassed many predictions of the future, but this specific example is still astounding. Three years ago is not that long!! In that time we went from “off by an order of magnitude” to “faster than most laptops”.

What’s even more important than the benchmarks, though, is how the consequences of this incredible leap of performance alters not only what’s possible on a phone, but what the common strategy should be.

Here’s a money quote from 2013:

Just to be clear: is possible to do real-time collaboration on on a mobile device. It just isn’t possible to do it in JavaScript. The performance gap between native and web apps is comparable to the performance gap between FireFox and IE8, which is too large a gap for serious work.

That gap is gone. So I suppose that means the iPhone 7 is now officially certified for Serious Work™ 😜.

Here’s what funny. In 2013, we built an iPhone app for our collaboration tool Basecamp using JavaScript and the web in a hybrid combo. We do have a lot of fun at work, but I’d like to think that the result were Pretty Serious none the less.

Using the mobile web as the core of our native apps was a gamble at that time. The institutional scar from Facebook abandoning HTML5 for pure native in 2012 was still fresh in the memory of most working at the intersection of the web and native. And to be fair, there were indeed some tradeoffs. Things clearly weren’t as fast as native, they were just fast enough.

That was an order of magnitude in performance ago! Now the performance of this strategy is not just fast enough to be passable, it’s more than plenty fast enough to be awesome. Or rather, a non-issue.

Granted, the performance of the iPhone 7 is a future that’s not yet widely distributed. Android in particular has a lot of catching up to do, but even at much less performance, they too are clearly within the habitable zone of performance. That place where other things just matter far more.

Now I’m not saying going hybrid carries no tradeoffs. There are still some interactions that occasionally feel less than native, and thus still lack that last tiny bit of polish. And there are clearly still applications, like high-end 3D games, that need every ounce of performance they can get. But with the performance afforded us today, the spectrum of applications that can be knock-it-out-of-the-park awesome using a hybrid web/native combination is very large indeed. And much, much larger than it was in 2013 when we forged ahead.

The productivity advantages of developing a multi-platform service using the hybrid approach are simply astounding. There’s simply no way we could have built Basecamp 3 in 18 months to cover the web for desktops, web for mobile, native iOS, native Android, and email without a hybrid and majestic monolith. Not without ballooning our development teams dramatically, anyway. That’s five platforms covering some 200+ separate screens of functionality.

This feels similar in many ways to the situation I outlined in Ruby has been fast enough for 13 years. When performance improves, it’s not just that the things we already do get faster. It’s that we can do new things, in new ways. Ways that would have seemed impossibly slow before. Ways that make people who had to do the hard work of fitting a full computer demo into 4Kb cry, but that none the less lifts the productivity of the masses.

It also reminds me of an anecdote from John Carmack, the legendary programmer of Doom and Quake. He talked about how when he was working on a new game, he had to program not for the performance of today, but for the performance of three years in the future, when his game would be finished. If he programmed for today, it would look dated when it launched. Doom and Quake always looked amazing because of this.

Think of that when starting a new app today. Are you programming for the state of the world as it looked in 2013? Or 2016? Or 2018? Program to where the performance puck is going to be, not where it’s been.


If you want to checkout how a hybrid app can feel, Basecamp 3 has some great examples in the iOS app and Android app. Both are sitting pretty with a 4.5/5.0 rating from hundreds and hundreds of ratings.

New in Basecamp 3: Webhooks

We recently launched a full API for Basecamp 3. That’s great for when you want to programmatically retrieve or post content. But what if you just want your program to be told when there’s something changes? Well, that required polling at regular intervals. Bah!

With our newly launched webhooks, you can instead have Basecamp call your program when things you care about change. Let’s say you want to resend all messages in the company-wide basecamp onto a mailing list. Just register a webhook about the “message” content type, and Basecamp will call a URL of your choice with all the details about the new message.

This new feature also allows integrators like Zapier to make more efficient, faster links between other apps. So even if you don’t write your own programs, you stand to benefit.

How to setup a new webhook

You can programmatically register new webhooks through the webhooks API. That’s great when you have a full Basecamp 3 integration that is already authenticated with OAuth. Then you can just add the webhooks you need automatically.

But you can also setup webhooks via the web UI:

Step 1: Select “Set up webhooks for this Basecamp”

Step 2: Click “Add a new webhook” — this is also where you’ll see existing webhooks added

Step 3: Enter the HTTPS payload URL we should call and select the content types you want to hear about.

Step 4: When the content type you’ve asked to hear about changes in the Basecamp, you can click on the webhook from the index to see the latest deliveries. Complete with request/response logs for debugging.

Happy integrating!

Let me remind me


Truly learning a lesson is deeper than simply knowing the lesson. It’s possible to know a whole lot more than you’re capable of acting on with regularity. And it’s the act, not the mere knowing, that makes the difference.

When it comes to physical lessons, we instinctually know this. I know the steps it takes to do a kickflip ollie on a skateboard, but I still haven’t practiced nearly enough to pull it off. I wouldn’t expect to be able to do this without spending hours and hours of repetition.

But when it comes to mental lessons, it seems like most of us don’t respect the need to practice. I wrote about how this applies to learning something like programming to the level of mastery a few days ago. But it applies to life and business lessons as well.

Take the simple example of “I shouldn’t infer intention from action”. I know and believe this, but I still fail to practice it frequently.

Let’s say someone criticizes my work. I might infer that they’re just doing this to needle me needlessly because I criticized their work a few weeks back. That this really isn’t about the work, but it’s about retribution for an earlier skirmish.

That might be true. It is one of the possibilities. But it’s probably not that likely. It’s probably more likely that there’s simply a mistake in my work and the person spotted it. Unrelated to whatever discussion we had about their work weeks back.

But we humans love to jump to conclusions and validate our insecurities. That’s why we need lessons and coping mechanisms to do better. And that’s why it’s not just enough to know them, but to practice and be reminded all the time.

A strategy I’ve successfully used to remind myself in moments of need is to look for emotional smoke. If I’m getting upset about someone pointing out an error in my work, and it’s not because they’re being a dick about it, then that’s smoke. If you tune your emotional smoke detector to go off when your mood changes or your temperament flares, you give yourself a pause to contemplate which lesson you should be practicing.

The instinctual autopilot is great when the sky is blue and there’s no turbulence, but as soon as the clouds gather and the ride gets rocky, it’s time to grab the wheel with intent. So that’s what I try to do. Be mindful of emotional disturbances, hit pause when I spot them, and go through my current curriculum to find the lesson that clearly still needs attention.

The most important lessons are so because they’re hard. They’re the ones that take the most work to internalize, lest we forget in the moment and take action without their input. They are the ones we need to hear over and over again.

Pause yourself. Remind yourself. Repeat yourself.

It’s OK to be impressed


I remember when it was cool to root for Apple. Back when they were the underdog crawling back from the brink of bankruptcy and fighting to give the world an alternative to Wintel. Those days are long, long gone. It is categorically uncool to root for Apple these days.

And that’s fair. They’re not an underdog any more. They don’t need anyone to cheer their lead. In fact, the opposite is true. Now that they’re the 600 billion gorilla, they need scrutiny and competition, not blind admiration.

But that doesn’t mean you can’t be impressed. Or even in awe. And I am most certainly in awe of Apple. It’s like watching Serena Williams over the years. Her domination in the sport hasn’t always made for cliffhanging action, but it’s been a marvel none the less.

There’s just something deeply inspiring about seeing what companies, teams, and people can accomplish at the peak of their ability. Especially when it’s happening not just for a single season, but as a reign of excellence.

That’s what we’re still watching: A reign of excellence. That doesn’t make the company, its products, or its people infallible. And various moments throughout this reign might have had more sparkles of innovation and disruption than what we’re seeing now, but there’s no denying what a juggernaut of impact it still is.

Also doesn’t guarantee it will continue tomorrow and certainly that it won’t forever. Nobody gets to reign forever (thank god!). But we shouldn’t be so proud not to recognize and applaud when it’s happening.

So Apple, I applaud you. We’re on the cusp of a decade since the first release of the iPhone. It’s astounding just how well you’ve been able to improve, iterate, and stay the champ for so long.


Perhaps no company has provided more direct inspiration for Basecamp than Apple. For a while it was so overwhelming that we banned all comparisons, just out of how common and trite they had become. But I’m getting more at ease with just surrendering to that towering shadow and be fine with it.

Learning and mastering isn’t the same


When I first began making applications for the web, getting started was hard. Just figuring out which components to download, how to configure them, and getting Hello World through it all was a daunting affair. Frameworks like Ruby on Rails changed that, and now it really is possible to animate a complete database-backed web application in 15 minutes or less.

That means these days you can go from (almost) zero prerequisites to a (sorta) working software prototype in a bootcamp’s worth of introduction material. That’s amazing. Basic proficiency has never been more attainable or approachable.

Given this leap, it’s no wonder that people mistake the beginning for the end. That getting started is the same thing as knowing it all. But it remains a completely unrealistic expectation, and thus a mistake. Building a complete information system, like, say, your Basecamp or Shopify or GitHub or Zendesk remains real work that requires deep skill. It’s foolish to pretend otherwise.

And perhaps we have pretended otherwise at times. I’m certainly guilty of focusing on just how easy we made getting started that the conversation often didn’t extend to the work it takes to finish. Simply because that’s what felt like the main obstacle to getting more people onto the path of learning at the time.

Now that this obstacle is mostly cleared away, it’s time to focus on the latter part of the discussion: Becoming really good at anything takes time. Web development is no different, not even with Rails. We’ve changed the game from “hard to learn, hard to master” to “easy to learn, hard to master”. Yes, the second part remains the same.

I’m still learning. I’m still getting better. And I’ve been at this web development game for damn close to twenty years (if you count when I started dabbling with HTML/CSS). That doesn’t at all mean it’ll take you twenty years to become good at it, but it probably does mean that you should have realistic expectations about what you can learn in three or six months.

And it’s not so much about how long it’ll take you to learn your way around the framework or the language or the ecosystem. It’s as much as how long it’ll take you to become an expert. It’s one thing knowing there are 10 different ways to do a thing; it’s another to know which is a better fit and when.

This is part of why I like to compare writing software with writing prose. Most people in the developed world will have basic proficiency writing their native tongue by the time they finish high school. But how long does it take to become a great writer? Longer than that. Much longer, in most cases. Few find that surprising.

Yet plenty of people do seem to be genuinely surprised that a developer who just picked up Ruby on Rails, or any other extensive framework, language, or ecosystem, doesn’t make all the right choices the first time they’re faced with them. And that’s just plain silly.

Let’s continue to celebrate how easy we’ve made getting started, but let’s also set a realistic timeline for mastery. Not to scare anyone off the journey, but to prepare them for it. A glorious, years-long journey of learning. It’s a lot of fun if you know what you’re in for.

In defense of junk stats (and junk food)

A reasonable amount of indulgence makes reasonable sense

I think Noah Lorang is exactly right on the data-nutritional value of real-time dashboards. It’s all empty calories. Like a bag of M&Ms or a serving of McD french fries. Salt, fat, and sugar. If that’s the main diet of information you’re using to grow your business, you’re not going to end up in a healthy place.

But there’s another way to use real-time dashboards. That is to realize that, yes, they are indeed empty calories, and, no, they shouldn’t supplant a properly prepared, slow-data analysis of what’s really going on, BUT. BUT.

SOMETIMES YOU JUST WANT A DAMN CHEESEBURGER!

Sometimes, intentionally being unhealthy feels so good it’s worth it. Especially if you’re just in need of a little spike because the going is tough or you’re trying something new that’s hard. That doesn’t for a moment mean you’re giving up on the virtues of #SlowData, merely that you accept your the existence as flawed and succumb to the occasional vice.

My wife occasionally gives me grief about drinking soda. And I know it’s not good for me, but holy fuck a three-degree-Celsius-chilled, cane-sugar Coke served in a glass bottle is just damn delicious. The way I justify this indulgence is with hyperbole: Everyone needs a vice. It’s either this or hookers and blow!

I kinda think the same of real-time dashboards. They’re a vice, but vices are fun. They don’t give me deep insights, but cheap highs to keep going. They’re quick and early, and sometimes wrong. But if we’re trying something new to attempt to move the needle, I’ll accept the risk of the early indicators being wrong before they’re statistically significant in trade for the snappy feedback.

It’s possible to like Mozart’s Symphony №4 and Justin Bieber at the same time. It’s possible to accept both your virtues and vices. Just as long as you know which is which and keep the ratio between them right.


I’d like to think that we drive most consequential decisions at Basecamp through reasoned logic, statistically signicant empirical findings, but I’ve also come to accept that some times we Just Wing It. Some times we just go on gut or on belief. You be the judge of whether we got that balance right, if you give Basecamp 3 a try.

Hunting for great names in programming

One of the real delights of programming is picking great variable, method, and class names. But an even greater treat is when you can name pairs, or even whole narratives, that fit just right. And the very best of those is when you’re forced to trade off multiple forces pulling in different directions. This is the story of one such case.

It started with a simple refactoring. We allow people to post arbitrary URLs in Basecamp 3’s chat, which we’ll attempt to fetch and display inline, if its an image, movie, or a Twitter URL. There’s a security concern where we don’t want to allow internal IPs in those URLs, like 127.0.0.1, and then have our Downloader class attempt to trigger an internal request that may sidestep other security precautions.

The specific policy isn’t as important as the fact that this precondition was originally just part of the Downloader class, but now I also needed it in our forthcoming webhooks API. Because just like with previewable chat URLs, webhooks allow users to set URLs that our system then calls. Same underlying security issue to deal with.

No problemo: Just extract the protection into its own module and call it from both spots. First attempt at naming this new module gave me PrivateNetworkProtector, which seemed like a fine choice until I considered the method names that’d work with it:

  • PrivateNetworkProtector.protect_against_internal_ip_address(ip)
  • PrivateNetworkProtector.verify_ip_address_isnt_private(ip)

Hmm. I didn’t like any of those choices. Both are a little too wordy and both include a negative. And come to think of it, I wasn’t even that thrilled with the word Protector. It implies something like surge protection, where it just negates the effects of an outlier input. That’s not really what’s going on here. We’re on the lookout for malicious attempts, so a better word would be more forceful. More focused on a threat, not just a risk.

Let’s see what the programmer’s best friend, the thesaurus, had to offer:


Lots of possibilities there, but the one that really spoke to me was Guard as in PrivateNetworkGuard. Nice. Now I can imagine some big burly fellow checking credentials with an attitude. Just the right image. So back to the right method name. Let’s try the two choices similar to what we had earlier:

  • PrivateNetworkGuard.guard_against_internal_ip_address(url)
  • PrivateNetworkGuard.verify_ip_address_isnt_private(url)

Hmm, neither of those are right either. I mean, you could use guard again, but the double confetti of this repetition just annoyed my sensibilities. What if we thought of the Guard as something like a prefilter, like before_action in Action Controller? That’s promising:

  • PrivateNetworkGuard.ensure_public_ip_address(url)
  • PrivateNetworkGuard.ensure_no_private_ip_address(url)

Still not quite right. The ensure verb just doesn’t quite fit with the forceful idea of a Guard. It’s too meek. This isn’t a casual checking of credentials. This is IF THIS HAPPENS, I’M RAISING AN EXCEPTION AND BUSTING THE REQUEST!

That lead me to think of the programming language Eiffel and the concept of design by contract. So I browsed the Wikipedia entry to see if I could mine it for a good word. Eiffel uses require to state preconditions, which is what we’re doing here, but that also didn’t see right: PrivateNetworkGuard.require_public_ip. That’s more like something you’d write in a specification to tell well-meaning actors what they’re supposed to do. This wasn’t about well-meaning type, but rather the nefarious kind.

So I tried the thesaurus again, going off ensure for alternatives:


No great candidates here. I mean, they’d all work, but they don’t feel quite right. And that’s really what this whole expedition is about. Not just finding something that could work, but something where you go: Yes! That’s perfect! How about something in the realm of Guard then?


Nope. That doesn’t do it either. But one word stood out as an idea: Police. The Guard is policing the incoming ip addresses to make sure that an internal one doesn’t slip through. That’s interesting. Let’s keep pulling on that thread:


ENFORCE! That’s it. Let’s try it out:

  • PrivateNetworkGuard#enforce_public_ip(url)
  • PrivateNetworkGuard#enforce_no_private_ip(url)

In some ways I like the negative version better, since it strikes to the heart of the responsibility: This is about catching those malicious URLs with private IPs more than its about vetting that something is public. Two sides of the same coin, perhaps, but still an important distinction.

In the end, though, I went with PrivateNetworkGuard#enforce_public_ip(url) because I liked keeping a positive method name more than the slightly more apt negative version. Weighing those two subtle trade offs and picking the concern that mattered more.

Now this might seem like a lot of effort to expend searching for a slightly better name, but it goes straight to the heart of programming with a smile. I ventured out to find a great name, not just a passable one. And besides, this whole exercise might have taken five minutes at the most. So not exactly blowing the budget, but definitely lifting my spirit. Isn’t programming great?


Basecamp 3 was laboured over with tons of this kind of excessive/obsessive care. You’ll never be able to tell, but you can trust that we put this sort of consideration into the product in all sorts of other ways that are visible. It’s fun to bother with quality that can’t be seen.