I get the rush to provide technical comparisons to something that was just revealed five minutes ago, but none of what you just said is actually how Hotwire or Turbo works. There's no client DOM in memory on the server, there's no html buttons that call native code.
There are forms being submitted, there are normal requests happening, there are templates being rendered on a per-request basis (just like a full page load).
This is like a normal web application that renders HTML, from the perspective of how the server works. Just as scalable as every other Rails application that renders HTML. Be that GitHub or Shopify or Zendesk or Basecamp or HEY or any of the many, many other apps that have long ago definitively proven that Rails Scales.
Although I do find the commentary that the current #1 comment on the HN thread is literally a "bUt DoES iT SCaLE??" take, based on a misunderstanding of how this works. All is indeed as it's always been
Then you can centralize all your configuration in config/environments/* and config/initializers/. The best practice being that you set the configuration in config/environments/ and you read that configuration point and do something with it in config/initializers/*.
I'm working with code bases that have passed the decade mark now. They're not of trivial size. They're still imminently maintainable. They are proudly Rails Applications.
Everyone has their own definition of complexity. I make no mince about a decade worth of developing Basecamp is where I draw my primary experience from.
That system is small by web scale standards -- only 70 million requests/day, 1.5 terabyte of DB data, half a petabyte of file storage, two data centers, and about 100 physical machines -- but probably still larger than 97% of all Rails apps.
Also, plenty of data stores (memcached, redis, multiple MySQLs, solr), many 3rd party libs, job servers, integrations, and more.
So no, it's no Facebook or Yahoo or Google. But it also isn't a toy system, except in the sense that we're still having so much fun playing with it.
Responsive views only go so far. Trying to shrink an app designed for a desktop browser down to a 4" phone is a fool's errand. Much easier to go with different templates in that case.
Love when people play code ping pong. There's too much talk and not enough play in these threads. But I have to ask you, do you really think that splitting out those two classes improved things for the example? Or was this just future coding for possibly-maybe extension points? Because from where I'm sitting, it made things less clear and harder to follow with no additional upside.
Yes, when stumbling across bad code, the first instinct should be: how can I make this simpler. Not how can I wrap this bad code in more convoluted patterns. Don't use a big word when a small one will do.
Additionally, I find that the key problem with bloated models stems from missing domain models (often has-many-through models). Not from moving the logic of the existing models into more noun classes. In your example here, the purpose of the ticket is to tie a user to a grouper: that's exactly the place to put logic that governs that connection!
Finally, what gets my goat is this notion that these patterns are necessitated by "advanced deployments". As it was some law of nature that when your app hits a certain size, you have to introduce this litany of misfit patterns. That's like arguing that if your book is longer than 300 pages, it must also use really big words and complex sentence structures. What?
The solution to large applications is to double down on simplicity, not give up on it. It is even more important to get the basics right. Execute them beautifully before even contemplating to freewheel from there.
On a separate note, I feel like this article series might better be titled "The Missing Parts of Our Knowledge of Basic Rails Features".
Reinventing basic features doesn't make your Rails deployment "advanced", it just makes it convoluted. There's no bonus prize for introducing fancy patterns to situations that do not call for them.
Further more, here's the definition of the Active Record pattern, as described by Martin Fowler: "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data". The key part is that last sentence.
So a quote like what follows simply misunderstands the purpose the Active Record pattern: "A recurring theme in these posts is that ActiveRecord models should be very simple interfaces to a datastore – the User model should be responsible for validating and persisting attributes of a user, like name and date-of-birth, and not much else."
See the example diagram (which I actually drew for Martin back before even starting Rails!): http://www.martinfowler.com/eaaCatalog/activeRecord.html -- it includes domain logic methods like "getExemption", "isFlaggedForAudit", and so forth. Exactly like the pattern describes.
Ensuring that the Ticket is valid is obviously a domain model concern. Policy objects can be a fine idea when they're swappable and you need to allow for multiple different policies. This is not one of those cases.
Here's a much simpler approach that keeps the validation logic in the domain model and uses features that Rails has had since the dawn of the framework: https://gist.github.com/dhh/9672827
I asked for specific code, you gave me specific code, so here's how I would have structured that without the (imo, needless) command pattern: https://gist.github.com/dhh/9348053
It includes a lot of more context than your example, so I was guessing liberally at the missing pieces. But hopefully this is still easily digestible.
Let's just say I have philosophical differences with the views presented in that presentation.
Also, I never said never. I said this particular example was a poor example and the general principle derived from it was equally poor. Garbage in, garbage out.
Why would the delivery of the emails fail? Because your SMTP server is down? That's an exceptional state, handle it with exceptions -- not with conditions.
Or maybe because the email addresses are invalid? Handle that when they are captured. It's way too late for that here.
If reassigning a case is part of the domain, it belongs in the model.
I don't know how to put this gently, but this concoction looks like a hot mess with no separation between modeling and controlling.
These commands have a ton of domain logic all tangled up with activity reporting, formatting, and parameter parsing. It looks like a big ball of mud to me :(
Yes, rewriting a system after it's already settled and designed can indeed bring a cleaner code base about. But don't confuse that with "better architecture".
Don't even get me started on the hand-wavy "loosely coupled".
If this is a poor example, pick a good example. I'll be happy to code ping pong you whatever example you choose.
One way to spend hundreds of thousands of LOC on an application is to stuff it with needless abstractions. That doesn't make it "advanced", and it's not Rails that's falling over, it's probably just some shitty code.
Maybe that's a clue that your chosen paradigms weren't that helpful. Or rather, that they convoluted the code base instead of clarified it. It's certainly a red herring that you need to teach your abstractions to new developers and that it's an endeavor to do so.
POROs are great, though. Our app/models is full of them. We even added app/services too. The trouble I have is with people who, like you, fall in love with the flawed notion that their application is such a special snowflake that it deserves "advanced techniques". Bullshit. There are good techniques and bad techniques. Size of the application is rarely a factor, except in ballooning the ego of the programmer.
Anyway, the invitation stands. Present any piece of real code and we can ping pong on it. I enjoy talking about specifics and improving real code. I detest "oh that was just a poor example, but the general principle is..." kind of debates. If you can't produce a good example, you don't have a general principle.
Here's another version that doesn't even use private methods in the controller and uses a PORO for the email grouping: https://gist.github.com/dhh/9333991
Coulda, woulda, damn shoulda. You're future coding with your "what ifs". Most controller actions are not reused. The time to extract for reuse is when you need to reuse. Not crystal balling about that you probably will be in the future.
Because when the reuse case actually arrives, you might find that you need to reuse some, but not all of the action. If you're literally doing the exact same thing for both web and api, why are you using a separate API controller? Just use respond_to with formats.
Second, yes, you shouldn't have side-effects like sending email in your models. But you don't need to! Just stick that logic in your controllers. That's what it's there for -- to render the views (of which emails is one of them -- see http://david.heinemeierhansson.com/2012/emails-are-views.htm...).
The AR callbacks are wonderful for coordinating the domain model. Often times you'll want to create auxiliary objects when something else is created. That's what's it's there for. Or to otherwise keep the integrity of the domain model in place.
I rewrote the code in this example to use the "Beginner's Version" of Rails (sigh). You judge which you like better: https://gist.github.com/dhh/9333694
There are forms being submitted, there are normal requests happening, there are templates being rendered on a per-request basis (just like a full page load).
This is like a normal web application that renders HTML, from the perspective of how the server works. Just as scalable as every other Rails application that renders HTML. Be that GitHub or Shopify or Zendesk or Basecamp or HEY or any of the many, many other apps that have long ago definitively proven that Rails Scales.
Although I do find the commentary that the current #1 comment on the HN thread is literally a "bUt DoES iT SCaLE??" take, based on a misunderstanding of how this works. All is indeed as it's always been