Доставка снюса в Бангкоке

Стандартная доставка в Бангкоке в этот же день еслит заказали до 11 часов утра или на следующий если после 11 и стоит 100 бат. В понедельник до 11 заказали заказали в понедельник до вечера вам доставят, после 11 утра то доставят во вторник. Стандартная доставка стоит 100 бат.

Так же есть экспресс доставка снюса снюса в Бангкоке здесь и сейчас стоит 300 бат.Курьер после получения заказа сразу поедет доставлять вам снюс в Бангкок. Доставляем не во все районы Бангкока.

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

Для стандартной доставки необходим точный адрес, фамилия и имя на англ и местный номер телефона, если его нет то номер телефона отеля.
Нерабочие дни в Тайланде это воскресенье и праздники Таиланда(всегда смотрите по ссылке когда в Тайланде праздники). Экспресс доставка работает и в выходные и в праздники.

Если у вас есть какие то сомнения, попросите перед отправкой фото вашего товара.
На все отправки даем трекинг номер для отслеживания доставки.

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


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

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

Ассортимент снюса в Бангкоке можно посмотреть тут  https://pumainthailand.com/gde-kupit-snyus-v-bangkoke/
Наша группа в вк  https://vk.com/snus_bangkok

Связаться со мной можно в вк http://vk.com/pumainthailand или в Телеграме https://t.me/stufently

Построение процессов с нуля: от хаоса к порядку

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

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

Исходные данные нашего отдела: небольшая (5–10 человек), частично распределенная (некоторые сотрудники работают удаленно, некоторые в офисе) продуктовая команда с заказчиками внутри самой компании. Веб-проекты. Нет специалистов по системному администрированию внутри отдела, но есть занимающиеся этим отделы в компании.

Коммуникации в команде

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

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

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

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

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

Для начала мы завели чат в Telegram. Но потом, в связи с ростом команды, мы поняли, что в одном чате нам уже тесно, и перешли в Slack. Там мы разбили общий поток на тематические каналы и установили четкие правила — по каким поводам в какой канал писать. Это помогло избежать смешения полезной информации и флуда.

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

  • Аудио- и видеозвонки — Skype for Business.
  • Задачи ведем в Jira.
  • Базу знаний храним в Confluence.

Планирование, исполнение и контроль задач

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

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

Для борьбы с этим мы разработали и внедрили свою культуру ведения задач:

  • Работа не должна выполняться, пока не заведена соответствующая задача. Это помогает держать всегда актуальной историю развития проекта и работы команды.
  • При работе с задачей необходимо своевременно менять ей статусы. В нашем случае хватает четырех статусов: «To do» — стартовое состояние задачи, «In progress» — задача, которая находится в процессе выполнения, «On hold» — задача, которую начали выполнять, но работы приостановлены (ждем какой-то дополнительной информации), «Done» — задача готова. Готовность задачи должен так или иначе подтвердить заказчик или менеджер внутри команды.
  • Со временем количество задач и проектов сильно увеличилось, так что общий список работ мы разбили на отдельные подпроекты, и задачу следует заводить согласно этому списку подпроектов.
  • Задаче необходимо назначать приоритет. Это помогает понять, какие задачи в каком порядке требуется выполнять, чтобы причинить проекту максимальную пользу.
  • Периодически проводится ревью задач и их приоритетов. Так как проекты живут и развиваются, а планы бизнеса иногда меняются, то со временем некоторые задачи становятся менее или более актуальными или вовсе требуют своего удаления.
  • Некоторые ключевые обсуждения по задачам, которые производились устно или письменно в чате, требуют фиксации финального решения в самой задаче, чтобы при ее выполнении мы всегда видели самую актуальную информацию о ней и о ее истории. Нередко бывает, что изначальная постановка задачи после ряда обсуждений трансформируется в нечто совсем иное, и нам нужно следить за этим.
  • Если внутри команды задача передается от одной группы специалистов к другой, то передающая группа обязана зафиксировать в ней все необходимые артефакты знаний, которые надо передать следующей группе. Например, группа дизайна перед передачей задачи в группу разработки должна прикрепить макеты и всю необходимую для разработки документацию. Это позволяет избежать лишних вопросов, отвлечений и переключений контекста.
  • Прикрепление коммитов к задачам. С помощью некоторых правил именования коммитов у нас автоматически привязываются к задачам ссылки на коммиты в GitLab. Это сильно помогает в разработке понимать, кто, что, как и когда делал по данной задаче. И в обратную сторону, по правильно именованным коммитам всегда можно найти задачу, содержащую причину внесенных изменений.

