class DepartureFetcher
  include ActiveModel::SerializerSupport

  def initialize(params)
    @time = params[:time] ? Time.zone.parse(params[:time]) : Time.current
    @stop_id = params[:stop_id]
  end

  def departures
    @departures ||= stop_times.map { |stop_time|
      stop_time_update = realtime_updates.for_stop_time(stop_time)
      Departure.new(date: @time.to_date, stop_time: stop_time, stop_time_update: stop_time_update)
    }
  end

  def stop_times
    @stop_times ||= StopTime
      .where(stop: stop, trips: { service_id: Service.for_time(@time) })
      .where("departure_time > :start_time AND departure_time < :end_time", time_query)
      .includes(:route, :trip, :stop)
      .order(:departure_time)
  end

  def valid?
    @stop_id.present?
  end

  private

  def time_query
    {
      start_time: @time - 10.minutes,
      end_time: @time + 1.hour
    }
  end

  def stop
    @stop ||= Stop.find_legacy(@stop_id)
  end

  def realtime_updates
    @realtime_updates ||= Metro::RealtimeUpdates.fetch
  end
end

class DepartureFetcher
  include ActiveModel::SerializerSupport

  def initialize(params)
    @time = params[:time] ? Time.zone.parse(params[:time]) : Time.current
    @stop_id = params[:stop_id]
  end

  def departures
    @departures ||= stop_times.map { |stop_time|
      stop_time_update = realtime_updates.for_stop_time(stop_time)
      Departure.new(date: @time.to_date, stop_time: stop_time, stop_time_update: stop_time_update)
    }.sort_by(&:time)
  end

  def stop_times
    @stop_times ||= StopTime
      .where(stop: stop, trips: { service_id: Service.for_time(@time) })
      .where("departure_time > :start_time AND departure_time < :end_time", time_query)
      .includes(:route, :trip, :stop)
  end

  def valid?
    @stop_id.present?
  end

  private

  def time_query
    {
      start_time: @time - 10.minutes,
      end_time: @time + 1.hour
    }
  end

  def stop
    @stop ||= Stop.find_legacy(@stop_id)
  end

  def realtime_updates
    @realtime_updates ||= Metro::RealtimeUpdates.fetch
  end
end

Sort departures rather than stop_times
Sort departures rather than stop_times

There are cases where realtime updates change the order of departures.
Ordering by departure.time ensures that they are always ordered by
actual departure time.