Back to all posts
20 min read

When 'Simple Refactoring' Isn't Simple: A Pattern Recognition Exercise (Part 1)

When 'Simple Refactoring' Isn't Simple: A Pattern Recognition Exercise (Part 1)

TL;DR: Bad technical advice isn’t just wrong - it’s often the visible part of a larger grift system. I’ve spent years observing scammer communities to keep my pattern-recognition brain occupied. When RubyCademy posted “simple refactoring” advice that was technically worse than the original, I recognized the playbook.


I spend a lot of time in unusual places on the internet. Scammer Telegram groups. Grifter Discord servers. Communities where people share “growth hacks” that are actually just repackaged fraud.

I’m not there to play hero. I’m not the police. My brain just works in a particular way - it races, overclocks, needs complex information to process quickly. YouTubers can’t produce content fast enough. But scammers? They’re always scheming, always thinking laterally, always finding new angles.

It costs me about 250$ to maintain access to these communities. Worth it for the cognitive exercise.

Over the years, you start recognizing patterns. The language. The tactics. The way they establish false authority and manufacture urgency.

So when I saw this tweet from RubyCademy, something clicked.

The Tweet That Started It

# ❌ "Before"
class RolePermissions
  def self.permissions_for(role)
    case role.to_sym
    when :admin
      %w(read write delete)
    when :operator
      %w(read write)
    else
      raise UnknownRoleError.new(role)
    end
  end
end

# ✅ "After"
class RolePermissions
  PERMISSIONS = {
    admin: %w(read write delete),
    operator: %w(read write)
  }.with_indifferent_access.freeze

  def self.permissions_for(role)
    PERMISSIONS.fetch(role) { raise UnknownRoleError.new(role) }
  end
end

Their claim: “Don’t Overcomplicate Refactoring”

Their advice:

  • ✨ Store roles and permissions in a hash for lookups
  • ✨ Use with_indifferent_access for string/symbol keys
  • ✨ Use Hash#fetch for lookups and error handling

Wait - let’s pause on that claim: “Don’t Overcomplicate Refactoring”

If they had just said “Here’s another pattern you can use” - that would be fine. Different approaches exist. Trade-offs matter.

But they didn’t say that. They said the original code was “overcomplicated” and their version was “simpler.”

Also notice: They’re using with_indifferent_access - that’s Rails-specific, not Ruby. So this isn’t even a generic Ruby pattern. It’s a Rails-specific “refactoring” that claims to simplify… but actually doesn’t.

Let’s break down what’s actually wrong here. Not “style preference” wrong. Technically, objectively wrong in multiple ways.

The Technical Breakdown

Problem 1: Breaks Static Analysis and Developer Tooling

Original (case statement):

case role.to_sym
when :admin
  %w(read write delete)
when :operator
  %w(read write)

Your IDE, LSP, and static analyzers can see:

  • All possible roles at parse time
  • Exact return types for each branch
  • Can autocomplete role names
  • Jump-to-definition works
  • Refactoring tools can safely rename roles across the codebase
  • Type checkers (Sorbet, RBS, Steep) know the return type is Array[String]

“Refactored” (hash lookup):

PERMISSIONS = {
  admin: %w(read write delete),
  operator: %w(read write)
}.with_indifferent_access.freeze

Your tooling sees:

  • A hash that could contain anything (opaque at parse time)
  • with_indifferent_access makes it worse - now keys could be strings OR symbols
  • No autocomplete for role names
  • No jump-to-definition for roles
  • Type checkers have to use T.untyped because the return type is dynamic
  • Find-all-references misses hash key usages

Impact on AI coding assistants:

Claude, Codex, Gemini rely on LSP for code intelligence. Without it:

  • They can’t suggest valid role names
  • They can’t place debug statements intelligently
  • They struggle with metaprogramming and dynamic lookups
  • You’ll see them get “frustrated” trying to understand runtime behavior

Grok on Twitter will claim it’s fine, then quietly try to invoke Claude CLI to do the actual refactoring work.

Debugging impact:

Want to add a breakpoint or binding.irb/binding.pry?

Original: Just put it inside the case branch you care about:

when :admin
  binding.irb # 🔍 Debug here
  %w(read write delete)

“Refactored”: You have to place it BEFORE the hash lookup, then manually inspect what came back:

result = PERMISSIONS.fetch(role) { raise UnknownRoleError.new(role) }
binding.irb # 🤷 What role was this? What did it return?
result

Good luck debugging production issues with that.

Problem 2: Static Evaluation at Load Time

PERMISSIONS = {
  admin: %w(read write delete),
  operator: %w(read write)
}.with_indifferent_access.freeze

This executes once when the class loads. Your permissions are frozen at boot time.

In real applications, permissions are almost never static. You need:

  • Database-driven permissions
  • Context-dependent access control
  • Feature flags that modify permissions
  • Environment-specific rules

The original case statement could easily be extended:

when :admin
  Permission.for_role(:admin).to_a
when :operator
  calculate_operator_permissions(context)

Try doing that with a frozen constant. Good luck.

Problem 3: Measurably Slower in Production

This isn’t theoretical. I benchmarked both versions on Ruby 3.4.7:

Speed Results:

  • Valid roles (symbol input): Original is 1.78x faster (9.7M vs 5.4M iterations/sec)
  • String input: Original is 1.37x faster (7.6M vs 5.6M iterations/sec)
  • Error cases (unknown role): Original is 1.31x faster (1.7M vs 1.3M iterations/sec)

The “refactored” version is consistently slower in every scenario.

Now remember: this is permissions code. It runs on:

  • Every controller action
  • Every view render with authorization checks
  • Every API request
  • Every background job that checks permissions

If your app does 1000 permission checks per request, you just added:

  • 80 microseconds per request on the happy path
  • More latency on your 99th percentile
  • Worse cache locality due to hash indirection

One junior dev sees this “refactoring” pattern, applies it to 50 hot paths in your codebase, and boom - your application is measurably slower. No single change is obvious, but the aggregate effect kills performance.

Problem 4: Unnecessary Rails Overhead

with_indifferent_access converts your simple Ruby hash into an ActiveSupport::HashWithIndifferentAccess object.

This adds:

  • Memory allocation per access
  • Method call overhead (it’s not a simple hash lookup anymore)
  • ActiveSupport dependency where none existed

For what benefit? The keys are already consistently symbols. There’s no mixed string/symbol access in the original code.

If you’re worried about someone passing "admin" as a string, just call .to_sym on the input. Don’t wrap your entire hash in ActiveSupport magic.

Problem 5: The “Simple” Version is More Complex

Count the concepts in each version:

Original:

  • Case statement (basic Ruby)
  • Symbol conversion (.to_sym)
  • Array literal (%w())
  • Custom error raising

“Refactored”:

  • Constant definition
  • Hash literal
  • Array literal
  • .with_indifferent_access (ActiveSupport magic)
  • .freeze (immutability)
  • .fetch with block
  • Custom error raising

The “simple refactoring” added three additional concepts and a Rails dependency. That’s not simplification - that’s complexity theater.

Problem 6: Worse Error Debugging

PERMISSIONS.fetch(role) { raise UnknownRoleError.new(role) }

This is a case statement with extra stack frames. When this fails, your stack trace includes:

  1. The fetch method
  2. The block execution
  3. Your error raise

The original case statement fails directly. Cleaner stack traces = faster debugging.

The Response Pattern

When I pointed out these issues on Twitter, RubyCademy didn’t reply to my technical critique in their original thread.

Instead, they made a separate post responding to someone else:

At @RubyCademy, we respect all opinions.

No question is stupid, only a chance to learn.

Rudeness is not welcome. 🙏

Thank you, @grok, for the insightful reply and your sense of nuance. 🙏

Notice the pattern:

  1. Ignore the technical critique directly
  2. Post to a different thread where they look reasonable
  3. Frame my technical correction as “rudeness”
  4. Use Grok as a “witness” to validate their approach

I tagged Grok in my reply. Grok responded that the “new pattern is prettier.” Not because it’s true - but because Grok is instructed to de-escalate and not take sides. That’s literally in its system prompt.

The Victimhood Play

I had other things to do, so I moved on.