Коммуникации с заказчиком

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

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

В таком подходе кроется сразу несколько проблем:

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

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

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

Часто бывает, что заказчик, вместо того чтобы прийти с проблемой, приходит с просьбой о реализации уже выдуманного им решения. Чтобы не удивлять ни себя, ни заказчика результатами работы по ТЗ, составленному «на салфетке», мы создали базовый чек-лист вопросов для заказчика. Уже на основании этих ответов легче понять и заказчику, чего же он хочет на самом деле, и команде разработки, что от нее требуется. А потом настает черед задавать какие-то наводящие вопросы для уточнения или выявления требований.

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

Список вопросов для заказчика:

  • Какова цель проекта? Какую проблему это решает? Какое business value оно несет?
  • Это единственное возможное решение данной проблемы? Если нет, то какие еще есть варианты?
  • Есть ли какие-то общие требования, проходящие сквозь весь проект? Например, если это интернет-магазин, то он должен полностью соответствовать законодательству, регулирующему онлайн-торговлю.
  • Есть ли функциональные требования? Что должен делать раздел (страница, проект)? Например, раздел должен предоставлять информацию о продуктах компании и через форму на странице собирать желающих задать по этому продукту вопросы или приобрести его.
  • Есть ли нефункциональные требования? Как хорошо он должен это делать? Например, время открытия страницы должно быть не больше 5 секунд.
  • Дополнительные требования. Свободный формат, в котором можно излить душу.

Очередная проблема, с которой нам довелось столкнуться: задачи, поступающие одновременно со всех сторон. Когда много заказчиков по разным проектам, то каждый хочет выставить своей задаче наивысший приоритет. В общем идеальном случае, наверное, такую проблему нельзя решить на 100%. Как живем с этим мы? С внедрением дисциплины в постановке и ведении задач, а также некоторых элементов Agile-методологий, наш пул задач стал более упорядоченным, прозрачным для внешнего наблюдателя, а главное — прогнозируемым. Мы наладили порядок и планы у себя внутри команды, и нам осталось только усилить обратную связь с заказчиками. В обсуждении приоритетов, сроков и планов мы научились строить аргументированный диалог, а не слепо кидаться на задачи и постоянно тушить разгорающиеся пожары (которые на самом деле не всегда актуальны и не всегда пожары).

Также в рамках этого пункта мы постарались искоренить классический антипаттерн «чайка-менеджмент», когда заказчик или менеджер прилетел, «наложил» задач — и улетел в полной уверенности, что раз он задачу поставил, то непременно на выходе получится отличный результат. Как показала практика, результат при таком подходе получался не самый впечатляющий. Как с этим бороться? Тут тоже нет какого-то универсального совета, какой-то волшебной фразы, изменяющей поведение людей. Для этого необходимо разговаривать, просвещать, объяснять, показывать, можно сказать воспитывать. Только просветительская работа и либо очень наглядные, либо очень измеримые (а желательно и то, и другое) позитивные и негативные примеры помогут в достаточной мере побороть «чайка-менеджмент».

Dev vs Ops

И последняя важная наша проблема — проблема в коммуникациях между отделами Dev и Ops.
Мы столкнулись с классической ситуацией, когда разработчики не очень хорошо понимают, как работает сервер, а сторонняя команда админов не очень представляет, как работает приложение. Каждое затруднение на стыке этих двух сфер давалось нам с болью и большими временными затратами. Трудно было даже диагностировать, на какой стороне проблема:

  • Это вы там напрограммировали!
  • Нет, это у вас там что-то с сервером!

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

Разработка

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

  • Codestyle. Довольно важно выработать и явно утвердить правила оформления кода. Во-первых, эстетически приятнее видеть в проекте единый стройный вид, а не зоопарк разных стилей и стандартов. Во-вторых, это повышает читаемость кода, а как мы знаем, большинство времени программист не пишет свой код, а читает чужой.
  • Именование коммитов. Мы ввели определенные правила именования коммитов, чтобы по имени коммита было понятно, какие изменения были внесены, кем и в рамках какой задачи.
  • Код-ревью. Специфика наших проектов и команды такова, что некоего обязательного код-ревью, без которого не влить свой код в продакшен, у нас нет. Однако мы взяли за правило, что хотя бы один человек смотрит тот код, который пушит коллега. Это помогает как замечать какие-то недочеты, привносить альтернативные идеи, так и просто быть в курсе всех частей системы. Код-ревью стало особенно актуальным с ростом количества и сложности проектов, из-за чего каждый разработчик уже не успевает поработать на всех проектах достаточно, чтобы понять все их детали.
  • Согласование архитектуры внутри команды на ранних этапах. Часто бывает, что в стремлении создать фичу побыстрее, фронтенд начинает делать что-то свое, бэкенд быстро начинает свое, а потом оказывается, что это интегрируется с большим трудом или не интегрируется вообще. В разработке крупных фич мы заранее совместно обсуждаем архитектуру, форматы обмена данными и т. п. В результате интеграция перестала быть долгой и болезненной.
  • CV Driven Development. Это проблема, при которой разработчики тащат в проект новые (свежие, модные, высокооплачиваемые) технологии не ради проекта, а ради галочки в резюме. Мы открыты для новых технологий и стараемся технологически развивать наши проекты, однако тут важно соблюсти баланс, при котором будет и технологический прогресс, и успешно выполненные в разумные сроки проекты. В этой скользкой теме важны две вещи: чтобы заказчик не давил сроками так, что разработчикам не продохнуть, и чтобы команду разработки (ну или, по крайней мере, компетентного тимлида или техлида) заботило не только развитие своего профиля в LinkedIn, но и благополучие проекта в целом.

Рефакторинг, технический долг и принцип непрерывного улучшения

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

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

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

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

Внедрение Agile-методологий

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

Первое, что мы сделали, — организовали ежедневные стендапы внутри команды. Так как команда распределенная, то в Slack отвели для этого отдельный канал, в котором каждое утро каждый сотрудник пишет три пункта: над какими задачами работал вчера, над какими планирует работать сегодня, есть ли какие-то проблемы, мешающие его работе. Флудить в этом канале, обсуждать чьи-то задачи или проблемы запрещено. Этот канал строго для агрегации информации о состоянии дел. Остальные обсуждения должны вестись в соответствующих тематических каналах. Это позволило каждому человеку в команде понимать, чем занимаются его коллеги, что происходит с проектом в общем, кому можно и нужно помочь. Если без стендапов проблемы замалчивались, а спустя долгое время неожиданно выяснялось, что задача еще не готова, то сейчас наглядно видно, кому требуется помощь, что нужно сделать, чтобы работа команды стала эффективнее.

Далее мы решили вести разработку по спринтам. Каждую пятницу в конце дня мы проводим ретроспективу спринта, смотрим, какие задачи были в планах, что готово, что не готово, и если что-то не готово, то почему так произошло. Думаем о том, какие у нас были проблемы и что мы могли бы сделать, чтобы избежать подобных трудностей в будущем. Затем планируем спринт на следующую неделю исходя из загруженности разных направлений внутри команды и бизнес-приоритетов. В результате в начале недели все разработчики знают, чем им заниматься и в какой последовательности, а бизнес знает, какие его потребности будут покрыты в ближайшее время, и может уже формировать свои «хотелки» на следующие спринты. Стоит сказать, что мы не избавлены от внезапных задач, которые могут нарушить наши планы на спринт. В этом случае нужно действовать исходя из конкретной специфики работы: как часто такие задачи возникают? Насколько много? Можно ли это спрогнозировать? В нашем конкретном случае в разработке мы опытным путем подсчитали, сколько времени в среднем приходится на незапланированные задачи и стараемся закладывать этот запас в спринт. Отдельно стоит отметить, что после начала работы по спринтам нам удалось конкретно выяснить, как много работы нам поставляется на вход внепланово, проанализировать и сократить это количество, тщательно обсуждая приоритеты с заказчиком и наглядно показывая, как сиюминутное желание получить не самую необходимую фичу прямо сейчас влияет на общую продуктивность всей команды.

