These are the practices you should follow for robust and well maintained code.
Single Object Responsibility
In object-oriented programming, the single responsibility principle states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility. The reason it is important to keep a class focused on a single concern is that it makes the class more robust.
Read more on c2
Well named methods and classes
"There are only two hard problems in Computer Science: cache invalidation and naming things.” - Phil Karlton.
The importance of well-named classes and methods can't be emphasized enough. When another developer (or you, a couple of weeks down the line) is reading the code, the names should make it obvious what the class / method is supposed to to. At the very least, keep away from cryptic abbreviations, as illustrated by Steven Deobald here.
Single assertion per test
Tests are easier to read if there is a single assertion, that tests a particular aspect of the functionality.
The above test can be split up into the following
Rails Best Practices
Skinny controller, fat model
Push all logic to the model.
In fact, there are standard patterns available for each of the 7 RESTful actions, so try to fit into one of them.
Use ! as a name for any method that saves in the database
By convention, ! is used for any ruby method is dangerous in some way. One dangerous effect that we often care about is that modifying an entity that will be persisted.
When to use this also fits in to transaction management, and these are decisions that should be made on a case by case basis.
But, for the most part, it's usually a good idea to name methods that do a save in the db as a side effect with a !, and use either exceptions or the errors collection to handle error scenarios.
Do not use RJS
This is bad for multiple reasons:
- Security implications of eval() on the client side
- Debugging requires you to keep a lot of context about what state the dom is in. In the above example, it is important to know that the .story div is hidden
- The API that we have written is pretty much tied to a particular page. If i'm calling my API from another screen, I will need to create another div with the same class (story)
Use handlebars to render AJAX calls
Maturity for AJAX calls is as follows:
- Worst: Use RJS (see above)
- Bad: Render HTML in the output of the method, and just attach that HTML to the DOM
- Good: Let the API return some JSON. Use Mustache or underscore to render
Have a very RESTful API
REST is based on a PHd paper by Roy Fieldings.
Borrowing from REST, the rails world has evolved it's own set of best practices around how to build a URL. One thought is to have every URL be one of seven standard actions. Every modification of a resource can be modeled as a CRUD operation on another resource.
can be remodeled as follows
In the latter case, we are creating a cancellation resource within an order. Internally, the service will cancel the order as the cancellation object is created. The cancellation object may or may not need to be actually stored in the database, or even modeled. The cancellation object is a facade to the external world, to help any HTTP aware client understand your interface.
C2 also has a nice article on REST
Make sure you return the correct error codes
A fundamental part of REST is to leverage HTTP status codes to describe the results of the recent operation.
Some important status codes that we use more often than others are
- 200 => :ok
- 201 => :created
- 30x => redirect_to (url)
- 404 => :not_found
- 422 => :unprocessable_entity
- 500 => this happens when the server crashes. Avoid this at all costs.
Also, see the full list of status codes.
Do not expose too much in a JSON/XML API
Remember that once you expose an API, you will have to honor it. Be very conservative, especially when exposing nested resources, and belongs_to relationships.
For most apps, you probably want to handle transactions at the controller level
For most CRUD operations, you probably want the entire operation to happen, or not happen. Transactions are something that should be studied on a case by case basis. However, for most cases, the easiest place to start and finish the transaction is at the controller level, so that your model is not littered with it.
Use settings logic to push configuration into files
Settings logic is a great way to have configuration outside of the application, and is a fantastic alternative to using global variables.
Especially when you are doing a feature toggle, or something else of the sort.
Not every model has to be database backed
One common anti pattern that is often observed is to have every single class inherit from ActiveRecord::Base.
This is clearly a no-no. Like with every other programming language, in ruby you will need classes to encapsulate various behavior that does not need to be persisted to the database.
You can include ActiveModel into any class to get some of the nice things you get from ActiveRecord, like to_json, etc...
Controller tests should include an assert on the status code
This will provide you with a helpful failure if your test fails due to a permission error / internal server error. Thus you readily know that the failure is due to something outside your actual assertion, rather than have to figure that out working backwards from a failure message related to your actual assertion.
The failure here doesn't provide a clue that the problem lies in the test setup. But when you assert on the expected status code:
Now you know the problem lies elsewhere.