"We used Xcode for nothing"
I was interviewed on Giant Robots Smashing into Other Giant Robots about Basecamp for iPhone, the recent RubyGems.org security issues, Chemex brewed coffee, and more.
You’re reading Signal v. Noise, a publication about the web by Basecamp since 1999. Happy !
I was interviewed on Giant Robots Smashing into Other Giant Robots about Basecamp for iPhone, the recent RubyGems.org security issues, Chemex brewed coffee, and more.
Some of my favorite feedback on Basecamp for iPhone has been that the app feels wicked fast, and all native. The app actually is a mix of web and native UI, but it’s difficult to see where the line is drawn. The majority of the content shown in the app is web: From the login screen, to posting a message, and even uploading photos on a comment, that’s all done using UIWebView.
Chrome inspired us that web views could be pushed to the next level, and we wanted to make use of our existing fast, mobile HTML5 views. Going web wasn’t always solid: Some views started as web, went native, and ended up back in a web view. In the end, the app evolved into a simple web content browser with a native feel on top. It’s certainly a compromise, but we’ve been really happy with the hybrid architecture so far.
Continued…It’s been over 8 months since our workplace experiment last July, when I got an entire month to buckle down and make an iPhone app. Last Friday, Basecamp landed in the App store. It’s been a long journey of simulator runs, device testing, and app crashes, but I’m convinced that I wouldn’t have been able to ship an iPhone app at all without RubyMotion.
I’ve tried to jump into mobile development a few times over the past few years, and I got stuck every time. I made a Android app in Eclipse, wrapped my head around Lua and Corona SDK, and tried to deal with Objective-C, Xcode, and Interface Builder. Not only did I have to throw out my existing toolset and workflow to break into this world, I had to ramp up with new APIs, frameworks, and more.
RubyMotion came at the perfect time: I wanted to make mobile apps, I didn’t have to throw out my existing workflow, and I could still write Ruby! Here’s what I learned about my experience with RubyMotion from the past few months.
Continued…This Wednesday (Feb 6) from 6-7pm, Adrian Holovaty discusses his new project Soundslice, a HTML5 Web app for annotating YouTube videos.
Tickets are just $10. The talk will be hosted at our offices.
This is a “how the sausage gets made” technical talk with plenty of hairy details about implementation: the approach to software design/architecture, the thought process on UI details and corner cases, and JavaScript tips/tricks discovered along the way.
Broadly, he’ll talk about his approach to building a “desktoppy” app in the browser with HTML5.
Adrian is one of the smart ones. If you’re in Chicago, don’t miss this talk. We’re limiting the talk to 45 people, and tickets will go fast, so register today and don’t miss it.
Just doing work without an eye on the big picture rarely satisfies anyone. We want to know what we’re doing matters and that means knowing why we’re doing it. This is true in all walks of life and certainly so in software development.
If you simply ask a programmer to implement a feature, but fail to share the purpose or the justification for said feature, all you get is a code monkey. Code monkeys merely translate requirements into code verbatim. There’s no room to trade concessions with the designers if I don’t know what you’re trying to do.
So not only is it demotivating not to know why, it’s also bad for productivity. Figuring out what the problem really is is how good programmers judo the problem: “Yes, what you’re proposing is one way to solve it, and that’ll take 50 hours. But we can solve the same underlying problem by doing this instead and that’ll only take 8 hours. How about it?”
Sharing the context shares ownership. Now we’re all working on solving the problem the best way we can. Not just laying the bricks in the order the big man tells us to.
There’s nothing more appealing than thinking you’re just that good. Our brains are optimized for fondly remembering our successes and quietly downplaying our failures.
The casinos in Vegas are primed for this by making it relatively likely you’ll win something early on. By the time you realize that the slight edge of the house means over the long run it’ll win everything back and more, well, you’ve probably spent more than your first ATM stop and then some.
Future coding is a lot like playing the roulette. If you can guess where the requirements of tomorrow will land today, you’ve just scored the ultimate programmer’s prize: looking like a wizard. You saw the future and you were right! High five, brain!
That’s the almost irresistible draw of “flexibility”—often just a euphemism for building half or whole features before you know how they’re supposed to work or whether you need them. Just like in Vegas, the worst thing that can happen is to be right about this once in a while.
Because like the casinos, the house of program complexity will take all your little bets on the future and add them up until you just don’t know how this damn program got so deep into technical debt!
Running up the debt on your code is not just about the quick hacks and dirty commits you know you really should clean up (but just don’t). No, the far more insidious kind of debt is that acquired in the name of “flexibility”.
Every little premature extraction or abstraction, every little structure meant for “when we have more of this kind”, every little setting and configuration point for “things we might want to tweak in the future”. It all adds up, and at a dangerous rate.
The road to programming hell is paved with “best practices” applied too early. Very few abstractions, extractions, or architectures are inherently bad (although I’ll contend that a few are). But most of them are downright evil if applied before they’re called for.
To take the scare quotes off flexibility, you have to be willing to write no code at all. The “Simplest Thing That Could Possibly Work” and “You’re Just Not Going To Need It” are both sayings to help you get there.
This really does mean not a single parameter to a method that has not yet found a use. No needless public methods that might get reused later (not even for testing!)
The hardest—and yet most important—thing in the world of design is the conviction to say no.
There are so many programming books out there, but most focus on specific technologies and their half-life is incredibly short. Others focus on process or culture. Very few focus on the timeless principles of writing good code, period. The following five books had the biggest influence on my programming style and development:
If you consider programming to be a subset of writing, and I certainly do, then you would also do well to read Elements of Style and On Writing Well. I’ve found reading those made me a better programmer as well.
Reading these five to seven books will give your programming chops more vitamins and nutritional value than a couple of year’s worth of blog posts and tutorial.
Using URLs instead of ID references in your APIs is a nice idea. You should do that. It makes it marginally more convenient when writing a client wrapper because you don’t have to embed URL templates. So you can do client.get(response[:person][:url])
instead of client.get("/people/#{response[:person][:id]}")
. But that’s about it.
The recurrent hoopla over hypermedia APIs is completely overblown. Embedding URLs instead of IDs is not going to guard you from breakage, it’s not going to do anything materially useful for standardizing API clients, and it doesn’t do much for discoverability.
Preventing breakage
According to hypermedia lore, you will be able to willy nilly change your URLs without needing to update any clients. But that’s based on the huge assumption that every API call is going to go through the front door every time and navigate to the page they need. That’s just not how things work.
If I want to request a message off a project in Basecamp, I would have to do something like this GET /projects
, GET /projects/1
, GET /projects/1/messages
, GET /projects/1/messages/2
. That’s great for the first fumbling in the dark discovery, but it doesn’t work as soon as I bookmark that last URL because I want to send comments to it later.
Just like bookmarks in the browser break if you change the URL, so will any client that’s stored a URL for later use.
Because breaking URLs is such a bad idea, people tend not do it. If you look at the successful APIs on the web, they’ve stayed remarkably stable because that’s the best way to prevent breakage. Like the W3C says: Cool URIs don’t change. Which means this isn’t much of a problem in the wild and even if it was, hypermedia APIs would still have big holes with direct links break.
Enabling discoverability
Good API docs explain what all the possible attributes of a resource are. They explain the possible values of those attributes. The options available and so forth. Thinking that we can meaningfully derive all that by just telling people to GET /
and then fumble around to discover all the options on their own just doesn’t gel with me.
How do I know that the data I happen to be stumbling across includes everything that’s possible to do? What if the project I request doesn’t have any documents attached with it? How do I know how to find those and how to add new ones?
Standardizing API clients
The idea that you can write one client to access multiple different APIs in any meaningful way disregards the idea that different apps do different things. Just because there’s a standard way to follow a resource to a sub-resource doesn’t mean that you can just write one generic client that then automatically knows how to work with any API.
Any generic API will not know what to do with the things it get. It won’t know the difference between Flickr photos and Basecamp projects. The assistance of being able to follow a link to go from photo to comments and project to messages is nice, but as explained in the beginning, not exactly earth shattering.
Every single application is still going to need a custom API client. That client needs to know what attributes are available on each resource and what to do with them.
In summary, here’s a low-fi solution to these three problems that doesn’t require a spec or involving the IETF:
On top of that, be a chap and use URLs in IDs because convenience is nice and it doesn’t take too much effort if you’re using something like jbuilder anyway. But don’t go thinking that you’ve magically solved all these problems just because you did.
We’ve been down this path to over-standardization of APIs before. It lead to the construction of the WS-deathstar. Let’s not repeat the same mistakes twice. Some times fewer standards and less ceremony is exactly what’s called for.
Different models in your Rails application will often share a set of cross-cutting concerns. In Basecamp, we have almost forty such concerns with names like Trashable, Searchable, Visible, Movable, Taggable.
These concerns encapsulate both data access and domain logic about a certain slice of responsibility. Here’s a simplified version of the taggable concern:
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, as: :taggable, dependent: :destroy
has_many :tags, through: :taggings
end
def tag_names
tags.map(&:name)
end
end
This concern can then be mixed into all the models that are taggable and you’ll have a single place to update the logic and reason about it.
Here’s a similar concern where all we add is a single class method:
# current_account.posts.visible_to(current_user)
module Visible
extend ActiveSupport::Concern
module ClassMethods
def visible_to(person)
where \
"(#{table_name}.bucket_id IN (?) AND
#{table_name}.bucket_type = 'Project') OR
(#{table_name}.bucket_id IN (?) AND
#{table_name}.bucket_type = 'Calendar')",
person.projects.pluck('projects.id'),
calendar_scope.pluck('calendars.id')
end
end
end
Concerns are also a helpful way of extracting a slice of model that doesn’t seem part of its essence (what is and isn’t in the essence of a model is a fuzzy line and a longer discussion) without going full-bore Single Responsibility Principle and running the risk of ballooning your object inventory.
Here’s a Dropboxed concern that we mix into just the Person model, which allows us to later to route incoming emails to be from the right person:
module Dropboxed
extend ActiveSupport::Concern
included do
before_create :generate_dropbox_key
end
def rekey_dropbox
generate_dropbox_key
save!
end
private
def generate_dropbox_key
self.dropbox_key = SignalId::Token.unique(24) do |key|
self.class.find_by_dropbox_key(key)
end
end
end
Now this is certainly not the only way to slice up chubby models. For Visible concern, you could have Viewer.visible(current_account.posts, to: current_user)
and encapsulate the query in a stand-alone object. For Dropboxed, you could have a Dropbox stand-alone class.
But I find that concerns are often just the right amount of abstraction and that they often result in a friendlier API. I far prefer current_account.posts.visible_to(current_user)
to involving a third query object. And of course things like Taggable that needs to add associations to the target object are hard to do in any other way.
It’s true that this will lead to a proliferation of methods on some objects, but that has never bothered me. I care about how I interact with my code base through the source. That concerns happen to mix it all together into a big model under the hood is irrelevant to the understanding of the domain model.
We’ve been using this notion of extracting concerns from chubby models in all the applications at 37signals for years. It’s resulted in a domain model that’s simple and easy to understand without needless ceremony. Basecamp Classic’s domain model is 8+ years old now and still going strong with the use of concerns.
This approach to breaking up domain logic into concerns is similar in some ways to the DCI notion of Roles. It doesn’t have the run-time mixin acrobatics nor does it have the “thy models shall be completely devoid of logic themselves” prescription, but other than that, it’ll often result in similar logic extracted using similar names.
In Rails 4, we’re going to invite programmers to use concerns with the default app/models/concerns and app/controllers/concerns directories that are automatically part of the load path. Together with the ActiveSupport::Concern wrapper, it’s just enough support to make this light-weight factoring mechanism shine. But you can start using this approach with any Rails app today.
Enjoy!
I was on In Beta a while ago, and we talked about the “guilt” that comes with maintaining open source projects. I feel both intensely passionate and exceptionally guilty with how I contribute to open source. I love getting patches into new projects, and the needs of existing projects are demanding.
An open source contributor doesn’t feel guilt as in a crime, but guilt as in time: What is best to spend your yours on? Steve Klabnik has a strategy for dealing with a limited amount of time:
The basic idea is this: you try to minimize the things that are bad, and maximize those that are good.
My strategy has built on Steve’s: minimize the guilt of open source, maximize the passion from open source. I’ve been thinking a lot about what creates passion, and what builds up guilt. It’s also worth considering what destroys passion, and what can tear down guilt.
Continued…