Также мы перешли от длинных релизов к коротким. Раньше мы получали ТЗ, неделями или месяцами делали целый пак фич и только тогда показывали заказчику. В результате часто оказывалось, что заказчик либо передумал, либо ожидал не совсем того, и мы начинали переделывать все или часть того, что уже сделали. А вносить изменения в уже готовый большой набор фич — это сомнительное удовольствие. Сейчас мы демонстрируем каждую фичу как можно раньше, чтобы заказчик принял решение — то ли это, что он на самом деле хотел, или надо что-то изменить. Чем быстрее он либо утвердит, либо отправит на доработку, тем меньше трудозатрат мы вложим, следовательно, тем быстрее фича придет в продакшен. В результате фичи стали поступать в продакшен быстрее, быстрее проверяются гипотезы, быстрее пошло развитие проекта.

Bus factor

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

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

  • Все работы проводятся только если есть задача в багтрекере. Это позволяет составить полную историю изменений в проекте.
  • В случае если где-то в чате или на митинге обсудили изменения по задаче — обязательно в самой задаче отражаем итог этого обсуждения.
  • Коммиты именуются с понятным указанием произведенных изменений и ссылкой на задачу. При этом в багтрекере интеграция настроена так, что в самой задаче появляется ссылка на коммит в Gitlab. Это позволяет сохранить историю не только самой цели задачи и ее обсуждений, но и сохранить информацию о конкретных изменениях и их исполнителях.
  • После разработки очередной фичи программист пишет в Confluence документацию, описывающую ее общую бизнес-логику и особенности реализации, если это необходимо.
  • При возникновении каких-то крупных проблемных инцидентов ведется postmortem в формате «что и когда случилось — что делали — что нужно было на самом деле делать — как избежать такой проблемы в будущем».

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

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

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

Заключение

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

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

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

Автор: Евгений Антонов, руководитель группы разработки Positive Technologies


ссылка на оригинал статьи https://habr.com/ru/company/pt/blog/455545/

Машинное обучение и анализ данных: магистратура Высшей школы экономики в Санкт-Петербурге

В этом году запускается новая магистерская программа «Промышленное программирование» на факультете «Санкт-Петербургская школа физико-математических и компьютерных наук» Петербургского кампуса Высшей школы экономики. Эта программа, как и магистерская программа «Разработка программного обеспечения» в Университете ИТМО (мы о ней писали тут), создана в сотрудничестве с компанией JetBrains. В данном посте мы расскажем, что объединяет эти две магистратуры и чем они отличаются.

Что объединяет эти две программы?

  1. Программы обеих магистратур разрабатывались «с нуля» в сотрудничестве с представителями ведущих IT компаний и действующими учёными из разных областей компьютерных наук.
  2. Обучение в обеих магистратурах очень плотное и рассчитано на студентов, которые хотят учиться и готовы тратить большую часть своего времени на учёбу.
  3. В обеих магистратурах большое внимание уделяется практике: в течение каждого семестра студент работает над каким-то учебным проектом под присмотром руководителя, а в конце семестра докладывает о результатах. Кроме того, между первым и вторым курсом студенты проходят летние практики и в конце лета докладывают о результатах.
  4. В обеих магистратурах планируются небольшие наборы, маленькие группы на практических занятиях, регулярные опросы студентов и прочее.
  5. Часть преподавателей будет работать в обеих магистратурах.
  6. Расположение.

    Так уж получилось, что здание ВШЭ находится через дорогу от БЦ «Таймс», где проходят почти все занятия магистратуры ИТМО – заглавное фото здания ВШЭ сделано из окна учебной аудитории ИТМО.

В чем разница? Какую программу выбрать?

Если у этих программ так много общего, то возникают закономерные вопросы: в чём тогда разница и какую программу выбрать?

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

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

Как следствие, эти две магистерские программы практически не пересекаются по курсам:

Несмотря на это, мы хотели бы сохранить возможность взаимодействия этих двух программ между собой. О чём идет речь? Нам хочется, чтобы студенты одной программы имели возможность пройти интересные им курсы второй программы и наоборот. Для этого мы со следующего года планируем перевести программы в сетевой режим, заключив соответствующее соглашение между НИУ ВШЭ — Санкт-Петербург, Университетом ИТМО и JetBrains. В предстоящем же учебном году мы предложим студентам обеих программ соответствующие курсы в рамках факультативов. Кроме того, некоторые командные проекты, которые мы будем предлагать для студенческих практик, тоже будут общими, т.е. над одним и тем же проектом, возможно, будут совместно работать студенты из двух вузов.

