Розробка бекенду сайту на Ruby (Ruby on Rails)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка бекенду сайту на Ruby (Ruby on Rails)
Середня
від 1 тижня до 3 місяців
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка бекенду сайту на Ruby (Ruby on Rails)

Rails залишається одним із найпродуктивніших фреймворків для команд, яким потрібно швидко запустити продукт, не будуючи кожен шар з нуля. Conventions over configuration тут — не маркетинговий слоган, це буквально опис того, як працює кодогенерація, роутинг, ORM і тестування.

Де Rails у формі

Контентні платформи, маркетплейси, SaaS з мультиарендністю, адміністративні панелі — там, де важливіше швидкість ітерації, ніж сира продуктивність. Shopify, GitHub, Basecamp — все це Rails під капотом при масивних навантаженнях. На Rails 7 з Puma + Falcon або Unicorn навантаження 3–5k RPS на інстанс — норма для правильно написаного додатку.

Сучасний стек

Rails 7.1 + Hotwire (Turbo + Stimulus) + PostgreSQL — стек, який не вимагає окремого React-фронтенду для більшості задач. Для API-only режиму:

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.api_only = true
    config.middleware.use ActionDispatch::Cookies
    config.middleware.use ActionDispatch::Session::CookieStore
  end
end

Active Record та міграції

# db/migrate/20240315_create_orders.rb
class CreateOrders < ActiveRecord::Migration[7.1]
  def change
    create_table :orders do |t|
      t.references :user, null: false, foreign_key: true
      t.integer :status, null: false, default: 0
      t.decimal :total, precision: 10, scale: 2, null: false
      t.jsonb :metadata, default: {}
      t.timestamps
    end

    add_index :orders, :status
    add_index :orders, :created_at
    add_index :orders, [:user_id, :status]
  end
end
# app/models/order.rb
class Order < ApplicationRecord
  belongs_to :user
  has_many :items, class_name: 'OrderItem', dependent: :destroy

  enum :status, { pending: 0, paid: 1, shipped: 2, delivered: 3, cancelled: 4 }

  scope :recent, -> { order(created_at: :desc) }
  scope :for_period, ->(from, to) { where(created_at: from..to) }

  validates :total, numericality: { greater_than: 0 }

  after_update_commit :broadcast_status_change, if: :saved_change_to_status?

  private

  def broadcast_status_change
    ActionCable.server.broadcast("order_#{id}", { status: status })
  end
end

Сервісні об'єкти

Для бізнес-логіки, яка не помещається в модель:

# app/services/orders/create_service.rb
module Orders
  class CreateService
    Result = Data.define(:success, :order, :errors)

    def initialize(user:, params:)
      @user = user
      @params = params
    end

    def call
      ActiveRecord::Base.transaction do
        order = @user.orders.build(status: :pending)
        items = build_items(order)

        order.total = items.sum { |i| i.price * i.quantity }
        order.save!
        order.items << items

        PaymentJob.perform_later(order.id)
        Result.new(success: true, order: order, errors: [])
      end
    rescue ActiveRecord::RecordInvalid => e
      Result.new(success: false, order: nil, errors: e.record.errors.full_messages)
    end

    private

    def build_items(order)
      @params[:items].map do |item_params|
        product = Product.find(item_params[:product_id])
        OrderItem.new(
          order: order,
          product: product,
          price: product.current_price,
          quantity: item_params[:quantity]
        )
      end
    end
  end
end

Controller та сериалізація

# app/controllers/api/v1/orders_controller.rb
module Api
  module V1
    class OrdersController < ApplicationController
      before_action :authenticate_user!

      def index
        orders = current_user.orders.recent.page(params[:page]).per(25)
        render json: OrderSerializer.new(orders, { meta: pagination_meta(orders) })
      end

      def create
        result = Orders::CreateService.new(
          user: current_user,
          params: order_params
        ).call

        if result.success
          render json: OrderSerializer.new(result.order), status: :created
        else
          render json: { errors: result.errors }, status: :unprocessable_entity
        end
      end

      private

      def order_params
        params.require(:order).permit(items: [:product_id, :quantity])
      end
    end
  end
end

Сериалізація через jsonapi-serializer:

class OrderSerializer
  include JSONAPI::Serializer

  attributes :status, :total, :created_at

  has_many :items, serializer: OrderItemSerializer
  belongs_to :user, serializer: UserSerializer
end

Фонові задачі з Sidekiq

# app/jobs/payment_job.rb
class PaymentJob < ApplicationJob
  queue_as :payments
  sidekiq_options retry: 3, backtrace: 5

  def perform(order_id)
    order = Order.find(order_id)
    return if order.paid?

    result = Payments::StripeService.new(order).charge

    if result.success?
      order.paid!
    else
      order.cancelled!
      raise PaymentFailedError, result.error_message
    end
  end
end
# config/sidekiq.yml
concurrency: 10
queues:
  - [payments, 3]
  - [mailers, 2]
  - [default, 1]

Кеширування

# Russian-cache через Redis
def cached_categories
  Rails.cache.fetch("categories/all", expires_in: 1.hour) do
    Category.active.includes(:children).to_a
  end
end

# Фрагментне кеширування в API
def index
  categories = Rails.cache.fetch_multi(*Category.active.pluck(:id).map { "category/#{_1}" }) do |key|
    id = key.split('/').last.to_i
    Category.find(id)
  end
  render json: categories.values
end

Тестування

# spec/services/orders/create_service_spec.rb
RSpec.describe Orders::CreateService do
  let(:user) { create(:user) }
  let(:product) { create(:product, price: 99.99) }

  describe '#call' do
    subject(:result) do
      described_class.new(user: user, params: { items: [{ product_id: product.id, quantity: 2 }] }).call
    end

    it 'creates order with correct total' do
      expect(result.success).to be true
      expect(result.order.total).to eq(199.98)
    end

    it 'enqueues payment job' do
      expect { result }.to have_enqueued_job(PaymentJob)
    end
  end
end

Деплой

Puma в кластерному режимі + Nginx як реверс-прокси — стандартна схема. Kamal (від Basecamp) спрощує деплой в Docker без Kubernetes:

# config/deploy.yml (Kamal)
service: myapp
image: registry.example.com/myapp

servers:
  web:
    hosts: [10.0.0.1, 10.0.0.2]
    options:
      memory: 512m
  workers:
    hosts: [10.0.0.3]
    cmd: bundle exec sidekiq

env:
  secret: [RAILS_MASTER_KEY, DATABASE_URL, REDIS_URL]

Графік

API для мобільного додатку (аутентифікація, 10–15 ресурсів, Sidekiq): 1–2 тижні. Повноцінний SaaS-бекенд з мультиарендністю, підписками, вебхуками та розвинутою логікою: 4–6 тижнів. Рефакторинг Rails 4/5 на 7.1 з оновленням гемів — зазвичай 2–3 тижні на аудит і патчинг.