codahale.com٭blog

free ringtones and wallpapercasting crowns ringtonespink ringtonebow like mine ringtone shortie wowcoldplay ringtones
Coda Hale lives in Berkeley, CA, where he writes about Ruby on Rails, usability, web design and development, and the occasional bit about bicycles.

CRUD is not the only user interaction metaphor, thank you

Update: This post takes some liberties with the context and intent of the quotes within it. Most of this was unintentional, but all of it was thoughtless of me; they deserve better. The post itself is not all that interesting, but the comments within have some really awesome back-and-forth over the semantics of web applications.

Update 2: Evan Weaver has written a post which makes a much better set of points than I have, but he agrees with me, so I’ll pretend like I had it all worked out ahead of time but just didn’t feel like typing it all up. ;-)

With that in mind…


There’s been a huge push lately to integrate RESTful web services into the core of Rails, and I think that’s awesome. Anything that advances the cause of light-weight, human-readable web services strikes me as a good idea. The Rails approach is a good one, not just because it has characteristically beautiful code, but also because it looks fantastically easy to work with.

That said, I think some people are taking it a little too far, like this article on habtm, “if your models aren’t namespaced, why should your controllers be?”:

The New CRUD is about using your controllers to expose your models and various other things as objects. If your models aren’t namespaced, why should your controllers be?

With this new approach, you start thinking about how to move your controllers over to a flat namespace and still have your separate backend layouts for editing actions.

First, the reason that your models are not modularized is because the unit testing framework dies a horrible death when you use modularized models, not because it’s a design decision that makes any sense. Thus the flat namespace for Rails models is not a solid base from which to extrapolate the design of the controllers.

Second, namespaced controllers fit just fine with the latest CRUD push by rails-core. Rails 1.1.3 breaks the default routing for modularized controllers, but that’s fixed for 1.1.4. A bug in a revision release shouldn’t determine your architecture plans, and in the meantime you can work around this issue by either freezing Rails to the stable branch or adding explicit routes to your modularized controllers:

map.connect 'admin/users/:action/:id', :controller => 'admin/users'

Finally, and most importantly: CRUD is not the only user interaction metaphor. You should choose an interface (and thus, a controller’s structure) based on what your users’ goals are, not what your system metaphor ideology dictates.

When people use modularized controllers, it’s usually because they realize that they have two or more groups of people using their application who have overlapping yet mutually exclusive needs. For an online store, the information returned by a Read action for customers will be different than that of an action for store employees. To try to shoehorn these two needs into the same interface is bad design, plain and simple.

A flat namespace for your controllers only makes sense when your users are all doing roughly the same thing. That’s not always the case, and to overlook that possibility in order to stuff your application domain into some cool new paradigm is not responsible software development. Don’t take this to mean I don’t like RESTful systems–I do–or that I think the latest CRUD push in Rails is bad–I think it’s awesome–but the idea that CRUD is the one, true pattern of user interaction… well, that just seems a bit silly.

Speaking of silly, search does not work with a CRUD metaphor, despite what people may think:

Instead, consider using a Search model. Create a search resource that holds all the options for the query. Then you can execute it whenever you want. POST /searches (post data contains query options) would redirect to GET /searches/42 or GET /people/?searches=42. You could even make it so that searches are unbound until execution, like blocks: GET /searches/42?on=people.

*sigh*

Why is this a good idea? So a single HTTP transaction can be replaced with two? Because the POST verb just doesn’t get enough love these days? A search session is a Read operation, end of story. Instead of saying “give me the record whose ID is 4,” you’re saying “give me the records which have ‘frankenberry’ in the description.” Not, “create a search session with these parameters, then read it and the magic results you have appended to the end of it without my say-so, then finally destroy it.”

So why would people want to do this?

Now, I just do a quickie insert of the pertinent info into our DB, then apply the search to the engine (totally different middleware). Boom! Instant ’search history’. To find out why someone did something ‘downstream’ in the app, looking up their most recent search is trivial.

Shorter version: “I confuse searching with logging.”
Solution: Rename your model LoggedSearch.

Consider this: have you ever wanted to:

  • Know what your users are searching for?
  • Allow your users to save their search criteria for later use?
  • Define a “google alert” type feature in your app?

If you wanted to do any of these, having a search model object would save your bacon. Imagine having an Alert object that has a 1-many relationship with your Search object.

Shorter version: “I confuse searching with logging, preferences, and alerts.”
Solution: Make LoggedSearch, SearchSetting, and SearchAlert models.

This is the kind of crap which buried XML-RPC, folks: overengineering a solution because you’ve got a shiny, new hammer and the world is your nail. The first thing one should do when modeling an application domain is to figure out what people actually want to do with it. If that lines up with CRUD, awesome. If not, don’t force it. It just adds complexity where none is needed, which is exactly the thing RESTful web services were designed to avoid.

Moral of the story? No single metaphor fully expresses the richness of human-object interaction, and any totalist ideology will result in a poorer user experience. CRUD is a solution, not the solution.

