How to Remove Single Table Inheritance from Your Rails Monolith

The word “content” is unfortunately super generic, so it was impossible to do a simple, global search and replace, so I tended to do a more scoped search trying to account for the variations.When removing STI, these are the things you should search for:The singular and plural forms of the model, including all of its subclasses, methods, utility methods, associations and queries.Hardcoded SQL queriesControllersSerializersViewsFor example, for content, that meant looking for::content — for associations and queries:contents — for associations and queries.joins(:contents) — for join queries, which should be caught by the previous search.includes(:contents) — for eager loading second-order associations, which should also be caught by the previous searchcontent: — for nested queriescontents: — again, more nested queriescontent_id —for queries directly by id.content — method calls.contents — collection method calls.build_content — utility method added by the has_one and belongs_to association.create_content — utility method added by the has_one and belongs_to association.content_ids — utility method added by the has_many associationContent — the class name itselfcontents — the plain string for any hardcoded references or SQL queriesI believe that is a pretty comprehensive list for content..And then I did the same for lab, readme, and project..You can see that because Rails is so flexible and adds many utility methods, it’s hard to find all of the places that a model ends up being used.How to Actually Replace the Implementation After You’ve Found All the CallersOnce you’ve actually located all the call sites of the model you’re trying to replace or remove, you get to rewrite things..In general, the process we followed wasReplace the method behavior in the definition or change the method at the call siteWrite new methods and call them behind a feature flag at the call siteBreak dependencies on associations with methodsRaise errors behind a feature flag if you’re unsure about a methodSwap in objects that have the same interfaceHere are examples of each strategy.1a..Replace the method behavior or querySome of the replacements are pretty straightforward..You put the feature flag in place to say “call this code instead of this other code when this flag is on.”So instead of querying based on contents, here we query based on canonical_material.1b..Change the method at the call siteSometimes, it’s easier to replace the method at the call site to standardize the methods called..(You should run your test suite and/or write tests when you do this.) Doing so can open up the path to further refactoring.This example demonstrates how to break the dependency on the canonical_id column, which will soon no longer exist..Notice that we replaced the method at the call site without putting that behind a feature flag..In doing this refactoring, we noticed that we plucked the canonical_id in more than one place, so we wrapped up the logic to do that in another method that we could chain onto other queries..The method at the call site was changed, but the behavior didn’t change until the feature flag was turned on.2..Write new methods and call them behind a feature flag at the call siteThis strategy is related to the method replacement, only in this one, we write a new method and call it behind a feature flag at the call site.. More details

Leave a Reply