HN Books @HNBooksMonth

The best books of Hacker News.

Hacker News Comments on
Rails AntiPatterns: Best Practice Ruby on Rails Refactoring (Addison-Wesley Professional Ruby) (Addison-Wesley Professional Ruby Series)

Chad Pytel / Tammer Saleh Pytel / Saleh · 5 HN comments
HN Books has aggregated all Hacker News stories and comments that mention "Rails AntiPatterns: Best Practice Ruby on Rails Refactoring (Addison-Wesley Professional Ruby) (Addison-Wesley Professional Ruby Series)" by Chad Pytel / Tammer Saleh Pytel / Saleh.
View on Amazon [↗]
HN Books may receive an affiliate commission when you make purchases on sites after clicking through links on this page.
Amazon Summary
The Complete Guide to Avoiding and Fixing Common Rails 3 Code and Design Problems As developers worldwide have adopted the powerful Ruby on Rails web framework, many have fallen victim to common mistakes that reduce code quality, performance, reliability, stability, scalability, and maintainability. Rails™ AntiPatterns identifies these widespread Rails code and design problems, explains why they’re bad and why they happen—and shows exactly what to do instead. The book is organized into concise, modular chapters—each outlines a single common AntiPattern and offers detailed, cookbook-style code solutions that were previously difficult or impossible to find. Leading Rails developers Chad Pytel and Tammer Saleh also offer specific guidance for refactoring existing bad code or design to reflect sound object-oriented principles and established Rails best practices. With their help, developers, architects, and testers can dramatically improve new and existing applications, avoid future problems, and establish superior Rails coding standards throughout their organizations. This book will help you understand, avoid, and solve problems with Model layer code, from general object-oriented programming violations to complex SQL and excessive redundancy Domain modeling, including schema and database issues such as normalization and serialization View layer tools and conventions Controller-layer code, including RESTful code Service-related APIs, including timeouts, exceptions, backgrounding, and response codes Third-party code, including plug-ins and gems Testing, from test suites to test-driven development processes Scaling and deployment Database issues, including migrations and validations System design for “graceful degradation” in the real world
HN Books Rankings

Hacker News Stories and Comments

All the comments and stories posted to Hacker News that reference this book.
Oct 08, 2012 · dkannan on Will Rails be the new PHP
check http://www.amazon.com/Rails-AntiPatterns-Refactoring-Addison...

other than that, 1. write tests 2. good community. for some measure of good

This is certainly interesting. I like the event based model. Here are my thoughts:

If your controllers are getting fat and spaghetti like, I wouldn't blame MVC. There are many patterns one can use that are compatible with MVC without going to a completely different architecture. For instance, if you need to encapsulate multi-model interaction you can create a presenter abstraction that the controller simply calls into. Just because there is a bunch of code that ends up in the controller because "you don't know where to put it" doesn't mean there isn't a better place to put it without re-designing the entire application.

Another thing is while I love the data-binding event deal, this really only works for client-side frameworks like Backbone. Server-side kinda goes at the window, but I suppose you could add a framework that piped event signals in the response from changes that occurred in the ORM layer (although this further couples components).

I'm not convinced that separating the data from the methods that operate is a great idea unless you want to go purely functional. Doing this clearly breaks encapsulation, throws any notion of OOP out the window, and still binds the "operations" too closely to the data. Generally speaking, the data is useless without the operations and vice versa.

So the real difference between MOVE and MVC, at least according to this article, is that models are no longer useful on their own, the controllers aka "operations" know everything about how to manipulate models (it shouldn't), and models generate events which are only consumed by views, which makes no sense if you wanted to use the model on its own (but you can't without your operations).

Generally speaking, the model should MODEL something. That includes data and behavior. Without both, you have a database, and a set of operations kinda like a C library but only because OOP hadn't been invented yet.

In my mind this further couples all of the components because it actually increases the reliance on each other for them to be useful.

MVC isn't dead. There is a good chance you are using it wrong. I highly suggest Rails Antipatterns. While it is specific to Rails, it works for MVC in general. http://www.amazon.com/Rails-AntiPatterns-Refactoring-Addison...

edit: spelling

