Как стать автором
Обновить
49
0.1
Дмитрий Козлюк @kozlyuk

программист в MITIGATOR, преподаватель в МЭИ

Отправить сообщение
  1. Сколько по времени длится практика и стажировка?

  2. У тех, кто успевает сделать учебное задание ("спроектировать web API по выбранной им доменной области с простыми CRUD-операциями"), сколько остается времени на реальные задачи? Есть ли учебный этап в стажировке?

  3. Собственно, не раскрыта тема статьи: как взаимодействует разработчик с практикантом на этапе реальных задач, какие требования к soft skills такого разработчика?

Почему каждая библиотека конфигурации на Go пытается делать всё сразу, притаскивая код или зависимости для форматов, которые не нужны в конкретном проекте? Явно же есть две задачи: 1) прочитать ключи и значения из env/file/whatever, 2) разложить значения по полям структуры конфига в соответствии с ключами. Достаточно определить что-то вроде type KV interface { Get(string) (string, bool) }, который возвращает библиотека, решающая первую задачу, и принимает библиотека, решающая вторую. Тогда для каждого источника достаточно было бы одной библиотеки, которую можно стандартным образом совместить с библиотекой-раскладывателем и другими библиотеками-источниками.

Важное отличие записи через конвейер от записи в файл в том, что конвейер запускается в initial namespace, и поэтому работает для дампов из контейнера, а путь к файлу интерпретируется в mount namespace процесса, дамп памяти которого пишется. Для контейнер настройка с файлом позволит получить дамп, только если каталог настроенного пути примонтирован как том контейнера.

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

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

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

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

P. S. Вы круто сделали. Просто я считаю, что производственная практика должна быть иной.

защищенной системы управления базами данных (СУБД) Astra Linux

О каком компоненте речь?

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

  • написать Prometheus-экспортер для метрик AMD uProf (практикант пишет весь код сам);

  • доработать генератор тестового трафика в части протокола sFlow (вспомогательный код не жалко дать);

  • покрутить определенные настройки ClickHouse, чтобы оптимизировать работу одного компонента (код практиканту особо не нужен).

В прошлые годы получалось давать задачи на DM/ML, выгружая образец данных. Разумеется, все эти задачи ставятся не в вакууме — практикант должен послушать, понять и уточнить, если надо, зачем и как это будет использоваться, развернуть среду разработки и для тестов.

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

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

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

Вместо "программной смеси":

dot -Tplain mcu.dot | awk '/^edge / { print $2 " " $3 }' | tsort >order.txt

Из недостатков — не показывает, для каких компонент порядок не важен.
Кстати, если подать на вход приведенный пример, tsort находит в нем цикл:

tsort: -: input contains a loop:
tsort: CORTEX_M4
tsort: NVIC

Скорее, руководство (manual/guide?) состоит из множества howto, сгруппированных по темам. Иногда в нем есть и reference, и explanation. То есть руководство и не должно входить в классификацию, это формат издания.

Техническая документация не только для кода. Например, есть система защиты от атак. Есть howto, как настроить автоматическую защиту от атак определенного вида, в ходе которого настраиваются конкретные алерты. Есть reference со всеми доступными алертами и их видами (в подсказках к UI не развернешься). Есть explanation логики, по которой организованы алерты.

Автор развил изложенные на сайте Divio идеи: https://diataxis.fr/.

А вот для долгосрочных объектов работает банальная parent-child древовидная модель (ну... ок, можно сказать RAII + классический malloc()/free()). Т.е. просто деаллоцируются куски дерева объектов/ресурсов рекурсией. Ну или какой вариант key-value in-memory базы данных со своим отдельным менеджером памяти (LMDB/MDBX).

В эту схему не укладывается случай, когда долгоживущие объекты 1) используются и даже создаются на fast path — in-memory database слишком медленные; 2) должны освобождать свои ресурсы, как только стали не нужны — они дефицитные; 3) могут уничтожаться вместе с родителем в иерархии, но держит их живыми не родитель, а использующие. Ядро не буду приводить в пример, так как там всё особенное. Но вот userspace драйверы в DPDK именно такие, и там масса багов от того, что написаны они на C с самопальными счетчиками ссылок. Высокопроизводительный и экономный системный софт, в общем.

Как это объясняет поломанность modern C++ или самого RAII?

Как впрочем и весь Modern C++ c их RAII (тоже фундаментально поломано на старте, но это тема отдельной большой статьи).

Можно пояснить или указать, где почитать об этом?

объект класса-функтора с конструктором от необъектной лямбды

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

А вот с функциями высших порядков дело обстояло бы проще.

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

Вы утверждали, что всё захваченное лямбдой хранится в каком-то месте, не связанном с лямбдой, и поэтому лямбды могли бы быть не объектами (у которых есть место хранения), а функциями (у которых нет места хранения). Мой пример опровергает это: 1) захваченные лямбдой по значению переменные не существуют в момент вызова лямбды, 2) захваченные копии хранятся внутри std::function, но для создания такого std::function нужен объект с этими данными внутри. Следовательно, лямбды не могут быть функциями в общем случае, потому что приведен случай, где они должны быть объектами.

https://wandbox.org/permlink/Z2TVJjyUW6Hnr00G
Никаких бесхозных значений. Лямбда в make_request() захватывает this по ссылке и request_id по значению. Затем она сохраняется в std::function. Но поскольку std::function конструируется на основе лямбды, это значит, что захваченное содержалось внутри лямбды, то есть, она не была функцией. Никакого "лексического" захвата быть не может, потому что std::function реализуется чисто средствами C++, и реализация её конструктора не может проинспектировать, что из make_request() использует лямбда — конструктор видит только объект-функтор и знает его размер.

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

Речь идет про объекты в терминах стандарта C++, а не принципов ООП. Объект принципиально отличается от функции тем, что имеет тип и хранится где-то (*). Это нужно лямбдам, чтобы хранить захваченный контекст. Захватывать контекст требует типичное применение лямбды, когда она вызываются не из той функции, которая лямбду создает (например, создается при старте IO, а вызывается из event loop).

Лямбда сделана именно объектом класса, а не объектом какого-то нового для C++ вида, чтобы не умножать сущности. Про инкапсуляцию у такого объекта, кстати, можно и говорить: интерфейс фиксирован, все данные-члены скрыты.

(*) Да, технически код функции хранится в памяти, но с точки зрения стандарта C++ можно только получить указатель, через который можно вызвать функцию, но нельзя оперировать её кодом как находящимся где-то блоком байт.

Лямбды позволяют сделать больше, чем вложенные функции, благодаря захвату контекста, как объясняет KanuTaH в соседней ветке. Если добавлять захват вложенным функциям, как вы там же предлагаете, получатся те же лямбды, только всегда именованные. Но в реальном C++ функция и объект с operator() — сущности разных категорий. Зачем запутывать язык особым типом функций, вложенными, которые на самом деле являются такими объектами? Сделали просто синтаксический сахар для объектов, не вводя новых сущностей.

1
23 ...

Информация

В рейтинге
3 328-й
Откуда
Москва, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность