6 ms·
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".
by dhh 13y ago
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.
- nottombrown 13y agoYou can't possibly think that there's never a use case for the Interactor pattern. Please watch Uncle Bob's talk: http://www.confreaks.com/videos/759-rubymidwest2011-keynote-architecture-the-lost-years http://www.confreaks.com/videos/759-rubymidwest2011-keynote-...
- dhh 13y agoLet'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.
- rhizome 13y agoI can imagine people thinking that this is a strength, but my frustration with that talk is that it isn't presented with much to do with Rails.
- halostatue 13y agoI can possibly think that there's never a reason to reach for a pattern when it's better to let a pattern emerge from your code and then refine it. As far as the video is concerned I personally won't bother to watch it—Bob Martin sells snake oil as “clean code”.
- tomblomfield 13y agoMy contention is that there often are times when the complexity of the business requires a fairly extensive set of operations to occur in concert, including the creation of several models, email-sending etc. In this case, the Right Place (tm) to put these is in POROs / Interactors that can be tested in isolation and re-used across the code-base if necessary. This code should not live in controllers and certainly not in ActiveRecord models. It sounds like you agree with that, since you're using them in your own codebase? In that case, is there not an advantage to standardising the API for these POROs in Rails? Simple methods like fail!, success?, failure? and message Another way of asking the question - why do so many Rails developers fail to realise that they can use these concepts? Why are so many Rails codebases packed full of enormous ActiveRecord models that are crippled with numerous before_save callbacks? Sure, shitty coders write shitty code. But can't you lend them a hand?
- cgag 13y agoI don't get why POROs would need some special API. I also don't get why you're pulling code out into things like ConfirmGrouper that still depend on the DB. Why not a true PORO and something like ConfirmGrouper.for(grouper)? Why not just write methods that return bools? Though I don't know if I agree that the actual sending of emails belongs in a PORO either and not a controller. It seems like determining whether or not emails need to be sent is the kind of logic I would put in a PORO and do the actual email sending from a controller method as in DHH's example. Edit: Just saw DHH's second example, that's essentially how I would write it.
- ajsharp 13y ago> In this case, the Right Place (tm) to put these is in POROs / Interactors that can be tested in isolation and re-used across the code-base if necessary. Key phrase: "if necessary". It sounds like you're advocating for building abstractions before you actually have a reason to use them. Your reason seems to be "we may need to reuse them in the future" or, "one can see where we might move in a direction where we'd want want to re-use this code", or worse yet, "we're definitely going to build some functionality after this release when we'll want to re-use this stuff." I've been there, really, I have. Build the abstraction when you actually need it, not when you think you're going to need it, or pretty close to needing it, etc. Because 98% of the time, YAGNI. > In that case, is there not an advantage to standardising the API for these POROs in Rails? Not a big enough that I've come across to warrant building domain-specific abstractions and special rules that, as @dhh mentioned, will serve only to raise the barrier to entry to your codebase.
- Killswitch 13y ago> "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." - DHH, March 3rd 2014 That's going in my quote book. Thank you DHH.
- AndyDavis3416 13y agoI hope I am not sticking my head into the lion's mouth here, but here (https://gist.github.com/BiggerNoise/9334673 https://gist.github.com/BiggerNoise/9334673) is a link to a gist of some code in one of our apps. Cases have Tasks and Activities. Activities are always associated with a case, and may be associated with a task. In the controller, when assigning a case, one would type: AssignCaseCommand.new(params).execute! The command class implements the Command pattern (the pretty well established name for interactors) and is a Composite as well. BTW - the same command that assigns the tasks of a case is also used to merely assign a task. And, if we're just recording an activity, we would just use the create activity command. I think it would be insanity to try and capture the rules associated with re-assigning tasks in the controller. (I am pretty sure that you would agree.) --Minor Edit for clarity--
- dhh 13y agoIf 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 :(
- nthj 13y ago> 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". The main difference I see between the Rails philosophy and most of these "Use SRP and Interactors and things!" blog posts are that Rails is way more interested in using the correct tool for the job, and most of these blog posts are of the "use this for everything and every one!" variety. I have one project right now that I decided I wanted a Command for one action. I'm accepting input from a webhook that I can perform asynchronously, it interacts with like 5 different models, and I wanted to beat up on the tests pretty thoroughly. So I did that. Elsewhere I'm using concerns to make like 4 different models easily sortable. And other places I'm using Module#concerning to keep models organized. ONE TRUE WAY is overrated. I'd rather use whatever works given the context.
- andycroll 13y agoThis. This. This.
- gregmolnar 13y ago"ONE TRUE WAY is overrated. I'd rather use whatever works given the context." That's it.
- dablweb 13y agoI think that labelling the use of POROs as an "advanced technique" is simply wrong. How could it be? In fact it's simpler than implementing an AR callback (with more complexity behind the scenes than anything!) You are using app/services and POROs in your own app? So are we. But I guarantee they don't look like yours because there is no endorsed convention. Why not? Because Rails is still clinging to such an outdated idea as MVC? Pure MVC at a large scale is bullshit. "Size of the application is rarely a factor". I don't agree. Size of the application is always a factor. A big factor. What I'd like to see is the community embrace conventions outside the fabled "MVC" paradise. Rails is "convention over configuration", and is content to hold your hand for your first thousand lines of code... but then what?
- jlebrech 13y agosometimes more abstraction makes for simpler code, it's just that there's now simple presentation code for the frontend and also simple code for persistence. avoiding a new layer of abstraction because it can just about be done in vanilla rails isn't a solution, it's just throwing more lines of code at the problem. recently i've found that everything i do at the frontend intermixed with the controller, models and helpers could be made simpler with two way databinding. it's also simpler, if someone want's to see if it looks right you can plug in some fixtures and tweaking css can be done independantly instead of needing to write something in ruby to affect how something in html looks. maybe for a project with a large codebase is to use lighter frameworks such as backbone rather than having to do full rewrites in emberjs or angularjs. and let the code rewrite itself, ie. just clean up the portions of code that were only there for presentation.