Programming
It used to be one of the biggest pains of web development. Juggling different browser versions and wasting endless hours coming up with workarounds and hacks. Thankfully, those troubles are now largely optional for many developers of the web.
Chrome ushered in a new era of the always updating browser and it’s been a monumental success. For Basecamp, just over 40% of our users are on Chrome and 97% of them are spread across three very recent versions: 16.0.912.75, 16.0.912.63, and 16.0.912.77. I doubt that many Chrome users even know what version they’re on — they just know that they’re always up to date.
Firefox has followed Chrome into the auto-updating world and only a small slice of users are still sitting on old versions. For Basecamp, a third of our users are on Firefox: 55% on version 9, 25% on version 8. The key trouble area is the 5% still sitting on version 3.6. But if you take 5% of a third, just over 1% of our users are on Firefox 3.6.
Safari is the third biggest browser for Basecamp with a 13% slice and nearly all of them are on some version of 534.x or 533.x. So that’s a pretty easy baseline as well.
Finally we have Internet Explorer: The favorite punching bag of web developers everywhere and for a very good reason. IE represents just 11% of our users on Basecamp, but the split across versions is large and depressing. 9% of our IE users are running IE7 — a browser that’s more than five years old! 54% are running IE8, which is about three years old. But at least 36% are running a modern browser in IE9.
7% of Basecamp users on undesirables
In summary, we have ~1% of users on an undesirable version of Firefox and about 6% on an undesirable version of IE. So that’s a total of 7% of current Basecamp users on undesirable browser versions that take considerable additional effort to support (effort that then does not go into feature development or other productive areas).
So we’ve decided to raise the browser bar for Basecamp Next and focus only on supporting Chrome 7+, Firefox 4+, Safari 4+, and, most crucially, Internet Explorer 9+. Meaning that the 7% of current Basecamp users who are still on a really old browser will have to upgrade in order to use Basecamp Next.
This is similar to what we did in 2005, when we phased out support for IE5 while it still had a 7% slice of our users. Or as in 2008, when we killed support for IE6 while that browser was enjoing closer to 8% of our users.
We know it’s not always easy to upgrade your browser (or force an upgrade on a client), but we believe it’s necessary to offer the best Basecamp we can possibly make. In addition, we’re not going to move the requirements on Basecamp Classic, so that’ll continue to work for people who are unable to use a modern browser.
Basecamp Next, however, will greet users of old browsers with this:

When I did all the programming for the original version of Basecamp back in 2003, we ended up shipping with just about 2,000 lines of code. A lot has happened in those eight years and we’ve acquired a more delicate taste of just how beautiful we want the basics executed.
This means significantly more code. Here are the stats from running “rake stats” on the Rails project:

On top of that we have just over 5,000 lines of CoffeeScript — almost as much as Ruby! This CoffeeScript compiles to about twice the lines of JavaScript.
Basecamp Next is running Rails 3.2-stable and we’ve got a good splash of client-side MVC in the few areas where that makes sense through Backbone.js and various tailor-made setups.
Got any questions about our stack or code base? Post them in the comments and we’ll fill you in.
Three years ago, I wrote about how improvements in technology keep allowing us to punt on sharding the Basecamp database. This is still true, only more so now.
We’ve grown enormously over the last three years but RAM keeps getting cheaper and FusionIO SSD’s keep getting faster. If anything, it seems like recent advances in SSD technology are accelerating and it’s ever more unlikely that we’ll need to shard Basecamp.
Basecamp remains a perfect candidate for sharding. Isolated accounts, no sharing between them. Yet the cost in increased complexity is constant while the cost of throwing hardware at the problem keeps dropping.
It’s like how some old people have a hard time dealing with inflation and “they want how much for a gallon of milk these days?”. Technologists who grew up when RAM cost $1,000 per megabyte can have a hard time dealing with the luxury of RAM being virtually free (we just bought about a terabyte worth of RAM for a Basecamp Next caching system that cost just around $12,000).
The progress of technology is throwing an ever greater number of optimizations into the “premature evil” bucket never to be seen again.
I saw some questions on David’s last posts about remote work and hiring asking how we evaluate potential employees. Racing metaphors aside, tossing new developers on real projects is a time honored tradition here, and one that we haven’t written yet about “what” exactly happens along with the “why”. Here’s how my trial month went, and what I learned.

