Pull to refresh
6
0
Send message

Architecture Description Log, который внедрен в крупных компаниях, таких как Google,

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

каждая вторая компания хочет от кандидата понимание SOLID, использование принципов DRY, KISS, YAGNI и вообще, по-хорошему надо работать по TDD, а еще знать, понимать и применять паттерны проектирования.

На мой взгляд, это неадекватные требования для джуна, и компании, которые такое пишут в требованиях, скорее дискредитируют себя. Вы справедливо усомнились в том, как на самом деле пишут код в таких фирмах. TDD я за 15 лет опыта вообще ни разу не видел, да и в чистом виде он невозможен в компилируемых языках - нужно написать хотя бы интерфейс рабочей логики, иначе тесты не скомпилируются даже :) SOLID я начал понимать лет через 10 практики (именно по-настоящему понимать, применяя к своему опыту, потому что прочитать слова и осознать их может любой). "Паттерны проектирования" часто писал, но не знал, что это называется какими-то модными словами, да и опять же почти никто и никогда на практике не обсуждает код в терминах паттернов. Конечно, я не самый умный программист, но такой вот у меня опыт.

На чем я бы советовал сфокусироваться? На том, как работает компьютер и программа. Есть классическая книжка Никлауса Вирта "Структуры данных + алгоритмы = программы". Сама эта книга, пожалуй, не лучшее чтение на сегодняшний день, но её заголовок совершенно верно формулирует суть. Ключевые вопросы создания приложения, это как и откуда данные поступают, как хранятся долговременно, как устроен доступ к ним, как размещаются и организуются в оперативной памяти, ну и какие алгоритмы реализуют трансформацию данных. Это особенно важно для бэкенда.

Прикольно, лет 10 назад и представить было трудно, что подобный уровень будет достигнут.
Интересно, как ChatGPT сможет поддерживать кодовую базу, исправляя баги и добавляя фичи, не ломая обратную совместимость и не приводя БД в некорректное состояние? Мне кажется, что пока еще не сможет, но в будущем дойдёт и до этого.

Я довольно давно играю в Го, и там был похожий процесс: сначала программы могли соперничать только с новичками, потом, с внедрением алгоритмов Монте-Карло, они начали побеждать сильных любителей, люди начали использовать такие программы для анализа партий и тренировок. Закончилось это появлением AlphaGo, которая начала разносить в пух и прах сильнейших профи, и анализ партии с помощью нейросети стал уже мэйнстримом. Причем, даже если лоди не понимают, почему надо играть именно так (но чаще всего понимают), ход программы, видимо, всё равно правильный.

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

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

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

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

  2. Спросить, какие проекты ему запомнились, и что интересного или забавного он может припомнить из своей практики. На этом этапе кандидат может немного успокоиться, а я могу посмотреть, рефлексирует ли он над своей работой, испытывает ли интерес к программированию, и как вообще формулирует свои мысли. Этот этап занимает минут 10, максимум 15.

  3. Дать элементарную задачу, например, найти дубликаты в массиве, и уверить кандидата, что нет никакого подвоха. Тут я смотрю, насколько быстро задача будет решена (по-хорошему, кандидат просто тут же за несколько минут пишет решение), и что человек в принципе способен написать код. 5 - 10 минут.

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

  5. В оставшееся время я отвечаю на любые вопросы, которые есть у собеседуемого.

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

Еще один интересный подход - попросить кандидата заранее сделать короткую презентацию на любую интересную ему техническую или околотехническую тему.

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

Про зарплаты есть хорошее опросное исследование. Я 4 года в Германии, и с моим опытом оно совпадает. Чтобы узнать налоги, есть хороший онлайн калькулятор Для одного будет налоговый класс (steuerklasse) 1, если есть неработающий партнёр, то можно сделать 3. Не забудьте снять галку с церковного налога (In der Kirche? = Nein).

В целом, если вы не попадете в FAANG или какую-то особо щедрую компанию, то, скорее всего, проиграете в деньгах по сравнению с Россией. По крайней мере, в России точно есть вакансии, где на руках оставаться будет больше. Также приготовьтесь к тому, что в крупных городах жильё вы не сможете купить ни за какой разумный срок (в Мюнхене хорошая квартира 60-70 метров будет стоить порядка миллиона евро, есть дешевле, но ради таких нет смысла уезжать из России).

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

Этого недостаточно, так как нужно ещё как-то соединить данные и обработчик в рабочем потоке. Поэкспериментируйте с кодом, увидите, где узкие места.

