User.create(params[:user])
You see a simple line of code, and you can immediately say (assuming you know User is an AR model) what it’s doing. The problem here is that people confuse simplicity with convenience. It’s convenient (aka “easy”) to write this in your controller and get the job done, right?Now, this line of code is not simple, it’s easy to write it, but the code is extremely complicated under the hood because:
- params must often go through db-specific coercions
- params must be validated
- params might be changed through callbacks, including external systems causing side-effects
- invalid state results in setting error messages, which depends on external system (i.e. I18n)
- valid params must be set as object’s state, potentially setting up associated objects too
- a single object or an entire object graph must be stored in the database
This lacks basic separation of concerns, which is always damaging for any complex project. It increases coupling and makes it harder to change and extend code.
This feels like the core of the argument of the whole post. It does not seem correct. In isolation, the call to User#create seems magical. But there's not enough context here to criticize it. We don't know enough to say whether there's inadequate separation of concerns.
No matter how we handle "users", we are going to need to do all 6 things in the list the author presented. No matter how we structure those 6 things, there is going to be a single entrypoint function that kicks them off --- end users won't be pushing 6 different buttons to drive them. So: what's the better design here?