The first week
I’ve never been to Chicago, outside of O’Hare airport. I have terrifying memories of that airport, so I was cautious to visit. I once left an a320 Pocket Retro Emulator in a seat pocket while deboarding, and didn’t realize it until soon before my connection was to leave. I sprinted back through 2 terminals to find the plane had already left for Nashville. I hope someone else is enjoying a slightly buggy version of Tetris Attack. My first week at 37signals went much better than this experience.
The first week in the office was great. Getting up to speed was a breeze: I fired Ruby and Rails up on a fresh box with rbenv and ruby-build. After cloning down a few repositories, and the usual run-around of “what passwords/sites do I need access to”, I was tossed into the fray of our support queue.
I was introduced to Assistly, which we use for customer support. I have to say, our support team is awesome to work with. We typically have 3 developers rotating in on-call, usually on a weekly or so basis. Here’s basically what on-call for a 37signals developer looks like:
- Support is stuck with an issue, and our set of internal tools isn’t helping.
- An issue is called out for developers to look at.
- Cue hacking montage as we dive into that specific product and various gems’ codebase to find the problem. This usually involves a trip into our server cluster to look for logs, find emails that didn’t process or hook up to a Rails console and hit the database.
- If it’s a bug, try to squash it. At least make sure we know about it in GitHub Issues.
- Ship it! Bug fixes and more are logged through Champagne, our internal change tracking tool. Champagne is in charge of publishing our Changes page.
Sometimes there’s not always an easy fix, or it’s a deeper issue. Typically we’ll discuss these over in Campfire to begin and see if anyone’s seen it before. This usually escalates into kicking off or contribute to ongoing discussion in Basecamp about how to solve the root problem.
Back to remote
On my first week back in Buffalo, I was tasked with creating a fresh application we’ve needed for a while. The workflow for discussing changes and features was pretty much what I expected: Here’s a list of things to accomplish, let’s see what we can get done in a week. The timeboxed aspect of the project was definitely new to me, and I think it was a great factor in focusing on what needed to get done instead of what could be done. I’m very new to a product-focused company, and this was a great way to be dropped into the environment where it’s really the customer and user experience that comes first and foremost.
After that second week remote, I went back to on-call. I really enjoy the balance of the on-call work. It’s usually split 50/50 between helping customers with issues blocking their work, and improving our infrastructure and apps. There’s lots of dirt to sweep up, including deep bugs that need investigating and apps/gems that need a fresh coat of Ruby or Rails.

