Choosing between folders, gems, and Rails engines for organizing code: opinions?

This message was imported from the Ruby/Rails Modularity Slack server. Find more info in the import thread.

Message originally sent by slack user U782WI24OVK

I’ve got another question for you folks.

Say you’ve end up with a big ball of mud and you’re about to start splitting into components. I think generally you’d have 3 options:

A) Plain-old folders.
B) Gems.
C) Rails Engines.

Correct me if I’m wrong but I believe option C is what most of you have chosen or use so far. I just want to understand why and if you’d still make the same choice if you had the option to start again.

B and C are on some level very much the same: collocating tests with isolated parts of code ensures that you remove coupling between these gems (remember that engines are just gems+Rails).

A - Folders do not give you that. Combine something like packwerk with folders and they can become a transition tool to B or C.

When analyzing B vs C I mostly think about with a “functional core, imperative shell” lens: gems for functional cores, C for imperative shell

Message originally sent by slack user U78EKGG059M

Stephan is right on the money. We use B and C also and using pretty much the same criteria. We could more aggressively factor into functional core but Gems (at least in our world) are not allowed to have dependencies on engines so they tend to be things that can be tested in isolation.

Message originally sent by slack user U783JSN8X0E

we’re just starting down the road of A + Packwerk, so I don’t have much advice except as a data point :smile:

Message originally sent by slack user U78E2Q4FDIQ

I put together a little flowchart in this blog post. It helps decide when to B or C, but +1 to all of the above.

https://kellysutton.com/2020/03/12/how-to-break-apart-a-rails-monolith.html

Message originally sent by slack user U782WI24OVK

<@U783JSN8X0E> Are there any particular reasons why you chose A vs B or C?

Message originally sent by slack user U782WI24OVK

<@U78E2Q4FDIQ> That’s a nice read. If I understand correctly, private AR models would be similar to the option A above? If yes, assuming you’ve already discovered the scope and boundaries of the domain you want to extract, would you still choose option A over C as a first step?

Message originally sent by slack user U782WI24OVK

Also, say you end up reconsidering a design decision and decide to roll some bits back to the monolith or to other component(s). Is there any significant cost of doing so from a B or C approach vs A?

Message originally sent by slack user U78E2Q4FDIQ

Private AR models can be done in A, B, or C, but I think it’s a good place to start

Message originally sent by slack user U78E2Q4FDIQ

i.e. the process of making AR models private starts you down the path of true discovery of boundaries

Message originally sent by slack user U78E2Q4FDIQ

I think of these things of a continuum: A should make C easier, so we should consider doing A in-place before extracting.

Message originally sent by slack user U782WI24OVK

A should make C easier, so we should consider doing A in-place before extracting.

Narrowing down my question even more: Would that apply for new code as well? Say you want to write a component that replaces logic buried in the monolith and so will eventually be deprecated.

The costs of extracting something vs merging it back together are vastly different (merging is virtually free). As such, for new code, consider starting separated even if you are not completely sure bout the boundary.

Message originally sent by slack user U783JSN8X0E

<@U782WI24OVK> A + Packwerk seemed to us like a good incremental step towards B/C with less overhead. We might still do B/C but the level of effort for us would be higher so we’d need a compelling reason it’s worth it

Message originally sent by slack user U783JSN8X0E

We also really like <@U78E2Q4FDIQ>’s article about private AR models and we’ll probably adopt that approach as well :100: it’s just harder to adopt incrementally