Ruby on Rails Modernisierung für Schweizer Unternehmen
Seit 2009 spezialisiert auf Ruby on Rails. Schweizer Marktpartner seit 2012. Wir modernisieren Legacy-Rails-Anwendungen statt sie neu zu schreiben.
Projekt unverbindlich anfragenWer wir sind
USEO ist ein auf Ruby on Rails spezialisiertes Softwarehaus mit über 15 Jahren Erfahrung. Wir konzentrieren uns auf das, was die meisten Agenturen vermeiden: die Modernisierung bestehender, geschäftskritischer Rails-Anwendungen. Statt riskanter Komplett-Neuentwicklung stabilisieren, refaktorieren und upgraden wir Legacy-Systeme schrittweise.
Unsere Verbindung zur Schweiz ist langjährig und konkret: Seit 2012 betreuen wir die führenden Schweizer Karriereplattformen Yousty.ch und Professional.ch. Im Jahr 2025 haben wir für das Schweizer Startup Versus (getversus.app) einen mobilen MVP für iOS und Android entwickelt. Wir kommunizieren auf Englisch und Deutsch und arbeiten als verlängerte Werkbank Schweizer Teams.
Inhaltlich konzentrieren wir uns auf vier Bereiche: Legacy-Rails-Modernisierung, Code-Audits, dedizierte Senior-Teams und Fractional-CTO-Beratung. Wir nehmen keine Junior-Entwickler in Kundenprojekte. Alle Engineering-Entscheidungen werden von Senior-Engineers mit Schweiz-Erfahrung getroffen.
Schweizer Datenschutz (nDSG) & Ruby on Rails
Das revidierte Schweizer Datenschutzgesetz (nDSG) hat technische Auswirkungen auf jede Rails-Anwendung, die Personendaten verarbeitet. Drei Bereiche stehen besonders im Fokus: Audit-Protokolle, Einwilligungsmanagement und Datenportabilität. Rails bietet für jeden Bereich saubere Implementierungsmuster.
1. Audit-Protokolle für Datenzugriffe
Das nDSG verlangt nachvollziehbare Protokolle darüber, welche Personendaten wann und durch wen verändert wurden. In Rails lösen wir dies typischerweise mit dem audited-Gem, das auf ActiveRecord-Callbacks aufbaut. Es protokolliert jede Änderung, den auslösenden Benutzer und einen Zeitstempel ohne Boilerplate-Code in jedem Modell.
Synthetic Engineering Context: Integration von audited
# Gemfile
gem 'audited'
# db/migrate/_install_audited.rb (vom Generator erzeugt)
rails generate audited:install
rails db:migrate
# app/models/user_profile.rb
class UserProfile < ApplicationRecord
audited only: [:email, :phone, :address]
# Nur sensible Felder protokollieren, nicht alle Spalten
end
# Abfrage des Audit-Trails
profile = UserProfile.find(42)
profile.audits.last.audited_changes
# => {"email" => ["old@example.ch", "new@example.ch"]}
profile.audits.last.user
# => # 2. Einwilligungsmanagement (Consent Management)
Das nDSG erfordert eine granulare, jederzeit widerrufbare Einwilligung pro Verarbeitungszweck. Ein einzelnes accepted_terms_at-Feld auf dem User-Modell reicht nicht aus. Wir empfehlen ein separates Consent-Modell mit Verarbeitungszweck, Status und Widerrufszeitpunkt.
Synthetic Engineering Context: Consent-Modell
# db/migrate/_create_consents.rb
create_table :consents do |t|
t.references :user, null: false, foreign_key: true
t.string :purpose, null: false # 'marketing', 'analytics', 'newsletter'
t.boolean :granted, default: false, null: false
t.datetime :granted_at
t.datetime :revoked_at
t.string :source # 'signup_form', 'preferences_page', 'cookie_banner'
t.timestamps
end
# app/models/user.rb
class User < ApplicationRecord
has_many :consents
def consented_to?(purpose)
consents.where(purpose: purpose, granted: true, revoked_at: nil).exists?
end
end
# Vor jeder Marketing-Aktion prüfen
if user.consented_to?('marketing')
MarketingMailer.with(user: user).campaign.deliver_later
end 3. Datenportabilität
Das nDSG gibt Personen das Recht, ihre Daten in einem strukturierten, maschinenlesbaren Format zu erhalten. Diese Exporte sollten asynchron erfolgen, da sie Datenbankabfragen über mehrere Modelle hinweg auslösen.
Synthetic Engineering Context: Asynchroner Datenexport
# app/services/data_portability_export.rb
class DataPortabilityExport
def initialize(user)
@user = user
end
def to_json
{
profile: profile_data,
consents: consents_data,
activities: activities_data,
exported_at: Time.current.iso8601
}.to_json
end
private
def profile_data
@user.as_json(only: [:email, :full_name, :created_at])
end
def consents_data
@user.consents.as_json(only: [:purpose, :granted, :granted_at, :revoked_at])
end
def activities_data
@user.activities.last(1000).as_json
end
end
# app/jobs/data_export_job.rb
class DataExportJob < ApplicationJob
def perform(user_id)
user = User.find(user_id)
export = DataPortabilityExport.new(user).to_json
DataExportMailer.with(user: user, export: export).ready.deliver_later
end
end Rails Modernisierung in 3 Phasen
Eine erfolgreiche Modernisierung ist kein Big-Bang-Rewrite, sondern ein schrittweiser, kontrollierter Prozess. Unser Vorgehen unterteilt die Arbeit in drei klar abgegrenzte Phasen, die jede für sich messbaren Geschäftswert liefern.
Phase 1: Audit & Stabilisierung
Bevor wir eine einzige Zeile Code anfassen, schaffen wir Sicherheit. Wir containerisieren die bestehende Anwendung mit Docker, um eine reproduzierbare Entwicklungsumgebung herzustellen. Wir messen die Testabdeckung mit SimpleCov und identifizieren die kritischsten Code-Pfade ohne Testschutz. Wir profilieren die Anwendung in Produktion (Scout APM, rack-mini-profiler), um die echten Performance-Bottlenecks zu finden, nicht die vermuteten.
Synthetic Engineering Context: SimpleCov-Schwelle in CI
# spec/spec_helper.rb
require 'simplecov'
SimpleCov.start 'rails' do
add_group 'Services', 'app/services'
add_group 'Background Jobs', 'app/jobs'
minimum_coverage 70 # Anfangsschwelle für Legacy-Apps
minimum_coverage_by_file 50
end
# .github/workflows/ci.yml
- name: Run tests with coverage gate
run: |
bundle exec rspec
# SimpleCov gibt Exit-Code 1 zurück, wenn Schwelle unterschritten Phase 2: Inkrementelle Upgrades
Wir überspringen niemals Rails-Major-Versionen. Stattdessen nutzen wir das next_rails-Gem für einen Dual-Boot-Ansatz: Die Anwendung läuft gleichzeitig unter der alten und neuen Rails-Version. Tests werden gegen beide Versionen ausgeführt. Deprecation Warnings werden behoben, bevor die Migration finalisiert wird. So upgraden wir typischerweise 4.2 → 5.2 → 6.1 → 7.x in separaten, jederzeit zurückrollbaren Schritten.
Synthetic Engineering Context: Dual-Boot mit next_rails
# Gemfile
gem 'next_rails', group: :development
if ENV['NEXT_RAILS']
gem 'rails', '7.0.8'
else
gem 'rails', '6.1.7'
end
# Bash: Beide Versionen installieren
$ bundle install
$ NEXT_RAILS=1 bundle install
# Tests gegen beide Versionen
$ bundle exec rspec
$ NEXT_RAILS=1 bundle exec rspec
# Production läuft weiter mit alter Version,
# Staging mit neuer Version. Wechsel erst nach grünem CI. Phase 3: Refactoring & Architekturverbesserung
Nach erfolgreichem Upgrade beginnt die nachhaltige Verbesserung. Fat Controllers werden in Service Objects extrahiert. N+1-Queries werden mit includes, preload und Counter-Caches eliminiert. Veraltete Background-Job-Frameworks (DelayedJob) werden durch Sidekiq ersetzt. Jede Refactoring-Maßnahme wird durch Tests abgesichert, die bereits in Phase 1 entstanden sind.
Synthetic Engineering Context: Service Object Pattern
# Vorher: Fat Controller (45 Zeilen create-Action)
class OrdersController < ApplicationController
def create
# 45 Zeilen mit Validierung, Stripe-Aufruf, E-Mail-Versand,
# Inventory-Update, Loyalty-Punkten und Slack-Notification
end
end
# Nachher: Service Object kapselt Geschäftslogik
# app/services/orders/place_order.rb
module Orders
class PlaceOrder
Result = Struct.new(:success?, :order, :errors, keyword_init: true)
def initialize(user:, params:)
@user = user
@params = params
end
def call
ActiveRecord::Base.transaction do
order = build_order
return failure(order) unless order.save
charge_payment(order)
notify_warehouse(order)
award_loyalty_points(order)
Result.new(success?: true, order: order)
end
rescue Stripe::CardError => e
Result.new(success?: false, errors: [e.message])
end
# ... private methods
end
end
# Controller schrumpft auf 7 Zeilen
class OrdersController < ApplicationController
def create
result = Orders::PlaceOrder.new(user: current_user, params: order_params).call
if result.success?
redirect_to result.order, notice: 'Bestellung erfolgreich'
else
render :new, status: :unprocessable_entity
end
end
end Bereit für den nächsten Schritt?
Lassen Sie uns gemeinsam analysieren, wie wir Ihre Rails-Anwendung modernisieren und für die Zukunft rüsten können. Keine Verkaufsgespräche, sondern ein technisches Gespräch über Ihre konkrete Situation.
Projekt starten