Then later, I get a notification: I was tagged in a new post. RubyCademy was trying to paint themselves as the victim of my “rudeness.”

I replied asking for benchmarks, not vibes. Show me data that proves your refactoring is better.

They didn’t reply.

Instead, they:

  1. Blocked me on the RubyCademy account
  2. Blocked me on their personal account (I didn’t even know who ran it at that point)

Here’s the thing: If I wanted to just “attack” them and look correct, I would have:

  • Used a dummy account
  • Deleted my comments after they replied
  • Stayed anonymous

I used my real account. I left my comments up. I was expecting them to prove me wrong with data.

That’s how technical discussions work. You show benchmarks. You demonstrate performance. You provide evidence.

If someone shows me I’m wrong with data, I learn something. That’s valuable.

But they didn’t have data. They had vibes. And when asked to provide evidence, they blocked me instead.

The Difference Between Disagreement and Grift

Before I explain the Twitter block exploit, let me show you what good faith disagreement looks like.

Julik Tarkhanov is a Ruby developer. We started on the wrong foot too.

The sequence:

  1. He posted something technical
  2. I replied with direct technical critique (same as with RubyCademy)
  3. He checked my blog
  4. Saw I’m “always direct”
  5. Blocked me - didn’t like the style

Same starting point as RubyCademy. Technical post → My direct critique → Block.

But here’s what happened next:

Julik reflected on his own reaction. He examined why he responded defensively to directness. He wrote about it publicly, acknowledged his bias, and we eventually became friends.

We found we had more in common than we thought. Now we respect each other’s work.

The difference:

Julik’s response: “Maybe I reacted to tone instead of substance. Let me examine my defensiveness.”

RubyCademy’s response: “Let me make a separate post, play victim, use Grok as validation, and block anyone who asks for data.”

Both experienced the same “rude Seuros” moment. One led to growth and friendship. The other led to manufactured victimhood and echo chambers.

This is how you tell the difference:

  • Good faith: Engages with substance eventually, even if tone was off-putting initially
  • Grift: Never addresses substance, only manages perception

I’m not against being blocked. Personal boundaries are fine. Julik blocked me, reflected, came back. That’s healthy.

But blocking to silence critics in an ongoing thread while your followers amplify your narrative? That’s not a boundary. That’s exploitation.

Let me be clear about something else: I’m not always correct. I’m opinionated.

When I’m wrong and you know you’re right, facts are on your side. Show me the data. I’ll learn something.

That’s why I never argue about subjective things - religion, color preferences, food choices. Those are matters of taste. No objective truth to find.

But technical stuff? There are only 3 possible outcomes:

  1. I’m wrong - Show me benchmarks, I learn something
  2. You’re wrong - I show you benchmarks, you learn something
  3. We’re both wrong - Even if we’re both technically correct, we were wrong for ignoring each other’s valid perspective

In all three cases, someone learns. The community gets better.

This is why I engage directly with technical claims. Not to “win.” Not to look smart. But because technical discussions have verifiable outcomes.

When someone responds to “show me benchmarks” with blocking and victimhood posts, that’s when I recognize the pattern. They’re not interested in outcomes 1, 2, or 3. They’re interested in maintaining the appearance of authority.

The Twitter Block Exploit

Here’s why this matters: Twitter has a bug that gets weaponized.

When someone blocks you on Twitter:

  • You can’t reply to ANY comment in that thread
  • You can’t see their posts
  • But their followers and bots can still boost the conversation
  • You’re silenced while the echo chamber amplifies their version

This creates a system where you can:

  1. Post wrong technical advice
  2. Get corrected by experts
  3. Tone police the correction
  4. Block the expert
  5. Claim victimhood
  6. Let your followers boost the narrative
  7. The expert can’t respond in the thread

You’ve just manufactured a one-sided conversation that looks like consensus.

This is the same tactic I see in scammer communities:

  • Block everyone who calls you out
  • Block dissatisfied customers
  • Block domain experts
  • Post your claims
  • Your bots and loyal followers boost it
  • New people see only the positive narrative

The blocked experts can’t object, but your supporters can amplify. You’ve created an artificial echo chamber that looks like organic agreement.

This Isn’t About One Code Example

