Findings 8 and 9 of the 2026-08-18 architecture review — both Worth exploring, filed together because they are the same
complaint at two scales: a concept expressed more than once, with nothing keeping the copies honest.
8. Like and Repost are one module, written twice
Twelve files: like.rb/repost.rb, both controllers, both button partials, and both pairs of
turbo-stream templates. The diff between the two models is the class name; between the controllers,
the identifiers; between the partials, an HTML entity and a counter name. A third engagement type
costs six more files.
Deletion test: deleting either side halves one concept rather than removing one — the duplication is
the concept being expressed twice.
Judgement from the review: stable duplication, and this is a proof of concept, so it is not urgent.
Worth doing when a third engagement type appears, or alongside finding 6, which edits the same three
models.
9. One rule, several implementations
| Rule |
Written where |
Risk |
| tags are lowercase |
tag.rb:5 (normalizes), post.rb:122, application_helper.rb:5, tags_controller.rb:7 |
four copies; only one also strips whitespace |
| what a username is |
user.rb:3 — /\A[a-z0-9_]{3,20}\z/; routes.rb:28 — /[A-Za-z0-9_]+/ |
two definitions disagreeing on case and length |
| the ranking formula |
ranked_feed.rb:203–207; script/scaling-curve:39–42 |
a comment is the only thing binding them |
Each rule gets one home and the others call it. The formula is the cheapest of the three and worth
doing on its own: one spec that runs both implementations and compares turns a comment into a gate.
Findings 8 and 9 of the 2026-08-18 architecture review — both Worth exploring, filed together because they are the same
complaint at two scales: a concept expressed more than once, with nothing keeping the copies honest.
8. Like and Repost are one module, written twice
Twelve files:
like.rb/repost.rb, both controllers, both button partials, and both pairs ofturbo-stream templates. The diff between the two models is the class name; between the controllers,
the identifiers; between the partials, an HTML entity and a counter name. A third engagement type
costs six more files.
Deletion test: deleting either side halves one concept rather than removing one — the duplication is
the concept being expressed twice.
Judgement from the review: stable duplication, and this is a proof of concept, so it is not urgent.
Worth doing when a third engagement type appears, or alongside finding 6, which edits the same three
models.
9. One rule, several implementations
tag.rb:5(normalizes),post.rb:122,application_helper.rb:5,tags_controller.rb:7user.rb:3—/\A[a-z0-9_]{3,20}\z/;routes.rb:28—/[A-Za-z0-9_]+/ranked_feed.rb:203–207;script/scaling-curve:39–42Each rule gets one home and the others call it. The formula is the cheapest of the three and worth
doing on its own: one spec that runs both implementations and compares turns a comment into a gate.