Все новости с меткой: ssh


... или история “плохого” iOS-погроммиста

Статья написана под вдохновением рассказа вот этого мужчины
Скрытая опасность: впереди много букв и элементы подгорания субъективного мнения автора

Вступление

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

Для начала обозначим, что если у вас есть аккаунт в LinkedIn, то вам периодически будет кто-то написывать. Так случилось и со мной. В принципе, это ОК, Вас будут пытаться захерить различные рекрутеры. Это все прекрасно 


Яндекс и сознание

Каждый день я пользуюсь Яндекс.Почтой, главной страницей Яндекса с новостями и виджетами падающего курса рубля вместе с растущими ценами на нефть и на топливо, Дзеном с персональной подборкой статей, Такси (которое стало убером, или убер стал такси, не суть важно), Навигатором, Картами, Транспортом, Диском, Маркетом. Это все классные сервисы, плотно влились в мою жизнь

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

Слушать музыку я тоже люблю, но всегда считал, что она должна быть бесплатной. Еще раз повторюсь - бес-плат-ной. Если ты адекватный артист, то ты будешь зарабатывать на концертах. А за счет бесплатной музыки бесплатно и доступно для всех раскручиваться среди поклонников. Но Яндекс.Музыкой я так и не проникся из-за подписки. Слушать Виктора Цоя ведь надо за подписку, чтобы деньги отчислялись его автору, это стимулирует выпуск новых песен. Вы идею, надеюсь, поняли. Говорят, что сам Цой жив до сих пор

Также я не понял, зачем настойчиво предлагается отовсюду в любой непонятной ситуации скачивать Яндекс.Браузер, который сумеет защитить меня от вирусов на линуксе, вместо хрома. Либо ускорить мой домашний оптоволоконный 100Mbps интернет, мол страницы станут еще быстрее загружаться. Ведь разработка Браузера - всего лишь попытка откусить кусок пирога от Хрома, внедрить рекламу директа и максимально следить за пользователем, чем же он там занимается в этих самых интернетах. В такой проект я наверно бы никогда не согласился пойти

Но это все лирика, в целом я очень люблю сервисы Яндекса, попробовать сделать что-то полезное стоило бы. У меня даже домен привязан на pdd.yandex.ru и MX запись на почту привязана к Почте. Да-да, они там все знают и хранят тайну моей переписки

С чего все началось

На почту приходит типичное письмо “А не хотите ли пройти собес в Яндекс, вы такой классный, судя по профилю в Линкедине”. Думаешь, льстит, чертовка, хотя, у меня и так все вроде неплохо, но чем черт не шутит

Взвешивание “за” и “против”

Как минимум надо понять, чем я потенциально хочу заниматься. Недавно я проникся идеей телевизора, увидев его на главной странице Яндекса, а я ведь работаю уже более 7 лет в этой предметной области в холдинге у крупнейшего спутникового оператора. В свое время выпустил ряд приемников, в которых моей задачей было колбасить UI от сore-компонентов до уровня независимых приложений на OSD + немножко middleware бизнес-логики на плюсах под embeded linux (всякие кинозалы с dsmcc карусели и прочее)

Чуть позже я перешел на разработку сервисов для iOS для этих самых спутниковых приемников, например, в виде виртуального пульта, который голосом от того же заточенного под нас Yandex.SpeechKit понимал произносимые названия каналов и переключал их. Чуть позже появилось приложения а-ля “давайте смотреть футбол и не пропускать важные моменты, пока ты на кухне / в туалете” путем ретрансляции контента с приемника по RTSP / HLS. А затем и вовсе независимо от приемника смотреть телевизор из кармана, пока ты едешь в трамвае с интернетиком и опаздываешь на очередной матч. Все моменты можно отмотать, смотреть сначала или поставить на паузу, красивые картиночки передач ну и все такое… Классно в общем

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

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