ricw
Couldn't agree more. As long as you find a consistent way of splitting and placing your "Controller" code, MVC works perfectly fine. The problem is that a "Controller" is only vaguely defined and always application dependent. However, finding a good split and place for code is not easily accomplished and, in my experience, needs a couple of iterations to be perfected. The MOVE split seems useful for event based systems (webapps?), however it doesn't seem to encapsulate as wide a range of apps as the more vague MVC does..
None
None
pestaa
Sorry, but data-bound events are not useless in server context. It might be exactly what we need when fat controllers go out of control. Perhaps you meant they are too much of a complication if taken too far to work across all concurrent requests?

Binding data and behavior together breaks the rule of single responsibility, which I believe is far worse than designing separate roles in sepearate components that work together. Models should model the data structure only, if you need a presentation abstraction anyway for multi-model operations. Why not go the extra mile and use this abstraction level for all operations?

It is a false dichotomy that you go either full-OOP or purely functionaly. Models wrap knowledge as the article suggests, That means that, in addition to getters and setters, they might contain functions that let you check "is this the user's password?". I envision these methods to work on a single attribute, whereas multi-attribute and multi-model business logic belongs to operations.

MVC isn't dead, but it was a local maximum OOP gave us. Event-driven development seems to raise and work well with objects and datasets.

alttab
If "fat controllers go out of control" then you've already made multiple mistakes. I don't think re-inventing the wheel is the right approach to fixing a problem when the developer can understand their own mistakes instead of blaming a framework or architecture.

Are you suggesting that OOP breaks the rule of single responsibility? Any 'operation' that modifies the internal data should belong on the model. Would turnLeft() belong on a Car model? Or do you pass a Car into a turnLeft operation? In my mind a turnLeft operation shouldn't know the details of how to turn a car left, or a bike left, or a boat left. These operations belong in the Car, Bike, and Boat.

drone
Somewhat pedantic, but just throwing this out there: cars, bikes, and boats are all forms of transportation which can turn. They should probably inherit the concept of turning left, and then specify the exact meaning in their context.

Obviously, I don't say "turn your handlebars such that the left side is closer to your chest," or "rotate your steering wheel counter-clockwise," when I mean to tell someone to turn left at the next intersection. I might, however, tell a sailor to steer to port at the next buoy. =)

EDIT: forgot to say, fwiw, that I agree that such actions do not belong in the model. The things go in the model, and the only actions the model should support are those relative managing the data in the model, not interacting with the data... That is, after all, what the controller is for.

alttab
Im having a time parsing what you mean. Are you saying a car should not turn left, and that a controller should turn the wheel left? Please clarify
drone
... it was a roundabout way of explaining how right you were, or at least in my initial reading of it.

I see thing which is called a Car, an instance of which is stored in the Model, which may contain other things, including other types of Transports. The user interacts with the View, instructing a left turn - the controller grabs the Car (why it grabs the Car, and not the Bike is undefined =), and tells it to turn left. None of the M, V, or C understand what a left turn is - but the controller knows that the Car can perform such a thing if instructed. The Controller knows it can tell the Car to turn, because the Car is descended from a Transport, and all Transports have a virtual method for turning.

That last sentence was in my head and unfortunately took over my comment.

alttab
Ah I understand. Yes polymorphism in this case would hide the details, and the implementation for turning the car belongs inside of that object.
einhverfr
I dunno about the last part.

I think any logic inherently tied to the data should be in the model. The controller shouldn't have to worry about internals of the model any more than necessary.

Here's an example. Suppose I create an i18n framework for handling numbers. I certainly am going to include in it a function for converting the number into the current user's localized format, and another one for converting the number from the current user's localized format. I will probably also have functions to convert to/from db formats and to/from arbitrary formats. Not all of these will be called by the controller (in most cases, just the ones to/from the user's localized format and maybe an arbitrary format on rare occasions).

This way the controller script can just call something like $number->to_output; and get the localized string back. Other model objects can call $number->to_db and get a string suitable for entry into the db.

Interestingly in LedgerSMB 1.4 where we have done this, the number of cases where the model calls number i18n functions are remarkably small. These are usually called in the model or in the view simply because the controller probably shouldn't have to worry about this, and the view gets to worry about display logic.

drone
Converting the display format of data when requested by a view is exactly what a model is supposed to do.

However, instructing a contained object to take some action at the user's request that is unrelated to display or management of contained objects is neither the responsibility of the view or the model, but specifically the task of the controller.

