# Antipattern Prevention

> Avoid common Ruby and Rails antipatterns that degrade maintainability and performance. Use when writing new code, reviewing PRs, or refactoring existing code in Doorkeeper.

- Skill: `doorkeeper-gem/antipattern-prevention` (Agent Skill)
- Install (CLI): `npx skillmds@latest add doorkeeper-gem/antipattern-prevention`
- Raw SKILL.md: https://api.skillmd.com/api/skills/doorkeeper-gem/antipattern-prevention/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: doorkeeper-gem (https://skillmd.com/u/doorkeeper-gem)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/doorkeeper-gem/antipattern-prevention

---


# Antipattern Prevention

When writing or reviewing code in Doorkeeper, use this skill to avoid common antipatterns that degrade maintainability and performance.

## 1. Ruby Iteration Instead of SQL

**Severity:** High

```ruby
# BAD — loads all tokens into memory
AccessToken.all.select { |t| t.expired? }

# GOOD — push to database
# The codebase uses expiration_time_sql for this:
# lib/doorkeeper/models/concerns/expiration_time_sql_math.rb
```

```ruby
# BAD
ids = AccessToken.pluck(:id).select { |id| id > 100 }

# GOOD
AccessToken.where("id > ?", 100).pluck(:id)
```

## 2. Fire and Forget (Missing Error Handling)

**Severity:** High

```ruby
# BAD
def fetch_jwks(uri)
  response = Net::HTTP.get(URI(uri))
  JSON.parse(response)
rescue
  nil
end

# GOOD — use the existing HttpFetcher which handles timeouts and errors
# Reference: lib/doorkeeper/http_fetcher.rb
def fetch_jwks(uri)
  response = http_fetcher.fetch(uri)
  JSON.parse(response)
rescue HttpFetcher::FetchError => e
  Rails.logger.warn("JWKS fetch failed: #{e.message}")
  nil
end
```

## 3. Inaudible Failures (Silent Save)

**Severity:** Medium

```ruby
# BAD — fails silently
token.save

# GOOD — raises on failure
token.save!

# ALSO GOOD — check return value
unless token.save
  handle_error(token.errors)
end
```

## 4. Callback Complexity

**Severity:** High

```ruby
# BAD — hidden side effects
class AccessToken
  after_create :notify_admin, :update_metrics, :send_webhook
end

# GOOD — explicit orchestration in request objects
class AuthorizationCodeRequest
  def before_successful_response
    find_or_create_access_token(...)
    super
  end
end
```

Doorkeeper uses `before_successful_response` / `after_successful_response` hooks — this is the correct pattern.

## 5. Bare Rescue

**Severity:** High

```ruby
# BAD — catches Exception, including SystemExit, Interrupt, NoMemoryError
rescue Exception => e
  nil
end

# BAD — swallows all StandardError subclasses indiscriminately
rescue
  nil
end

# GOOD — specific exception classes
rescue JWT::DecodeError, JWT::ExpiredSignature => e
  handle_jwt_error(e)
end
```

## 6. String Interpolation in SQL

**Severity:** Critical (security)

```ruby
# BAD — SQL injection
where("token = '#{params[:token]}'")

# GOOD — parameterized
where(token: params[:token])
where("token = ?", params[:token])
```

## 7. String Equality on Secrets

**Severity:** Critical (security)

```ruby
# BAD — timing attack
token == stored_token

# GOOD — constant-time
ActiveSupport::SecurityUtils.secure_compare(token, stored_token)
```

## 8. Tight Coupling to ActiveRecord

**Severity:** Medium

Doorkeeper supports multiple ORMs. Protocol logic in `lib/doorkeeper/oauth/` should use the model mixin interface:

```ruby
# BAD — AR-specific in protocol code
AccessToken.where(token: value).lock.first

# GOOD — use mixin method
AccessToken.by_token(value)
```

## 9. Shotgun Surgery

**Severity:** Medium

If adding a new token attribute requires editing 8+ files, consider whether the design is right. The `custom_attributes` pattern shows how to add token attributes generically without shotgun surgery.

## 10. Monolithic Methods

**Severity:** Medium

```ruby
# BAD — one method doing too much
def authorize
  validate_client
  validate_scopes
  validate_redirect_uri
  create_grant
  generate_response
end

# GOOD — Doorkeeper's validation DSL
validate :client,        error: Errors::InvalidClient
validate :redirect_uri,  error: Errors::InvalidRedirectUri
validate :scopes,        error: Errors::InvalidScope
```

## Quick Detection Patterns

```bash
# Bare rescue
grep -rn "rescue$" lib/ app/

# Silent save
grep -rn "\.save$" lib/ app/

# SQL interpolation
grep -rn 'where(".*#\{' lib/ app/

# String equality on secrets
grep -rn '== .*token\|== .*secret\|token.* ==' lib/ app/

# Ruby filtering instead of SQL
grep -rn '\.all\.select\|\.all\.map\|\.all\.each' lib/ app/
```

## Verification

After changes:

1. `bundle exec rubocop` — catches many antipatterns automatically
2. `bundle exec rspec` — ensures behavior hasn't regressed
3. Manual review of the diff for the patterns above

