The Danger of a Single Capital Letter: How I Almost Ruined a Redmine Instance
2026-01-26
Back to overviewI love Ruby. To be more precise, I love Ruby on Rails. It’s incredibly intuitive and allows for rapid development. But there is a catch: Ruby gives you a very sharp knife, and it’s remarkably easy to accidentally shoot yourself in the foot with it.
For years, I’ve been developing plugins and scripts for Redmine. Recently, I took on a new client for whom I host Redmine and provide custom enhancements.
The Request
The client needed a simple automation script. The logic was straightforward: every minute, the script should check a specific project for new tickets. If a ticket was created by a user with a specific email domain, it should be moved to a corresponding project. For example: "If an email comes from user@dell.com, move that issue to the Dell project."
To implement this, I added a custom field called "Customer Email Domain" to the project settings, allowing the client to map domains to their respective projects. The Implementation
I wrote a small script to run as a cron job every minute. Here is the core logic: Ruby
issues.each do |issue|
if new_project
# The fatal line
Issue.update(project: new_project)
puts "Moved Issue ##{issue.id} (#{owner_email}) to project ##{new_project.id}"
else
puts "No target project found for #{owner_email} (Issue ##{issue.id})"
end
end
"Fuck"
At first glance, it looks fine, right? Wrong.
Within minutes of deploying, I realized I had made a catastrophic mistake. Instead of moving one ticket at a time, my script was moving every single ticket in the entire database to the new project. Every. Single. Minute.
The Culprit: A Single Capital Letter
The mistake lies here:
- Wrong:
Issue.update(project: new_project) - Right:
issue.update(project: new_project)
What’s the difference?
issue(lowercase) refers to the specific object inside the loop. Calling .update on it changes only that one record.Issue(uppercase) refers to the Class/Model itself. In Rails, calling .update on a Model without a specific ID or scope applies that change to all records in the table.
It was a tiny typo with a massive consequence. Because of that one capital letter, the script told the database: "Update all Issues and set their project to X."
The Lesson
We all make mistakes, but the difference between a "bad day" and a "career disaster" is preparation. In this case, the damage was manageable because the client was still in the early stages of onboarding. More importantly, I had automated daily backups. I was able to restore the database to its previous state and fix the mess quickly.
So, dear colleagues: Always make backups, and more importantly, test them. You never know when a single Shift-key press might try to ruin your day.