Из всех существенных минусов, что офис в Питере находится мягко говоря в жопе неудобно, в неком Полюстрово, где в радиусе 4км нет метро. Вот я живу на севере города, и даже не за КАДом и даже не на Парнасе (боже упаси), а просто в обычном спальнике на Озерках рядом с метро. Казалось бы и офис находится в северной части рядом с центром. Тем не менее, мне надо добираться час до Финляндского вокзала (что на метро, что на автобусе), + развозка раз в 20-30 минут + дорога. Амортизированно выходит 3 часа в сутки ты будешь тратить на дорогу + 9 часов в офисе. Это на 2 часа больше, чем я трачу до офиса текущей своей работы. На велике в среднем добираюсь за 22 минуты, на самокате - за 35, если это лето. 2 часа оверхеда умножаем на 22 рабочих дня в месяц = получается 66 часов, это почти трое суток. Вам нужны лишние трое суток выходных в месяц? Я бы не отказался. Для Питера час до работы это уже считается плохим временем, если ты живешь в черте города, и не в каком-нибудь из разновидностей гетто аля Девяткино или Кудрово

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

Проверки на адекватность

Первый созвон с рекрутером был минут на 5, где они нащупывают твои ожидания по ЗП. Я назвал среднюю температуру по больнице (читать хедхантер), с небольшим отклонением в некоторой окрестности своего нынешнего оклада. А еще меня спросили “как у меня с алгоритмами”, на что я ответил, что в свое время словил диплом на олимпиаде школьников по программированию и поступил в ИТМО, и там у нас был классный курс как по дискретке, так и по “Алгоритмам и струкрутам данных”. Кстати, это лучшее и толковое время, которое было в универе, причем полностью бесплатно. Рекрутера этот ответ устроил. Еще спросили, “а умеете вы на чем” - я ответил, что на плюсах и на Swift, на objc уже года три как не пишу ничего, только врапперы core-библиотек на плюсах для Swift. Сказала, "да, это все вообще прекрасно"

Что меня смутило, что рекрутеру было плевать, что я 3 года не обновлял свой профиль в линкедине, на резюме было так же плевать, мол “оно нас мало интересует”. Получается, что опыт вообще не важен, можно сразу из школы идти туда. Я по приколу все же отправил до кучи свое резюме на 1 листик, когда меня уже позвали на интервью, но получил стандартную отписку “Андрей, это все прекрасно, что вы рассказали о своем бесценном опыте в своем резюме

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

Предварительный скайп-кол

Тут пытаются за час понять, стоит ли тратить на тебя еще 4 часа времени 4х человек. По скайпу действительно позвонил в назначенное время, ровно минута в минуту, человек из команды Кинопоиска и мы с ним славно побеседовали. По техническим вопросам платформы iOS и по Swift там все просто и понятно, вопросы простые

Единственное я не понял, зачем меня спросили вот это:

Доктор дал пациенту 4 таблетки, 2 из них одного типа, 2 другого. Все одинакового цвета. Принимать нужно утром 2 таблетки разного типа, и 2 таблетки разного типа вечером. Если перепутать - то пациент умрет. И тут таблетки перемешались и внешние ничем не отличаются. Как ему не помереть??

Тут я конечно же представил в голове 4 объекта в массиве, имеющим общий интерфейс “таблетка”, унаследованных таблеткой с определенной функциональностью. Как я их не крутил комбинаторно, так и не понял различные “правильные” варианты исхода событий, чтобы пациент в конце концов не помер. В итоге говорю, что хз как быть. Оказывается, таблетки можно делить пополам!!! Да, вы не поверите, как и в программировании можно, взять объект и поделить пополам, например Float или Double. И тогда пациент съест за 2 подхода все таблетки поровну. Ну окей. Чуть позже выяснилось, что эту задачку дают в пятом классе. Видимо не дотягиваю до пятиклассника...

А, еще:

Есть 8 одинаковых шаров, один из них поддельный и весит легче. Как за 2 взвешивания понять, какой из них поддельный

Сначала подумал про задачу с бинарным поиском и методом “разделяй и властвуй”, но нет, получится 3 операции. Все проще оказалось, решение нашел, но так и не понял, что это было и что мы пытались проверить в моих компетенциях. Отображение на алгоритмы в этой задаче я не увидел, лишь интуицию, которая не факт что может прийти в столь волнительный момент в рамках ограниченного времени

Еще была задача, видимо из классики:

Есть связный список. С какого-то индекса начинается цикл. Как понять с какого индекса и сам факт наличия цикличности?

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

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

После собеса - фидбек, прямо с пылу с жару. Рекрутер на полном серьезе говорит: “Вот вы с таблетками не разобрались, задача вам не засчитана. На решение цикла предложили неоптимальное решение, но сойдет за решение. А вот с шарами вроде ок”. Я мысленно думаю, как жить-то дальше с этим. Думаю, нууу даааа, как же я мог не додуматься, что таблетки (читать “неделимый объект” в контейнере) можно пополам делить. Мрак

Но на фоне общей картины на серию очных собесов позвали. Посчитали достойным этого

Псевдоочные интервью

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

Рис. 1. Ресепшн офиса Яндекса в СПб. За буквой "Д" скорее всего находится портал в параллельную вселенную

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

Ну и хорошо, сэкономил время на дорогу и на платную парковку. Это было даже выгодно для меня, потому что в Яндексе на секции с кодом или по алгоритмам любят писать прямо на бумажке, ручкой, где нет ни бекспейса или возможности вставить кусок кода между двумя уже написанными строками. Иногда на доске. Доска круче тем, что там есть поддержка бекспейса. При этом интервьювер ожидает (о чем я уже понял после интервью, правил игры никто не объяснял), что код должен быть написан изящно и классно компилироваться, уже быть отлаженным, не падать, не ловить Memory / Time Limit Exceed. Задача безбожно не засчитывается, если хотя бы для одного хитровырожденного теста имеет место быть Wrong Answer. В конце интвервью это заносится все в протокол собеса, вместе с кодом на бумажке, либо копируется код из "блокнота", если беседовали в скайпе. Да, по скайпу код пишется просто в текстовом онлайн-редакторе, что экономит время на писанину рукой или на доске

Забегая вперед, выяснилось, существует еще система отложенного штрафа. Т.е. интервьювер говорит, что принял задачу, а потом спокойно под микроскопом может посмотреть этот в ужасе наделанный за 5 минут код. Другой коллега может тоже посмотреть на эту живопись и сказать “я придумал тест, где это не будет работать” - и тебе вручают отложенную красную карточку за задачу на следующий день путем звонка, мол "вот вы, оказалось, еще и тут херни напороли, тут есть баг для редкого случая, поэтому задачу не засчитываем"

В целом, не буду сильно углубляться по каждому интервью, потому что три раза меня собеседовали адекватные iOS-разработчики из разных команд, интересовались, как не потерять память, как работать многопоточно, race condition, GCD, как хранить данные в iOS. Попросили написать кусок кода в CollabEdit на тему “как скачать асинхронно картинку с интернета и отобразить ее в UIImageView”. Получается, надо наизусть знать классы UrlRequest, UrlSession.shared.dataTask(with: request). Я благо подсмотрел, Xcode был под рукой, но без него сходу не помню их конструкторы, точные названия и порядок параметров в кложуре сессии. Ведь в IDE работает автокомплит, ты выбираешь нужный метод из списка - и все работает. Да и класс по работе с API я написал лишь однажды 2 года назад, и радостно забыл, потому что  он работает прекрасно, и мне его трогать каждый день вовсе и не нужно. Кстати здесь интервьювер был вполне удовлетворен фрагментом кода и общей идеей, т.е. понятно, что код бы не скомпилировался из-за отсутствия контекста и других зависимостей

Почему-то некоторые интервьюверы очень сильно углублялись в давно депрекейтнутый objc, про method swizzling и на тему “что такое NSObject  и зачем он нужен”, на что я отвечал, что в Swift он мне вообще во всем проекте ни разу не был нужен, кроме как для  Key Value Observing некоторых фреймворков, например AVAudioSession, когда в твоем плеере кто-то более сильный убероккупант отжал ресурс аудиосессии (например, кто-то звонит)

Предсказуемыми были и вопросы о том, как работают структуры данных изнутри в Swift и за какое время они работают. Узнал из нового, что NSMutableArray в реализации включает себя замкнутый циркулярный буффер, при удалении нулевого элемента он не занимается копированием всех элементов в начало, а просто смещает счетчик начала массива на единицу. Круто, оптимизация!

На архитектуре помимо сетований на тему SOLID, VIPER, MVVM, МVP и DI, попросили спроектировать почтовый клиент для мобильной Яндекс.Почты и как бы это выглядело в плане архитектуры. Причем с поддержкой аттачей и прочих плюшек. Очень абстрактно за 30 минут на доске размером в 360 градусов по всем стенам мне удалось основную идею передать, и то, время уже подходило к концу, поэтому детали старались опустить. Интервьювер был адекватен и сказал, что идею общую понял и расписывать не надо досконально каждый метод. В конце дал задачку:

Функция занимала 60% времени программы, а после оптимизации стала занимать 20% времени программы. Во сколько раз сама программа стала быстрее?

Я решил в целом за 3 минуты, вспоминая пропорции из 8 класса, так что вроде раздуплился, но прикольно было почувствовать себя школьником

На секции с кодом мы почему-то снова много беседовали про давно депрекейтнутый и забытый objc, зачем-то подняли тему про разницу в управлении в памяти между gc и reference counting. Я что-то помнил из курса джавы университета, поэтому ответил про gc что-то вразумительное. Тем не менее ARC в iOS используется уже более 5 лет и я хз, к чему был этот вопрос. Рассказал, как течет память при reference cycle и как победить, интервьювер вроде как остался удовлетворен

Немножко попробовали написать код критической секции для безопасного контейнера, когда в него ломятся несколько потоков сразу. Я дал 2 способа реализации с помощью NSLock (аля мьютекс), а также добавить в серийную последовательную очередь (а-ля DispatchQueue). Причем для возврата значения из контейнера прямо здесь и сейчас использовали DispatchQueue.sync. Для случая с мьютексом, его надо разлочить после ретурна и выхода из скоупа при очистке стека вызова. При этом надо сначала вернуть значение, и только потом сделать unlock. В Swift это делается через defer { }, но можно было бы написать враппер типа MutexLocker и сложить его на стеке. В общем, это все прекрасно, с заданием справились

Тонкостей типа “что будет, если мы законформили 2 интерфейса с одинаковой сигнатурой методов и какой из них вызовется” - я сказал, что никогда так не делал, так что хз. Или "можно ли вызвать дефолтную реализацию протокола в Extension в уже overriden-методе класса". Странная идея, поэтому пришлось угадывать, сходу не угадал, но по наводящим вопросам понял, что нельзя

Алгоритмы

Как догадались, раз я это выделил отдельной статьей, то у меня здесь подгорело маленько. Помимо околоолимпиадного школьного прошлого, у нас в универе, для получения допуска к экзамену по АСД, мы сдавали лабы в тестирующей системе, правда время было не ограничено, что-то около недели. Количество попыток не ограничено. Язык С++ или Джава. Нагло спиздить код у одногруппника не выйдет, даже путем подмены функций или названий переменных. В конце семестра висела доска позора с пиздецами по code-style писанного добра. Но все базовые структуры данных и алгоритмы мы написали, вплоть до 2-3, авл, декартовых, красно-черных деревьев, максимальные потоки минимальных стоимостей и прочие графы

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

Ладно, стучится ко мне в скайп интервьювер, в отличие от предыдущих, он опоздал на 5 минут. Без прелюдий и вазелина вопросов дает задачу:

Есть числа. Например [3, 1, 7, 2, 4, 10, 8]
Нужно вывести строку из рейнджей “1-4,7-8,10”

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

По решению, ну тут в принципе все очевидно, числа сортируем сначала, если соседние отличаются не более чем на единицу, то правую границу текущего рейнджа увеличиваем тоже, иначе выводим. Это все прекрасно

Как выяснилось, интервьювер вообще в глаза не видел этот непонятный Swift и заявил, что мое решение работает за квадрат. Я говорю "с чего бы"? “А вот видите, вы когда говорите к стрингу += он берет предыдущий стринг и копирует результат аппенда в новый стринг”. Я грю снова, "с хуя ли чего бы"? Ну как ему кажется, что стринг в Swift не мутабельный и вот тут происходят накладные расходы. На что я ему начал доказывать, что я объявил стринг как var переменную, от чего и происходит его мутабельность. Он сначала пошел гуглить (простите, Яндекс, но он действительно гуглил) и нашел какую-то документацию, что есть некий NSMutableString из objc и что он мутабельный, на что я снова говорю, иди в документацию и стринг у меня мутабельный и мол мой алгоритм работает линейно. В качестве аналогии я ему объяснял на примере релокации вектора, что это происходит редко, когда сайз выходит за пределы капасити. Потратив драгоценные минуты, он извинился, что да, ступил. Еще я потратил время, что объяснял ему о том, что такое guard в Swift и зачем я написал его

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

Есть строка из символов, например “ABCDEF”
Нужно научиться понимать, что другая строка, пускай “EFD”, будет являться подстрокой исходной строки. При этом символы должны идти подряд, у которых порядок вовсе и не важен

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

Окей, подумав, говорю, давайте заведем множество (а-ля Set), в котором будут храниться символы текущей подстроки, а по искомой подстроке мы будем двигаться на (n - length) символов подстроки, добавлять один новый следующий символ, удалять старый символ и сравнивать эти два множества

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

И тут он говорит, "а что делать, если символы будут повторяться?? Как мы их сможем сложить в множество с существующим ключем??" В итоге, я помню, что в плюсах есть std::multiset, куда можно складывать несколько ключей, но в Swift такого контейнера нет. Я говорю, представим, что у нас есть класс MultiSet, у которого есть интерфейс, как у Set, но на вопрос hasItem он вернет не true, а чиселко элементов. Он грит окей, пусть будет так

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

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

В итоге

Спустя день отзвонился рекрутер. Говорит, вы успешно прошли 3 “очных” собеседования, а вот последнее по алгоритмам нет. По техническим вопросам, которые относятся непосредственно к моим ежедневным потенциальным обязанностям, я справился. А вот на последнем собесе по алгоритмам я одну задачку решил, а вторую решил неоптимально, мой код не слишком классный, как казалось бы. В отчете еще интервьювер написал, что я выдумал некий “призрачный” MultiSet, хотя мы договорились, что опускаем реализацию, времени нет писать враппер. Понятно, что он внутри себя будет хранить Dictionary (читать хеш-таблица) с int’овым значением для повторяющихся ключей. А еще мой код не работает вообще. Хз почему, может они в IDE вставили и проверили. Я не проверял, ибо по правилам игры нельзя, да и время уже истекло

Сказали, что “вы лох пидр не смотря на удачную серию остальных интервью, из-за внутренних распорядков компании мы не можем вас взять из-за последней задачки по алгоритмам”. Думаю, ужас-то какой, как жить! Я спокойно бы мог написал за квадрат, отладить, оптимизировать, протестировать на разных входных данных и уложиться в рамки ограниченного времени собеса, но нет же, надо было, придумать и выдать идеально рабочий код за линию...

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

Я, кстати, сразу же после фидбека предложил рекрутеру перепройти собес по алгоритмам на плюсах с неопаздывающим интервьювером, и я удивился, что мне даже перезвонили. И тут мне сказали, что я вообще дно и даже первую задачу не решил, когда руководитель команды посмотрел на мой код и придумал вырожденный случай, где будет баг. Ну да, подумав в спокойной обстановке я понял, в условиях ужаса сжатого времени во время собеса я забыл про случай, когда в массиве идут одни и те же элементы. А ведь никто и не просил, я должен был сам об этом догадаться. Так что “приходите через год снова, обычно года хватает, чтобы стать лучше”. Только, что должно во мне измениться - я так и не понял. Быстрее думать в отведенный лимит времени по таймингу?)) Я сказал, что “спасибо, но я больше не приду

Чуть позже инсайд из компании дал ценную инфу по алгоритмам - "на первую задачку дается 20 минут, на вторую 10 минут подумать и 30 накодить". Это в идеале. Фактически это не так. На чтение условия и вопросы - время не отводится. На опаздывания интервьювера и тупые вопросы по синтаксису доп. время так же не дается. Вот если не уложитесь в это время и не родите идеальный работающий код на бумаге или в блокноте  - вы лох и пидр и вообще никто, вы не сможете погроммировать в масштабах промышленного погроммирования

Я уверен на 146%, что сам интервьювер никогда не накодит любые 2 задачки в отведенные лимиты, не зная заранее решения. Прям чтобы работало идеально, не падало, обрабатывало все вырожденные случаи корректно. Шутки ради можно попробовать взять 2 задачки из тимуса или топкодера и полагаться на метрики вида “метод оценки скорости решения Геной Короткевичем

И чего?

В конце концов у меня остались смешанные чувства. Как минимум я не понял, что мне нужно подтянуть по алгоритмам, чтобы уметь решать эту задачу оптимально и за 10 минут на “подумать”. Я не уверен, что пройдет год и я смогу “быстрее думать”. Ведь эта задача не отправляет к знаниям каких-то классических алгоритмов и/или структур данных

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

Что ж, печаль. Как же без таких охуенных навыков вида “писания и запуска программы без конопилятора” (читай: ходить без ног) можно в принципе делать карманный телевизор для кинопоиска? Ну конечно же, никак

Если коротко

Для тех, кто не понял, что здесь произошло - объясню на пальцах: представьте вы приходите на собеседование водителем грузовичка в Магнит

  1. Сначала тебя мучают вопросами, почему машина едет вообще, можно слегка спросить физики об угловой скорости при движении на выпуклом мосту, дадут простую задачку вычислить ее для определенной кривизны моста и скорости грузовичка
  2. Затем вопросы по устройству грузовичка: как перебрать движок или снять коленвал. В целом, ты когда-то этим занимался или владеешь на уровне "я видел, как это делается", поэтому на словах можно донести основную идею
  3. Заодно спросят, как вообще работает трамвай, ну и какой-нибудь дизель-поезд ТЭП-70, и до кучи велосипед. Хоть ты давно и не катаешься на велике, уже лет 10 как, но что-то в памяти осталось и ты рассказываешь, как перебрать втулку, вилку, спицы и все такое. В остальных вопросах делаешь лишь какие-то предположения, т.к. никогда не был машинистом поезда или трамвая. Но дать правильный ответ крайне желательно
  4. Затем тебя сажают на табуретку, дают баранку в руки, и говорят “поехали, заберем там со склада нечто и отвезем на адрес такой-то”. Адреса заранее неизвестны. В это время ты должен изобразить, как едешь по улице в условиях ограниченной узкой улочки, все повороты должен помнить наизусть по памяти без навигатора, затем разгрузиться у магазина. В это время на тебя смотрят, как ты переключаешь передачи (вербально, без машины), оценивают стиль вождения и количество нарушений. Потом говорят, ну окей, вроде приехали. А потом дают обратную связь, что вот, во время интервью вы там по дороге выехали разок за сплошную, а в конце и вовсе оказывается перехали пешехода. При этом продолбались по времени и не приехали вовремя на склад. Поэтому по внутренним распорядкам компании мы вас не можем взять как водителя категории ЦЭ

Подводя итог, я маленько разочаровался одной из ведущей IT-компании в России. С таким неадекватным подходом овчинка выделки не стоит. Требования к испытаниям тянут на Гугл. Только с каких это пор Яндекс - это Гугл? Это вовсе не Гугл, далеко не Гугл. По зарплатам и близко не Гугл.  Хотя с другой стороны можно или нужно поискать недостатвки и в себе. Любой опыт - это тоже опыт. Ну и конечно же, “найдется все”

Спасибо тем, кто дочитал до конца! Возможно, эти знания вам пригодятся!

Подробнее




UPD: статья актуализирована от 02.2025.
Причина: FirstVDS поднял цены х2 на тарифы по виртуализации OpenVZ, тем самым мотивировав переезжать на сервера с KVM по старым ценам.

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

Что ж, смелее, приступим. Речь пойдет про выделенный сервер на основе Ubuntu 24.04 LTS


Чем будем редактировать

Многие инвалиды пользуются нано, но я таки советую использовать vim, тк не буду скрывать, что в нем настолько удобно работать, в том числе и создавать сайты прямо там, на удаленном сервере, не держа никаких локальных копий. Он может поначалу показаться сложным, но потом вы просто жить без него не сможете. Ставим:

$ apt install vim

UPD: кажется на Ubuntu 24.04 vim был уже предустановлен, это радует.

Чтобы выйти из вима, напишите :q. Ура! У вас получилось) Чтобы начать пользоваться - загуглите что нибудь на тему "vim basics" и я думаю уже через 10 минут вы запросто сможете начать править конфиги на лету.

Чуть правее мне припомнилась довольно забавная шутка про emacs.

 

Меняем ssh-порт сервера по умолчанию

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

$ vim /etc/ssh/sshd_config

Меняем параметр Port вместо 22 на любой свой перезапускаем

$ service ssh restart

Теперь можно подключиться к серверу, используя дополнительный параметр -p

$ ssh root@yourserver -p YOUR_CUSTOM_PORT

Также, чтобы потом не вводить постоянно порт при подключении, нужно поправить конфиг ssh на локальной машине

$ vim ~/.ssh/config

Этого файла может и не быть у вас, даже папки .ssh, но тем не менее туда нужно написать

Host YourServerIp
Port YourSSHPort

 

Делаем авторизацию на сервер без ввода пароля по RSA ключу

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

$ ssh-keygen

Соглашаемся со всем, если машиной пользуетесь не только вы, то рекомендуется ввести какой-нибудь пароль, который будет запрашиваться при попытке использования ключа. Затем копируем ваш паблик кей на сервер

$ ssh-copy-id root@yourserver

В последний раз вводим пароль от сервера, после этого можно подключится как обычно:

ssh root@yourserver

 

Выключаем SSH-авторизацию по паролю (рекомендуется)

Чтобы исключить возможность взлома вашего сервера путем подбора паролей рекомендуется вообще отключить авторизацию на SSH по паролю. Перед этим желательно настроить авторизацию по Publickey к серверу с нескольких запасных машин на случай поломки/кражи вашего текущего девайса. Также хостинг как правило умеет по запросу сбросить SSH пароль на вашем сервере.

$ vim /etc/ssh/sshd_config

Находим строчку PasswordAuthentication и меняем с yes на no. Ребутаем тачку

$ service ssh restart

 

Устанавливаем Apache2 и PHP8

Ставим веб сервер вместе с PHP.

$ apt install apache2 libapache2-mod-php php php-curl php-gd

После установки убедимся, что все работает

$ cd /var/www/html/
$ rm index.html
$ vim index.php

В открывшемся новом файле напишем

<!--?php phpinfo(); ?-->

Сохраняем файл в виме, нажав :x. Теперь можно открыть браузер и написать айпишник вашего сервера. На нем вы увидите информацию о php :)

 

Устанавливаем MySQL

$ apt install mysql-server mysql-client php-mysql

После установки подключимся к свежеустановленному серверу. При этом пароль рута не задан, нужно нажать Enter при запросе пароля.

mysql -uroot -p

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

 ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass';

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

CREATE USER 'user'@'localhost' IDENTIFIED BY 'password';

GRANT ALL PRIVILEGES ON *.* TO 'user'@'localhost';

 

Перенос баз данных со старого сервера

Если необходимо импортировать базы данных с предыдущего хостинга, необходимо сделать на нем дамп. Возможно что у вас данные хранились на старом MySQL-сервере с более не поддеживающимся движком MyISAM, однако в новых MySQL по умолчанию используется InnoDB. Здесь очень важно сдампить только необходимые ваши базы данных, чтобы случайно не прихватить с собой системные таблицы mysql и так далее.

$ mysqldump -uroot -p --databases DATABASE1 DATABASE2 | gzip > databases.sql.gz

Затем можно напрямую скачать файл со старого хотсинга на новый через wget или scp. После этого для импорта необходимо написать уже на новом сервере

$ gunzip < databases.sql.gz | mysql -uroot -p

Кстати, если на предыдущем хостинге mysql сервер доступен извне, то mysqldump можно выполнять сразу на новом сервере, всего лишь указав удаленный адрес сервера, используя параметр --host="remoteIp"

 

Отправка почты с сервера для сайтов

Обычно большинство php скриптов отправляют почту, испозуя простую функцию mail. В свою очередь php использует утилиту sendmail. Но мы не собираемся устанавливать свой почтовый сервер, это не выгодно по ресурсам, будем использовать яндекс-почту к примеру для отправки писем. Для этого установим:

$ apt install sendmail ssmtp

Подредактируем конфиг /etc/ssmtp/ssmtp.conf

root=postmaster

# The place where the mail goes. The actual machine name is required no 
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=smtp.yandex.ru:465

# Where will the mail seem to come from?
#rewriteDomain=

# The full hostname
hostname=example.com

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
FromLineOverride=YES
UseTLS=YES
AuthUser=email
AuthPass=password

Если желаете, чтобы почта отправлялась от имени вашего домена, то для этого вам необходимо зарегистрировать свой домен и прикрутить к нему почтовый ящик на pdd.yandex.ru, тк яндекс не разрешает отправлять почту с несуществующих адресов.

 

Настраиваем виртуальные хосты в apache

Все сайты, как правило, хранятся по папочкам с точным названием домена в директории /var/www/. Положим, у нас есть домен test.ru, а все файлы от сайта хранятся в папке /var/www/test.ru. Убедимся, что у файлов и папок правильные аттрибуты для чтения и записи для пользователя www-data,  иначе apache не сможет их прочитать и отдать. А если у вас будет какое-нибудь кеширование / аплоад, то и записать ничего не сможет.

$ cd /var/www/

$ chown -R www-data:www-data .

Cоздадим для него виртуальный хост в настройках apache:

$ vim /etc/apache2/sites-available/default.conf

В новом файле напишем конфиг:

<VirtualHost *:80>
    ServerAdmin admin@test.ru
    ServerName test.ru
    ServerAlias www.test.ru
    DocumentRoot /var/www/test.ru/
    ErrorLog ${APACHE_LOG_DIR}/test.error.log
    CustomLog ${APACHE_LOG_DIR}/test.access.log combined

    <Directory />
        Options FollowSymLinks
        AllowOverride All
    </Directory>
    <Directory /var/www/test.ru/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
    </Directory>
</VirtualHost>

Сохраним файл и расскажем apache про новый сайт (если он еще не был включен):

$ a2ensite test.ru

$ service apache2 restart

Возможно еще понадобится включить модуль rewrite, если в вашем сайте используются ЧПУ вида http://mysite.com/pages/view/article_id, чтобы apache не пытался ходить по вложенным папкам и искать там файл article_id.

$ a2enmod rewrite

$ systemctl restart apache2

 

Кофигурация домена на DNS сервере

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

Тип записи Поддомен Значение
A @ server ip address
A www server ip address

 

Все, после того, как вы пропишите IP-адрес вашего сервера для вашего домена у DNS-сервера, то можно смело через 24 часа вбивать название вашего домена в адресную строку - и вуаля, сайт работает! По такой же аналогии настраиваются и остальные домены. Кстати, в качестве бесплатного днс-хостинга можно использовать бесплатную версию админки Яндекс 360 (бывший pdd.yandex.ru), а также прикрутить на домен яндекс-почту, чтобы вам могли письма отправлять прямо на адрес домена типа admin@test.ru.

 

Разруливаем статику и динамику с помощью nginx

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

Что ж, ставим nginx

$ apt install nginx

В итоге nginx будет слушать 80ый порт, тогда апач стоит повесить на какой-нибудь другой, например 8080. Поправим это в конфиге:

$ vim /etc/apache2/ports.conf

Заменяем параметры портов на свои:

NameVirtualHost *:8080
Listen 8080

Теперь настраиваем конфиг nginx

server {
    listen 80 default; # этот конфиг - умолчательный для 80 порта
    server_name _;  # хитрый ключик, обозначающий, что этот конфиг применим для любого сайта

    set $sathost $host;  # В sathost будет лежать имя сайта. Так же должна называться директрия с сайтом

    # убираем www
    if ( $host ~ ^(www\.)?(.+)$ ) {
        set $sathost $2;
    }

    if (!-d /var/www/sites/$sathost) { # если не нашли директорию с именем запрошенного сайта
        set $sathost default-site-foler;
    }
    
    root   /var/www/sites/$sathost; # конень сайта определяем автоматически
    index index.php index.html index.htm; # в каком порядке искать индексные файлы

    location / {   # правила ниже применяются для любых запросов

        proxy_pass http://serverIp:88;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;

        proxy_connect_timeout 120;
        proxy_send_timeout    120;
        proxy_read_timeout    180;
    }

    location ~ \.(png|jpg|exe|deb|js|css)$ { # static
        expires  1d;
        break;
    }  

    location ~ /\.ht { # в файлах, начинающихся на «.ht» могут лежать пароли или оставшиеся настройки от Апача - отдавать это ни к чему.
        deny  all;
    }
}

После правки этого конфига не забудьте поменять в настройках каждого сайта в /etc/apache2/sites-available порт на 8080 в ноде VirtualHost

Все, теперь можно перезапустить nginx и apache и проверить работу ваших сайтов.

# service apache2 restart

# service nginx restart

Вроде бы это все из базовой настройки сервера. Казалось бы, ничего сложного и нет. Конфигурация называется LAMP и расшифровывается как Linux + Apache + MySQL + PHP

Подробнее