Top score!
One thing I learned really quickly was that our users are creative: If you give them the ability to do anything, they will do everything! I found this out the hard way when we got a support issue for an extremely broken Basecamp page. After some investigation, I found out that Basecamp todos allow HTML tags, and they forgot to close one. After a fruitless hunt for why this behavior existed, I shipped a fix to strip out HTML tags and clean up ones that might cause the page to break.
The flood of support issues started almost immediately. Apparently a lot of users customize their todos with colors, images, and more. Suddenly, they couldn’t, and it was my fault. This might be a new personal top score for breaking things at a new job. I reverted the fix and deployed once again, and we apologized to those users.
Keep kickin’
I’m still getting used to remote work, but I’m enjoying the greater freedom and flexibility so far. The discipline of working near so many distractions is something I’ll be working on for a while.
This is my second official month being a signal, and I’m definitely still learning about our infrastructure and the internals of our products. I have to say though, it’s been a lot of fun and I’m excited for what’s next. If you have any other questions about the trial month experience let me know!
I remember the first time I interviewed for a front-end programming position and got asked how to do something in JavaScript on a white board. The specifics are vague, but it’s crystal clear how stupid it made me feel and how little it had to do with the actual job.
Since then I’ve rarely heard a kind word about the parlor tricks of programmer hiring, but I’ve heard plenty of scorn. There’s certainly a minority of puzzle solvers who love to get their fancy tickled like this, but I sure wasn’t one of them and neither have most programmers I’ve met.
I’ve known fabulous programmers flame out in the quizzing cage and terrible ones excel. So unless you’re specifically hiring someone to design you the next sorting algorithm, making them do so on the white board is a poor gauge of future success.
The only reliable gauge I’ve found for future programmer success is looking at real code they’ve written, talking through bigger picture issues, and, if all that is swell, trying them out for size.
(If you need help posting a comment, feel free to use any of these samples: “You make todo lists, you don’t need real software engineers”, “Math is actually really important, you know!”, “Google is worth one gajillion dollars and they use quizzes, so there!”)
If I hear one more Silicon Valley type gush over a computer science graduate from CMU, MIT, or Stanford, I’m going to puke. Yes, yes, I’m sure these are mighty dandy nice schools, but you’re letting the stench of superiority and shallow whiff of superficial judgement pollute my airways.
The fantastic thing about programmers is that we don’t have to give a fuck about where they were trained because we have something much better available: We can look at what they actually do! We don’t need the indirection of pedigree to guess at their skills, we can look at their code and know it.
Here’s a list of the top tier schools that helped shape the fine band of programmers we employ at 37signals:
- Lawrence University
- Rochester Institute of Technology
- Brock University
- Washtenaw Community College
- California Institute of Technology
- Copenhagen Business School
- Brigham Young University
Remember, a real engineer doesn’t want just a religion about how to solve the problem. Like ‘object-oriented’, or ‘functional’, or ‘imperative’, or ‘logic programming’. This piece of the problem wants to be a functional program. This piece of the program wants to be imperative. This piece wants to be object-oriented and guess what—this piece want to be logic-based. And they all want to work together usefully, because of the way the problem is structured.
“Can you ask Sam about that? Stacker is his domain”, “I’d rather let Josh look at the router, he wrote it”, “Jon is better versed in associations, send it to him”.
The natural progression of programs is towards the territorial. When a programmer has weaved an intricate web of considerable complexity, others are loathe to enter his lair and he is loathe for them to do so.
This is despite the fact that we all agree that it’s bad for programs to become territorial. When only one or a few people know how to work on something, you get bottlenecks where progress is stunted until the master is ready. You risk the hit-by-a-bus factor where nobody knows how the system works if the master leaves. You ensure the annoyance of stakeholders who can’t understand why another minion can’t fix his urgent problem.
But this problem can’t be solved with a slogan. You can proclaim that “we shouldn’t have territorial parts of our program” until you turn blue, but nothing is going to change until you accept the cost of avoidance.
The first step of acceptance is to recognize that sending someone fresh in to fix a single issue in a complex part of the code is expensive. It’s going to take Pratik five to ten times the effort to fix a single issue in Stacker that it’s going to take Sam. And the odds are that even that is not enough to appreciate the internal coherency of the system, which means that the fix is likely to be a butcher’s job, and Sam will have to rewrite it afterwards anyway.
To broaden the base of knowledge, you’re going to have to let someone else not only spend considerable effort getting up to speed. Then you’re going to have them deal with more than just a quick fix. Let them deal with a raft of issues and let them spend the time of the original creator to learn it all.
To do all that, they can’t do anything else at the same time. That feature you want do is now going to be pushed a few days or a a week out. Until you’re ready to delay things you really want done, it’s fruitless to bemoan that parts of the code base territorial.
I recently received an email from someone who was getting into programming, and was asking for advice on how to proceed. He had a project in mind, and had started on it, but had run into areas where his current knowledge was insufficient to puzzle out the solution.
First of all, I was very impressed that he had actually started work. Ideas are a dime-a-dozen, and one of my least favorite things are “idea people” who feel like their work is done when they come up with an idea, and all that’s left is to find a programmer who is willing to fill in the blanks. That this person came to me after first trying to solve it himself was a huge mark in his favor.
Sadly, I wasn’t able to help him take his project further, but it gave me a chance to think back on the times that I’ve been a beginner (whether it was web programming, or iOS programming, or even something unrelated to software entirely), and to contemplate how I approached those beginnings.
I identified four things that I’ve found were fundamental to my particular learning style. Obviously, there are as many learning styles as their are learners, but these are what work for me.
Continued…
It used to be a jarring experience to setup a new machine for development, but progress has paved the dirt road into a silky smooth autobahn. These are the tools we use today:
- Homebrew: Remember how painful it used to be to get imagemagick installed? Now it takes about a minute. “brew install imagemagick”. Same story for git and other Linux dependencies.
- rbenv/ruby-build: We have some apps running on Ruby 1.8.7, some on 1.9.2, and some on 1.9.3. ruby-build makes it easy to compile all three, rbenv makes it easy to switch between them on a per-project basis. We run rbenv in production as well, so all you need to do to change the Ruby version there is alter .rbenv-version—development and production is always on the same page.
- Bundler: Not everyone at 37signals loved Bundler at first, but now that it’s stable, they’ve been won over. I now curse whenever I have to use an old application that hasn’t been setup with Bundler. Manually tracing down dependencies?! How prehistoric!
rake setup: All our apps has a rake setup task that’ll run bundler, create the databases, import seeds, and install any auxiliary software (little these days) or do any other setup. So when you git clone a new app, you know that “rake setup” will take care of you.
- Pow: No more messing with Apache or nginx for local development. All it takes for Pow to add another app is a symlink. All the apps are always configured and available at basecamp.dev, highrise.dev, etc without messing with the hosts file either.
Thanks to Max Howell for Homebrew, Sam Stephenson for rbenv/ruby-build and Pow, and Carl Lerche/Yehuda Katz for Bundler. Thanks to them, starting from scratch has never been easier.
Have you noticed that software feels cheap when UI elements move around on the screen without notice? Web applications are particularly vulnerable to this problem. Browsers give image elements a default size if they do not have explicit width and height attributes. Once these images have loaded, they expand or contract to their full size, causing all other elements on the page to reflow in response.

Unsized images reflow the page when they load
We try to avoid this in our applications, but it’s easy for an image tag to slip through the cracks. That single tag might be repeated many times in a loop, each instance causing the on-screen furniture to shift around in an unseemly way.
Here’s a tip for catching unsized images during development. Add this CSS rule somewhere in your stylesheet:
img:not([width]):not([height]) {
border: 2px solid red !important;
}
Then any images without width and height attributes will be drawn with a red border so they’re easy to spot.

The Changelog posted a podcast interview with our very own Sam Stephenson. He talks about CoffeeScript, the Rails 3.1 asset pipeline, open source projects Pow and Sprockets, and the development of Basecamp Mobile.
The key difference to me is that when I look at a piece of Javascript code, I see parentheses and braces and semicolons and line noise. And when I look at CoffeeScript, I can see the code that I’ve written.
When I’m writing CoffeeScript I’m still thinking in Javascript—I just have to type less.
One of my favorite things is the ending: the interviewers ask “Who do you look up to?” That’s a great question to ask anyone who is doing good work.
For programmers, it’s never been easier [to find a job]. The world of open source software is such an easy way to get into showing off your work — and it makes you feel good in the process.
One of the big advantages if you go to Harvard or Stanford and you want to get into investment banking is that you’ll meet a lot of people that will make it easier for you to get into investment banking. If you want to be a great software developer, you can do all of that without paying $60,000 a year in tuition, just by putting in some sweat equity in improving the comments in the open source world.
I’m constantly being asked, “Do you know any Rails programmers?” If you’re a programmer today and you’re not employed, you’re not looking or you’re not doing the right things: (A) get involved in open source development, (B) learn a development environment that’s hot right now.
February 7, 2006 – When you build an app always look out for the non-essential features. Make sure they don’t make it into your v1.0. They slow down your release, they dilute your focus, they require resources that pull you away from perfecting the core of your app, and they open the door to more bugs at launch.
Like most programmers starting a new gig, I spent my first day at 37signals setting up my work environment. I thought it might be interesting to keep track of what I installed along the way…
SizeUp for managing windows

Alfred for launching applications

Continued…
Here’s what happened in Campfire when I found out Noah, our stats guy, uses a calculator that came out in 1981:

