Когда вы открываете Яндекс Go для заказа такси и вводите адрес, приложение за доли секунды показывает цену. Кажется, что это несложно: взять расстояние и время в пути, умножить их на значения из тарифа. На самом деле, за этой ценой стоит не один десяток факторов — например, скидки, спрос, геозоны. И это ещё без учёта того, что пассажир взял с собой кота или лыжи.

В этой статье — о том, как мы вынесли алгоритм ценообразования из кода сервиса, почему не взяли готовый скриптовый язык, что такое цепочка преобразований цены и зачем нам понадобился формальный верификатор.

Прайсинг не заканчивается на офере: где ещё считается цена

Расчёт цены начинается в момент, когда пользователь открывает приложение и указывает точки А и Б. Прайсинг получает набор параметров — список тарифных категорий, точки назначения и другие данные о поездке — и возвращает цену по каждой категории. Этот первый расчёт мы называем офером: пользователь видит цены, выбирает тариф и подтверждает заказ.

Заканчивается ли на этом участие прайсинга в дальнейшей судьбе заказа? Конечно, нет. Пассажир мог перепутать Ленинский проспект и улицу Ленина и попросил поменять точку назначения. Или понял, что нужно заехать в магазин по дороге. Или оказалось, что на карте нет денег, — придётся переключиться на наличные, и тогда пропадёт скидка от банка‑партнёра. Во всех этих случаях прайсинг пересчитывает цену для активного заказа.

Может ли цена измениться только в процессе поездки? Нет. Например, пользователь попросил остановить машину за квартал до точки назначения. Или офер считался с учётом платной дороги, но по факту по ней не поехали. Поэтому при завершении заказа таксометр делает запрос в прайсинг: нужно пересчитать цену по актуальным параметрам поездки. Если таксометр не смог достучаться до прайсинга, например из‑за плохого интернета, водитель звонит в диспетчерскую, и заказ закрывают через неё.

У водителя и пользователя могут возникнуть вопросы по цене. Пользователь может посмотреть детализацию — её тоже генерирует прайсинг — или обратиться в поддержку. Поддержка также идёт в прайсинг за подробностями. Если выяснится, что цена посчитана некорректно — например, не применилась скидка за заказ, — оператор может пересчитать её, исправив только нужные параметры.

И наконец, ценой интересуются не только водитель и пользователь. Аналитики, которые считают влияние цены на назначение водителей, тоже идут в прайсинг. 

Упрощённо схему всех обращений в прайсинг можно представить так:

Виды цен: чем они отличаются и когда переходят друг в друга

Мы различаем три разновидности цены.

Первая — фиксированная стоимость. Именно её вы чаще всего видите в Яндекс Такси. Цена рассчитывается на этапе создания офера и, как правило, не меняется до конца поездки. В основе расчёта — маршрут от Яндекс Карт.

Вторая — цена по таксометру. На главном экране она отображается с тильдой или надписью «от». Офер тоже считается по маршруту от Карт, но в процессе и при завершении поездки используются параметры, которые собирал таксометр.

Фиксированная стоимость может превратиться в цену по таксометру. Например, если водитель остановился слишком далеко от точки назначения или точка назначения в процессе поездки менялась слишком много раз.

Третья — интервальная стоимость, разновидность фиксированной. Мы не фиксируем цену точно, а гарантируем, что она попадёт в интервал, с которым пользователь согласился в момент принятия офера, — если, конечно, в заказе не случился переход на таксометр.

Геозоны, тарифы и почему Москва — пригород Рязани

Обычно при заказе такси пользователь указывает две точки — отправления (А) и назначения (Б). Иногда точка Б не указана или точек назначения больше одной, но цена в этих случаях считается похожим образом. Рассмотрим базовый случай.

Что нужно, чтобы посчитать цену?

Во‑первых, тарификация километров и минут. Она живёт в сервисе тарифов. Здесь важно не запутаться в терминах: в Яндекс Такси тарифом мы называем геозону — обычно это город. А то, что все привыкли называть тарифом — «Эконом», «Комфорт», «Комфорт+», — мы называем тарифной категорией.

Во‑вторых, нужно понять, в какой геозоне находится пользователь и в какую он едет. Например, есть две геозоны:

  • moscow — границы Москвы;

  • moscow_activation — расширенная зона вокруг Москвы.

Если точка подачи попадает в moscow_activation, для расчёта выбирается тариф moscow. Из‑за этого направление влияет на интерпретацию зон. Для маршрута Москва → Рязань Рязань будет считаться пригородом Москвы. Для маршрута Рязань → Москва, наоборот, Москва будет считаться пригородом Рязани.

После того как мы определились с тарифом, мы приступаем к обработке маршрута, каждая точка которого либо лежит в moscow, либо нет. На основании этого мы можем разбить маршрут на интервалы. Например, для маршрута:

[
   {
       "lat": 55.456816,
       "lon": 37.294845,
       "dist": 0,
       "time": 0
   },
   {
       "lat": 55.456489,
       "lon": 37.294612,
       "dist": 10,
       "time": 6
   },
   {
       "lat": 55.455437,
       "lon": 37.293983,
       "dist": 15,
       "time": 12
   },
   {
       "lat": 55.455402,
       "lon": 37.294226,
       "dist": 20,
       "time": 18
   },
   {
       "lat": 55.456523,
       "lon": 37.294895,
       "dist": 25,
       "time": 24
   },
   {
       "lat": 55.457299,
       "lon": 37.295352,
       "dist": 30,
       "time": 30
   }
]

Мы получим разбивку:

[
   {
       "zone":"moscow",
       "dist": 10,
       "time": 6
   },
   {
       "zone":"suburb",
       "dist": 15,
       "time": 18
   },
   {
       "zone":"moscow",
       "dist": 5,
       "time": 6
   }
]

На основании этой разбивки мы сможем построить базовую цену:

География при этом не обязана делиться только на «город» и «пригород». Разбивка может быть, например, «город» — «пригород» — «аэропорт», и для каждой геозоны можно задать свою тарификацию километров и минут.

Кот, лыжи и динамическое ценообразование: что ещё влияет на цену

Однажды разработчик из Сочи решил достать старые лыжи и поехать в Красную Поляну. В это же время у аналитика заболел кот — его срочно нужно везти в ветеринарную клинику. А проджект‑менеджер из Ташкента собрался на велотрек за городом на своём новом скоростном велосипеде. Что общего у этих историй? В каждой из них случайно выбранный водитель может быть не готов везти заказ: аллергия на животных, опасение за салон, просто машина не приспособлена для перевозки велосипеда или лыж. Поэтому нам нужен механизм, который позволит водителю заранее сказать: «Я готов выполнять определённые требования пассажира». Если пассажиру нужна такая опция — она учитывается в цене.

Второй фактор — динамическое ценообразование, или сурдж, как мы его называем. Цена зависит от баланса спроса и предложения в моменте. Подробнее о том, зачем это нужно и как считается, можно почитать в статье наших коллег. Со стороны прайсинга задача одна: корректно применять параметры сурджа к цене.

Шесть требований, которые определили архитектуру

Примеры выше — лишь малая часть сценариев, которые затрагивает расчёт цены. Над Такси работают десятки команд, и каждая время от времени меняет что‑то в механике ценообразования. Из этого вырастают требования к прайсингу:

  • Правки в алгоритм должны быть простыми. Разработчик, аналитик или менеджер, которому нужно что‑то изменить, должен справиться, зная только свою зону ответственности и не вникая в остальное.

  • Новая функциональность должна включаться быстро. Если мы объявили, что в 12:00 запускаем новый процесс подачи в аэропорту Шереметьево, — ровно в 12:00 у пользователей должна появиться возможность им воспользоваться.

  • Каждый расчёт должен быть объяснимым. Это нужно и для отладки новой версии алгоритма, и для разбора обращений пользователей.

  • Цена должна пересчитываться с небольшими изменениями параметров — как в процессе поездки, так и после неё.

  • История изменений цены должна храниться. Мы обязаны уметь обосновать каждую цену, которая была рассчитана в заказе.

  • И наконец, любое изменение должно быстро откатываться. Если в алгоритме допущена ошибка — нельзя ждать часы до следующего деплоя.

Откуда берутся параметры и почему запросы идут параллельно

Параметры ценообразования мы называем BackendVariables. Они описываются в yaml‑файле — по этому описанию плагин кодогенерации строит структуру на C++ и методы для сериализации и десериализации.

...
    UserData:
        type: object
        additionalProperties: false
        properties:
            has_yaplus:
                type: boolean
                default: false
            has_cashback_plus:
                type: boolean
                default: false
            selected_loyalty_program:
                type: string
...
    BackendVariables:
        type: object
        additionalProperties: false
        required:
          - country_code2
          - tariff
          - zone
          - category
          - user_tags
          - user_data
          - surge_params
          - requirements
          - category_data
          - exps
        properties:
            paid_supply_params:
                $ref: '#/definitions/PaidSupplyParams'
            country_code2:
                type: string
            zone:
                type: string
            category:
                type: string
            user_tags:
                type: array
                x-taxi-cpp-type: std::unordered_set
                items:
                    type: string
            surge_params:
                $ref: '#/definitions/SurgeParams'
            discounts:
                $ref: '#/definitions/DiscountsInfo'
          ...
struct UserData {
  bool operator==(const UserData &other) const = default;

  bool has_yaplus{};
  bool has_cashback_plus{};
  ::std::optional<::std::string> selected_loyalty_program{};
};

UserData Parse(const formats::json::Value &elem, formats::parse::To<UserData>);

logging::LogHelper &operator<<(logging::LogHelper &lh, const UserData &v);

::formats::json::Value
Serialize(const UserData &value,
          ::formats::serialize::To<::formats::json::Value>);

void WriteToStream(const UserData &value, formats::json::StringBuilder &sw,
                   bool hide_brackets = false,
                   const char *hide_field_name = nullptr);

void PrintTo(const UserData &obj, std::ostream *os);

Во входных параметрах лежат довольно разнородные объекты: параметры платной подачи, скидок, стоимости ожидания и дополнительных услуг, типа перевозки животного или габаритного груза. Каждый объект — это просто набор чисел и строк, но его вычисление нетривиально. За большинством параметров стоит отдельная команда, которая отвечает за несколько микросервисов, вычисляющих этот параметр.

Отсюда задача: есть несколько сервисов, которые поставляют данные в сервис расчёта цены. Нужно переложить их в BackendVariables.

Решение простое: разные источники данных не зависят друг от друга — значит, можно обрабатывать каждый по отдельности. Интерфейс, который этим занимается, мы назвали процессором. Каждый процессор наследуется от класса AbstractAnyProcessorBase и меняет свои поля BackendVariables, не трогая чужие. Поэтому берём и перекладываем.

template <typename SourceIdT, typename ProcessorIdT>
struct AbstractAnyProcessorBase {
    virtual ~AbstractAnyProcessorBase() = default;

    virtual ProcessorIdT GetId() const = 0;

    virtual UpdateContextResult
    UpdateContext(const AbstractFetchDataContext<SourceIdT>&, const Context&, const ProcessorDeps&)
        const = 0;
};

В методе UpdateContext пользователь определяет способ заполнения полей BackendVariables. Метод обязан возвращать список полей, которые были изменены.

Но откуда процессор берёт данные? Например, для получения скидки нужны данные пользователя, для параметров повышенного спроса — данные по тарифу. Здесь сразу видны три проблемы. Для одного источника данных может понадобиться другой. Последовательно запрашивать каждый источник нельзя — их слишком много. И часть источников некритична: если сервис скидок недоступен, прайсинг должен продолжать работать.

Решение: строим граф зависимостей источников, затем запускаем асинхронные запросы в порядке топологической сортировки. Каждый источник данных наследуется от SourceBase:

template <typename SourceIdT, typename Data, SourceIdT SourceId, bool IsRequired>
struct SourceBase : public AbstractSourceBase<SourceIdT> {

    // Метод для получения данных из источника
    virtual Data Get(AbstractFetchDataContext<SourceIdT>& ctx, SourceDependenciesBase<SourceIdT> deps) const = 0;

    // Метод для сохранения данных
    void UpdateContext(AbstractFetchDataContext<SourceIdT>& ctx, SourceDependenciesBase<SourceIdT> deps) override {
        if (!value_) {
            try {
                value_.emplace(Get(ctx, deps));
            } catch (const std::exception& ex) {
                if constexpr (IsRequired) {
                    throw;
                } else {
                    LOG_WARNING("Error loading resource {} : {}", ToString(SourceId), ex.what());
                    value_.emplace(Data{});
                }
            }
        }
    }
    
    //  Метод для получения данных в источнике B от источника  A
    const Data& GetData() const {
        if (!value_) {
            throw std::runtime_error(fmt::format("No data in source {}", ToString(SourceId)));
        }
        return *value_;
    }

private:
    std::optional<Data> value_;
};

Для каждого процессора известно, какие источники ему нужны:

struct CashbackRatesProcessor : public AbstractProcessor<ProcessorId::kCashbackRatesProcessor> {
    CashbackRatesProcessor(InitializationContext& init_context)
        : cashback_rates_source_(init_context.GetSource<CashbackRatesSource>())
    {}

	...

private:
    const SourcePtr<CashbackRatesSource> cashback_rates_source_;
};

Зависимости между источниками строятся через InitializationContext — он же ищет циклические зависимости:

template <typename SourceIdT>
struct InitializationContext {
private:
    std::unordered_map<SourceIdT, std::shared_ptr<AbstractSourceBase<SourceIdT>>> created_sources_;
    std::vector<SourceIdT> currently_initialized_source_;
    std::unordered_map<SourceIdT, std::unordered_set<SourceIdT>> required_sources_;
protected:
    template <typename T>
    std::shared_ptr<T> GetSourceImpl() {
        // Метод проверяет отсутствие циклических зависимостей и возвращает источник данных
    }

public:
      template <typename T>
      std::shared_ptr<T> GetSource() {
          if (currently_initialized_source_.empty()) {
              throw std::runtime_error("GetSource() available only during initialization");
          }
          required_sources_[currently_initialized_source_.back()].insert(T::source_id);
          return GetSourceImpl<T>();
      }
};

Таким образом, для каждого запроса на расчёт цены мы инициализируем список процессоров — например, CashbackRatesProcessor, CouponInfoProcessor, CommonInfoProcessor и другие:

 const std::vector<std::shared_ptr<AnyProcessorBase>> processors = {
        init_context.CreateProcessorBase<backend_variables::CashbackRatesProcessor>(),
        init_context.CreateProcessorBase<backend_variables::CouponInfoProcessor>(),
        init_context.CreateProcessorBase<backend_variables::CommonInfoProcessor>(),
		...
};

Запускаем асинхронные задачи на получение данных из нужных источников:

...
source_futures.push_back(utils::Async(ToString(source) + "_load", [source_id = source, &sources, &ctx, &deps]() {
                       try {
                           sources.at(source_id)->UpdateContext(ctx, deps);
                           return source_id;
                       } catch (const std::exception& e) {
                           LOG_ERROR() << "Error during source " + ToString(source_id) + " loading: " + e.what();
                           throw;
                       }
                   }));
...
while (true) {
 if (auto done_source_idx = engine::WaitAny(source_futures); done_source_idx.has_value()) {
     ...
 }
 engine::current_task::CancellationPoint();
}

Ждём завершения, после — обогащаем BackendVariables:

for (const auto& proc : processors) {
	const UpdateContextResult&
                affected_fields_opt = proc->UpdateContext(data_context, proc_context, pdeps);
    if (!affected_fields_opt) {
        LOG_DEBUG() << "Processor " << ToString(proc->GetId()) << " not used";
        continue;
    }
}

Для асинхронных задач используем фреймворк userver.

Почему алгоритм ценообразования не живёт в коде сервиса

Схема данных меняется редко — добавление нового параметра ценообразования не ломает существующий алгоритм. С самим алгоритмом другая история: в среднем он модифицируется дважды в неделю.

Представим, что код алгоритма живёт прямо в сервисе. А мы почитали комментарии в интернете и всё же решили реализовать ту самую замечательную идею наших пользователей:

if(backend_variables.user_info.is_iphone) {
   return price * 100;
}

Нам важно включить новую функциональность с минимальной задержкой между подами. Но так деплоить — не лучшая идея: сервис pricing-data-preparer развёрнут на пятидесяти подах, выкатка занимает около 40 минут.

Можно обернуть новую функциональность в проверку динамического конфига:

if (config.enable_iphone_pricing) {
	if(backend_variables.user_info.is_iphone) {
    	return price * 100;
 	}
}

Но и здесь есть проблема. Алгоритм меняется часто — код быстро превратится в неподдерживаемое месиво из if’ов.

Теперь представим: A/B‑тест показал, что фича понравилась пользователям. Мы решили оставить её навсегда и удалили enable_iphone_pricing из кода. Выкатили сервис — и выяснили, что A/B‑тест был проведён некорректно и функциональность на самом деле не зашла. Нужно откатываться. Но в ту же ревизию попали правки алгоритма для пассажиров с собакой‑поводырём — откатиться на предыдущую никак нельзя. На хотфикс, проверку в CI, билд тяжёлого сервиса и выкатку уйдут часы.

И есть ещё одна проблема. Наши сервисы живут в огромной экосистеме: логи, метрики, асинхронный фреймворк, библиотеки для обращений к другим сервисам. Над всем этим работают сотни разработчиков Яндекса, поэтому деплоить сервисы стоит как можно реже.

Так что же делать? Ответ: не писать код алгоритма ценообразования в коде сервиса.

Язык преобразований цены

Итак, код алгоритма не живёт в сервисе. Где же тогда? В админке Такси.

Алгоритм ценообразования должен быть простым — C++ здесь не подойдёт. Можно было взять скриптовый язык — JavaScript или Lua, — но мы пошли другим путём... Мы сделали ещё один DSL.

Грамматика простая и привычная для большинства разработчиков — и не только. Связывание выражения с именем — let x = expression;. Изменить связь нельзя. В этом смысле в языке нет переменных — только константы. Управление потоком — if и тернарный оператор. Для работы с optional‑значениями используется конструкция as:

if (optional_value as value) {
    // if optional_value is not empty, use `value` as alias for *optional_value
} else {
	// if optional_value is empty
}
let x = (optional_field as field) ? field : default;

Язык поддерживает pack именованных значений (он же NamedTuple):

let pack = {x: 1, y: "str"};
let x = pack.x;

Поддерживаются пользовательские функции. Каждая возвращает NamedTuple:

function foo(x: double, y: double) {
	return {x_1 = x, y_1 = y};
}

let x = foo(x = 9, y = 1).x_1; // x == 9

Циклов нет — намеренно. Вместо них конструкция fold:

function foo(elem: double, sum: double) {
    return {sum = sum + elem};
}
let result = fold(container as elem, foo, {sum = 0});

В коде на C++ программа на нашем языке представлена классом Program:

class Program {
      public:
       ...

      pricing_platform::lang::models::CalcResult Calculate(
          const lang::variables::BackendVariables& fix,
          const lang::variables::DynamicContext& ride,
          const lang::variables::TripDetails& trip,
          const lang::variables::EvaluatingContext& context,
          const lang::variables::Meta& metadata,
          const ::pricing_platform::lang::models::FeaturesSet& features,
          const bool enable_debug = false
      ) const;
   };

Использовать в коде можно примерно таким образом:

const auto program = parser.Parse(source);
const auto result = program.Calculate(fix,ride, trip, context,metadata, features, false);

Для описания грамматики используем ANTLR 4. Язык достаточно простой, и у нас есть его полная спецификация — это позволяет валидировать код, написанный на нём. Для этого используем формальный верификатор Z3 — он проверяет, что программа на нашем языке не может привести к некорректному результату.

Как преобразования выстраиваются в цепочку 

При проектировании мы исходили из того, что каждая команда отвечает за свою зону ответственности, — и в части данных, и в части алгоритма. Алгоритм ценообразования представляет собой цепочку преобразований цены, которые применяются последовательно друг за другом.

Каждое преобразование — это чистая функция от базовой стоимости, BackendVariables и служебных параметров, которая возвращает цену и метаданные. Таким образом, алгоритм ценообразования — это композиция преобразований цены.

Продуктовых сценариев может быть много — от обычного заказа такси до совместных поездок. Для каждого можно соорудить свою цепочку.

Как DSL и сервис работают вместе

Итак, у нас есть данные на C++ и код на нашем языке — нужно научить преобразования цены взаимодействовать с данными.

Сначала задаём схему: что входит в контекст вычислений и что возвращает каждое преобразование. calculation_parameters — имена переменных, доступных в любом преобразовании, calculation_result — то, что каждое преобразование возвращает.

calculators:
- name: taxi-pricing
  space_name: taxi
  schema_file: backend_variables.yaml
  calculation_parameters:
    - variable_name: fix
      type_name: BackendVariables
    - variable_name: ride
      type_name: DynamicContext
      inner_variables:
        - variable_name: ride
          type_name: TaximeterVariables
        - variable_name: price
          type_name: Price
    - variable_name: trip
      type_name: TripDetails
    - variable_name: context
      type_name: EvaluatingContext
    - variable_name: metadata
      type_name: Meta
  calculation_result:
      price:
          variable_name: price
          type_name: Price
      additional_results:
        - variable_name: metadata
          type_name: Meta

В schema_file описываем схему данных, с которой работают преобразования: тип каждой входной переменной задаётся явно. Тогда в коде вида:

if (fix.category == "econom") { ... }

fix — значение уже знакомого нам типа BackendVariables.

Но здесь есть проблема: BackendVariables — это структура на C++, а выражение fix.category — конструкция нашего языка. Чтобы программа работала, нужно связать имя поля с членом BackendVariables. Встроенной рефлексии «перечисли поля по имени» в C++ нет — поэтому мы используем Boost.Fusion.

Библиотека позволяет представить структуру как кортеж и обращаться к полям по индексу. При помощи кодогенерации для каждой структуры генерируются макросы вида:

BOOST_FUSION_ADAPT_STRUCT(handlers::libraries::pricing_functions::BackendVariables,
        (std::optional<::handlers::libraries::pricing_functions::PaidSupplyParams>, paid_supply_params)
        (std::string, country_code2)
        (std::string, zone)
        (std::string, category)
        ...

Дальше строим отображение типов C++ → типы языка прайсера: для каждой адаптированной структуры регистрируем описание полей.

const TypeMapping& FillMapping<pricing_functions::taxi_pricing::lang::models::PricerData::PricerLabel>() {
	...
	TypeMappingMaker::MakeTypeMapping<lang::variables::BackendVariables>();
	TypeMappingMaker::MakeTypeMapping<lang::variables::DynamicContext>();
	...
    return TypeMappingMaker::MappingInstance();
}

По Fusion‑последовательности собирается список полей:

    template <typename T, typename = std::make_index_sequence_t<boost::fusion::result_of::size<T>::type::value>>
    struct ListFields;

    template <typename T, size_t... idx>
    struct ListFields<T, std::index_sequence<idx...>> {
        static std::vector<types::ValueField> List() {
            return std::vector<types::ValueField>({MapField<T, idx>()...});
        }
    };

    template <typename T, typename = void>
    struct TypeMapper;

    template <typename T>
    struct TypeMapper<
        T,
        std::enable_if_t<boost::fusion::traits::is_sequence<T>::value>> {
        static auto Map() {
            return std::make_unique<types::BindedStruct>(details::GetTypeWithName<T>(), ListFields<T>::List());
        }
    };

    template <typename T>
    static const types::BaseType& TypeMappingMaker::MakeTypeMapping() {
        auto& mapping = MappingInstance();

        if (const auto it = mapping.find(typeid(T)); it != mapping.end()) {
            return *it->second;
        }

        const auto& result = *mapping.emplace(typeid(T), TypeMapper<T>::Map()).first->second;
        if constexpr (!std::is_same_v<T, AddOptionalT<T>>) {
            MakeTypeMapping<AddOptionalT<T>>();
        }

        return result;
    }

В момент парсинга программы мы понимаем, что фрагмент вида fix.paid_supply_params — это обращение к полю структуры fix. Тогда мы можем сделать две вещи: проверить, что у fix действительно есть поле paid_supply_params, — если нет, программа не распарсится. И если поле есть — по std::type_index взять из мапы типов описание, зарегистрированное в MakeTypeMapping, и связать поле с типом в языке преобразований цены.

const types::BaseType& GetTypeMapping(std::type_index type) {
    static const auto& type_mapping = FillMapping();
    if (const auto it = type_mapping.find(type); it != type_mapping.end()) {
        return *it->second;
    }
    throw std::domain_error("Attempt to use non-adapted type `" + compiler::GetTypeName(type) + "`");
}


explicit Type(const std::type_index& type): type_(types::GetTypeMapping(type)) {}

Что получилось и куда это пошло дальше

Мы вынесли правила ценообразования в отдельный слой между сервисом на C++ и бизнес‑логикой расчёта. Теперь типовое изменение алгоритма не требует пересборки и деплоя сервисов прайсинга: вместо выкатки сервиса, которая занимала около 40 минут, применяется новая версия цепочки преобразований.

Что изменилось:

  • алгоритм, который меняется в среднем два раза в неделю, теперь обновляется без деплоя сервиса;

  • откат правки занимает время применения версии алгоритма, а не полный цикл хотфикса;

  • каждый расчёт стал воспроизводимым: сохраняются входные параметры, цепочка преобразований и промежуточные результаты.

Проект по выносу алгоритма ценообразования из кода сервиса оказался удачным — мы платформизировали наработки и предложили их другим командам. Сейчас платформу прайсинга используют в Такси, Доставке и зарядках для электромобилей.

Платформизация также помогла расставить акценты: разработка отвечает за техническую составляющую, а аналитики прайсинга стали основными мейнтейнерами алгоритмов ценообразования. Для этого была выделена отдельная небольшая команда, ответственная исключительно за платформу.

Главный вывод: часто меняющийся алгоритм, за который отвечают разные команды, лучше хранить не в коде сервиса, а как проверяемую и версионируемую цепочку преобразований.

Комментарии (101)


  1. wepp
    05.08.2026 09:13

    Пользователь может посмотреть детализацию — её тоже генерирует прайсинг

    Ну, не очень-то может. По крайней мере осенью 2025 года поддержка отвечала так. Сейчас проверил последний заказ - пишет только стоимость поездки и чаевые.

    Скрытый текст
    Ответ поддержки, когда я пытался понять стоимость платного ожидания
    Ответ поддержки, когда я пытался понять стоимость платного ожидания


    1. achekalin
      05.08.2026 09:13

      Ценообразование у Такси идет не от тысячи факторов, а от общей мысли - "может ли юзер куда-то деться без нашей машины?"

      Юзер открыл приложение, увидел вместо привычных 300 руб почему-то 900, закрыл. Через 5 минут открыл, потому что "куда ж он денется?" - там будет 1300.

      Так что из кода или в код, но оверпрайс попер последние месяцы так, что прямо ой.

      Ах да, конкурентов же скупили - уж не яндекс ли? )


      1. ganzmavag
        05.08.2026 09:13

        Юзер открыл приложение, увидел вместо привычных 300 руб почему-то 900, закрыл. Через 5 минут открыл, потому что "куда ж он денется?" - там будет 1300.

        По такой логике им нет смысла показывать ни 300, ни 900.


        1. nidalee
          05.08.2026 09:13

          В следующий раз вы увидите 900, вспомните что уехали за 1300, и сочтете 900 хорошей ценой.


          1. ganzmavag
            05.08.2026 09:13

            Сервис получается готов потерять 400 рублей ради моих хороших воспоминаний?


            1. nidalee
              05.08.2026 09:13

              Нет, реальная цена 300, все выше уже прогрев. Им нет принципиальной разницы 1200 или 900, они уже в жирном плюсе.


              1. BugM
                05.08.2026 09:13

                Нет, реальная цена 30

                Таксисты точно готовы вас везти за 200? 100 уйдут Яндексу и на налоги.

                Таксистов при цене 300 точно будет примерно столько же сколько клиентов?


                1. achekalin
                  05.08.2026 09:13

                  Ну в прошлый раз за 300 если человек уехал?

                  А еще прикалывает уровень авто: мне что эконом приезжает, что комфорт или к+ - они явно отличаются только годом, но не состоянием.

                  Ну и ответы ТП в стиле "доколебался - вот тебе 100 руб промокода, ни в чем себе не отказывай"


                  1. BugM
                    05.08.2026 09:13

                    Такое бывает когда таксистов вышло слишком много. Яндекс их активно разгоняет в другие зоны.

                    Можете открыть стандарты машин Яндекса и проверить. Новая машина Комфорт или Комфот+, через Н лет она же уставшая Эконом. Не вижу проблем. Новая машина явно комфортнее и лучше для пассажира.

                    Смежная история. Одно время в дорогих тарифах каршеринга были очень уставшие Бехи. Зачем? Почему? Непонятно. Будь они дешевыми, нет вопросов. Но они были прямо по х3 цене к дешевым как будто тебе хорошую БМВ дают покататься.

                    Поддержка Яндекса это боль. Тут согласен.


                  1. ganzmavag
                    05.08.2026 09:13

                    Ну в прошлый раз за 300 если человек уехал?

                    Тогда снова вопрос, почему за 300 уехал, когда оказывается готов за 1300? Вы тоже серьёзно считаете, что человека, который точно готов платить 1300, возят то за 300, то за 900 просто потому что все равно у компании много денег?


                    1. nidalee
                      05.08.2026 09:13

                      Легко: уехал за 1300, потому что было очень надо. Очень надо не всегда. Я могу запросто лечь спать на работе, а не ехать домой, если Яндекс в очередной раз задрал ценник до 2000 вместо 1000. Но иногда дома нужно что-то сделать. Они, несомненно, ведут курс нищеброда, и понимают, что иногда такое прокатит (поэтому в дождь и прочие медные трубы ценник у всех подскакивает), а иногда нет - и ценник магическим образом через 10 минут снова падает до привычной тысячи.


                      1. ganzmavag
                        05.08.2026 09:13

                        Они просто учитывают баланс спроса и предложения. Много простаивающих водителей - цена низкая, желающих пассажиров больше, чем свободных машин - цена высокая. Почему это (реальное) объяснение для вас выглядит менее правдивым, чем загадочная версия про то, как они пытаются угадать, можете вы переночевать на работе или нет.


                      1. nidalee
                        05.08.2026 09:13

                        Нет, ну конечно Бритва Оккама и все дела, но баланс спроса и предложения не объясняет "высокого спроса" в 2 часа ночи в рабочий день, где 80 каких-то мутных типов якобы пытаются уехать с ВДНХ :)

                        Нет и объяснения тому, почему возникает дизбаланс предложения - почему некоторые зоны на карте водителя ГОРЯТ фиолетовым, а у дома через дорогу спрос нулевой (вообще без оверлея).

                        Кроме того, Яндекс уже ловили на рисовании "высокого спроса" по их собственному приложению погоды (которое рисовало дождь там, где его нет) и к постройке заведомо не оптимальных маршрутов для экспериментов и "разгрузки стандартных" с подачи ЦОДД (это как раз те "сомнительные маршруты" на которые постоянно жалуются в комментариях).

                        Ну и в итоге что имеем? Fool me once, shame on you.


                      1. Kaneton
                        05.08.2026 09:13

                        В точку! Приезжаешь ночью в рабочий день в маленький городок, смотришь на полностью пустые улицы, потом смотришь в приложение и видишь что свободно 2 машины из 20 и 70 человек хотят уехать. Но стоит тебе нажать "заказать" и тебе тут же назначается машина. А вот эти 70 человек уже уехали за 5 секунд на 2 свободных машинах? А вообще эти 70 человек сейчас с нами в этой комнате?


  1. xSVPx
    05.08.2026 09:13

    А можно попросить вас еще "вынести" куда-нибудь из приложения для заказа ТАКСИ все те свистоперделки, которые туда захреначили за последние годы ?

    Я принципиально ничем этим не пользуюсь и пользоваться не буду. Из вредности. И эта вредность - результат деятельности компании, так то я добрый довольно.

    Ничего кроме чистой ненависти все эти баннеры с купи-купи-подпишись у меня не вызывают, особенно когда они вылезают вместо нужной информации в обычных ныне условиях очень зашейпленного траффика.


    1. uho
      05.08.2026 09:13

      Есть "Fasten" взамен убера, в котором только такси. Но он "для молодежи" (читай для тех, у кого нет денег), там не все тарифы (нет "Elite"), но баннеров существенно меньше. За поездку там водители получают меньше, и, соответственно, могут назвать нищебродом, по крайней мере так было на старте. Есть ощущение, что поездки там ставят в приоритет "провинившимся" водителям, ну и пару раз приезжали совсем убитые тачки.


      1. IsUnavailable
        05.08.2026 09:13

        У меня, в этом фастене, цены были в среднем больше чем в основном приложении) Проверял одновременно)


    1. Ioanna
      05.08.2026 09:13

      Поддерживаю. Обычно человек открывает приложение для вызова такси, когда у него срочное дело или плохое самочувствие. А ему вываливают кучу рекламы и где-то на задворках интерфейса поле для ввода адреса, которое еще надо найти.


      1. ganzmavag
        05.08.2026 09:13

        Прямо скажем - их самый платежеспособный клиент заказывает такси в состоянии из бара в ночь с пятницы на субботу


        1. Dmitry_604
          05.08.2026 09:13

          Да, бывает и такое, но в целом если именно в частую точку - как-то кстати быстро проходит - одна кнопка "домой" и вторая вызов нужного тарифа. ЧЯДНТ?

          Да обвязка вся эта раздражает местами но юзабельность именно вызова такси вполне нормальная, правда я go уже для другого и не использую (скоре всего в этом и ключ)


    1. ideavi
      05.08.2026 09:13

      Да, и вот это тоже уберите и прекратите каждый раз выставлять обратно 100500 галок в Notifications!


      1. reishi
        05.08.2026 09:13

        И хоть ставь, хоть не ставь, все равно будут приходить
        И на почту тоже


  1. sergeyns
    05.08.2026 09:13

    не один десяток факторов — например, скидки, спрос, геозоны

    марка и уровень заряда телефона на цену влияют?


    1. Siemargl
      05.08.2026 09:13

      и [кредитная] история состояние банковских счетов [жены] /s =)


    1. Hlad
      05.08.2026 09:13

      У меня мощное подозрение, что каждому клиенту попросту привязан "коэффициент нищеброда", меняющийся от 1 до 100. И при определении цены его тупо дёргают, а по итогу заказа/отказа от заказа - меняют


      1. Yuriy_krd
        05.08.2026 09:13

        А как сделать, чтобы у меня был статус "нищебродский нищеброд" - не подскажете ? Если он нигде, кроме экосистемы яши не фигурирует, то я был бы рад такому статусу.


        1. Dmitry_604
          05.08.2026 09:13

          Если вам интересно будет смотреть на то как отказываются водители и машина не назначается несколько минут или вообще - пишите в поддержку, просите, да


        1. nidalee
          05.08.2026 09:13

          Как минимум, тыкайте постоянно "пожаловаться на цену" (реально работает по какой-то нелепой причине!) и не соглашайтесь ездить дороже обычного, вообще. Можно еще накачать альтернативных приложений такси, список аппок они все равно видят.


    1. potapovjakov
      05.08.2026 09:13

      if (config.enable_iphone_pricing)

      Конечно)


  1. TsarIoann
    05.08.2026 09:13

    Передаю привет бывшим коллегам из прайсинга Яндекс Такси! И тебя, Глеб, тоже помню :)

    Приятно видеть, что разработанный в 2019-2021 под моим руководством новый прайсинг со своим языком программирования для преобразований цены всё ещё используется (для сторонних читателей: я 2,5 года был тимлидом команды прайсинга, стоял у её истоков).

    Такое чувство, что эту статью мы могли написать ещё 4 года назад, когда я ещё не покинул компанию и не уехал работать в другую страну.

    Жалко, что не рассказываете про свежие штуки. Но видать коммерческая тайна :)

    P.S. config.enable_iphone_pricing — я смотрю, вы не боитесь злых шуток xDD


    1. geolocator
      05.08.2026 09:13

      Жалко, что не рассказываете про свежие штуки. 

      Свежая теперь там только реклама


    1. pink0D
      05.08.2026 09:13

      новый прайсинг со своим языком программирования для преобразований цены 

      а поделитесь опытом, когда переходили с С++ на "свой язык, где правила быстренько делают админы", как подходили к QA ? Для старого С++ модуля наверняка же была куча юнит-тестов (ведь да?), которые при кривом добавлении новых правил просто падали, а когда админ сам на псевдо-языке что-то настраивает сразу в проде, то кто проверит валидность новых правил, что они в минус не загонят компанию?


      1. TsarIoann
        05.08.2026 09:13

        Всё обстоит ровно наоборот, чем Вы предполагаете: для новых правил на псевдо-языке нужно прямо в админке писать кучу тестов с тестовыми данными и ожидаемыми результатами. И, если я верно помню, то были ещё тесты на целые цепочки правил.

        Вы слишком хорошего мнения про "старый C++ модуль", так как не было там никакого единого модуля. До 2019 года была помойка полная (по историческим причинам), разбросанная по нескольким бэкендам, а в водительском приложении своя, всё на разных языках (C++, Python, C#, Java). Тесты покрывали лишь какие-то крупицы этой функциональности, а расчёты могли не сойтись, если их произвели разные части кода. Всё держалось лишь благодаря тому, что в то время логика расчётов была ещё не очень сложной. Но когда требовалось добавить что-то новое (скидку за Яндекс Плюс, например), то начинался какой-то ад. Поэтому и родилась идея, что нужно прайсинг унифицировать и сделать удобным в использовании. Теперь расчёты всегда консистентны, нет разных мест кода, каждое из которых считает при помощи собственной реализации.


  1. PereslavlFoto
    05.08.2026 09:13

    Когда вы открываете Яндекс Go для заказа такси и вводите адрес, приложение за доли секунды показывает цену.

    На сайте не показывает.

    Подскажите, пожалуйста, отчего у вас таксишная Москва не совпадает с административной?

    Спасибо.


    1. vis_inet
      05.08.2026 09:13

      На сайте не показывает.

      А несколько лет назад показывали...

      Автор, интересно, с чем это связано?


  1. IsUnavailable
    05.08.2026 09:13

    мы почитали комментарии в интернете и всё же решили реализовать ту самую замечательную идею наших пользователей

    Делаете вид, что это всё шуточки и идея на самом деле не внедрена?)


    1. Sitro23
      05.08.2026 09:13

      Тоже подумал, что пытаются отмазаться


  1. thethee
    05.08.2026 09:13

    Мы сделали ещё один DSL

    Это напомнило мне одну забавную историю.


    1. navferty
      05.08.2026 09:13

      Пожалуйста, скажите мне что это выдуманная история...


      1. press_a_key
        05.08.2026 09:13

        Да нет, очень жизненно. Особенно момент когда ты не разобрался в гениальности очередного набора костылей и используешь его вопреки замыслами автора. И для себя я сделал важный критерий в плане адекватности людей и стоит ли с ними работать. Если человек понимает, что он просто написал очередное решение под задачу, возможно не самое лучшее, и готов рассказать(или написать внятную документацию), то с таким легаси можно работать. Если же тебе говорят, что все очевидно, ты просто не умеешь читать код, но при этом код других проектов ты нормально читал, то нужно линять из этого места. Потому что такие "гении" где-то в глубине души понимают что они накодовертили, и на любые попытки объяснения начинаются срывы.


      1. zum
        05.08.2026 09:13

        Пожалуйста, скажите мне что это выдуманная история...

        Я не могу даже поставить себя на место Тома, чтоб построить цепочку умозаключений, чтоб такую систему, какая была описана в посте, построить. Может мне стоит пересмотреть понятие «programming virtuouso».

        Мне кажется, что история больше не про «легаси и костыли», а про идиотов в управлении.


  1. AVikont
    05.08.2026 09:13

    Даже после такого разбора всё-равно найдутся люди, распространяющие байки про ценообразование на основе модели телефона, операционной системы или страны регистрации.


    1. adante
      05.08.2026 09:13

      А вы думаете, что если бы в той сотне учитываемых параметров случайно оказалась phone_model или battery_status, они бы вам здесь об этом рассказали?


    1. konst90
      05.08.2026 09:13

      Опровергнуть эти байки (или не байки, никто ж не знает) можно только одним способом: опубликовать полный алгоритм ценообразования со всеми его коэффициентами. Чтобы любой желающий мог взять конкретно свой маршрут и проверить, рассчитает ли алгоритм цену поездки так, чтобы она совпала с выставленной ему ценой.

      Но этого, разумеется, сделано не будет.


      1. AVikont
        05.08.2026 09:13

        Тогда какой смысл в этой статье, если ей не верят?


        1. Tomasina
          05.08.2026 09:13

          Самопиар


  1. aegelsky
    05.08.2026 09:13

    Всё что я знаю о ценообразовании яндекс.го на примере моего села на 150к человек - оно стало практически бесполезным.
    Проехать 3-5 мин за 300-400+ или пройти пешком за 20 мин?
    Учитывая время ожидания такси - пешком сравнивается в ноль по времени, а по деньгам выигрывает бесконечно.
    За этот год я пользовался им раза 2 (и то если бы был один - пешком скорее всего пошёл бы), когда оно стоило х2-3 проезда в общественном - пользовался чаще. Это всё что я знаю о текущем ценообразовании.


  1. jlllk
    05.08.2026 09:13

    Из дома в гостиницу с ребёнком(детское кресло) — 1500р.

    Обратный путь через день — 2000р. Спросил у водителя, повышен ли у него коэффициент за эту поезду. Повышен не был.

    Обе поездки в обед без пробок. Время ± одинаковое.

    Ну и кто вы после этого? Хочется пожелать, чтобы вам самим везде прайс выставляли по вашим же алгоритмам.


  1. glorden
    05.08.2026 09:13

    как человек который работал в ятакси скажу что я бы лучше возил по средним ценам, чем по тем, который яндекс ставит - или оверпрайс или настолько нищеброд тариф что хочется плюнуть в лицо тем кто эти тарифы считает.
    особенно радует что бенз в цене вырос с 40 до 65 рублей, а минимальная цена на поездку на 5 рублей и вот ты радостно едешь до клиента 3,5 километра (и не знаешь куда поедет пасс ибо функция платная увидеть точку Б) и потом с клиентом едешь 1 километр и получаешь за вычетом комиссии и налога 60 рублей.


    1. ED-209
      05.08.2026 09:13

      и не знаешь куда поедет пасс ибо функция платная увидеть точку Б

      Как это платная? Я всегда думал, со стороны таксиста выглядит примерно так: "Есть некий оптимальный ограниченный радиус по гелокации от водителя, внутри на карте которого появляется пассажир и машет лапкой, мне из пукта А в пункт Б. Сумма такая-то (сформирована алгоритмом). Взять заказ? Y/N"

      А как без точки Б можно планировать поездку? Пассажир может сказать отвезти его через три дома, а может через весь город по пробкам на 2 часа.


      1. glorden
        05.08.2026 09:13

        сначала видишь только А, приезжаешь до пассажира и только потом видишь точку Б. Стоимость увидишь уже после того как поездка начнется.


        1. ED-209
          05.08.2026 09:13

          А если там Б окажется Нижний Новгород? А к пассажиру уже приехал.. От лица пассажира он же заранее видит ему назначена машина, радуется.


          1. Daddy_Cool
            05.08.2026 09:13

            В этом и смысл. Иначе таксисты будут брать только выгодные заказы. А за отказ от заказа - штраф. Как-то вызываю такси, заказ принят, но... такси не едет. А оно прям в паре минут ходьбы (но на авто подъезжать чуть дольше). Дохожу, говорю, едем? Едем. Доезжаем, спрашиваю - а почему не ехали к точке? Таксист: "Не хотел".


            1. ED-209
              05.08.2026 09:13

              Не справедливо, как по мне. На базовых условиях, на старте:

              Для пассажира:

              *известен маршрут

              *известны расходы

              Для таксиста:

              *неизвестен маршрут

              *неизвестны доходы

              Перекос явно в сторону пассажира.


              1. xSVPx
                05.08.2026 09:13

                Перекос был бы, если бы пассажиру не НАДО было ехать.

                Есть огромное количество людей, которые будут в большой попе, если машина не придет. Пробовали на костылях перемещаться ?

                Такси имеет статус чуть ли не общественного транспорта, и соответствующие "перекосы".


                1. ED-209
                  05.08.2026 09:13

                  Не согласен.

                  Раньше также ездили и больные и хромые и в дождь и в стужу. У пассажира было два варианта:

                  а) Поймать бомбилу на дороге и договориться о цене

                  б) Позвонить в таксопарк, условно, какое-нибудь такси "Тройка", где ему диспетчер сразу сходу цена 300!! Машина через 10 минут.

                  И либо едь, либо не едь. Чаще, все-таки, соглашались, и ехали. Рынок был таксистов, могли диктовать свои условия. Сейчас пассажир диктует свои условия: "Cтою курю, поездка 200р, Комфорт+, оплатил бонусами 150р, спишут 50р, пусть будет "Молчаливый водитель, Приятная музыка".


                  1. nidalee
                    05.08.2026 09:13

                    Практика показывает, что таксисты готовы стоять сутки и ждать одного жирного лоха, которого можно отвезти от вокзала\аэропорта за 5000. Так никто никуда не уедет. Поэтому вводят перекос. "Та" сторона уже была и в хвост и в гриву исследована в нулевых, когда был "рынок продавца". Теперь рынок покупателя, ему так выгоднее. Ждать "лоха" - это не жизнеспособная стратегия, это не "вид транспорта", это уже ближе к разводам чем к услугам.


                  1. Daddy_Cool
                    05.08.2026 09:13

                    Я тут вышел из метро, и дождь... думаю - проедусь на такси... поездка пять минут, а пешком 20. Мне показывают... 1000+ руб.
                    Прошелся под дождем.
                    ---
                    Это так называемый "прогнозируемый спрос", на который жалуются сами таксисты - цены задираются и... никто не едет.


                    1. Aggle
                      05.08.2026 09:13

                      Аналогично. Обычно стоило 200 рублей, в дождь предложили доехать за 750. Не нищеброд, но отваливать три с лишним цены, благодаря гениальным создателям алгоритма? "Пионэры — идите в жопу!" (ц)


                  1. warner
                    05.08.2026 09:13

                    Кстати иногда таксисты находят случаи и возвращают обратно "рынок таксистов".

                    Ездил на Moscow Raceway. Пора ехать обратно, уже вечер, электричек никаких не ходит, и т.п. Яндекс показывает 3-3.5 тысячи до Москвы. Но водитель никак не находится, при этом я вижу на парковке пачку машин такси, и в обклейке яндекса тоже. Сначала думал что это ждут клиентов, но они просто стоят. Пошёл спросить, а мне говорят - до ближайшего метро доехать - 10 тысяч, или можешь стоять ждать заказ из своего приложения, всё равно никто не приедет. Ну и всё, или едь за 10к, или пытайся поймать какую-нибудь попутку, или ночуй в ближайших кустах.


                    1. xSVPx
                      05.08.2026 09:13

                      Кто поумнее посадил бы троих по 5000...


                      1. konst90
                        05.08.2026 09:13

                        Кто поумнее - таксистом не работает обычно.


                      1. xSVPx
                        05.08.2026 09:13

                        Кто поумнее к этому моменту уже уехали с тремя пассажирами за 15000 скорее всего.


                  1. xSVPx
                    05.08.2026 09:13

                    Если так, тогда ты не двигаешься по выделенным полосам и вообще не имеешь никаких преференций нигде. Работай как бомбила.

                    Вы как-то хотите и рыбку, и остальное. 100% перекос в сторону водителя такси. Так в целом в сфере услуг не бывает. Исключение - жестко монопольная ситуация. Т.е. яндекс, наверное мог бы, но как на это посмотрело бы фас и что сделало в ответ - это большой вопрос, точнее не вопрос сделало бы предельно больно скорее всего.

                    И с другой стороны., Можно, конечно, вытирать об клиента ноги. И некоторые будут вынуждены терпеть. Но очевидно, что денег так заработается яндексом меньше. Собственно все бомбилы за год скорее всего не зарабатывали столько, сколько сейчас за час.

                    Я как раньше не буду уже скорее всего. Будете хреново возить - куплю себе тачку и вместо сотен поездок в год от меня их будет ноль. Как только начнется эта хрень, которая раньше была, я в этом участвовать перестану. И деньги уйдут другим.


        1. DenisArd
          05.08.2026 09:13

          Таксисты признаются, что если стоимость "нищебродская", пассажира могут и высадить. Это нормально считается?


          1. glorden
            05.08.2026 09:13

            то что вы называли "высадить" скорее выглядит как "отказ посадить в авто с последующей отменой заказа".

            я редко, но именно так и отказывался. приехал, увидел точку Б, понял что это будет минималка по стоимости и если идут ко мне 4 человека, то отменяю заказ и уезжаю.

            разговоры и конфликты стараюсь не разводить. отказаться от заказа я могу по правилам, ну да потеряю там баллы активности и время на все эти маневры, но реально никаких эмоциональных сил нет везти 4 тела чтобы получить в руки 60 рублей.


            1. DenisArd
              05.08.2026 09:13

              Речь о ситуации, когда водитель после посадки пассажира видит, что поездка будет по тарифу Fasten, который ниже обычного. Видел комментарии водителей, которые признаются, что в таком случае могут реально высадить пассажира из машины.


              1. xSVPx
                05.08.2026 09:13

                Чисто теоретически он и обоссать тебя может ведь. Есть из чего.

                Но, мне такая дичь не попадалась. Могут тупо уехать не посадив. И то, довольно редко.

                Исключение - минивэны. Эти где-то в четверти случаев совершенно отморожены.


                1. Dieul
                  05.08.2026 09:13

                  >обоссать тебя может ведь

                  Клиентоориентированность.

                  Разве это не проблема между агрегатором и таксистом? А не между таксистом и клиентом?


                  1. konst90
                    05.08.2026 09:13

                    Проблема в том, что агрегатор сидит на двух стульях: одним седалищем он называет себя словом "Такси", устанавливает цену и берет деньги с пассажира, а другим вертит со словами "я просто оказываю информационные услуги", когда что-то идёт не так.


                  1. xSVPx
                    05.08.2026 09:13

                    Какого рода проблема ?

                    Если регулярно что-то такое делать - тебя дисквалифицируют по оценкам либо накажут рублем по жалобам. Зачем они так делают я не знаю, вероятно новички какие-то. Ну т.е. в условиях москвы если ты подал машину всегда выгоднее отвезти пассажира. Даже без учета рейтингов итп, выгоднее везти этого, чем ехать до другого.

                    С минивэнами там другая история. Они в 90х. Их мало, и часто их пассажиры очень уязвимы, а альтернативы не всегда есть. Поэтому они активно монетизируют это. Это как бомбилы возящие в аэропорт.


                1. Tomasina
                  05.08.2026 09:13

                  А что с водителями минивенов? Корона на голове?


                  1. xSVPx
                    05.08.2026 09:13

                    Разное. Часто просто сбрасывают заказы не в аэропорт.

                    Некоторые вымогают дополнительные деньги.

                    Итд итп.

                    Но не все. Где-то четверть. А три четверти совершенно нормальных.


    1. ganzmavag
      05.08.2026 09:13

      я бы лучше возил по средним ценам, чем по тем, который яндекс ставит - или оверпрайс или настолько нищеброд тариф

      Тогда бы у вас было просто меньше денег. Те, кто заказывает дешево, просто бы не поехали по средней цене, при этом те, кто согласен был ехать дороже, заплатили бы среднюю цену - то есть меньше.


  1. rabitagorgor
    05.08.2026 09:13

    "Чем многословнее отмазка, тем она менее убедительна".


  1. catana
    05.08.2026 09:13

    Практически полностью уничтожив конкурентов Яндекс назначает цену с потолка, что подтвердят не только лишь все. Вы хотя бы для приличия разделили агрегатор на несколько юр.лиц.
    И еще такое наблюдение - в самолете для детей места дешевле, а в Яндекс такси - специальный более дорогой тариф.


    1. DenisArd
      05.08.2026 09:13

      По поводу детей - это потому что детское кресло нужно. Не каждый таксист работает по детскому тарифу, соответственно меньше предложение - выше цена.


      1. glorden
        05.08.2026 09:13

        да, совершенно верно.
        а если кому то надо 2 кресла, то в небольших городах наверное можно и не найти машину даже в яндексе-монополисте.


        1. xSVPx
          05.08.2026 09:13

          Ну так и три по идее вместить можно. Даже в минивэнах никогда 3 не видел. А трое детей - норма для нормального государства, двое мало...

          Боюсь тут только некая регуляция со стороны государства может помочь, и скорее в виде оплаты части чека. Ну и пассажиры должны выбирать при заказе нужен ли им багажник и сколько мест в салоне. Т.е. должна быть возможность подобрать для тех у кого три кресла с собой и пассажиров без детей(я в 90% случаев один еду и спереди), и за рейсы с детьми им жирно доплатить.


          1. Tdaa1
            05.08.2026 09:13

            Трое детей может и норма, но трое маленьких детей это скорее исключение.

            Я понимаю что есть люди которые каждый год готовы рожать, но обычно между ними несколько лет и когда 1-2 требуется кресло третий уже достаточно взрослый.


            1. xSVPx
              05.08.2026 09:13

              Достаточно для чего ?

              Дети 3-6-9 лет сколько потребуют специальных мест ?

              А 1-5-10 ?

              1-10-20 звучит уже лучше, но последнего предстоит родить лет в 50... 20 ему будет когда тебе 70...


          1. Aggle
            05.08.2026 09:13

            А трое детей - норма для нормального государства, двое мало...

            Где ж такие нормальные государства...


    1. konst90
      05.08.2026 09:13

      В самолёте детское место дешевле потому, что так требует закон.

      А более дорогой тариф в такси потому, что перевозка ребенка сложнее, чем взрослого: как минимум нужно кресло (причем разное в зависимости от возраста), которое в остальное время занимает часть салона или багажника, плюс время на его снятие-установку.


      1. xSVPx
        05.08.2026 09:13

        Время оплачивается же всегда дополнительно...


        1. nidalee
          05.08.2026 09:13

          Только время ожидания.


          1. xSVPx
            05.08.2026 09:13

            Как минимум установка кресла во время ожидания и попадет. А демонтаж попадет во время ожидания следующего пассажира.

            Или вам попадались водители, которые вначале стартовали поездку, а потом начинали кресло устанавливать ?

            В целом не в установке печаль. Она занимает времени не больше, чем извлечение из багажника чемодана.

            Дети более сложные пассажиры. Могут что-то испачкать итп. Но и не в этом печаль.

            Некоторые родители... совершенно неуправляемы и абсолютно безумны. Вероятно действие гормонов. Вот это - немаленькая проблема.

            И да, рисковать водители согласны только за деньги. Это вполне нормально.


            1. nidalee
              05.08.2026 09:13

              Согласен. С животными та же история. Это не оскорбление, это опыт попытки уехать со своим зверем :)


              1. xSVPx
                05.08.2026 09:13

                С животными у многих еще и неэфемерная аллергия. Т.е. они физически не могут везти. Сопли бахромой итп.

                Ну и владельцы животных тоже "такое себе", могут с леопардом на поводке выйти...


    1. Tomasina
      05.08.2026 09:13

      Последний месяц "по многочисленным просьбам пользователей" Яндекс сделал детский тариф чуть дешевле простого, и это дало обратный эффект - детский тариф стали заказывать ушлые пассы - приезжаешь по детскому а они без детей, "потому что так дешевле". А пассы с детьми теперь дольше ждут такси, ибо водителей с креслами и бустерами немного.


  1. RighteousHippie
    05.08.2026 09:13

    Шёл 2026 год. Примеры интерфейса мобильных приложений до сих пор делают с блевотной чёлкой (монобровью). Как-будто для вас это эталон дизайна мобильных приложений. Это мрак.


  1. Sitro23
    05.08.2026 09:13

    Я так и не понял, почему микросервисы выкатываются по времени как монолит. Как логи и метрики препятствуют быстрой выкатке?


  1. ViTaRga
    05.08.2026 09:13

    В итоге чистыми для водителя получается просто днище!

    51 км за 1680 руб. грязными для водителя (Москва, тариф Комфорт). Вычесть комиссию Яндекс, минус бензин, минус амортизация, получается просто днище!


    1. Dmitry_604
      05.08.2026 09:13

      Фиг знает но в Питере так выросли тарифы за последние несколько лет, что не понятно как можно быть в минусе, ну либо яндекс реально забирает половину..

      Обычно указанная вами сумма километров за 15 в Питере уже выскакивает на комфорте (ЭКоном ну процентов на 10 дешевле, а бывает и дороже давно не ездил, смысла нет с такой разницей)

      И центра ехать мне километров 7-8, уже под 1000 обычно


  1. entze
    05.08.2026 09:13

    Добавить поворот карты чтобы подойти к точке посадки когда не рядом с тобой?


  1. Dieul
    05.08.2026 09:13

    Честно, вы так накрутили с прайсингом, что одно радует что есть на рынке альтернативы.


    1. Dmitry_604
      05.08.2026 09:13

      Это какие? Общественный транспорт? Пешком?


      1. nidalee
        05.08.2026 09:13

        Тоже хотелось бы знать. А то я в том году пробовал раза 3-4 "альтернативы", ни разу за мной машина просто не приехала. Нет их и все.


  1. Tomasina
    05.08.2026 09:13

    А есть ли приоритеты при назначении водителя? Например, если поездка в ветклинику, или травмпункт/поликлинику, то назначается ближайший водитель, а не тот, у которого выше рейтинг, но он географически дальше.


    1. nidalee
      05.08.2026 09:13

      "Самый быстрый" это вариант тарифа при заказе.


      1. xSVPx
        05.08.2026 09:13

        Я так понимаю человек хочет "самый крутой", а не самый быстрый.

        В целом, в москве больших проблем в обычных тарифах не припомню. Но эконом "такое себе", мне как-то раз попался водитель весом килограмм ну не знаю... по ощущениям тридцать. Совершенно выглядел как узник концлагеря. Я как-то не рискнул выяснять почему.

        Глухие попадаются довольно часто. Но с ними как раз ноль проблем. Отлично обычно и водят и всё остальное.

        В целом неэконом несколько лучше. И по-моему у них есть в комфорт+ отсечка по рейтингу, т.е. не ниже сколько-то должно быть. Маме всегда его вызываю, проблем не припомню.


  1. vis_inet
    05.08.2026 09:13

    Похоже, что автор статьи не ответил ни на один вопрос?