Applications tend to grow over time. In the beginning of an application’s life, things are typically pretty easy. Rails provides “slots” for us to put our code in. Display-related code goes in the views. Domain logic and persistence-related logic go in the models. The code that connects the two goes in the controllers.
But over time the slots fill up. Models get bloated to hundreds or even thousands of lines. Models become miscellaneous grab bags of unrelated methods. Controllers get cluttered with custom actions and grow large and hard to understand. Entropy takes over and things become a mess.
At this point, many developers sadly head down a bad path. They observe that, early on, Rails helped them with everything including code organization, and so now that they need more help, they go looking for “more Rails”. They’re run out of framework and now they want more framework. They look for tools and libraries to help them organize their code. But what’s needed isn’t tools and libraries. What’s needed are code organization skills. And tools and libraries can’t be a replacement for skills.
Frameworks are really good for certain things. They’re good for abstracting away repetitive or low-level work and giving developers leverage. Frameworks can also be good for imposing structure on a codebase, but only within narrow limits. Before long, simple heuristics (e.g. “put this type of code in models, this type of code in controllers,” etc.) cease to be enough. At that point an actual human has to take the reins from the framework and apply judgment. Non-trivial codebases are too large to be kept tidy by the simplistic heuristics that frameworks are able to provide.
If “more framework” and libraries and gems aren’t the answer, what is the answer, exactly? That’s basically the same question as “how do I write good code?” People have of course written many books attempting to help answer that question. There’s a lot to it. Learning the answer takes years.
To me, writing good code has to do with choosing clear names for variables, classes and methods; conceiving of clear and useful models and abstractions; refactoring regularly in order to keep entropy under control and keep the code understandable; and using automated tests to ensure that refactoring is possible. The best single resource I can recommend to cover these topics in a well-rounded way is Code Complete by Steve McConnell.
Regarding structure, I personally use a lot of namespaces and POROs and my Rails apps. The namespaces help me keep related things next to each other and signal to me, when I’m trying to understand a piece of code, what surrounding code is relevant and what I can ignore. Organizing my model behavior into POROs helps me avoid bloated Active Record models. When I notice that an Active Record model is starting to lose cohesion, I’ll often look in my model for “missing abstractions” and split those missing abstractions off into new POROs. The resulting structure has very little to do with Rails. I’m mainly just leaning on the principles of object-oriented programming.
I think Rails is the greatest web framework ever created. The things Rails helps with, it does a fantastic job of helping with, including helping to organize code during the early stages of an application’s life. But once your application grows beyond a certain size, Rails can no longer help you much and you’re on your own to decide how to keep your code organized.