| What an awesome way to advocate for code change. Very pretty. Unfortunately, I also think it's faulty. First, it doesn't actually advocate anything concrete. There's some hand-waving to Sinatra and other Rails features, but nothing concrete. If you're going to make such a pretty proposal, it should come with a call to specific action that people can get behind. This is double true when it comes to API design. It's all fine and good to general ideas and principles guiding you, but when the code hits the editor is when all the constraints and trade-offs are revealed. I could fill a book with all the premature ideas I had for API rewrites that turned out not work when applied to the tough reality of real code. But I'm wearing too far into hand-waving territory too, so let me address a few of the points raised: 1) "Not specifying URLs directly leads to poor [URL] API design and other ills": There are only so many (reasonable) ways to specify a URL if you want to follow REST principles. You name your resource and you give it an identifier. /products/1-some-perma-id became a pattern because it was both simple, repeatable across models, and extractable. Why spend time coming up with a unique URL structure for every model that you want to expose when they follow the same pattern of using the model name as the url identifier? This is exactly what Rails does and always have done. Spot patterns currently done by hand, extract said pattern into a convention, allow people to move on from thinking about how to do X until they hit an exception. This, in my mind, is exactly what leads to great API design and simple solutions. You do the same as everyone else when the choice is less important than the consistency. Now I welcome the praise for Sinatra. I think it's completely awesome. I'd use it for smaller projects myself. But that's exactly where conventions don't give you that much. If you're exposing, say, 20 urls to the public, you don't gain a bunch from having a convention that follows a set pattern. In fact, cutting out the middle man of an abstraction can make the code seem easier to read and more immediate. That trade off flips when you have 100, 500, or (as is the case of Highrise) 2000 routes exposed to the public. When 95% of those follow the same pattern, there's big gain in the consistency of a convention. The last 5% are handled by outlet valves that allow you to declare whatever exceptions you need. 2) "The Seven Action Names Don't Help": I think the default constraint of 7 is the most important part of the positive effects you get from following REST in Rails for internal organization. Again, this isn't theory, but extracted experience from practice. Rails used to require you to map everything by hand. Lots of people ended up with mega controllers that had 25 actions because there was no easy pushback. By the time the controller was too big it felt like too much of a hassle to break it up just to add "one more action". We still have GlobalController in Basecamp to remind us of what that was like. Having the default conversion that turns /product/1 into show is also just pretty code. The two alternatives lined up are not very pretty. I'll take "def show" over "get(:member) do |id|" or "get "/api/v1/report/:id" do |id|" any day when it comes to a large, consistent URL surface. 3) "Assets are resources": I completely agree here. This will be addressed in Rails 3.1 when we get the asset pipeline going. But as always, the proof is in the code. I'd love to see an even simpler routes system, but none of the arguments presented in the article gives any indication that the proposed ideas will lead to that. I've been both surprised and wrong before, though, so please do investigate. Again, though, kudos for the presentation. I wish more people passionate about API design would take the time to do something as pretty. But with more concrete code examples, please ;) |
I actually like the HTTP-style method approach. You mention pushback to prevent people from creating mega-controllers, and I think this takes it even a step further. It really ties the controller to the resource. As mentioned in the article, this is already how the methods are called in the tests.