Let me be clear: This isn’t about this specific refactoring.

This is about the pattern.

A junior developer sees this tweet. Trusts it because it’s from an “Academy.” Applies it to 50 hot paths in your codebase.

Now your application is:

  • Measurably slower (1.78x on every permission check)
  • Harder to debug (no breakpoint precision)
  • Invisible to static analysis (your IDE is blind)
  • Resistant to refactoring (find-replace doesn’t work)
  • Breaking AI coding assistants (they rely on LSP)

And when you try to fix it, you can’t find all the instances because the pattern is in hash keys, not in searchable code.

That’s the real damage.

Not the tweet. Not the one example. The pattern propagation from authority figures who don’t understand (or don’t care about) the consequences.

The Pattern I Recognized

This playbook isn’t new. I’ve seen it in FOREX scam groups, crypto pump-and-dump servers, course-selling operations.

The pattern:

  1. Give confident wrong advice - Present opinions as facts
  2. Manufacture authority - Call yourself an “Academy”
  3. Tone police critics - Reframe technical correction as “rudeness”
  4. Appeal to AI authority - “Even Grok agrees!” (misrepresenting diplomatic responses)
  5. Create social proof - “12,500+ learners!” (unverifiable)
  6. Sell certainty - Courses, subscriptions, “mastery” programs

The “Academy” naming pattern is deliberate.

But wait - notice the spelling: RubyCad_e_my. Not RubyAcademy. Rubycademy.

Classic deniability play. It:

  • Looks like “Academy”
  • Sounds like “Academy” when spoken
  • Markets itself like an Academy
  • But technically isn’t spelled “Academy”

So when called out, they can claim: “We never said we’re an Academy! It’s Rubycademy!”

Same tactic as fake phishing sites: Amaz0n.com, PayPa1.com, Micros0ft.com. Designed to fool you while maintaining plausible deniability.

One of the biggest grifters in the online education space is Andrew Tate. Guess what domain he bought? university.com

Not “Tate’s Courses.” Not “Hustlers Training.” University.

Because the word carries authority. It implies accreditation, peer review, academic rigor. None of which exist.

When someone calls themselves an “Academy” without:

  • Teaching credentials
  • Peer review of their content
  • Corrections when wrong
  • Contributions to the field they’re teaching

They’re borrowing authority they haven’t earned.

Let me be clear about what bothers me:

If this was called “RubyTips” or “Mehdi’s Ruby Blog,” I wouldn’t care.

If it was free content for learning and exposure, I’d ignore the mediocre advice. Everyone starts somewhere.

But when you:

  1. Call yourself an “Academy” (borrowed authority)
  2. Sell courses (75$ each, 49$/year subscriptions)
  3. Give objectively wrong technical advice
  4. Block critics who ask for data

That’s when it becomes a pattern worth calling out.

I’ve been contacted by junior developers who bought these courses. Their feedback? “Mediocre at best.”

The content is recycled from publicly available sources:

  • Chris Oliver’s GoRails
  • Ryan Bates’ Railscasts (RSpec for Zombies, etc.)
  • Evil Martians blog
  • Thoughtbot guides
  • Community blog posts

I know these sources. I’ve learned from them. They’re excellent - and they’re free or reasonably priced.

RubyCademy’s courses? Repackaged versions of this freely available content, sold as “mastery” behind an “Academy” brand.

The only “original” content? The flashcards. And they’re wrong.

I’m not the first person to correct him. Other developers have pointed out technical errors in his cards and posts before. He ignored them. Western names, Chinese names - easy to dismiss when you’re building an audience.

This isn’t even the first time he’s posted this same “refactoring” advice. He posted a similar version in 2024. Same bad advice, different year. The pattern repeats.

But when I showed up with benchmarks, something broke. Maybe it was the directness. Maybe it was the Arabic name hitting too close to home. Maybe he tried to get Claude or Codex to prove me wrong and they confirmed the benchmarks instead.

I imagine the conversation:

Him: “Prove Seuros is wrong about the performance.” Claude: “I ran the benchmarks. His version is 1.78x faster.” Him: “Can you make mine look faster somehow?” Claude: “No, they’ll run it on their own machines.” Him: “Fine, create a dramatic post that makes me look like the victim.” Claude: “I can’t help with creating misleading content or manipulating public perception. If you believe your approach has merit, I’d be happy to help you present technical evidence or run additional benchmarks to better understand the trade-offs.” Him: “Fuck it, I’ll write it myself. Canceling my subscription, getting Cursor.”

Whatever happened, the response was predictable: ignore the data, play the victim, block the critic.

This wasn’t a one-time mistake. This is a pattern of ignoring technical feedback while continuing to sell “mastery.”

Even worse, the flashcard concept itself isn’t original - language learning has used flashcards for decades. “What’s ‘rabbit’ in Spanish?” - that works for vocabulary memorization.

But programming isn’t vocabulary. It’s pattern recognition and composition. You don’t need to memorize that Hash#fetch exists. You need to understand:

  • When to use it vs case statements
  • Performance implications
  • Debuggability trade-offs
  • How it composes with the rest of your system

Flashcards teaching isolated syntax without context create developers who can recite methods but can’t architect systems.

When someone gives objectively wrong technical advice, doubles down when corrected, AND sells courses on the topic, we need to talk about it.

Who TF is RubyCademy?

Let me save you a Google search. All of this is publicly available information:

Background:

  • Founded by Mehdi Farsi
  • Claims: “12 years Rails experience, 6 years tech blogging”
  • Reality check: Start digging

Ruby/Rails Contributions:

  • Search rails/rails contributors: Not listed
  • Check Rails core team: Not listed
  • Rails committers: Not listed
  • Significant gems: activerecord-search with 7 GitHub stars

The “Academy”:

  • No teaching credentials listed
  • No recognized Rails community standing
  • Just blog posts since 2017 and courses for sale

There’s nothing wrong with teaching Ruby without being a Rails core contributor. Many excellent educators do exactly that.

But when you:

  • Give wrong technical advice
  • Double down when corrected
  • Call yourself an “Academy”
  • Sell it as “mastery”

That’s when my pattern recognition kicks in.

The Pricing Psychology

Let’s look at their structure (all publicly visible on rubycademy.com):

Individual Courses: 75$ each

  • 8 courses listed = 600$ total if bought separately

Subscription:

  • 49$/year for “all courses”
  • “All future courses included”

Student Program:

  • 80$ for 2 years
  • “Limited Time Offer” (always available)
  • Claims you’ll “stand out from peers”

The Hook:

  • “12,508 learners” (no verification)
  • Testimonials from people with MORE experience than the founder
  • “Ruby developers make 120k$+” (appealing to money motivation)

One testimonial: “I’ve been a professional Rails developer since 2005, yet I discovered new tricks today.”

That’s 2005. Rails was released in 2004. This person has been using Rails longer than RubyCademy has existed and they’re claiming to learn from it?

Either:

  • Fake testimonial
  • Paid endorsement
  • Most generous developer ever

Why This Matters

Bad code advice is everywhere. We all make mistakes. I’ve shipped plenty of bad code.

But there’s a difference between:

  • Making mistakes → learning → sharing lessons
  • Giving wrong advice → getting corrected → calling critics rude → selling courses anyway

The second pattern is what I watch for in scammer communities.

It’s the same system that:

  • FOREX “educators” use to sell trading signals
  • Crypto “experts” use to pump their holdings
  • Course “gurus” use to monetize confusion

They don’t need to know the right answer. They just need to:

  1. Sound confident
  2. Build authority (conferences, social proof)
  3. Tone police anyone who corrects them
  4. Sell certainty to confused developers

What You Can Verify Yourself

Don’t take my word for any of this. Everything is publicly available:

  • Search GitHub for Mehdi Farsi’s contributions to rails/rails or ruby/ruby
  • Check the Rails contributors page (7,048 contributors listed)
  • Look at the RubyCademy Twitter thread and my technical corrections
  • Review their pricing page and claims
  • Test the code examples yourself - run benchmarks, check behavior

Do the search. See what you find. Or rather, what you don’t find.

For reference: My name is in the Rails contributors list. Abdelkader Boudih. You can verify both claims - who contributed to Rails, and who didn’t.