Related: Reverse Polish notation [Wikipedia]
A week ago, it took about 15 minutes to run all of Basecamp’s tests.
Now, if you adhere to the test-driven development (TDD) philosophy, you’ll know that tests are meant to be run often. Like, every few minutes, preferably. The tests are what give you confidence in your code, and the ability to refactor with impunity, not fearing that your changes are breaking existing features. They are the safety net for our daily tight-rope walk across our code base.
But if your test suite takes a quarter of an hour to run, that puts a severe damper on your willingness to run it often, or even at all. Those tests definitely don’t get run every few minutes. They might get run before you commit a change. More likely, they get run around the end of a development cycle, when the new work is preparing for deployment. Although they can still be marginally useful when run like that, they are a far cry from what they might be.
At the beginning of this latest development cycle, I begged leave to spend a few days digging into our test suite to see what could be done. Just about all of the programmers here have had a turn looking into the tests, but with other responsibilities it was hard to find enough time to do more than prove the hypothesis-du-jour wrong. I hoped that with some dedicated time I might be able to make some significant improvements.
Continued…
Sometimes you need to take shortcuts to quickly prove a concept. I’m experimenting with a new view on Basecamp and I need some data to generate that view. I want all the messages on a project, grouped by day, and also the latest comment per message per day. I don’t know the “right” and performant way to put that query together. But I don’t need to worry about that. I’m just testing a concept here.
So I wrote a totally ugly, awkward implementation that gives me @posts_and_comments. And that’s ok. If this design is successful, the fact that I want @posts_and_comments isn’t going to change. What will change is the implementation that pulls that data out of the database and populates the instance variable. In order to make that future step as easy as possible, I’m hiding my ugly implementation behind a clearly named private method on the controller. The controller action calls `find_posts_and_comments_for_log` and as far as the view is concerned, nothing untoward is happening.
If the concept doesn’t work out, no problem. I didn’t spend too much time on the implementation. If the concept does work, the ugly implementation will be easy to replace later because it’s tucked behind a clearly defined method.
When your implementation is a total hack, put it behind a good interface. Then you can swap the implementation later without rethinking the design.
Among the things that people new (and old!) to Ruby find delightful are the little things it does to make the language feel intuitive. Case in point: unless.
The unless keyword is just if in reverse. It’s a conditional statement that executes only if the condition is false, instead of true. This lets you write little gems like this:
i += 1 unless i > 10
unless person.present?
puts "There's no such person"
end
However, as with anything that gives you a little power, it can be abused. The following abomination is but a sample:
unless !person.present? && !company.present?
puts "do you even know what you're doing?"
else
puts "and now we're really confused"
end
I do not doubt that there are people out there who can decipher the logic above without breaking a sweat. But for most folks, combining negations, multiple conditions, and (gasp!) else clauses with an “unless” statement makes for challenging reading. It also makes it far too easy to introduce bugs into the logic.
Some rules of thumb when using unless:
- Avoid using more than a single logical condition.
unless foo? is fine. unless foo? && bar? is harder to parse.
- Avoid negation. “Unless” is already negative. Piling more on only makes it worse.
- Never, ever, ever use an
else clause with an unless statement.
Not only will others thank you when they have to read your code, you’ll thank yourself when you have to return to that code a month or two down the road.
My keynote from RubyConf about why I continue to choose Ruby as my programming language.
Viewer discretion is advised: Colorful language, metaphors, and topics.
We chose to roll the 37signals Suite out in stages. Right now you can actually only upgrade to the suite. You need to have one of the existing products and then you go to your account screen and you click upgrade. That was a way for us to cut down a little bit of the complexity in this. This was already a long running project, [so the question was]: How can we get it out now rather than just spend another three months on it?
Here’s an interesting quote from Kent Beck. He says:
The product of software development is two-fold:
• The behavior of the system
• The options for extending and modifying that behavior in the future.
That second point summarizes what good software design is about. Each feature we build in our code gets us closer to release. But the way we build each feature can set us on different paths. If we don’t care how it works, as long as it works, then our launch may come and go without a problem. But when it’s time to return to that feature and make a change or an improvement, we’ll likely find ourselves wading through a tangle of spaghetti.
We want to take the other path, where each coding decision carries a question with it: What will I think of this code when I need to change it in six months? Will I understand it? Will changing this break other things, or will the change be isolated? This habit, to the extent that we can keep it up, allows us to keep improving our apps instead of getting stuck in spaghetti.