37 Responses to “CRUD is not the only user interaction metaphor, thank you”

  1. evan Says:

    Good post.

  2. rick Says:

    good post, but take it easy on the bold

  3. Coda Says:

    but i feel strongly about the subject matter ;-)

  4. Steve Says:

    Great post.

    I say keep the bold. Most people skim read webpages and miss the important bits - i know i do. The bold helps bring the attention back :0)

  5. scott Says:

    You just saved me a lot of time in that I was going to write a somewhat similar response to the habtm post. What is being proposed here does make sense some percentage of the time but there’s no way I’m going to force it onto code that is (IMHO) beautifully structured and does exactly what I and the user needs. I’m more likely to define the REST/CRUD versions of my actions *after the fact* and map them onto the appropriate pre-existing actions that evolved naturally from the systems I am building. Who would ever know or care?

  6. Jay Levitt Says:

    Thank God somebody wrote this… I’ve been feeling it but unable to articulate it.

  7. Chris Winters Says:

    I agree that it’s easy (and common) to create an architecture and interaction design that completely ignores the user. That said, I think you’re mixing complaints here. Does a user really care if you use one or two HTTP transactions under the hood?

  8. josh Says:

    Well, I can see you feel strongly about this subject. I’m not sure why you have to take shots at other people’s posts from your own blog rather than engaging in discussion where it was invited, but posts on my blog are always open for back-and-forth discussion and I try to be responsive there. Part of what makes the Ruby and Rails development communities great is their congenial nature. There’s no reason to attack someone or their ideas when they are just putting forth thoughts for discussion.

    My post on cruddy searches was part thought experiment and part documenting of a conversation I’d just had on IRC on the topic. I’m not advocating anything as yet, and I say that right in the article. I’m exploring the design space. I’m not “silly” enough to say that CRUD is the only design option, but I think it’s worth pushing it to see how far it can go. If you want to be part of that exploration, there’s always room for another point of view. If you want to expand the discussion to your own blog, a comment letting me know so I can participate would be nice.

  9. mly Says:

    Hey Coda,

    Glad to see my post on caboo.se / habtm.com inspired such boldiness on your part. I’m also glad to discover that I need to include more context from our irc channel when writing these things… I sometimes forget that other people read the blog. There have been a lot of people on #caboose saying that they’d LIKE to get rid of their controllers’ namespaces for various reasons except that they weren’t sure how to do some of the things I explained how to do in the post. I have an eidetic memory and know the API forward and backward, so it was as much of a thought excersize as anything else.

    Additionally, my project in particular has a fairly straightforward data model and benefits greatly from a name-space-less model and controller pool; getting rid of namespaced controllers for the mere sake of requiring authentication for all but a single action from one controller has made the whole thing much simpler to wrap your head around, especially if you had never seen the code or architecture before. That’s a big win for me. There are several other rails projects I’ve seen, especially open-source ones, that could benefit from a similar approach. They just aren’t complex enough to require namespacing.

    Many in the #caboose irc channel are excited about David’s new take on CRUD for precisely the same reasons; thinking about things this way can help keep controllers short and straightforward. With the way simply_restful works, a lot of the stuff like my post at http://blog.caboo.se/articles/2006/06/18/assert_pedantic_semantic_thing becomes unnecessary; the necessary validation logic happens before the controller ever sees the request. You get a lot of other quasi-perceptible things for free as well.

    You slam me for “making architecture decisions based on a framework bug”, but I think you missed my larger point that Rails doesn’t play well with namespaces well in general. I’ve been writing rails apps since December 2004 and I’ll be the first to admit that there are a lot of things rails doesn’t do well; that some things are under-loved and under-tested in the framework. Components are one such example, Namespaces are another. I’m fine with that, because it encourages me to keep things simple, which helps me write more straightforward software. Maybe you’re not fine with it, in which case Rails will probably cause you to chafe with indignation at several other points as well.

    Finally, a note regarding the cruddy-searches article: POSTing to a search, creating a model, and then redirecting to GET that search suddenly allows for a couple of things that might have been difficult to conceive of doing otherwise, perhaps most notably: /searches/42.rss whereby you could subscribe to a search. Perhaps such a thing doesn’t fit in every application’s domain, that’s fine. If your domain calls for throwaway searches, by all means build them that way.

  10. Coda Says:

    (Wow, that’s a lot of comments to respond to. Okay, here goes.)

    Chris–Using two HTTP transactions to accomplish what can be done in one means more complexity, more latency, more potential for failure, more bandwidth, more server load, and all for no apparent benefits. I don’t think it’s worth it, but that structure may provide more benefits for you than it does me.

  11. Coda Says:

    Josh–You seem to be taking my post personally, and I’m sorry if I offended you. I’m not calling you silly, but rather the idea that search is improved when viewed as a CRUD interaction.

    I didn’t post a comment to your blog because I believe that inter-blog communication is just as valid as intra-blog, not because I felt that my opinion would be unwelcome there. I had listed your post as one of the posts to which WordPress should send a trackback; on most blog platforms that functions to let an author know that another blog has referenced their post. I’m not sure if you have trackbacks disabled or if Typo doesn’t implement them, but it was not my intent to isolate you from this conversation.

    I think this may boil down to different communication styles, Josh, and I’m really sorry if I offended you. I respect pretty much everyone in the Rails community, and the way I wrote this post is the way I interact when I’m just tossing around ideas with people. I tend to be, well, boldy.

    I realize that you’re exploring the design space; part of that exploration is negative feedback, and that’s what my post is about. I think looking at CRUDdy search conflates the actual CRUD (sessions, settings, logs, alerts) with a separate, non-CRUDdy action: search. You proposed a thought experiment and we had different outcomes.

    Finally, I was kidding in my response to Rick. I use bold to highlight the actual point of a paragraph, especially when it’s an opinion piece that people won’t be scanning for code or configuration. I really don’t feel that strongly about this, mainly because I don’t have to treat all my search actions like CRUD transactions, and I’ve had a lot of success logging searches in models dedicated to such a task.

  12. Coda Says:

    mly–The added context goes a long way in resolving my issues with your post. Your post, without the associated bits in your brain, the IRC channel, etc., appeared to be calling for an end to namespaced controllers, which is a pattern that I’ve found very useful in my work with Rails. I agree that Rails doesn’t play very well with namespaces, but I think we should fix that, rather than work around it. (I realize that as someone who would probably not be involved in fixing it, it’s a very easy thing for me to say. But still.) I didn’t mean to slam you, or impugne your knowledge of Rails; rather, I was saying that namespaces are a very important thing indeed, especially for the large projects that people don’t think will work with Rails, and that’s something which should be resolved, not avoided. I don’t know if I’ve been chafed with indignation by that, but I’ve certainly come across times in which the application domain was complicated and irreducible enough that namespaces would have saved me the trouble of having four-word-long model names. So not chafed, more like itchy, and not indignation, more like miffedness. Itchy with miffedness. Yeah.

    And I still think that something like /searches/42.rss would work better as /search/franken%20berry.rss. The only case that I can think of in which non-transient searches would make sense is where searches are done asynchronously, as batch jobs or something.

  13. josh Says:

    Trackbacks are spam magnets. I’ve turned them off because I got tired of dealing with the volume of spam I was getting. Technorati is nearly as good (sometimes better) for finding references in other blogs, which is how I found yours. And I wasn’t personally offended, though I think someone with more personal attachment to their ideas might have easily been offended by the style of your post. My interest was in furthering the conversation, and I didn’t think that was what you were doing.

    I haven’t drunk the whole glass of CRUD kool-aid yet, though the sip I’ve had tasted fine. My philosophy is that when it makes something simpler, I’ll use it. If not, I can always stick with the current design approach. Either way, it’s nice to have another arrow in my quiver.

    By the way, a search model doesn’t have to be persistent. Just because you are describing an interaction in terms of a noun instead of a verb doesn’t mean you have to stash a record in the database.

  14. Coda Says:

    You know, I’ve never had much of a problem with spam trackbacks. I’m not sure if I’m lucky, talented, or just on the to-do list. Next time I’ll leave a comment if the trackback doesn’t show up on the original post. And I’ll try to tread more lightly next time. I should change the background in my WordPress admin dashboard to a picture of Matz; he’s nice, so we’re nice.

    The bottom line is, I guess I’m just leery of CRUD becoming another scaffolding, where you have many frustrated people trying to fit square pegs into round holes. So, to carry the koolaid metaphor too far, I’m worried about people drinking white koolaid with the braised beef and the red koolaid with the poulet au miel. Wouldn’t be proper, and it certainly wouldn’t taste as good as it could.

    The CRUD examples DHH has been throwing around are absolutely delightful, and I’m happy to see Rails integrate a light-weight web service foundation in its customary elegance. The Accept-aware, extension-aware respond_to addition has me prancing around like a puppy, since it’s a profoundly more elegant way of doing stuff I’ve already been doing. I love it when I get to boil my code down even further.

    But if a search model isn’t persistent, it’s not much of a model, and it certainly isn’t going to play well with CRUD. It would get creation down, but reading, updating, and deleting something which vanishes after the HTTP connection closes… that’s gonna be tricky. So it’s got to be persistent, even if that’s not in the database (or, like session data, not necessarily in the database).

    I’m speaking from experience, here, too. I wrote a small search engine for GPSgeek, and I’m logging all the searches to help improve the engine’s ranking algorithm and also to see what our customers are looking for that we don’t already provide.

    The difference is that I don’t see those log entries as being search models, since they don’t fully wrap the domain of “search.” Instead, they’re a snapshot of the input and output of our search function: SearchLogEntry. The actual search itself can take place without me logging it to this table.

    Likewise, a search alert would be a model which describes what I should do if a particular action matches a set of criteria. The actual matching function doesn’t take place within the rubric of CRUD. It’d be CRUMD, which isn’t half as snappy. Add in the searching and you’re talking about SCRUMD, which I think would either violate some copyright or set off a development methodology holy war.

    I’m all sorts of OK with an easy way to make my search results fart out HTML, XML, RSS, ATOM, CSV, ICL, PDF, SWF, LaTeX and bloody great big flashing GIFs, but I don’t lump that in with CRUD.

  15. Peter Says:

    I like your example about the online store. The customer and admin having different views of a particular product. I realize that the flat namespace issue is seperate from DHH’s slideshow. When the CRUD interface is available surely we will be able to do

    GET /storefront/product/1
    GET /admin/product/1

    I am also concerned about all this extra calls to redirect_to. DHH’s minor CRUD hack is using the wrong request for PUT and DELETE. He has to use POST for these. This new protocol abuse is in the name of design philosophy. If we are going to start using the verbs incorrectly for other perceived gains then shouldn’t we continue abusing them the way we have all along. That is, after a POST send a response more useful than a redirect. This is in the name of efficiency. AJAX is heralded for making web pages snappier and now we are going to start using two round trips for something that can be done in one? The old and new abuses I mention are of the same magnitude. The new one may improve the developers experience but the old one improves the user experience.

    I’ve asked this on some other blogs too but suppose we accept the new CRUD for everything paradigm. What happens when a POST calls create and it fails. If this create action can be called from many web pages, how will we even know where the POST came from? Are we going to attach the URL of the form’s page as a hidden field also? I know this same issue can come up with current Rails apps but it seems like this issue is even a bigger one with all CRUD interface.

    I think it is really healthy for the community to discuss these topics with energy so we learn the limitations of what is new.

  16. Pat Says:

    I hope I don’t sound like a nit, but this post seems to just point out issues with the “new CRUD” and misses its topic. Perhaps I’m missing something huge, cause while it’s titled “CRUD is not the only user interaction metaphor, thank you,” I didn’t see any discussion of an alternate metaphor that you find preferable in certain situations. I’m fairly new to all of this, so when you say CRUD’s not the only way, well I’d be very interested to see what other methods you prefer and in what situations.

    Anyway, on to some of your points.

    You initially said that taking this approach because of a bug in the framework is irresponsible. That makes sense, but then you said that the bug was fixed. Well now people would use it by choice, not because of a bug, so I don’t really see how that holds water anymore.

    “When people use modularized controllers, it’s usually because they realize that they have two or more groups of people using their application who have overlapping yet mutually exclusive needs.”

    Again this is showing my inexperience, but I’ve never understood how modularized controllers help in that regard. A public facing controller and an admin controller always seem to do much of the same thing, so there’s a lot of duplication. The only benefit from inheriting from a different controller is special authentication or logging…but is that really any better than writing a module to handle it and including it where necessary?

    Shorter version: “I confuse searching with logging, preferences, and alerts.”
    Solution: Make LoggedSearch, SearchSetting, and SearchAlert models.

    Two HTTP requests vs three search-related classes. To me, Josh’s way seems more clear and flexible. It’d be worth it to me to use an extra HTTP request to do that then.

  17. Pat Says:

    I forgot to mention this in my post, but one reason why I think CRUD is such a good metaphor in web development is that we simply don’t have to perform many complex operations. For the most part, you CAN boil everything down to basic CRUD stuff. We’re just pushing around data, nothing is particularly difficult. If you keep CRUD in mind, all of a sudden you realize that there are a lot of interesting things you can and want to capture when doing that data pushing.

    Again, I’m new to this idea and web development in general, but I’m having a tough time coming up with scenarios (and definitely can’t find anything in my own code) where CRUD is insufficient.

  18. Coda Says:

    Pat, we’re arguing over the semantics of web applications. Literally. :-) We’re all nits here, so welcome to it and bombs away.

    I think CRUD isn’t the only metaphor for user interaction because I like the richness of other verbs which can’t be accurately mapped to Creation, Reading, Updating, or Destruction. Searching and matching are two definite edge cases discussed here which I haved argued don’t at all fit the CRUD metaphor and shouldn’t be shoehorned in. You can’t boil everything down to CRUD; there are edge cases which break that metaphor, usually by requiring expanding a single transaction (”Give me all records from Nebraska”) into many different transactions (”Create a query to find all records from Nebraska, then read the query back so we’ll get the search results the elves magically appended to it, and then delete that query because we’re not using it anymore.”) for very little benefit. CRUD is fine for a large percentage of what most people are using Rails for, and the new additions will definitely improve things, but I disagree with trying to make edge cases fit into a paradign by lopping off its toes.

    I was taught that CRUD means a very specific thing. Create instantiates a persistent object somewhere (I don’t care where: database, memory, Elvis’s mind, whatever) with a particular set of attributes (the empty set is valid) and returns an usable reference to this new instance (this last bit is really handy but not necessary). Read takes that reference and returns the latest values of attributes of the persistent object. Update takes a reference and a set of attributes and changes the attributes of the referenced object to match the new set. Delete/Destroy takes a reference to an object and, at the very least, removes that reference from the set of valid object references. (It should probably also do some deleting, but sometimes regulations don’t let you actually do that.) These are the rules of the game, and they’ve been around a lot longer than have web applications: CRUD is another name for INSERT, SELECT, UPDATE, and DELETE, but ISUD sucks as an acronym. So CRUD describes the basic mechanics of SQL (but not the relational model), but you’ll notice that in order to make SQL useful, they had to add a whole bunch of stuff that you don’t find in the HTTP 1.1 verb list.

    So either we’re talking about extending the Read action to include the equivalents of ORDER BY, GROUP BY, DISTINCT, etc., which both spoils the elegance of what DHH has been demoing and requires that big chunks of metadata be set apart from the actual content, or we’re talking about using verbs outside of the CRUD rainbow (Cyan, Red, Umber, and Dung–it’s not terribly pretty).

    Finally, CRUD doesn’t work well across models, and searching is something which definitely works best when it returns a variety of data types. On my store, I don’t want to just return products, I also want to return categories, entries in the compatibility lists, our competitors’ products (so customers can see what benefits our products provide over others), etc. Now we have to extend Read to be able to return an arbitrary set of model types.

    One thing the New CRUD has going for it is that it makes for a great playmate with the recent trend towards rich many-to-many relationships. Adding a CRUD interface for Categorizations is an elegant solution to a problem which has seen many ugly attempts.

    Now to the specifics:

    There are two bugs: first, 1.1.3 broke the natural mapping of namespaced controllers. This is the bug that Mly was referring to in his post explaining the downsides of namespaced controllers. It’s fixed for 1.1.4 and has an easy workaround in the meantime, so it is a poor bug to make design decisions on. The second bug deals with the way that namespaced models are handled, specifically by the unit testing framework. This bug is why models have a flat namespace (or eat flaming death during unit testing), and the fact that the shape of the model namespace is based on a bug means that it’s a poor precedent on which to base the shape of the controller namespace, as the bug doesn’t affect controllers. Does that make more sense?

    The Admin:: namespace pattern is useful, IMO, because there are plenty of occasions in which the non-privileged user only gets to read model data, and usually in a very specific form which requires a certain amount of preparation. For example, in the site I just got through with, GPSgeek, there is an Admin::News controller, which provides an easy frontend for adding announcements, etc. The read action for that bit of CRUD is going to return a bunch of information which our customers do not need to see: whether or not the news item has been published, the new item’s Textile markup, etc. Mixing these two distinct actions into a single method introduces a grip of problems I’m not interesting in dealing with.

    If people are writing duplicate functionality into their controllers, then flattening the namespace and moving everything to CRUD extensions makes perfect sense, but the assumption that a read action on a model is the same for all people at all times… that sends up a red flag for me. Instead, I’d prefer to have multiple flavors of CRUD. For my news model, I’d have admin CRUD, which gets a boring Read action, and user CRUD, which gets a fantastically detailed Read action, but doesn’t get to play with the sharper tools in the shed. Peter’s got the right idea above:

    GET /news/1 #=> rendered from Textile to XHTML, no metadata beyond title and date
    GET /admin/news/1 #=> unrendered markup, admin details

    This is well within the CRUD metaphor, but is a good counterpoint to the idea that CRUD and flat namespaces necessarily go hand-in-hand. (But again, Mly’s post was in response to a specific conversation in which flattening the namespace both preserved the application domain and simplified the code, so his suggestion was just and good.)

    Regarding search logging, search alerts, etc., I guess I’m more OK with having many classes than I am with having classes which play many different roles to many different people. I’ve got a Product class, a Manufacturer class, a ProductType class, and a ProductOption class. That’s because the domain of our products is rich enough to warrant that, and trying to smash it all into a single class would make for some severe normalization problems down the line. Likewise, I don’t think that stuffing a bunch of attributes surrounding searching into a single class necessarily makes sense. If you have an asynchronous search, it does, but few people do that.

    When it comes to talking about those three potential pieces of functionality, they don’t fit into a single domain. A log entry is a piece of archival data; making it mutable strips it of its function. A search parameters model would come closest to wrapping the domain, and in the case of asynchronous searches, actually would be perfect. For synchronous searches (which describe the vast majority of searching online), though, the connection between the settings and the results is still handed off to a separate function which actually wraps the search domain. It’s like the homunculus theory of consciousness: if it’s just a little man in your head watching the images which come in through your eyes, what happens in the little man’s head? And a search alert has much the same problem. It doesn’t describe a search, it describes a set of criteria which, when matched by a separate function, should initiate a specific action. That’s a damn good idea, but if if you’re willing to call that a search, then you’d be willing to call a cron job a search for a specific time.

    If I had to make all of those work together, I’d have SearchLogEntry off in the corner, and then SearchSetting and SearchAlert in a one-to-many relationship, unless the searching engine doesn’t do conjunctions very well, in which case it’d be a many-to-many relationship, possibly with additional trigger information in the join model.

    (Okay, this post easily takes the cake for the longest average comment. I thought I was just writing a cranky one-off here, and it’s stretching into book length. Apparently the New CRUD is something which is really up in the air in terms of a design pattern. This has got to be the most interesting conversation I’ve had about Rails recently. Thanks, everyone, for such intense feedback!)

  19. jonathan Says:

    Hello Coda,

    I’ve followed your blog for a while; this morning, I find myself quoted on it! Unfortunately, it’s not very flattering. Mine was the comment on Josh’s blog, about which you said:

    ‘Shorter version: “I confuse searching with logging.”
    Solution: Rename your model LoggedSearch.’

    You can’t blame folks for having a strongish reaction when you’re dismissive like that, but I was more confused that anything.

    Sure, what I was talking about is equivalent to logging. My model is in fact called SearchOptions, which seems pretty equivalent.

    I must be missing something–what is your point? If you agree that it might make sense to model a ’search’ (however you want to slice it), then we seem to be in agreement.

    I use the model to tag subsequent interactions with the search context that inspired them, and to do that, I’ve got to tuck that info in a database (or put them in session, which has its own difficulties). I also give the customer a search history, they can emails searches easily, and it helps both Ajax interactions and page caching.

    I still think that it makes sense, *for my application*, to ‘create’ a new set of search options, which for me models the conjunction of a given set of options with a given user at a given time. Our customers ‘create’ searches. Those searches are associated with their account and with the things they do with the things they find. YMMV.

    As far as the HTTP overhead argument goes, I think that’s kinda weak. There’s up to a hundred thumbnails on our result pages. 100+1 isn’t that big a deal. Plus, while every Search is unique (and ‘logged’), the result of every search is *not* unique. By redirecting to another page I have the opportunity to cache result pages while still logging every search.

    In my comment on Josh’s site I was pretty clear that I empathize with the ‘con’ arguments. I’d just make the case that if search is really central to your business, and your search capabilities are sufficiently complex, then treating [Logged]Search[Options] as a model isn’t insane. It solves quite a few problems that ‘just logging’ certainly does not.

    The application has to justify it though. Otherwise, well, that’s what ‘the query string’ was invented for, and I couldn’t agree more.

  20. Peter Says:

    > As far as the HTTP overhead argument goes, I think that’s kinda weak. There’s up to a hundred thumbnails on our result pages. 100+1 isn’t that big a deal.

    Good point. Thanks for posting that.

  21. My New Mantra | Archives | codablog | Coda Hale Says:

    [...] My last post caused a bit of a stir, mainly because I was a bit of a jerk and quickly dismissed some folks who are, by many objective standards, actually cooler than I am. Sometimes I tend to get carried away with knocking down ideas which annoy me, and I forget that there are people and contexts behind those ideas which lend them a lot more weight and reason than I’d originally seen. [...]

  22. Coda Says:

    Jonathan–I think you’re the last person I quoted or referenced in this post, which means I’ve managed to offend every single person associated with this post except for myself (embarrassed doesn’t count as offended). Again, I really apologize if this came off as abrasive; that was not my intent. (Now, I think I’m down to just the folks I’ve pissed off in the comments.)

    My point, I guess, is that it’s not a CRUDdy search if you’re just wrapping the search options in a model. It’s CRUDdy search options. Searching remains out of reach for the CRUD metaphor, and Josh’s post was about trying to view search in a CRUDdy light. That doesn’t really quite work, because it inevitably boils down to talking about structuring search helper classes within the CRUD metaphor. But that’s still not search.

    I realize this isn’t much of a point, but if I had more to go on, I’d be looking for a book deal, not posting cranky articles on my blog. I don’t think we really disagree about much, I think I just pissed everyone off within the first paragraph or two. Sorry.

  23. jonathan Says:

    No problem! I know you’re a good egg, which is why I read your blog and responded.

    I’m still not clear on where you disagree with the idea of search being outside of the CRUDy metaphor, though.

    To me, if it is simple and search is really ‘within’ a single domain object, and search per se has no more significance to other elements of your application, then you’d be crazy to reify searches. That wouldn’t even be CRUDy, that’s just bad design–and I think that’s more or less what you’re reacting to. Parameterized queries are built into HTTP; as I said, that’s why they call it a ‘query string’.

    However, there are cases where it makes perfect sense to reify a search in the sense Josh suggests. Perhaps the search traverses multiple models. Perhaps it’s intended that searches be stored in association with their users. Perhaps searches can be sent to other users. Perhaps you’d like to draw a distinction between a search, and a search *result*. All of these might be sufficient cause to take the approach Josh outlined.

    I’d disagree with anybody who told me to do anything a certain way just because of ideology. Doesn’t mean I wouldn’t do it for my own reasons, though. :)

    I commented on Josh’s post because it was through studying the ATOM publishing protocol and generally meditating on things RESTful that I went the direction I did with search. When I heard DHH’s keynote, I kept thinking about how right he is that the simple act of encouraging yourself to think in only 4 verbs can lead to design insights. I also recall him saying that there will always be exceptions.

    I’m sure some dittoheads will start RESTifying everything, and that’s dumb. But it’s worth seeing how far you can push it, even if you go a bit too far. It’s a constraint, and good design requires them.

    Bottom line: I don’t see why search violates the CRUD metaphor, or at least it certainly fits into the REST/HTTP design nicely. If simple, single-scoped, and without other dependencies, then Read the resource with a query string. If complex, cross-cutting, and/or dependent or coupled with other important business logic or models, then make it a resource as Josh outlined.

    Whatever works!

  24. Coda Says:

    “Whatever works” would make for a good tattoo.

    Yes, technically you could call a transient search action a Read action, but it just doesn’t fit. In addition to boiling CRUD down to just In and Out, you’re also saying that whatever model behind that Read action uses a search query as a global identifier. That may fit the framework, but it sure as hell doesn’t fit the metaphor.

    And I disagree with your assertion that cross-model searches work better as CRUDdy objects. For one, CRUD gets really ugly unless its scope is limited to a single model: what kind of system would have a Read action which returns a Customer here, a Manufacturer there, and occasionally a Post, just to spice things up?

    The two areas, as I’ve said, in which search makes sense as a model is for permanent searches and asynchronous searches. But even there, I think it’s more accurate to call the former a collection of search settings, and the latter a search job. Both of them could be delineated from a search results model. These divisions are present within the domain of searching, and stuffing the functionality for all of these objects into a single model would, unless it made a hell of a lot of sense for a specific application, cause more hassle than not.

    Searching definitely fits into the REST pattern perfectly, but REST and CRUD are not necessarily the same thing. CRUD is something you can do over REST (though the POST verb kind of screws things up), but GET and Read are not necessarily the same thing.

    Using design limitations to drive creative insight is a decent strategy, but sometimes you honestly need to write a tanka, not a haiku.

  25. Chris Carter Says:

    Coda, I wrote a blog post about the CRUD revolution, and used your post as a basis of sorts, but I put a bit of a different spin on it. If you would like to read it, it can be found here http://concentrationstudios.com/articles/2006/07/03/the-new-crud-or-modeling-development-rails-with-rails

  26. Just As Loud » Blog Archive » CRUDdy Stir! Says:

    [...] CRUD is not the only user interaction metaphor, thank you [...]

  27. Peter Says:

    > For an online store, the information returned by a Read action for customers will be different than that of an action for store employees.

    I just put a couple hours of careful* thought into this since I am building an online store. I don’t see any overlap in the actions and a flat controller namespace would be fine. I was particularly worried about the actions in the product, cart_item, and cart controllers but all looks very clear. Which actions do you think will cause a problem?

    —-
    * doesn’t mean I’m right.

  28. Coda Says:

    Peter–There are some pretty good reasons to not have the same Read action for customers and staff.

    The public read action in my store returns only some of the product attributes. Lock_version, updated_at, created_at, etc. do not need to be publicly accessible, and aren’t. The product description is returned as rendered Textile instead of raw markup. The price differential is calculated, and only highlighted if greater than a certain amount. The list of products with which a product is compatible is collated and orderd by manufacturer into an easily parsable list. For each comparable product, a comparison is generated based on a certain business logic. Product options are flattened into a list, and product options which have no inventory in stock are not shown. Finally, two lists of products associated with the product, in terms of common purchase and superceded funtionality (e.g., second-generation antenna vs. first-), is collated, generating price comparisons where needed and appropriate.

    On the admin side of this, the staff see all of the product’s attributes, the SKUs and revision numbers, inventory levels, description markup, etc., etc., etc.

    Trying to smash these two actions into one would either depend on having the same action return different things depending on who I am (and not what I requested), or having the customer ignore potentially sensitive internal information. These are overlapping but mutually exclusive actions, hence the namespace. Your store may be completely different, in which case a flat namespace would make sense. Mine… we’re keeping the namespace. ;-)

  29. Peter Says:

    Coda, thanks for the reply. I’m going to push this point a little more and see what you think:) This is a useful discussion for me because I think that a change to a CRUD style probably means thinking differently.

    Of course the show actions were the ones I was most worried about and especially GET /product/5 (written the way DHH did in his slides).

    GET /product/5 is never what a customer really wants to see. A customer thinks they want to see a product show action but it’s not true. What the customer really wants to see is a page that allows them to create a new cart_item for product 5. After all, they are shopping and the objective is to add things to their cart and then checkout. They really want GET /cart_item;new?product_id=5. A customer does have database modification permissions for some models. And the admin side does not need GET /cart_item;new at all, unless they are shopping on behalf of a customer, in which case, they want to see exactly what the customer sees.

    Now, /cart_item;new?product_id=5 is an ugly URL but that is a problem for routing to sort out, which should not be too difficult. If not authenticated, map GET /product/5 to /cart_item;new and map POST /product/5 to /cart_item;create

    How does that sound?

    Actually it took me quite a while to realise that a product display is really a cart_item creation form. I was very happy with this realisation. I already have this in a live site but I didn’t organise the actions in the controllers I should have.

  30. Peter Says:

    If you don’t like the “if not authenticated” part in routing then always map GET /product/5 to /cart_item;new and map POST /product/5 to /cart_item;create. The admin can do GET /product/5;show to see the SKU etc.

  31. Peter Says:

    > Trying to smash these two actions into one would either depend on having the same action return different things depending on who I am (and not what I requested)

    But this happens all the time. I am a customer. I login to the store. I GET /product/5. And in the upper right corner of the webpage I see “Logged in as Peter Michaux”. I also see a _my_ cart summary in the left column.

    I like the comment one person made on another blog about the admin seeing the same views with bonus content and in-place editors. Maybe it wouldn’t work in all cases but the flat controller namespace would encourage it a little or at least make it a little simpler to implement.

    So having a view taylored to a particular user seems to be common and a good idea. I wonder how this fits into the strict CRUD-for-everything design concept. This may not be a complete fit but surely the CRUD way is to be taken with a grain of salt. There is a lot to think about with this new design philosophy.

  32. Coda Says:

    See, I think that the whole “regular interface, only with edit buttons” UI is a horrible idea for anything more complicated than a very simple CMS. It works as long as the properties you need to edit are simple and few; within those constraints, you’re right–that’s a good solution. But I find myself outside of those constraints all the time. The flat namespace doesn’t make this functionality possible, both the functionality and the flat namespace are made possible by the low complexity of the application domain.

    And I really think we’re getting away from what CRUD is on a theoretical level. Regarding the use of the Rails CRUD framework, bombs away. Hack that up as much as you like, and I won’t complain; hell, I’ll be really interested to see what’s possible with it. But using a CRUD framework to make your life easier doesn’t mean that you’re actually writing a CRUDdy application.

    When you’re talking about a new action, that’s not CRUD. That’s the UI for prompting a user to provide the information so the browser can initiate a Create operation. It’s outside the metaphor, especially as regards RESTful operations. In theory, you could explain it away by saying you’re making a Read action on a CreateSomething object which exists in the persistence layer as a Singleton, but then you’re talking about throwing C,U, and D out the window; plus you’ve lost all your wonderful model-controller sync.

    But we can ignore all that, provided you tell me the benefit of refactoring a browsing/searching oriented online store to be a CRUDdy interface for cart items, and how that outweighs designing an application which completely shatters the system metaphor of a store.

  33. Peter Says:

    > See, I think that the whole “regular interface, only with edit buttons” UI is a horrible idea for anything more complicated than a very simple CMS.

    True but for the medium hard cases, why not just fork to different view templates in the “show” action? It is the same data (eg a product’s info), maybe more or less of that data for some people, but with a different markup. I think this could work quite well for many cases before breaking down. And the product info page is the one sticky place in the whole store then a little workaround in that part and then the flat namespace is very good for everything else.

    > When you’re talking about a new action, that’s not CRUD.

    The “new” action to get a form is going to be in every controller even if it isn’t strict REST. This is because the server is not only providing data, the server is also providing the client with an application. If the client already had a store browsing application then we could go with just REST. If all online stores agreed to a data format (haha) then we could do this. I think this is where the REST-with-a-grain-of-salt has to come into play. It is pretty clear that DHH must be thinking along these lines because he wrote that the semicolon in the urls is supposed to look “less tasty”. He recognises it is necessary but that it isn’t completely RESTful.

    > But we can ignore all that, provided you tell me the benefit of refactoring a browsing/searching oriented online store to be a CRUDdy interface for cart items, and how that outweighs designing an application which completely shatters the system metaphor of a store.

    This is a little too terse for me. :S What kind of application interface will do this shattering? and will a customer intuitively understand it given the customer is already well familiar with the usual type of online store?

  34. Coda Says:

    Let’s say I make a RESTish online store. What benefits does that approach have over what I’m doing currently? How does it help me as a developer, and how does it help my client, who’s only trying to sell widgets, and could care less about my grand fun with new acronyms?

    And then how do those benefits compensate for that fact that my web application is modeling the database underneath it, and not the ideas I’m trying to store in the database?

    I’m trying to sell products, not rows in tables. Why expose the row/record metaphor more than I absolutely need to?

    I guess I’m looking for a cost/benefit analysis here, because it seems to me like the CRUD approach is a hell of a lot of work for the purposes of validating the CRUD approach.

  35. Peter Says:

    I only started looking at this CRUD stuff after DHH posted it on his blog. I thought, “this guy has been pretty clever so far and if he is evangelizing this as the future direction of Rails then I should check it out.” I read many helpful blog articles (yours included). Already my code is so much more simple and the ideas about what a site is really doing are more clear in my head. It gives a nice uniformity to the code. This could mean less decisions along the development path and easier maintaince. Less special cases is better. So I hope the answers to your questions are

    > Let’s say I make a RESTish online store. What benefits does that approach have over what I’m doing currently? How does it help me as a developer,

    I’m hoping easier and faster especially with the Rails goodies that are coming.

    > and how does it help my client, who’s only trying to sell widgets, and could care less about my grand fun with new acronyms?

    Cheaper, more robust website initially and in the long run.

    > it seems to me like the CRUD approach is a hell of a lot of work

    I would say that the CRUD approach already seems like less work. Have you tried making a CRUD controller like this link shows. It is really great.

    http://www.artofmission.com/articles/2006/07/05/rest-ful-technique-make-your-controllers-even-smaller

    Dressing up the CRUD approach in user friendly URLs is important but shouldn’t drive controller division of responsibility. Routing can take care of it.

    We aren’t trying to sell records but if the user wants to buy something they are trying to create records. The website is giving them friendly forms to do that.

    Like everything new I think we have to give it a serious try before we write it off as worse than the status quo. I’m doing that now.

  36. Coda Says:

    The link you provided doesn’t quite prove your point. It demonstrates taking what appears to be a really ugly CRUD controller and turning it into a clean CRUD controller by having the actions of the controller actually line up with the actions of the user. I mean, one of his actions was save_and_update. Yikes. So yeah, thinking about CRUD helped him–his original design was a mangled version of CRUD, his new design is a more standard version of CRUD. But that’s not viewing something which isn’t CRUD through the lens of CRUD, it’s implementing the thing correctly the second time around. A good idea, but not quite novel.

    My contention is not that the CRUDdy bits of Rails don’t help with CRUDdy applications. They do, that’s great, and I’m happy to see those changes. My contention is that there are massive amounts of functionality which cannot be mapped to the four operations of CRUD without breaking the CRUD metaphor.

    How do you crop an image with CRUD? How do you browse a hierarchical set of categories? How do you retrieve all non-published articles? All articles which have the approval of the desk editor, but not the section editor, which haven’t been flagged for review, with all their associated images, all the while checking that these actions are permissable according to the appropriate ACL with CRUD?

    Some stuff just plain doesn’t fit. When the actions of your users map out to CRUD, you can use CRUD patterns. Sometimes you can fudge it a bit, and that’s cool too. But there will be plenty of times in which the complexity of the application domain is too high for this, and you’ll need to actually start designing your controllers according to the actions your users want to perform, not according to the way your database or web application framework is structured. At some point you’ll be looking at your controller’s actions, each of which is 80 lines long and contains nested case statements inside conditionals, each with an average of 10 different return paths, and you’ll wonder where the hell all that simplicity went.

  37. Peter Says:

    > At some point you’ll be looking at your controller’s actions, each of which is 80 lines long and contains nested case statements inside conditionals, each with an average of 10 different return paths, and you’ll wonder where the hell all that simplicity went.

    I’m a little worried about this and wonder if it will happen to me. Great discussion. Thanks.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>