This isn’t “random person on Twitter criticizes educator.” This is “actual Rails contributor asks for basic technical evidence from someone selling Rails mastery courses.”

The Real Victims

The real harm isn’t to RubyCademy. They’ll be fine, selling courses whether the advice is good or not.

The real victims are:

  • Junior developers who internalize bad patterns
  • Senior developers who waste time undoing this technical debt
  • Teams that ship bugs because someone trusted a conference speaker
  • The Ruby community’s reputation when bad advice spreads

When I call out wrong technical advice, I’m not being mean. I’m trying to prevent the years of technical debt I’ve seen these patterns create.

Coming Up: The Bigger Picture

Honestly? I should thank Mehdi.

For years, I’ve wanted to tell a story about educational grift in tech. About how I saw this pattern in person. About what happened when I went undercover.

But I couldn’t just write it out of nowhere. It would sound paranoid. Unprovoked. Like I had an axe to grind.

Then RubyCademy posted their “simple refactoring” advice, played victim when corrected, and blocked everyone who asked for data. Perfect timing - now I’ve got the context to tell it.

In Part 2, you’ll learn about:

  • The conference where I saw my name in someone else’s course materials
  • How I infiltrated the operation by pretending to be a corrupt government official
  • The grift ring that landed a 340,000$ US government contract
  • Why this pattern keeps working

RubyCademy isn’t special. They’re just the most recent example of a pattern I’ve been tracking for years.

Thanks for the setup, Mehdi. Couldn’t have timed it better.


All information in this post is publicly available via search engines. I encourage you to verify everything yourself.

If you run a legitimate educational platform in the Ruby community, thank you. We need more good educators. This isn’t about you.

This is about pattern recognition and protecting developers from the same systems I study in scammer communities.


EDIT (October 23, 2025)

The story just got more interesting.

After this article went live, Mehdi posted a sales pitch claiming:

“I’ve spent nearly a decade teaching Ruby and Rails, and everything I’ve learned now lives inside two resources I’m proud of.”

Problem: He bought the RubyCademy domain from a French owner. He didn’t found this operation.

Greg Molnar (@GregMolnar) - an OSCP-certified Rails security consultant - remembered having technical clashes with the original French owner, not Mehdi.

Greg’s corrections to RubyCademy’s bad advice:

Both original tweets? Deleted. But the pattern continued under new ownership.

“Nearly a decade teaching Ruby and Rails”?

No. You bought a brand that was already posting mediocre advice, inherited the follower base, and scaled the operation.

Real educators who actually have a decade of teaching built things:

  • Chris Oliver - Created GoRails, Jumpstart, dozens of gems
  • Ryan Bates - Railscasts, CanCan, and actual tutorials that changed Rails education
  • Evil Martians - Countless open-source contributions, AnyCable, PostCSS
  • Thoughtbot - Factory Bot, Clearance, Suspenders, actual products

What does RubyCademy ship?

  • LaunchKit - Vibe-coded on top of BulletTrain and Avo, repackaged as original
  • Flashcards - With wrong technical advice
  • Courses - Recycled from free community resources

This isn’t “educator makes mistakes.” This is “bought a failing operation and claimed the credentials.”

When you search for Mehdi Farsi’s contributions to Rails or Ruby, you find… nothing. Because the “decade of teaching” is the brand’s age, not his experience.

He bought the domain, the audience, and the appearance of authority. Then claimed ownership of the timeline.

That’s not building. That’s acquisition and rebranding.


Captain’s Log, Stardate 2025.290 - End Transmission

Captain Seuros, Pattern Recognition Division “Empathy for victims, not for grifters”

🔗 Interstellar Communications

No transmissions detected yet. Be the first to establish contact!

• Link to this post from your site• Share your thoughts via webmention• Join the IndieWeb conversation

Related Posts

The RubyGems Coup: When Parasites Take the Host

Ruby Central forcibly removed the people who built RubyGems for over a decade, replacing them with a 'Director of Open Source' whose last Ruby code was a conference tutorial in 2010. This is the anatomy of a hostile takeover disguised as 'strengthening stewardship.'

rubyopensourcepatterns