Русский
Русский
English
Статистика
Реклама

Альтернативное собеседование на позицию разработчика ПО

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

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

Предлагалось сделать следующее:

  1. Изучить код и в двух словах предположить, что он вообще делает. (в проекте все довольно хорошо документировано, но вот у сторонних библиотек с документацией и комментариями иногда беда, и разработчикам приходится понимать, что там вообще происходит)

  2. Провести code-review, указать на подозрительные и плохие места и предложить, как можно их улучшить или переделать. Можно задавать любые вопросы и гуглить все что угодно.

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

class SomeServiceClient{ public:  SomeServiceClient();  virtual ~SomeServiceClient();  bool CallAsync(const std::string& uri,                 const std::string& param,                 const misc::BusServiceClient::ResponseCB& callback);  bool CallSync(const std::string& uri,                const std::string& param,                const misc::BusServiceClient::ResponseCB& callback); private:  misc::BusServiceClient ss_client_;  static const int kSleepMs = 100;  static const int kSleepCountMax = 50;};class SpecificUrlFetcher : public UrlFetcher { public:  SpecificUrlFetcher();  virtual ~SpecificUrlFetcher();  SomeData FetchData(const URL& url, const UrlFetcher::ResponseCB& callback); private:  bool SsResponse_returnValue{false};  char SsResponse_url[1024];  void SsResponseCallback(const std::string& response);  SomeServiceClient* ss_client_;};...static const char ss_getlocalfile_uri[] =    "bus://url_replace_service";namespace net {pthread_mutex_t g_url_change_callback_lock = PTHREAD_MUTEX_INITIALIZER;SomeBusServiceClient::SomeBusServiceClient()    : ss_client_(misc::BusServiceClient::PrivateBus) {}SomeBusServiceClient::~SomeBusServiceClient() {}bool SomeBusServiceClient::CallAsync(    const std::string& uri,    const std::string& param,    const misc::BusServiceClient::ResponseCB& callback) {  bool bRet;  bRet = ss_client_.callASync(uri, param, callback);  return bRet;}bool SomeBusServiceClient::CallSync(    const std::string& uri,    const std::string& param,    const misc::BusServiceClient::ResponseCB& callback) {  boold bRet  bRet = false;  int counter;  pthread_mutex_lock(&g_url_change_callback_lock);   ss_client_.callASync(uri, param, callback);  counter = 0;  for (;;) {    int r = pthread_mutex_trylock(&g_url_change_callback_lock);    if (r == 0) {      bRet = true;      pthread_mutex_unlock(&g_url_change_callback_lock);    } else if (r == EBUSY) {      usleep(kSleepMs);      counter++;      if (counter >= kSleepCountMax) {        pthread_mutex_unlock(&g_url_change_callback_lock);        break;      } else        continue;    }    break;  }  return bRet;}/**************************************************************************/SpecificUrlFetcher::SpecificUrlFetcher() {}SpecificUrlFetcher::~SpecificUrlFetcher() {}void SpecificUrlFetcher::SsResponseCallback(const std::string& response) {  std::unique_ptr<lib::Value> value(lib::JSONReader::Read(response));  if (!value.get() || !value->is_dict()) {    pthread_mutex_unlock(&g_url_change_callback_lock);    return;  }  lib::DictionaryValue* response_data =      static_cast<lib::DictionaryValue*>(value.get());  bool returnValue;  if (!response_data->GetBoolean("returnValue", &returnValue) || !returnValue) {    pthread_mutex_unlock(&g_url_change_callback_lock);    return;  }  std::string url;  if (!response_data->GetString("url", &url)) {    pthread_mutex_unlock(&g_url_change_callback_lock);    return;  }  SsResponse_returnValue = true;  size_t array_sz = arraysize(SsResponse_url);  strncpy(SsResponse_url, url.c_str(), array_sz);  SsResponse_url[array_sz - 1] = 0;  pthread_mutex_unlock(&g_url_change_callback_lock);}SomeData SpecificUrlFetcher::FetchData(const URL& url, const UrlFetcher::ResponseCB& callback) {lib::DictionaryValue dictionary;std::string ss_request_payload;misc::BusServiceClient::ResponseCB response_cb =lib::Bind(&SpecificUrlFetcher::SsResponseCallback, this);SomeBusServiceClient* ss_client_ =new SomeBusServiceClient();dictionary.SetString("url", url.to_string());lib::JSONWriter::Write(dictionary, &ss_request_payload);SsResponse_returnValue = false;SsResponse_url[0] = 0x00;ss_client_->CallSync(ss_getlocalfile_uri, ss_request_payload, response_cb);URL new_url;if (SsResponse_returnValue) {  new_url = URL::from_string(SsResponse_url);}delete ss_client_;return UrlFetcher::FetchData(new_url, callback);}}  // namespace net