Задача же демонстрационная, представьте, если обработчиков больше. Для простых случаев std::variant может сработать, хотя будут ещё накладные расходы на вызов visit() , а где-то и наивная имплементация подойдёт. А для обобщённого решения уже нет.

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

Проблема с std::variant в том, что с ним возможен только статический полиморфизм. Объявляя переменную, нужно сразу указать список типов, с которыми variant может работать. А даже две лямбды с одинаковым телом, определённые в разных местах будут иметь разные типы. То есть, невозможно объявить контейнер типа std::queue<std::variant<>> и класть туда что угодно. Есть std::any но с ним вы сами отвечаете за то, чтобы привести содержимое к нужному типу. Нет, без стирания типа или обычного динамического полиморфизма универсальную обёртку не сделать.

Справедливое замечание. Так как при меньшем числе параметров буфер в 64 байта в TaskWrapper избыточен, я уменьшил его до 20 байт для более честного сравнения. Получилось практически одинаковая производительность.

В принципе, как я и пишу в комментариях к коду SSO является в данном случае главным способом улучшения быстродействия. Самодельная виртуальная таблица никакого выигрыша в производительности не даёт, это просто способ сохранить информацию о типе обёрнутого объекта.

Лично мой взгляд - алгоритмические задачи слабо связаны с реальной практикой разработки. У меня 15 лет опыта, работал в телекоме, автопроме, финтехе, сейчас в одной из букв FAANG. Алгоритмические задачи я нахожу чудовищно скучными и никогда не решаю их вне подготовки к интервью. Когда готовлюсь, каждый раз отмечаю, что мой рабочий опыт помогает в лучшем случае в половине задач сложности Medium, и почти никогда в задачах Hard. Для меня это иллюстрация того, что эти задачи - вещь в себе. Чтобы их успешно решать, нужно выучить ряд приёмов, которые скорее всего вы никогда не встретите за пределами Leetcode.

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

Возможно, зависит от страны, но в Германии финтех существенно ниже FAANG по зарплатам.

Смотрите, если есть тип B, унаследованный от А, и тип С, не входящий в дерево наследования, то обычный полиморфизм не позволяет использовать объекты типов B и C взаимозаменяемо (через указатель на базовый класс A, например). Внешний полиморфизм позволяет это сделать. Таким образом, у этих двух подходов явно разные свойства. Поэтому есть и отдельный термин.

Насчёт причины, по которой есть выигрыш в производительности, да, я этого не написал, хорошо, что вы указали.

Вероятно, термин происходит из этой статьи. Мой перевод определения: "паттерн, который позволяет использовать классы, не связанные наследованием и/или не имеющие виртуальных методов, как полиморфные.

Если требуется готовая реализация обёртки для вызываемого объекта (callable), то существуют готовые библиотеки. Цель куда в примере - показать как, зная конкретные требования, несложно сделать оптимизацию вручную. Это, кстати, необязательный элемент для внешнего полиморфизма.

Конечно. Но это уже практические аспекты, которые зависят от конкретики системы, и с описанными в статье инструментами их можно решить как видится правильнее.

По теории, лучше не иметь слишком много потоков (в идеале не больше, чем ядер ЦП), а ввод-вывод мультиплексировать с помощью epoll или подобного.

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

Касательно изменения графа в процессе выполнения, это получается, что раньше у нас было состояние X, и по сигналу s переходили в состояние Y, а потом вдруг подменили указатель или map, и стали по сигналу s переходить в Z? Лично я противник подобной неявной логики, искать баги в такой - большая головная боль.

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

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

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

Так корутины в C++ и являются конечным автоматом. А если вы имеете в виду что-то вроде boost::statechart - с ним довольно много бойлерплейта и логика распыляется по имплементации разных состояний. На практике получается довольно запутанно. Поэтому, если нет ортогональных состояний, я бы не стал брать statechart или что-то подобное.

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

Спасибо за замечание, всё верно, можно использовать coroutine_handle<>, просто я ещё хотел показать, что можно использовать одни и те же ресурсы (потоки) для корутин одновременно с обычными задачами, что может быть важно, например, для рефакторинга.

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

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

Всё же мы продаём не услуги, а способность к труду. Разница в том, что ,если услуга не оказана, то можно и не платить, а если нанял программиста, и не можешь дать ему ТЗ (например, аналитики затупили), и работник ничего не делает, то платить всё равно придется. Именно за саму возможность использования рабочей силы.

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

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

1

Information

Rating
Does not participate
Registered
Activity

Specialization

Backend Developer, System Software Engineer
Senior
From 500,000 ₽
C++
Linux
Python
OOP
Git
SQL
Docker