Приёмные кампании на обе магистерские программы уже идут. Выбирайте ту, что вам более интересна и подавайте заявку!


ссылка на оригинал статьи https://habr.com/ru/company/compscicenter/blog/455606/

Глобальные состояния: зачем и как их избегать

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

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

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

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

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

Ты готов, дорогой читатель, вскочить на коня и познать своего врага? Вперёд, найдём эти глобалы и заставим их отведать вкус стали наших мечей!

Что такое состояние?

Начнём с основ, чтобы вы, разработчики, понимали друг друга.

Состояние (state) — это определение системы или сущности. Состояния встречаются в реальной жизни:

  • Когда компьютер выключен, его состояние — выключен.
  • Когда чашка чая горячая, её состояние — горячая.

В разработке ПО некоторые конструкции (например переменные) могут иметь состояния. Скажем, строка «hello» или число 11 не считаются состояниями, они значения. Они становятся состоянием, когда прикрепляются к переменной и помещаются в память.

<?php  echo "hello"; // No state here! $lala = "hello"; // The variable $lala has the state 'hello'.

Можно выделить два вида состояний:

Изменяемые состояния: после своей инициализации могут меняться в ходе исполнения вашего приложения в любой момент.

<?php  $lala = "hello"; // Initialisation of the variable. $lala = "hallo"; // The state of the variable $lala can be changed at runtime.

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

<?php  define("GREETING", "hello"); // Constant definition. echo GREETING;  GREETING = "hallo"; // This line will produce an error!

Теперь давайте послушаем гипотетическую беседу между Денисом и Василием, вашими коллегами-разработчиками:

— Дэн! Ты везде насоздавал глобальные переменные! Их нельзя поменять без того, чтобы всё не сломалось! Я тебя прибью!
— Нифига, Васёк! Мои глобальные состояния офигенные! Я вложил в них душу, это шедевры! Я обожаю свои глобалы!

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

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

<?php  namespace App\Ecommerce;  $global = "I'm a mutable global variable!"; // global variable  class Shipment {     public $warehouse; // local variable existing in the whole class      public function __construct()     {         $info = "You're creating a shipment object!"; // local variable bound to the constructor scope         echo $info;     } }  class Product {     public function __construct()     {         global $global;         $global = "I change the state now 'cause I can!";          echo "You're creating a product object!"; // no state here     } }

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

Если вы и правда так подумали, очень рекомендую продолжить чтение.

Глобальные состояния хуже чумы и холеры?

Самая большая диаграмма связей

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

Почему?

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

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

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

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

Это ещё не всё. Если вам нужно поменять состояние глобалов, то вы не будете представлять, на какую область видимости это повлияет. Приведёт ли это к неожиданному поведению другого класса, метода или функции? Успехов в поиске.

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

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

Коллизии имён глобалов

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

  • Во-первых, вам понадобится выяснить, что ваша библиотека использует глобальные переменные.
  • Во-вторых, вам понадобится вычислить, какая переменная использовалась в ходе исполнения — ваша или библиотеки? Это не так просто, имена-то одинаковые!
  • В-третьих, раз вы не можете самостоятельно изменить библиотеку, придётся переименовать свои глобальные изменяемые переменные. Если они использованы по всему приложению, вы будете рыдать.

На каждом этапе вы будете рвать волосы от ярости и отчаяния. Скоро вам уже не понадобится расчёска. Вряд ли вас соблазняет такой сценарий. Возможно, кто-то вспомнит, что JavaScript-библиотеки Mootools, Underscore и jQuery всегда конфликтовали друг с другом, если их не помещать в более мелкие области видимости. А, и знаменитый глобальный объект $ в jQuery!

Тестирование превратится в кошмар

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

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

Проблемы с параллелизмом

Изменяемые глобальные состояния могут доставить много проблем, если вам необходим параллелизм (concurrency). Когда вы меняете состояние глобалов в нескольких потоках исполнения, то по уши вляпаетесь в мощное состояние гонки.

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