Ответы будут под спойлером, нажимайте на него осознанно, пути назад уже нет.

Итак, ответы.
  1. У нас есть какой-то класс UrlFetcher, задача которого, судя по всему -- получать какие-то данные по какому-то URL'у. Унаследованный у него класс делает то же самое, только перед запросом обращается по какой-то шине сообщений к какому-то внешнему сервису, отправляя ему запрошенный URL, и вместо него получает от этого сервиса некий другой URL, который и используется дальше. Этакий паттерн Decorator.

  2. Сначала по мелочам:

    1. ss_getlocalfile_uri - глобальная переменная. Зачем? Можно было объявить ее внутри одного из классов.

    2. В некоторых местах объявляется переменная, а в следущей же строке ей присваеивается значение. Можно совместить.

    3. Странный стиль именования переменных и полей, например SsResponse_returnValue Далее по-серьезнее:

    4. Используется pthread-функции, при том что есть стандартные std::thread, которых в данном случае более чем достаточно.

    5. Используются Си-строки с методами типа strncpy(); по факту тут можно использовать std::string без каких-либо проблем.

    6. ss_client_ хранится в сыром указателе и удаляется вручную. Лучше использовать std::unique_ptr.

    7. Вместо usleep() лучше все-таки использовать std::this_thread::sleep()

    Еще серьезнее:

    8. В цикле в SomeBusServiceClient::CallSync если колбэк с ответом придет менее чем за kSleepMs до kSleepCountMax, то мы откинем ответ и не выполним задачу. Это плохо.

    А теперь еще серьезнее:

    9. Мы отправляем асинхронный запрос в message bus и ждем. Отправленный запрос по истечении таймаута не отменяется. Неизвестно, как работает этот message bus, но если вдруг у класса работы с ним есть какой-то таймаут по умолчанию, то стоит использовать его как kSleepCountMax*kSleepMs, а если ничего такого нет, то нужно как-то отменять уже отправленный запрос когда он нам стал не нужен (возможно callASync возвращает какой-нибудь id запроса?). Потому что если вдруг по какой-то причине ответ придет сильно позже, когда мы уже не ждем, а начали получать следущий URL, то случится полный бардак.

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

    10. Метод FetchUrl, судя по сигнатуре, изначально асинхронный. В наследуемом классе же по факту из асинхронного метода делается синхронный, потом блокируется до получения ответа, а уже потом вызывает действительно асинхронный метод родительского класса -- WTF? Почему нельзя было сразу сделать все асинхронно?

    11. Судя по логике работы (вызов FetchUrl синхронный и блокирует тред), SsResponseCallback должен выполниться в другом треде. При этом получается, что мы разблокируем мьютекст не в том потоке, где мы его блокировали. Для pthread это явный undefined behavior.

Ответы и замечания от кандидата позволяли составить представление о его уровне владения современными стандартами C++ и хорошими практиками, понимании асинхронности и многопоточности, дотошности на ревью и умении "отлаживать" код в голове. Ну и задать темы для дальнейшего разговора по душам.

Источник: habr.com
К списку статей
Опубликовано: 07.04.2021 12:05:29
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

C++

Системное программирование

C

Карьера в it-индустрии

С++

Собеседование

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru