DEV Community

Roy Shooshy
Roy Shooshy

Posted on

I Asked Copilot: "How Did They Think To Write Such Awful Code?"

Copilot as a story teller:

A Developer Psychology Analysis 🧠

Based on the codebase analysis, here's my theory of how this anti-pattern evolved:


The Story: A Series of "Reasonable" Bad Decisions

πŸ“… Phase 1: The Initial Requirement (Day 1)

Product Manager: "When a user connects a bank account and it gets verified, we need to:

  • Show them a success message
  • Track analytics
  • Move to the next step"

Developer: "Sure, I'll add that logic!"

// Initial code - probably looked reasonable
methods: {
  checkVerification() {
    const account = this.banks.find(b => b.slug === this.bank.slug)
    if (account?.state === 'VERIFIED') {
      this.$trackEvent({ action: 'bank verified' })
      this.openSummary()
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Problem: But WHERE to call checkVerification()? After connecting? In a callback?


πŸ€” Phase 2: The "Clever" Solution (Day 2)

Developer thinking:

  • "I need this to run automatically when the bank state changes..."
  • "Computed properties re-run when their dependencies change..."
  • "I know! I'll put it in a computed property!"
computed: {
  checkVerification() {  // ❌ Starting the mistake
    const account = this.banks.find(b => b.slug === this.bank.slug)
    if (account?.state === 'VERIFIED') {
      this.$trackEvent({ action: 'bank verified' })
      this.openSummary()
    }
    return ''  // "I need to return something..."
  }
}
Enter fullscreen mode Exit fullscreen mode

Why they thought this was clever:

  • βœ… Automatic - runs when banks or bank.slug changes
  • βœ… No need to remember to call it manually
  • βœ… "Reactive" - Vue handles it!

What they missed:

  • ❌ Computed properties should be pure
  • ❌ Side effects belong in watchers
  • ❌ This violates Vue principles

πŸ› Phase 3: The Problem Emerges (Day 3)

Developer: "Wait, my verification logic isn't running! Why?"

The issue: Computed properties are lazy - they only run when accessed in the template.

Developer's Options:

  1. βœ… Convert to a watcher (the right way - but requires understanding)
  2. ❌ Force evaluation in template (the quick hack)

Developer chose: Option 2 (the hack)

<template>
  <!-- Force it to run by accessing it -->
  {{ checkVerification }}
</template>
Enter fullscreen mode Exit fullscreen mode

Result: Worked! But now there's an empty string showing in the UI... 🀦


🎭 Phase 4: Hiding the Shame (Day 4)

Developer: "I can't have an empty string showing in the UI..."

Attempted fixes:

  1. CSS to hide it: <span style="display:none">{{ checkVerification }}</span>
  2. Comments around it: <!-- {{ checkVerification }} --> ❌ Doesn't execute!
  3. Conditional rendering: <span v-if="false">{{ checkVerification }}</span> ❌ Doesn't execute!

Then someone discovered: "What if I pass it as a prop?"

<some-component :data="checkVerification" />
Enter fullscreen mode Exit fullscreen mode

Result:

  • βœ… Gets evaluated (runs the side effects)
  • βœ… Nothing shows in the UI
  • βœ… "Works!"

🎨 Phase 5: Naming the Abomination (Day 5)

Code Reviewer: "What's this data prop? The component doesn't use it..."

Developer: "Uh... it's just a... dummy... prop... to trigger reactivity..."

Code Reviewer: "That's weird, but I guess it works. Name it dummy so people know it's not used."

<!-- The birth of :dummy -->
<component :dummy="checkVerification" />
Enter fullscreen mode Exit fullscreen mode

And thus, the pattern was established.


πŸ“ˆ Phase 6: Feature Creep (Weeks 2-4)

As features were added, the computed property grew:

computed: {
  passedVerification() {  // Renamed at some point
    if (this.bank?.slug) {
      let accountToCheck = this.banks?.find(...)
      if (!accountToCheck) return ''

      if (accountToCheck?.connectionStatus === 'pass' &&
          accountToCheck?.isConnected === true &&
          accountToCheck.state === 'VERIFIED') {

        // More features added over time:
        this.$trackEvent({ /* analytics */ })
        this.showProcessingModal = false
        this.onSelected(accountToCheck, this.fromOrTo)
        if (this.flow === 'add') {
          this.openSummary()
        }
      }
    }
    return ''
  }
}
Enter fullscreen mode Exit fullscreen mode

Nobody questioned it because:

  • βœ… It was working
  • βœ… Tests were passing (probably checking result === '')
  • βœ… The business logic was correct
  • βœ… "If it ain't broke, don't fix it"

The Psychology Behind It

1. Pattern Matching Gone Wrong 🧩

Developers learn patterns: "Use computed for derived state"
But they missed: "Never put side effects in computed"

2. Incremental Degradation πŸ“‰

No single commit was "terrible" - it degraded slowly:

  • Day 1: "Put logic in computed" - slightly wrong
  • Day 2: "Force evaluation" - getting worse
  • Day 3: "Use dummy prop" - now it's a hack
  • Week 2: "Add more features" - cementing the pattern

3. Works = Right (Fallacy) βœ…βŒ

"The tests pass" β‰  "The code is good"
"It works in production" β‰  "The architecture is sound"
Enter fullscreen mode Exit fullscreen mode

4. Lack of Code Review Depth πŸ‘€

Reviewers saw:

  • βœ… Business logic correct
  • βœ… Tests passing
  • βœ… No runtime errors

They missed:

  • ❌ Architectural anti-pattern
  • ❌ Misuse of Vue features
  • ❌ Technical debt accumulation

5. Time Pressure ⏰

"I need to ship this feature by Friday"
    > Learn the proper Vue pattern
    > Refactor to use watchers
    > Update all tests

"This hack works right now"
    > Ship it βœ“
Enter fullscreen mode Exit fullscreen mode

6. Framework Ignorance πŸ“š

They knew Vue syntax but not Vue principles:

  • βœ“ Knew computed properties exist
  • βœ— Didn't understand their purpose
  • βœ“ Knew props can be bound
  • βœ— Didn't understand reactivity deeply

How This Happens in Real Companies

Scenario A: Junior Dev + Rushed Senior

Junior: "How do I run code when bank verification completes?"
Senior: *Glances for 10 seconds* "Put it in a computed property"
Junior: "It's not running..."
Senior: *Distracted by meeting* "Bind it to something in the template"
Junior: *Creates :dummy hack*
Enter fullscreen mode Exit fullscreen mode

Scenario B: Copy-Paste Programming

Developer finds similar code elsewhere in codebase
"Oh, they use :dummy here too!"
Copies the pattern without understanding it
Pattern spreads like a virus 🦠
Enter fullscreen mode Exit fullscreen mode

Scenario C: The Expert Left

Original Dev: *Knows it's a hack, plans to refactor*
Original Dev: *Leaves company*
New Devs: "This is just how we do things here"
Enter fullscreen mode Exit fullscreen mode

The Warning Signs (That Were Ignored)

🚩 Red Flag #1: The Name

passedVerification()  // Sounds like it checks a boolean
return ''             // Returns empty string?! πŸ€”
Enter fullscreen mode Exit fullscreen mode

🚩 Red Flag #2: The Prop Name

:dummy="..."  // The word "dummy" screams "this is a hack!"
Enter fullscreen mode Exit fullscreen mode

🚩 Red Flag #3: ESLint Warnings

vue/no-side-effects-in-computed-properties
Enter fullscreen mode Exit fullscreen mode

Someone probably disabled this warning or ignored it.

🚩 Red Flag #4: Template Display

{{ passedVerification }}  <!-- Why display it if it's empty? -->
Enter fullscreen mode Exit fullscreen mode

🚩 Red Flag #5: Test Code

const result = wrapper.vm.passedVerification
expect(result).toBe('')  // Testing that it returns nothing?
Enter fullscreen mode Exit fullscreen mode

Lessons for Preventing This

1. Enforce ESLint Rules ⚠️

{
  "rules": {
    "vue/no-side-effects-in-computed-properties": "error"  // Not "warn"!
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Code Review Checklist βœ…

  • [ ] Does this computed property have side effects?
  • [ ] Are there props named "dummy", "temp", "hack"?
  • [ ] Does the code make you ask "why?"

3. Architecture Review πŸ—οΈ

Not just "does it work" but "is this the right pattern?"

4. Documentation πŸ“–

// BAD: No explanation
:dummy="passedVerification"

// BETTER: Would force questioning
// Using dummy prop to force evaluation of computed property
// with side effects (TODO: refactor to watcher)
Enter fullscreen mode Exit fullscreen mode

5. Onboarding πŸŽ“

Teach new devs:

  • Computed = pure calculations
  • Watchers = side effects
  • Methods = user actions

The Silver Lining ✨

This code taught us:

  1. How anti-patterns evolve incrementally
  2. Why code review must go deeper than "does it work"
  3. The importance of understanding frameworks, not just using them
  4. Technical debt compounds if not addressed

And now it's fixed! πŸŽ‰


Final Thought

They didn't wake up thinking: "I'll write terrible code today!"

They thought: "I'll solve this problem quickly so I can move on to the next feature."

The road to awful code is paved with small, locally-reasonable decisions that never got refactored. πŸ›£οΈ

That's why we refactor. That's why we learn. That's why we improve.

And that's exactly what we did here! πŸ’ͺ

Top comments (0)