Or MVC in general
Ruby is awesome, and Rails makes it even more awesome. I started my journey with Ruby on Rails about a year ago in order to make an API for asknative app for iOS. Before that I only did very little PHP and mostly frontend stuff. Anyway, here is what I wish someone told me when I was getting started.
1. Cache from the start
Caching is critical. If you didn't cache from the start you are never going to cache. Caching is a pain in the ass, and as long as you delay its going to be harder to add. Thinking that there is no need for adding cache, when you are getting started because there is no complex queries yet. Is the first step toward a bigger problem later.
In our case, we tried to add caching later in the process, and failed to do so. We've spent around 3 weeks trying different solutions with no luck. We ended up delaying this issue again, because the serialization gem we were using was not very handy with external cache, and requires to use its caching layer. Their caching system didn't work well with us, and they were rewriting their gem again to improve it and rethinking about cache. So now we either remove this gem or just wait till they add caching.
I'm not blaming them for that, it’s our problem that we didn't find out about this from the start. So do yourself a favor and setup caching from the beginning.
2. Never slack on Testing
Startups is about sprinting. Features will be added, modified, and removed and it has to be done fast. We tend to think the testing is an overhead. Maybe because your tests take too long to run. Maybe because you see the time take for adding necessary tests will be a waste of time. Maybe because specs are going to change anyway. Or maybe you are so confident of your code that you don't need to test your code.
The moment you say we'll do tests later, you are surrendering to your code. Testing is never a waste of time. And it is very important specially when you are in a startup. Because there is alway a constant change it means that you need to have a way to be 100% confident when you apply those changes to production.
“Move fast and break things”
Of course, but that doesn't mean you should do it intentionally.
Testing is tedious, takes time and effort to do it right, not fun, and sometimes it takes longer time to fix testing bugs. But having tests will make you the owner of your code, not a slave to it. Scared of touching your code or others code not to break anything. Scared of pushing to production, because you have to test it over and over to make sure nothing breaks.
3. Lazy requests
In any request, avoid doing any work as much as possible. If there is a task that is not directly related to the request, it should be running in the background as a worker.
Lets say there is a request that should update user profile. You only need to validate the data, then return ok to the frontend. Unless you are working with sensitive information, there is no need for the extra delay added for inserting or updating record in the database. Even if the request only sends one request to the database there will be callbacks and triggers firing all over the place before, after storing.
Maybe you're afraid that something would go wrong after accepting the request and you have to be absolutely sure. But how often does that happen? Lets say 90% of the request will get accepted, so why not make it faster for those people. Why do we have to make them suffer because of that?!
4. Put your code on diet
“Thin Controller, Fat Model” OR “Just move all your logic to models. It’s that simple.”
These are the worst advice ever. I don't know why a lot of people in the rails community are recommending this, but it has to die. Models are not your magic bag.
At first I was using models for everything. Everything that does something that used to be in the model. But browsing the model was nightmare. Attributes, queries, callbacks, validation, methods. One class rule them all.
Moving to Concerns seemed like a good option. After all its added by default in Rails 4. Moving all the common code in there, even it was used as a component for new features. Models looked much better, but then we realized that concerns is hiding under the rug. At some point scopes were a mess. And adding logic that talks to different models there makes the matter worse. Concerns are useful in very little cases, like having same attributes share between models, maybe queries too (but it’s arguable)
MVC is great, but its not enough. Don't limit your choices to either make fat controller or fat model, it will make your code harder to maintain. If you have bunch of code that needs to do something specific with many models, why not just create an object for doing that. Has its own api, and does a simple task, and thats it.
Working with single responsibility objects makes it easier to test, easier to read, easier to modify, easier to replace.
People used to put logic in controller for a reason. It can tell a story. If you read a controller you should have an overview of what it does. And its a good practice for the upper layer to control the ones below, and avoid same level objects to communicate to each other.
Having a middle layer between Controllers and Models minimize if not eliminate any logic in the controller, controllers will be more like a story. And changing models will not affect controllers because it doesn’t know about models. Service objects become like a genie, controller asks, and service objects will make that happen.
This applies to the Views too. You can add View Models or Presenters to pass the data into your views. Without making your views know anything about the models, relations, or how to format the data. Views will be so dumb that you don't have to test.
The less each layer in MVC knows about each other the better. If you changed your database from MySQL to MongoDB it won't matter. Each layer will be independent, and applying changes will be much easier.
- Think about caching from the start and get it to work.
- Never submit code without it’s tests.
- Never work with database in a request unless its absolutely necessary. Other than that, throw it in worker in the background.
- Use PORO (plain old ruby objects) to create layers that separate each part in MVC.
If you find this useful, please share it ☺