Избегание глобальных изменяемых состояний

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

Возьмём REST API: конечные точки получают какие-то HTTP-запросы с параметрами и отправляют ответы. Эти HTTP-параметры, отправленные на сервер, могут быть востребованы на многих уровнях вашего приложения. Очень соблазнительно сделать эти параметры глобальными при получении HTTP-запроса, модифицировав их перед отправкой ответа. Добавляем сверху в каждый запрос параллелизм, и рецепт катастрофы готов.

Глобальные изменяемые состояния могут также напрямую поддерживаться в реализации языка. Например, в PHP есть суперглобалы.

Если у вас откуда-то взялись глобалы, то как с ними быть? Как рефакторить приложение Дениса, вашего коллеги-разработчика, который создал глобалы везде, где только можно, потому что за последние 20 лет он ничего не читал по разработке?

Аргументы функций

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

<?php  namespace App;  use Router\HttpRequest; use App\Product\ProductData; use App\Exceptions;  class ProductController {     public function createAction(HttpRequest $httpReq)     {         $productData = $httpReq->get("productData");          if (!$this->productModel->validateProduct($productData)) {             return ValidationException(sprintf("The product %d is not valid", $productData["id"]));         }          $product = $this->productModel->createProduct($productData);     } }  class Product {     public function createProduct(array $productData): Product     {         $productData["name"] = "SuperProduct".$productData["name"]; // This is not what you should do; I talk about it later in the article.          try {             $product = $this->productDao->find($productData["id"]);             return product;         } catch (NotFoundException $e) {             $product = $this->productDao->save($productData);             return $product;         }     } }  class ProductDao {     private $db;      public function find(int $id): array     {         return $this->db->find(['product' => $id]);     }      public function save(array $productData): array     {         return $this->db->saveProduct($productData);     } }

Как видите, массив $productData из контроллера, через HTTP-запрос, проходит через разные уровни:

  1. Контроллер получил HTTP-запрос.
  2. Параметры переданы в модель.
  3. Параметры переданы в DAO.
  4. Параметры сохранены в базе данных приложения.

Мы могли бы сделать этот массив параметров глобальным, когда извлекли его из HTTP-запроса. Кажется, что так проще: не нужно передавать данные в 4 разные функции. Однако передача параметров в качестве аргументов функций:

  • Очевидно покажет, что эти функции используют массив $productData.
  • Очевидно покажет, что какие функции используют какие параметры. Видно, что для ProductDao::find из массива $productData нужен только $id, а не всё подряд.

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

Вы уже слышите, как Денис протестует: «А если у функции уже три и более аргументов? Если нужно добавить ещё больше, то вырастет сложность функции! И что насчёт переменных, объектов и других конструкций, которые везде нужны? Будете передавать их каждой функции в приложении?».

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

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

Ощущая себя оратором в афинском Акрополе, вы продолжаете:

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

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

Контекстные объекты

Контекстными называют те объекты, которые содержат данные, определённые каким-то контекстом. Обычно эти данные хранятся в виде конструкции «ключ-пара», как например ассоциативный массив в PHP. У такого объекта нет поведения, только данные, аналогично объекту-значению.

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

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

Вы скажете: «Это офигенно и всё такое, но что это даёт?»

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

Но у всего в разработке есть цена. Контекстные объекты могут вредить:

  • Глядя на аргументы функции, вы не будете знать, какие данные лежат в контекстном объекте.
  • В контекстный объект можно положить что угодно. Осторожнее, не положите слишком много, например, всю пользовательскую сессию, или даже большую часть данных вашего приложения. А то может получиться такое: $context->getSession()->getUser()->getProfil()->getUsername(). Нарушите закона Деметры, и вашим проклятием станет безумная сложность.
  • Чем больше контекстный объект, тем сложнее узнать, какие данные и в какой области видимости он использует.

В общем, я бы избегал использования контекстных объектов по мере сил. Они могут повлечь немало сомнений. Неизменяемость данных — большой плюс, но нельзя забывать и о недостатках. Если используете контекстный объект, убедитесь, что он достаточно маленький, и передавайте его в маленькую и тщательно определённую область видимости.

Если перед исполнением программы вы понятия не имеете, сколько состояний будет передано вашим функциям (например, параметры из HTTP-запроса), то контекстные объекты могут быть полезны. Поэтому их некоторые фреймворки их используют, вспомните, к примеру, объект Request в Symfony.

Внедрение зависимостей

Другой хорошей альтернативой глобальным изменяемым состояниям будет прямое внедрение нужных вам данных в объект прямо при его создании. Это определение внедрения зависимости: набора методик для внедрения объектов в ваши компоненты (классы).

Почему именно внедрение зависимостей?

Цель — ограничить использование ваших переменных, объектов или иных конструктов, поместить их в ограниченную область видимости. Если у вас есть зависимости, которые внедрены, а следовательно могут действовать только внутри области видимости объекта, то вам будет проще узнать, в каком контексте они используются и почему. Никакой тоски и мучений!

Внедрение зависимостей делит жизненный цикл приложения на две важные фазы:

  1. Создание объектов приложения и внедрение их зависимостей.
  2. Использование объектов для достижения ваших целей.

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

Многие фреймворки используют внедрение зависимостей, иногда в довольно сложных схемах, с конфигурационными файлами и Dependency Injection Container (DIC). Но вовсе не обязательно всё усложнять. Вы можете просто создавать зависимости на одном уровне и внедрять их уровнем ниже. Например, в мире Go я не знаю никого, кто использовал бы DIC. Ты просто создаёшь зависимости в основном файле с кодом (main.go), а затем передаёшь их на следующий уровень. Можно также инстанцировать всё подряд в разные пакеты, чтобы чётко обозначить, что «фаза внедрения зависимостей» должна выполняться только на этом конкретном уровне. В Go области видимости пакетов могут сделать какие-то вещи проще, чем в PHP, в котором DIC’и широко применяются в каждом известном мне фреймворке, в том числе в Symfony и Laravel.

Внедрение через конструктор или сеттеры

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

  • Если вам нужно знать, какие есть зависимости у класса, вам достаточно найти конструктор. Не нужно искать разрозненные по всему классу методы.
  • Настройка зависимостей при установке даст вам уверенность в безопасности использования объекта.

Немножко поговорим о последнем пункте: это называется «применение инварианта» (enforcing invariant). Создавая экземпляр объекта и внедряя его зависимости, вы знаете: что бы ни понадобилось вашему объекту, он настроен правильно. А если вы используете сеттеры, как вы узнаете, что ваши зависимости уже заданы в момент использования объекта? Можете пойти в стек и попробовать выяснить, вызывались ли сеттеры, но я уверен, что вам не хочется этим заниматься.

Нарушение инкапсуляции

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

Ты сказал «инкапсуляция»?

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

Начнём с начала. Что нам говорит Википедия про определение инкапсуляции? Языковой механизм ограничения прямого доступа к каким-то компонентам объекта. Ограничение доступа? Зачем?

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

Растущая область видимости и утечки состояний

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

Возьмём пример: Anemic Domain Model может увеличивать область видимости ваших изменяемых моделей. По сути, Anemic Domain Model делит данные и поведение ваших доменных объектов на две группы: модели (объекты только с данными) и сервисы (объекты только с поведением). Чаще всего эти модели будут использоваться во всех сервисах. Следовательно, есть вероятность, что какой-то модели будет всё время расти область видимости. Вы не будете понимать, какая модель в каком контексте используется, их состояние изменится, и на вас обрушатся все те же проблемы.

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

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

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

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

Возможности копирования состояний

Во многих случаях хорошим решением будет копирование состояний без их прямого изменения. Вернёмся к нашему примеру с Product, точнее, к этому методу:

class Product {     public function createProduct(array $productData): Product     {         $productData["name"] = "SuperProduct".$productData["name"]; // This is not what you should do; I talk about it later in the article.          try {             $product = $this->productDao->find($productData["id"]);             return product;         } catch (NotFoundException $e) {             $product = $this->productDao->save($productData);             return $product;         }     } }

Массиву $productData лучше оставаться неизменяемым. Если вы напрямую поменяете его состояние, а затем передадите другим функциям, то вскоре просто не сможете узнать, какое состояние принял этот массив.

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

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

Лучше сделать так:

class Product {     public function createProduct(array $productData): Product     {         // Since $productData is passed to other variable, it has to be immutable.         $name = "SuperProduct".$productData["name"];          try {             $product = $this->productDao->find($productData["id"]);             return product;         } catch (NotFoundException $e) {             $product = $this->productDao->save($name, $productData);             return $product;         }     } }

Вы ясно показали, что имя продукта не такое же, как исходное имя продукта из массива $productData. Вы продемонстрировали, что состояние изменено. Если вам нужно будет передать $productData в любой другой метод, то вы будете знать, что он всегда содержит исходные данные из HTTP-запроса.

Вы даже можете изолировать это изменение состояние в отдельном методе, чтобы было ещё очевиднее: «Внимание, я сейчас меняю это состояние».

Что насчёт глобальных неизменяемых состояний?

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

Безопасно ли их использовать?

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

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

К примеру, константа ShipmentDelay будет использована, надеюсь, только там, где реализована логика отгрузки товара. А если Денис, ваш коллега-разработчик, начнёт использовать ShipmentDelay для другой задержки, не относящейся к отгрузкам, то ваш глобал будет использоваться там, где он не имеет смысла. Глупо? Я видел много разработчиков, которые делают подобные странные вещи во имя священного принципа DRY.

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

Нужно ли находить и уничтожать все глобальные состояния?

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

Однако приложениям свойственно увеличиваться. Поэтому помните:

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

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

Однако я считаю, что чаще всего глобальные изменяемые состояния приносят больше вреда, чем пользы.

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


ссылка на оригинал статьи https://habr.com/ru/company/mailru/blog/454946/

Машинное обучение и анализ данных: магистратура Высшей школы экономики в Санкт-Петербурге

В этом году запускается новая магистерская программа «Промышленное программирование» на факультете «Санкт-Петербургская школа физико-математических и компьютерных наук» Петербургского кампуса Высшей школы экономики. Эта программа, как и магистерская программа «Разработка программного обеспечения» в Университете ИТМО, создана в сотрудничестве с компанией JetBrains. В данном посте мы расскажем, что объединяет эти две магистратуры и чем они отличаются.


Что объединяет эти две программы?

  1. Программы обеих магистратур разрабатывались «с нуля» в сотрудничестве с представителями ведущих IT компаний и действующими учёными из разных областей компьютерных наук.
  2. Обучение в обеих магистратурах очень плотное и рассчитано на студентов, которые хотят учиться и готовы тратить большую часть своего времени на учёбу.
  3. В обеих магистратурах большое внимание уделяется практике: в течение каждого семестра студент работает над каким-то учебным проектом под присмотром руководителя, а в конце семестра докладывает о результатах. Кроме того, между первым и вторым курсом студенты проходят летние практики и в конце лета докладывают о результатах.
  4. В обеих магистратурах планируются небольшие наборы, маленькие группы на практических занятиях, регулярные опросы студентов и прочее.
  5. Часть преподавателей будет работать в обеих магистратурах.
  6. Расположение.

Так уж получилось, что здание ВШЭ находится через дорогу от БЦ «Таймс», где проходят почти все занятия магистратуры ИТМО – заглавное фото здания ВШЭ сделано из окна учебной аудитории ИТМО.

В чем разница? Какую программу выбрать?

Если у этих программ так много общего, то возникают закономерные вопросы: в чём тогда разница и какую программу выбрать?

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

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

Как следствие, эти две магистерские программы практически не пересекаются по курсам:

Несмотря на это, мы хотели бы сохранить возможность взаимодействия этих двух программ между собой. О чём идет речь? Нам хочется, чтобы студенты одной программы имели возможность пройти интересные им курсы второй программы и наоборот. Для этого мы со следующего года планируем перевести программы в сетевой режим, заключив соответствующее соглашение между НИУ ВШЭ — Санкт-Петербург, Университетом ИТМО и JetBrains. В предстоящем же учебном году мы предложим студентам обеих программ соответствующие курсы в рамках факультативов. Кроме того, некоторые командные проекты, которые мы будем предлагать для студенческих практик, тоже будут общими, т.е. над одним и тем же проектом, возможно, будут совместно работать студенты из двух вузов.

Приёмные кампании на обе магистерские программы начнутся совсем скоро. Выбирайте ту, что вам более интересна и подавайте заявку!


ссылка на оригинал статьи https://habr.com/ru/company/hsespb/blog/455616/