einhverfr
Ok, so let's go further.

I want an email to be sent to me when the quantity on hand of a part gets below the re-order point.

So now, I build a program that listens for notifications, and when it gets one, loads the data, erases the queue, and sends me an email.

I build this so the app itself doesn't know this is required or is going on. Database triggers, queue tables listening scripts, and templates all are triggered when the db write takes place.

Now, what we have here are arguably two MVC environments where the model behavior of one triggers a model change in another, which triggers a controller to run, calls some other model stuff, creates an email (via a view) and sends it.

But this is the sort of action you are talking about, right? You think this violates separation of concerns?

drone
This is very unclear - the database is not the model in the app, it's the storage for the model. (In its own application though, it may be a model...) You've abstracted some application functionality into the data storage, ok.

I'm speaking to the point that if you are separating Model, View, and Controller, and you have complex objects in your model that can do many things, it would not make sense to put all of the logic about what those things can do, how to do them, and when to do them in the model. Otherwise, you're simply blending the model and the controller (which may not be a wrong thing to do) - so you just have MV.

Let me give a more concrete example: I have a model which represents a network of connected complex "devices," there are five types of devices, each that can do 50 distinct things. The model can add a new device, delete a device, or retrieve an existing device. It can also ask the devices what their names, and addresses are, and what features they support, on behalf of any view which needs that information.

It cannot, however, tell device 1, which is of type X to take action "foo". That, instead, is implemented by a controller attached to a view that shows a big button that says "Do Foo", and a drop-down with the list of available devices that can do Foo. The view passes the press of the button onto the controller, which asks the model for the selected device, and then executes a "do Foo" command on it, and then, perhaps, contacts the device again to check that "Foo" was completed.

If all such logic were built into the model, I'd have several hundred methods in it, and I'd be very quickly looking for somewhere to move all of this logic, like, you know, controllers.

einhverfr
The database is not the model but the db operations are usually in the model, right?

My point though is in essentially shimming functionality. In my example, for example, here is how it would work.

1) User requests "save this invoice", which activates a controller script which automates the model elements for "invoice" instantiates an invoice (since this is a stateless web app) and saves it.

2) The model saves the invoice by writing it to the db.

3) Saving to the db fires a trigger which checks ROP vs on-hand and where ROP is greater saves a record in a queue table and issues an asynchronous notification, which doesn't have any immediate effect.

4) Controller commits the db transaction. The DB sends out the notification and makes the rows in the queue table visible.

5) Controller for application b wakes up (let's say every 15 min) and polls for notifications. Ok, we got one. Look up the queue table, prepare the email (model stuff) runt he template (view stuff) and sent it out (model stuff again).

So what I am getting at here is a way to have model events have side effects which are outside the scope of Application A, but are at least triggered by model operations. This is not an anti-pattern really because application A is not relying on those side-effects for operation. However if you look at application A as the primary one and application B as contained within it, then application B can only be described as contained within the model of application A, from application A's perspective, because all of its functionality is abstracted behind the model. From application B's perspective, there is a model, a view, and a controller, and it doesn't care as to where notifications and queue table data is coming from. From it's perspective it is a loosely coupled app in its own right.

But as always what I am suggesting is that common sense usually determines the best place for a given piece of logic. LedgerSMB is very model-centric but there are cases where we have a fair bit of logic in the controller.* I think that anything which is inherent in the model belongs in the model. So back to the vehicle analogy, a bike object should support a $bike->turn('l') and a $bike->turn('r'). However telling the bike when to turn left is the controller's job.

* for example in contact and employee management, the controller has to coordinate a lot of different model operations, leading to unusually long/complex model code in the system.

Read this: http://www.amazon.com/Rails-AntiPatterns-Refactoring-Addison...

Easiest mistake in large rails apps is to let your controllers get too thick...

The book Rails AntiPatterns addresses many: http://www.amazon.com/Rails-AntiPatterns-Refactoring-Addison...
geekfactor
This looks like a really good resource. Thanks!
joshuacc
Refactoring Redmine is also good for detailed case studies. http://www.refactoringredmine.com
HN Books is an independent project and is not operated by Y Combinator or Amazon.com.
~ yaj@
;laksdfhjdhksalkfj more things
yahnd.com ~ Privacy Policy ~
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.