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

Легаси

И снова о Legacy. Вечная боль техдира

27.07.2020 20:17:32 | Автор: admin

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


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


Техдир пришёл к ним, поздоровался и спросил: Ребята, скажите честно, какой аццкий зверь меня ждёт в этом проекте? Потому что стейкхолдеры рассказали только о единорогах с радужными хвостами и розовых пони? Legacy, да?


Legacy, ..., грустно ответили разработчики.


Сказка закончилась. Началась работа и непростые решения.



Cитуация. Первый извечный вопрос русской интеллигенции Кто виноват?


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


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


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


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


И погнали. Оно работает с помощью каких-то негров с опахалами, шамана, бивня мамонта, летающей китайской крысы и панголина.


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


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


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


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


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


Проблема. Второй извечный вопрос русской интеллигенции Что делать?


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



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


А бизнес вообще не знает, что проблема есть. Бизнес вообще не понимает: Ну как так, MVP же работает?. Работает. А почему новую фичу нельзя вот прям счаз воткнуть? И с чем сталкиваются многие технические управленцы, они не могут объяснить, в чем конкретно проблема и самое тяжелое они не могут показать в метрике, измеримой бизнесу, чтобы они понимали, что в текущем состоянии разработка фичи стоит 100 рублей, а в оптимизированном, например, 10 рублей.


А у бизнеса свои заботы, у них там всё прёт, продажи пошли да пофиг, пусть 100 рублей, давайте выкатим новую фичу, мы сейчас Газпром захантим. Им вообще не до этого.


Решение проблемы. Ломать нельзя рефакторить. Или уходя гасите всех


И мы переходим к решению проблемы. Существуют различные попытки решить проблему.


Решение номер раз. Помножить на ноль


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


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


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



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


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


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


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


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


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


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


Решение номер два. И давайте тут чуть-чуть починим


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


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


Что у нас получается? А у нас получается, что команда ввела так называемый налог на непонятно что, формально называется налог на исправление технического долга. Но как он там исправляется, если бизнес придет и спросит А что там и как? Что у вас? Бизнесу, конечно, объяснят, но он не поймет. В чем заключается технический долг с точки зрения технаря, понятно.


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


Во втором примере новой концепции нет и это реально проблема.


Решение номер три. Золотая середина. Микроверсия нового продукта


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


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


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


И теперь важная тема. В книге Фредерика Брукса приводится приводится пример того, как раньше строили храмы и там был пример храма Temple Expiatori de la Sagrada Famlia в Барселоне, строящегося на частные пожертвования начиная с 1882 года, знаменитый проект Антонио Гауди.



Суть в том, что Sagrada Famlia придумал Гауди и он выработал архитектуру, каким храм должен быть, но так как проект огромный, его строили много поколений и он еще недостроен, его достраивают еще.


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



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


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


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


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


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


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


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


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


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


О проблеме, когда сталкиваются технический директор и Legacy, рассказал CTO Дмитрий Симонов, основатель канала Техдирские заметки. Так как сам испытал и не раз эту извечную боль техдира.

Подробнее..

Перевод Языки любимые и языки страшные. Зелёные пастбища и коричневые поля

07.05.2021 14:19:42 | Автор: admin


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

В опросах есть категории Самые страшные языки программирования (The Most Dreaded Programming Languages) и Самые любимые языки. Оба рейтинга составлены на основе одного вопроса:

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

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

Топ-15 страшных языков программирования:
VBA, Objective-C, Perl, Assembly, C, PHP, Ruby, C++, Java, R, Haskell, Scala, HTML, Shell и SQL.

Топ-15 любимых языков программирования:
Rust, TypeScript, Python, Kotlin, Go, Julia, Dart, C#, Swift, JavaScript, SQL, Shell, HTML, Scala и Haskell.

В списке есть закономерность. Заметили?

Худший код тот, что написан до меня


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

Причина, по которой [разработчики] считают старый код бардаком, заключается в кардинальном, фундаментальном законе программирования: читать код труднее, чем писать его.

Джоэл Спольски Грабли, на которые не стоит наступать

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


Scott Adams Understood

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

Вот почему вы их боитесь


Если реальный старый код незаслуженно считают бардаком, то может и языки программирования несправедливо оцениваются? Если вы пишете новый код на Go, но должны поддерживать обширную 20-летнюю кодовую базу C++, то способны ли справедливо их ранжировать? Думаю, именно это на самом деле измеряет опрос: страшные языки, вероятно, будут использоваться в существующих проектах на коричневом поле. Любимые языки чаще используются в новых проектах по созданию зелёных пастбищ. Давайте проверим это.1

Сравнение зелёных и коричневых языков


Индекс TIOBE измеряет количество квалифицированных инженеров, курсов и рабочих мест по всему миру для языков программирования. Вероятно, есть некоторые проблемы в методологии, но она достаточно точна для наших целей. Мы используем индекс TIOBE за июль 2016 года, самый старый из доступных в Wayback Machine, в качестве прокси для определения языков, накопивших много кода. Если язык был популярным в 2016 году, скорее всего, люди поддерживают написанный на нём код.

Топ-20 языков программирования в списке TIOBE по состоянию на июль 2016 года: Java, C, C++, Python, C#, PHP, JavaScript, VB.NET, Perl, ассемблер, Ruby, Pascal, Swift, Objective-C, MATLAB, R, SQL, COBOL и Groovy. Можем использовать это в качестве нашего списка языков, которые с большей вероятностью будут использоваться в проектах по поддержке кода. Назовём их коричневыми языками. Языки, не вошедшие в топ-20 в 2016 году, с большей вероятностью будут использоваться в новых проектах. Это зелёные языки.


Из 22 языков в объединённом списке страшных/любимых 63% коричневых

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

Java, C, C++, C#, Python, PHP, JavaScript, Swift, Perl, Ruby, Assembly, R, Objective-C, SQL


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

Go, Rust, TypeScript, Kotlin, Julia, Dart, Scala и Haskell

У TIOBE и StackOverflow разные представления о том, что такое язык программирования. Чтобы преодолеть это, мы должны нормализовать два списка, удалив HTML/CSS, шелл-скрипты и VBA.2

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

Теперь можно ответить на вопрос: люди действительно боятся языков или же они просто боятся старого кода? Или скажем иначе: если бы Java и Ruby появились сегодня, без груды старых приложений Rails и старых корпоративных Java-приложений для поддержки, их всё ещё боялись бы? Или они с большей вероятностью появились бы в списке любимых?

Страшные коричневые языки



Страшные языки на 83% коричневые

Топ страшных языков почти полностью коричневый: на 83%. Это более высокий показатель, чем 68% коричневых языков в полном списке.

Любимые зелёные языки



Любимые языки на 54% зелёные

Среди любимых языков 54% зелёных. В то же время в полном списке всего лишь 36% языков являются зелёными. И каждый зелёный язык есть где-то в списке любимых.

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

Курт Воннегут

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

Другими словами, Rust, Kotlin и другие зелёные языки пока находятся на этапе медового месяца. Любовь к ним может объясняться тем, что программистам не надо разбираться с 20-летними кодовыми базами.

Устранение предвзятости




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

Цикл хайпа языков программирования


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


Цикл хайпа языков программирования

У меня под рукой нет данных, но я отчётливо помню, что Ruby был самым популярным языком в 2007 году. И хотя сегодня у него больше конкурентов, но сегодня Ruby лучше, чем тогда. Однако теперь его боятся. Мне кажется, теперь у людей на руках появились 14-летние приложения Rails, которые нужно поддерживать. Это сильно уменьшает привлекательность Ruby по сравнению с временами, когда были одни только новые проекты. Так что берегитесь, Rust, Kotlin, Julia и Go: в конце концов, вы тоже лишитесь своих ангельских крылышек.3



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

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

Вот методика измерения TIOBE, а их исторические данные доступны только платным подписчикам, поэтому Wayback Machine. [вернуться]

2. HTML/CSS не являются тьюринг-полными языками, по этой причине TIOBE не считает их полноценными языками программирования. Шелл-скрипты измеряются отдельно, а VBA вообще не исследуется, насколько я понял. [вернуться]

3. Не все коричневые языки внушают страх: Python, C#, Swift, JavaScript и SQL остаются любимыми. Хотелось бы услышать какие-нибудь теории о причине этого феномена. Кроме того, Scala и Haskell два языка, к которым я питаю слабость единственные зелёные языки в страшном списке. Это просто шум или есть какое-то обоснование??? [вернуться]
Подробнее..

Какие навыки можно прокачать на проекте c большой кодовой базой

07.08.2020 18:11:02 | Автор: admin


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


Содержание



  1. Для кого этот текст
  2. Чему вы можете научиться на проекте с историей
  3. Какие вопросы задавать на собеседовании
  4. Советы тем, кто только начал работу с легаси-проектом
  5. Кратко


Я Павел Новиков, участвую в разработке мобильных приложений МойОфис Документы и МойОфис Почта. Это приложение для совместной работы с документами и почтовый клиент, которые начали создаваться в 2013 году, поэтому можно их назвать проектами с большой кодовой базой, где есть место и легаси в том числе.

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

Disclaimer для самых маленьких


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

Для кого этот текст


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

Junior


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

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

Middle


Задача Middle разработчика повышение своей автономности. Вы должны стать членом команды, которому можно доверить практически любую задачу на проекте. Это значит, что чем более разнообразные задачи есть на проекте, тем больше у вас потенциальных направлений роста.

Senior


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

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

Причем тут легаси?


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

Майкл Фезерс (Michael Feathers), основатель R7K Research & Conveyance, утверждает, что легаси код это код, не покрытый тестами. Преимущество такого подхода в том что, с первого взгляда, он претендует на объективность. Но в реальности могут быть два сценария:

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


Еще один взгляд на то, что такое легаси от разработчика Дро Хелпера (Dror Helper): No longer engineered continuedly patched and hacked (Легаси-код код код, который постоянно ломают и подпирают костылями вместо того, чтобы его развивать).

Веб-разработчик Николас Карло (Nicolas Carlo) считает, что легаси код это код, с которым некомфортно работать.

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

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

В общем, если проекту больше полугода, то скорее всего в нем будет легаси.

Чему вы можете научиться на проекте с историей?





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

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

Анализировать проект


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

Самое главное, что могу посоветовать для углубления в тему анализа проекта прочесть книгу Эффективная работа с легаси-кодом Майкла Физерса. Она старая и известная. В ней описывается большое количество практик по работе с унаследованным кодом.

И еще один совет зайти на сайт Understand Legacy Code. Это блог, посвященный одной тематике работой с легаси. Важно, что там можно подписаться на рассылку. Уверен, многие Android-разработчики знают про рассылки Android Weekly и Kotlin Weekly. Рассылка ULC тоже очень полезна. Она не навязчивая, с практическими статьями про рефакторинг и написание кода.

Делать рефакторинг


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

Проектировать и создавать архитектуру


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

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

Книги по архитектуре

Роберт Мартин (Robert C Martin). Чистая архитектура, Чистый код
Мартин Фаулер (Martin Fowler). Рефакторинг. Улучшение проекта существующего кода


Инструменты
для проектирования архитектуры


UML используем для проектирования.
PlantUML (PUML) библиотека и сервис, которые позволяют в текстовом виде описывать UML-диаграмму. Его можно хранить в git, следовательно можете к процессу обновления и обсуждения этих диаграмм подключить те инструменты, которые используете для работы с обычным кодом.


Декомпозировать


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

Автоматизировать и уметь применять CI/CD


Вам нужно понимать, что происходит с вашим кодом от момента, когда вы его пишете, до момента, когда он достигает устройства пользователя. И у вас будет возможность автоматизацию и CI/CD настроить, поддерживать и улучшать. В компаниях, где нет выделенной инфраструктурной команды, эти операции могут и должны (я в этом убежден) решать члены основной команды разработки. Современные инструменты (cloud CI, Docker) не обладают невероятной сложностью, но очень сильно упрощают жизнь команды. Вы сможете принести ей много пользы, если овладеете этими навыками.

Общаться


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

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

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

Какие вопросы задавать на собеседовании





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

Как устроен рабочий процесс и почему именно так


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

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

Например, если команда работает по Scrum, я бы спросил, что обсуждали на последней ретроспективе. Насколько команда вообще активна на этой встрече? Решаются ли поднятые проблемы?

Другой интересный для меня вопрос по внутренним процессам: как в команде проводится code review? Есть ли какие-то внутренние правила проведения review, которые помогают сократить его время? Существует ли общая база знаний, в которую заносят результаты холиваров, чтобы не устраивать их каждый раз?

Как происходит планирование, кто участвует и насколько активна команда


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

Сколько в команде людей, которые обладают всей картиной


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

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

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

Как ведется бэклог технического долга


Проблемы есть в любом проекте, и важно понимать, как именно команда с ними работает. О работе с техническим долгом очень хорошо написал alexanderlebedev в статье Ланнистеры всегда платят свои долги! (и технические тоже).

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

Советы тем, кто только начал работу с легаси-проектом





Боритесь с соблазном броситься переписывать все с нуля


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

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

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

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

Прогнозируйте изменения проекта


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

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

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

Проводите исследования


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

Уделяйте время документации


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

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

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

Кратко


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


Текст подготовлен по материалам моего доклада на митапе GDG.
Подробнее..

Recovery mode Немного про SOLID и суровое зомби-легаси

11.02.2021 22:07:58 | Автор: admin

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

Я систематически работаю с Delphi 7 (работа такая, чо). Поддерживаю и активно дорабатываю приложение, уходящее корнями в начало-середину нулевых и написанное в беспощадном императивно-процедурном стиле. Можно ли выжить ментально в подобном окружении? Ведь "единственный способ их победить -- не стать одним из них", не так ли?

Здесь на самом деле можно увидеть что-то вроде такого:

procedure TReport13Form.OnButton1Click(Sender: TObject);beginwith MainForm.WorkQuery.SQL dobeginAdd('Select ...');if ... thenAdd(', ...');Add('from ...');if ... thenAdd('join...');... // Ещё строк двадцать-тридцать подобного трэша.Add(Format('where id = %d and operDate = %s',[id, QuotedStr(DToS(DatePicker1.Date))]));

и прочие подобные непотребства.

Но переписать всё с нуля -- это что-то из области радикального религиозного фанатизма (как если бы кто-то взвизгнул и автоматически замахнулся тапком, только услышав про Delphi 7).

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

1) Важное примечание: нисколько не ущемляю право старорежимных специалистов на работу в подобном стиле и соответствующую ментальность в условиях глубоко прикладной разработки. У себя на работе (компания НЕ производит ПО, разработка обслуживает интересы основного бизнеса) в этом смысле получил идеальное разграничение интересов: я веду несколько проектов, пишу как хочу, думаю о технологии и принципах проектирования, использую git(2). Мне хорошо. У остальных тоже всё работает. Они тоже молодцы. Дела идут, контора пишет.

2) Таки да, больше никто git не использует - так и не смог убедить. Я же без систем контроля версий процесса разработки не представляю ещё с тех пор, как пользовался CVS (git тогда ещё не родился) и читал новинку Кента Бека про "Экстремальное программирование".

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

Что же, поищем ответ. За примерами далеко ходить не надо -- недавно написал микро-библиотеку для сохранения настроек внешнего вида формы (положение разделителей, порядок и расположение столбцов в таблице, при желании - прочие фантазии). В библиотеке VCL, концептуально разработанной в середине девяностых, с гибкостью отображения вообще проблемы. На самом деле не великая задача, пара часов работы и несколько классов, но радикально сократилось время на поддержку сохранения настроек внешнего вида. Буквально до нескольких строк кода. Попробую оценить её на соответствие культурному коду современного ООП.

Библиотеку, с благословения руководства, выложил на Битбакет под собственным копирайтом: https://bitbucket.org/danik-ik/layoutkeeper/

Там же лежит пример использования.

Комментарии по схеме:

ILayoutKeeper - основной интерфейс, с которым взаимодействует пользователь. Реализация отвечает за сбор данных и их сохранение в конкретном хранилище.

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

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

Там ещё есть в публичной части фабрики, я просто не стал перегружать схему.

Итак, пройдём по пунктам.

S. Single responsibility principle.

В форме (пользовательский код) осталось только то, что относится непосредственно к ней: регистрация компонентов, раскладка которых сохраняется, и загрузка/сохранение раскладки как реакция на события формы.

В самой библиотеке выделены интерфейсы и реализующие их абстрактные классы, обеспечивающие фреймворк. Конкретные реализации переопределяют минимальный набор событий фреймворка. Реализация отделена от фреймворка. Фреймворк отделён от реализации. Вроде, S.principle на месте.

O. Open-Closed principle.

Сразу скажу, что значимость в части Closed может нивелироваться в зависимости от условий (единоличное владение проектом, невеликий объём кодовой базы, адекватные регрессные тесты), но где же вы видели хорошо покрытое тестами легаси? К тому же я имел наглость назвать это творение библиотекой, что налагает ответственность в части обеспечения интересов неопределённого круга пользователей. О, этот вечный вопрос совместимости версий! Поэтому данный принцип одним махом выходит в цари горы.

В данном случае ключевым фактором становится наличие интерфейсов. Мало того, что именно в Delphi на интерфейсах работает чёрная магия подсчёта ссылок, так ещё и при желании можно без малейшего зазрения совести выкинуть даже приведённый в библиотеке фреймворк, и написать свою реализацию с нуля, но переиспользовать, например, процессоры, потому, что интерфейсы поделены в соответствии с принципом I. Впрочем, абстрактные классы, как правило, могут быть использованы как есть для реализации (Open) и не требуют при этом вмешательства в собственный код (Closed). Например, в реализации кипера мы можем использовать для хранения БД, или можем сохранять отдельные настройки для каждого из пользователей. При этом с точки зрения использующего библиотеку кода ничего не изменится (разве что где-то в одном месте поменяется фабрика).

L. Liskov substitution principle.

Самый трудный для понимания, потому, что чаще всего рассматривается на неправильном (личное мнение) примере: нам СНАЧАЛА предлагают спроектировать иерархию наследования (предлагают спроектировать сферические прямоугольник и квадрат в вакууме), и лишь ЗАТЕМ, при анализе решения, предлагают задаться вопросом, зачем это было надо, и соответствует ли решение высказанным "когда нибудь потом" пожеланиям. А тут всё (почти) просто: поведение наследника должно оправдывать ожидания от родителя. Если мы не ждём от прямоугольника, что у него будут независимо меняться ширина и высота (может, он вообще иммутабельный), то квадрат от него наследовать можно (в рамках исходной задачи).

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

По (а) всё прозрачно, приведённые примеры, кажется, полностью оправдывают ожидания, а учитывая соблюдение S там вообще сложно что-то испортить - интересы наследников не пересекаются с родителями (абстрактный фреймворк и реализация - это совсем о разном), а реализацию интерфейса за наследование в этом смысле можно не считать, ибо интерфейс сам по себе поведения не имеет.

По (б) - ежели пускаться во все тяжкие, то можно было бы сделать класс "сохраняемой формы" назвать его тоже TForm, унаследовать его от Forms.TForm и всегда вписывать модуль с новым классом в uses после Forms, чтобы использовался именно допиленный класс. Вы ещё не заплакали? Между тем, есть в проекте ряд компонентов, которые издревле именно так и завёрнуты (хитроподвывернуты?). И это реально работает, хотя и прямо нарушает принципы S и L.

I. Interface segregation principle.

Тут говорить особо не о чем: ни один из классов не стремится объять необъятное и даже не наследует более одного интерфейса. Собственно, это прямое следствие S. И собственно хранилище (ILayoutKeeper) полностью обособленно от "процессора" (ILayoutProcessor), который определяет ключ для хранилища и преобразует реальную раскладку в её строковое представление и обратно. Принцип не то, что соблюдается -- тут после S нужно ещё много нелепых телодвижений совершить, чтобы его нарушить.

D. Dependency inversion principle.

Хм. Считаю, что один только факт того, что библиотека была абсолютно безболезненно выделена как самодостаточный проект, говорит за соблюдение этого принципа. Если идёт сохранение в ini-файл, то здесь нет (и не должно быть, спаси, Господи) ссылки на MainForm, где где-то в публичной секции лежит эта инишка. Конкретный объект инжектится из пользовательского кода. Если будет, к примеру, сохранение в БД -- инструмент доступа к БД тоже будет передан извне. Библиотека вызывает методы объекта пользовательского кода (инишка), но не имеет зависимости от него (зависимость идёт строго в обратном направлении). Изменение пользовательского кода никоим образом не будет поводом для изменения библиотеки. В этом смысле D перекликается с S в современном понимании от дяди Боба, только на уровне библиотеки, а не класса - повод для изменения классов библиотеки может найтись только в ней же, в её логике. Зависимость от стандартной библиотеки, конечно же, зависимостью от пользовательского кода не является. Реализация "процессора" с зависимостью от коммерческой библиотеки EhLib вынесена в отдельный модуль и может использоваться исключительно по потребности, при наличии соответствующих компонентов в проекте.

Единственная имевшая место "грязная" зависимость -- модуль хелперов OeStrUtil, где многие функции работают в бизнес-контексте, и от которого для публичной версии я взял чистый по зависимостям огрызок. Можно было бы переписать разбор строк и на библиотеку из стандартной поставки (RxStrUtils.ExtractWord, к примеру), и я даже поначалу сделал это. Но что-то в полученном коде было настолько старорежимное, что я ничтоже сумняшеся стёр эту версию к свиньям собачьим. Пусть будет полезняшка в комплекте.

И как там было, когда я писал курсовые? Ах, да. Вывод.

В старорежимных проектах компромиссы практически неизбежны. Тем не менее, при желании остаётся возможность для стремления к лучшему. Самое суровое наследие ушедших времён часто позволяет создавать в себе "островки безопасности", которые могут постепенно разрастаться и со временем захватывают всё большую территорию в масштабах любимого болотца. А SOLID-принципы в определённой мере обладают синергетическим эффектом, обеспечивающим им взаимное влияние и поддержку. При этом ключевую роль играют взаимодополняющие S и I принципы, D и O необходимы для библиотек с неопределённым кругом пользователей (всё, что больше pet-проекта или действующего макета в натуральную величину), а L весь про то, что проектировать иерархию классов надо начинать только тогда, когда поймёшь, зачем эти классы вообще нужны (и что при этом может измениться, но это уже в зоне S и O).

Подробнее..

Перевод Код без тестов легаси

26.02.2021 12:14:08 | Автор: admin

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

Автор Николас Карло, веб-разработчик в Busbud (Монреаль, Канада). Специализируется на легаси. В свободное время организует митап Software Crafters и помогает с конференциями SoCraTes Canada и The Legacy of SoCraTes.

Данная статья была скомпилирована (и отредактирована) их двух статей Николаса: What is Legacy Code? Is it code without tests? и The key points of Working Effectively with Legacy Code. Показалось логичным рассказать о том, что такое легаси, а потом как с ним работать.

Что такое легаси?

Возможно, если вы задавались этим вопросом, то встречали определение от Майкла Физерса. Майкл выпустил книгу Working Effectively with Legacy Code в 2004 году, но она до сих пор актуальна. Комикс это отлично иллюстрирует.

В своей книге Майкл пишет своё определение:

Для меня легаси это просто код без тестов.

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

Это хорошее определение: чаще всего тесты отсутствуют, так что это хорошее начало. Но это ещё не всё есть нюансы.

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

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

Перейдём к моему определению легаси.

Легаси это ценный код, который вы боитесь менять.

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

Но есть нюансы.

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

Хорошие тесты помогают легко менять незнакомый код. А плохие тесты не помогают. Отсюда и определение Физерса.

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

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

А теперь один из важнейших нюансов.

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

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

В итоге мы получаем, что легаси это:

  • код без тестов;

  • который мы пытаемся понять, чтобы отрефакторить;

  • но боимся.

Что же делать?

Как же эффективно работать с легаси?

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

Добавить тесты, а затем внести изменения

Логично, что если добавить тесты, они помогут его прощупать и он перестанет быть устаревшим. Поэтому первое, что нужно сделать написать тесты. Только тогда мы будем в безопасности, чтобы рефакторить код.

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

  • Определим точки изменения швы.

  • Разорвём зависимости.

  • Напишем тесты.

  • Внесём изменения.

  • Отрефакторим.

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

Найти швы для разрыва зависимостей

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

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

Шов место, где можно изменить поведение программы, не меняя код.

Швы бывают разные. Если это объектно-ориентированный ЯП, то обычно это объект, например, в JavaScript.

export class DatabaseConnector {// A lot of codeconnect() {// Perform some calls to connect to the DB.}}

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

class FakeDatabaseConnector extends DatabaseConnector {connect() {// Override the problematic calls to the DBconsole.log("Connect to the DB")}}

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

Напишем unit-тесты

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

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

Чтобы избежать путаницы, Майкл даёт четкое определение того, что такое НЕ unit-тест:

  • он не работает быстро (< 100ms / test);

  • он взаимодействует с инфраструктурой, например, базой данных, сетью, файловой системой, переменными;

Напишите максимум тестов, которые обладают этими 2 качествами, при этом неважно, как вы их назовёте.

Иногда трудно написать такие тесты, потому что не понятно, что код должен делать. Тогда используйте специальную технику.

Тесты для определения характеристик

Это тесты, которые формализуют фактическое поведение части кода.

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

Это мощная техника, потому что:

  • В большинстве систем то, что код делает важнее того, что он должен делать.

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

Этот метод также называют Approval Testing (тестированием одобрения), Snapshot Testing или Golden Master.

Но обычно на всё это очень мало времени.

Когда совсем нет времени на рефакторинг

Несколько советов, если предыдущие не подходят.

Большие куски кода обладают гравитацией и привлекают ещё больше кода. Теория разбитых окон в действии: небольшой беспорядок влечёт за собой беспорядок серьёзнее. Если класс уже содержит 2000 строк, то какая разница, что вы добавите еще 3 if оператора и будете поддерживать класс длиной в 2010 строк?

Это всего лишь 3 if: тяжело себя убедить, что нужно потратить на них 2 дня, хотя и должны. Что делать, если действительно нет времени писать тесты для этого класса? Используйте техники Sprout (прорастание), Wrap (обёртывание) и скретч-рефакторинг.

Sprout

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

Рассмотрим на примере:

class TransactionGate {//  a lot of codepostEntries(entries) {for (let entry of entries) {entry.postDate()}//  a lot of codetransactionBundle.getListManager().add(entries)}//  a lot of code}

Допустим, нам нужно убрать дубли файла entries, но postEntries() трудно проверить нет на это времени. Мы можем прорастить код где-то ещё, например, в новом методе uniqueEntries(). Этот новый метод легко протестировать, потому что он изолирован. Затем вставим вызов этого метода в существующий, не проверенный код.

class TransactionGate {//  a lot of codeuniqueEntries(entries) {// Some clever logic to dedupe entries, fully tested!}postEntries(entries) {const uniqueEntries = this.uniqueEntries(entries)for (let entry of uniqueEntries) {entry.postDate()}//  a lot of codetransactionBundle.getListManager().add(uniqueEntries)}//  a lot of code}

Минимальные изменения, минимальный риск. Можете вырастить один метод, целый класс или что-то ещё, что изолирует новый код.

Wrap

Можно обернуть изменение, если оно должно произойти до или после существующего кода.

  • Переименуем старый метод, который хотим обернуть.

  • Создадим новый с тем же именем и подписью, что и старый.

  • Вызовем старый метод из нового.

  • Поместим новую логику до/после вызова другого метода.

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

class TransactionGate {//  a lot of codepostEntries(entries) {for (let entry of entries) {entry.postDate()}//  a lot of codetransactionBundle.getListManager().add(entries)}//  a lot of code}

Ещё один способ решить эту проблему это обернуть её, поэтому мы переходим к postEntries(), списку записей, из которых мы удалили дубли.

class TransactionGate {//  a lot of codepostEntries(entries) {// Some clever logic to retrieve unique entriesthis.postEntriesThatAreUnique(uniqueEntries)}postEntriesThatAreUnique(entries) {for (let entry of entries) {entry.postDate()}//  a lot of codetransactionBundle.getListManager().add(entries)}//  a lot of code}

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

class TransactionGate {//  a lot of code+ postEntries(entries) {+  // Some clever logic to retrieve unique entries+  this.postEntriesThatAreUnique(uniqueEntries)+ }+ postEntriesThatAreUnique(entries) {- postEntries(entries) {for (let entry of entries) {entry.postDate()}//  a lot of codetransactionBundle.getListManager().add(entries)}//  a lot of code}

Эти методы не идеальны, и у них есть недостатки. Но это полезные инструменты при работе с легаси. А при необходимости можно даже немного нарушить правила.

Скретч-рефакторинг

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

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

Выводы

Легаси будет везде, где бы вы ни работали, в каждой кодовой базе. Можно сопротивляться и чувствовать себя плохо, когда вы застряли в нём. А можно рассматривать это как возможность. Работа со старым кодом это очень ценный навык, его надо изучать теоретически (почитайте книгу Working Effectively with Legacy Code) и практиковать в ежедневных задачах.


Похожие и интересные статьи:

Больше новостей про разработку в Додо Пицце я пишув канале Dodo Pizza Mobile. Также подписывайтесь начат Dodo Engineering, если хотите обсудить эту и другие наши статьи и подходы, а также на каналDodo Engineering, где мы постим всё, что с нами интересного происходит.

А если хочешь присоединиться к нам в Dodo Engineering, то будем рады сейчас у нас открыты вакансииiOS-разработчиков(а ещё для Android, frontend, SRE и других).

Подробнее..

Перевод COBOL древний код, который управляет вашими деньгами

22.12.2020 10:22:28 | Автор: admin
image

Язык программирования COBOL старше Игоря Николаева. Люди, умеющие им пользоваться, часто того же возраста. Он лежит в основе целой финансовой системы и его нельзя оттуда убрать. Мы расскажем о том, как компьютерный язык управляет финансовой жизнью мира.

Когда Томас начинал программировать, на двое стоял 1969 год. Он был обычным парнем, только что выпустившимся из старшей школы в Торонто, не имея особых целей в жизни. Его отец был плотником, но в двери его семьи постучалась удача: оказалось, что руки у Томаса растут не из того места. Мой отец знал, что я даже не смогу приколотить одну доску к другой, смеётся он.

Поэтому его мать предложила нечто странное и новомодное: как насчёт программирования компьютеров?

В 1969 году компьютеры всё ещё были странными новыми диковинками размером с большой шкаф. Но по всему миру компании начали понимать, что эти устройства бесценны для всех задач, требующих мгновенных бухгалтерских расчётов, например, подсчёта зарплат. Вакансии предлагались любому, кто хотя бы немного умел кодить. Поэтому Томас нашёл небольшую школу-однодневку в деловом центре Торонто и за следующие два месяца изучил самый популярный на то время компьютерный язык: COBOL (Common Business-Oriented Language).

После выпуска его приняли на работу в отдел сортировки чеков крупного канадского банка. (Он не захотел говорить его названия, банки скрытны; кстати, если вы еще не догадались, Томас это псевдоним.) Тогда Томас ещё не был программистом банка, но за следующие несколько лет он чётко дал понять, что хочет им стать. Его работодатель оплатил несколько качественных курсов колледжа по кодингу и в 1978 году он начал долгую карьеру в качестве банковского программиста.

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

За следующие несколько лет Томас хорошо освоил COBOL и написал тысячи бесценных строк кода. Когда банк производил платежи, именно код Томаса ежедневно помогал ему всё правильно подсчитать. Шли 70-е, 80-е и 90-е, Томас с коллегами-кодерами написали десятки миллионов строк на COBOL. Есть одна система, которой он особо гордится, это мгновенно работающая программа, способная обработать от трёх до пяти миллионов транзакций в день. Это моё дитя! Первые куски этой программы он написал в 1988 году.

И дело в том, что его код по-прежнему работает даже сегодня.

Томас уволился из банка в 2007 году в возрасте примерно 60 лет, и когда он ушёл, банк всё ещё использовал его систему, которой к тому времени исполнилось 20 лет. Она написана ещё тогда, когда на голове у Томаса было намного больше волос, а хитом чартов была Groovy Kind of Love Фила Коллинза. Сегодня этому коду уже больше трёх десятков лет. И он по-прежнему обрабатывает миллионы записей в день. Томас считает, что бОльшая часть кода, который он и его коллеги написали в своё время, всё ещё работает, потому что банк не сможет без него функционировать.

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

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

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

Переживёт ли Томаса его код на COBOL?

Вероятно.

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

Представьте: в более чем 80% личных транзакций финансовых организаций США используется COBOL. Когда вы проводите своей пластиковой картой, то в 95% случаев обработку выполняет COBOL. Bank of New York Mellon выяснил в 2012, что у него есть примерно 112 500 отдельных программ на COBOL, состоящих почти из 350 миллионов строк; вероятно, это типично для большинства крупных финансовых организаций. Когда ваш босс вручает вам зарплатный чек, есть вероятность, что он подсчитан при помощи COBOL. Если вы занимаетесь инвестициями, то ваша торговля акциями тоже выполняется на нём. То же самое происходит и в здравоохранении: страховые компании США используют adjudication engines программное обеспечение, определяющее, сколько заплатят врачу или фармацевтической компании; они так же написаны на COBOL. Задумывались, почему при совершении покупок в розничном магазине продавец вводит данные в старомодный терминал с зелёным текстом на чёрном фоне? Потому что в системе инвентаризации используется COBOL. Или почему специалисты по бронированию авиабилетов пользуются тем же чёрным экраном с зелёными буквами, чтобы изменить данные рейса? О, это COBOL, это совершенно точно COBOL, смеётся ведущий инженер Faircom Крейг Бейли. Его фирма создаёт ПО, помогающее компаниям управлять этими старыми системами.

Никто точно не знает, сколько существует кода на COBOL, но, по оценкам, наиболее важные аспекты нашей повседневной жизни втихомолку обслуживает примерно 240 миллиардов строк кода. Вторым по ценности ресурсом в США после нефти являются 240 миллиардов строк COBOL, говорит Филип Теплицки, десятки лет писавший на COBOL для банков по всем США.

Нам часто говорят, что технологии процветают благодаря новым, передовым инновациям, желанию рисковать и создавать с помощью кода нечто новое, двигаться быстро и сокрушать препятствия, как написал молодой Марк Цукерберг на своей стене в Facebook. И каждый день мы действительно видим, как появляется безумный новый код, написанный на модных и новых языках. Возможно, вы видели новый ИИ, способный писать предложения как человек он создан благодаря Python, хорошо известному новому компьютерному языку. Когда Facebook добавляет новые функции в своё браузерное приложение, то кодеры часто используют JavaScript ещё один популярный язык.

Но как насчёт более старых и массивных отраслей, являющихся центральными для экономики? В них по-прежнему вездесущ COBOL. Из-за этого инновации усложняются. Как создавать и прикручивать новые возможности с помощью древнего языка, который неинтересен энергичным молодым кодерам? Если крупные старые банки это не компании, двигающие прогресс вперёд с помощью сервисов наподобие Venmo или Square, или других громких финтех-продуктов, то из этого следует, что COBOL является частью проблемы. Но если это так, то почему же Томаса по-прежнему тревожат на его заслуженной пенсии, чтобы он поддерживал жизнь в этих системах? Почему нельзя обойтись без них?


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

Программисты начали разработку COBOL в 1959 году. Когда его наконец выпустили десять лет спустя, в 1969 году, он стал первым компьютерным языком, благодаря которому компьютеры можно стало активно использовать в повседневной жизни. В конце 50-х у компьютеров только завершилась экспериментальная стадия. Обычные компании начали взвешивать возможную ценность приобретения собственного компьютера для перемалывания чисел. Однако проблема заключалась в том, что до появления COBOL кодинг был непонятным и сложным в изучении. Программисты часто писали ПО на какой-нибудь разновидности ассемблерных языков, команды которых были ужасно мудрёными. (Например, команда LXA A,K означает взять число, загруженное в ячейку A компьютерной памяти и загрузить его в индексный регистр K.) Усугубляло ситуацию то, что производители компьютеров часто создавали для своих машин собственные уникальные языки. Если написать отличный код для машины, то он не сможет работать на компьютере, изготовленном другой компанией.

Новое поколение амбициозных программистов считало это безумием. Одним из них была контр-адмирал ВМФ США Грейс Хоппер, обладавшая яркой индивидуальностью. (Именно она популяризировала выражение проще попросить прощения, чем получать разрешение.) Хоппер считала, что языки программирования должны сильнее походить на английский, чтобы их было проще учить и читать. В 1955 году она разработала язык FLOW-MATIC, предназначенный именно для этой задачи; например, для перемещения числа из ячейки A в ячейку D на нём нужно было просто написать TRANSFER A TO D.

В 1959 году программистка Мэри Хоуэс решила, что её отрасли нужен язык, на котором писать будет так же просто, как на FLOW-MATIC, способный при этом работать на любой машине. Она собрала комитет специалистов, в том числе из только зарождающейся отрасли бизнес-компьютеров, чтобы приступить к созданию языка под руководством министерства обороны. Задача заключалась в написании языка, который бы мог читать и понимать обычный менеджер компании, даже если он не учился на программиста.

Спустя десятилетие работы, активно продвигаемой множеством женщин-суперзвёзд этой отрасли, например, пионеркой компьютерных наук Джин Саммет, был создан язык, во многом напоминавший FLOW-MATIC и простой в понимании. Например, для сложения двух чисел можно было написать ADD Num1, Num2 GIVING Result. Чтобы выполнить вычисление три раза, нужно было написать PERFORM 3 TIMES.

Сложно переоценить важность COBOL, говорит адъюнкт-профессор истории Мар Хикс из Иллинойсского технологического института и автор книги Programmed Inequality. Он совершил в компьютерных вычислениях совершенно необходимый шаг. Язык заполнил ту нишу, которая оставалась пустой на протяжении первых лет компьютеров. И он изменил образ мышления, необходимый для написания программ.

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

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

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

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

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

COBOL был оптимизирован для выполнения именно этой задачи: обработки целого множества транзакций. Компьютерные языки часто имеют определённую когнитивную или творческую специализацию; каждый из них создавался под конкретный тип задач. Python превосходно работает с data science и искусственным интеллектом; Fortran был создан для преобразования математических формул в код; JavaScript создавался, чтобы программисты могли делать веб-сайты интерактивными.

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

У разработчиков было пятьдесят лет, чтобы сделать всё правильно, замечает Билл Хиншоу, управляющий агентством COBOL Cowboys, предоставляющим услуги программистов на COBOL.

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

Кодеры и пользователи программ COBOL, которые управляют миром, имели десятки лет на выявление и устранение проблем.

Адриана Стерн (на этот раз это не псевдоним!), ещё одна программистка, с которой я общался, работала на крупные канадские банки. Её карьера началась в 80-х, когда из систем всё ещё устраняли различные странные баги. Однажды она выяснила, что один банковский терминал в Квебеке передаёт в систему буквы со знаками ударения, чего не предусмотрел программист системы.

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

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

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

Именно поэтому такие системы COBOL настолько надёжны сегодня. Их отлаживали больше, чем практически любой другой код на планете. Громкое новое приложение наподобие TikTok может появиться и получить огромную популярность даже с кучей багов. Если количество лайков под твоим последним постом считается немного неправильно, то это не вызывает особых проблем. А если вдруг крупная розничная сеть ошибётся в инвертаризации или банк внезапно не сможет отправить деньги? Это вызовет масштабный финансовый хаос.

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

COBOL не просто быстр; он, как сказал мне Томас, стабилен, стабилен, стабилен. Один из разработанных им процессов получает каждый месяц файл с примерно 2,4 миллионов государственных пенсий и переводит соответствующие суммы на банковские счета людей. Мы подтверждаем и проверяем их за 11 минут. За двадцать лет процесс ни разу не дал сбой.

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

Хороший пример этого можно наблюдать во время пандемии. В первые дни Covid-19 бизнесы массово закрывались. Уволенные сотрудники рванулись онлайн, чтобы подать заявление на получение пособий по безработице, и веб-сайты многих правительств штатов не выдержали нагрузки. Губернатор Нью-Джерси сообщил прессе, что их системы COBOL отчаянно нуждаются в помощи, чтобы справиться с новыми потребностями. У нас в буквальном смысле есть системы, которым от сорока и более лет, заявил он.

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

С ума сходило веб-приложение между мейнфреймом и внешним миром. Именно оно падало, рассказывает программистка и писательница Марианна Беллотти, годами работавшая с государственными системами и следившая за этой системой Нью-Джерси. Но, по словам историка Хикса, властям было слишком неудобно признать ой, да, это сломались наши веб-системы.

Беллотти наблюдала подобные явления и в других государственных органах, например в Налоговом управлении США (IRS). Однажды её вызвали для помощи с неработающим веб-приложением IRS. После расследования выяснилось, что проблема и в самом деле была в новых программах, в куске плохо написанного кода на Java. Мейнфрейм с запущенным COBOL, напротив, гнал вперёд подобно Ferrari.

Мейнфреймы отвечали всего за несколько миллисекунд, говорит она.


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

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

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

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

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

Ну и мучение, подумал Гуарино. Он встретился с человеком, управлявшим этой старой системой ПО. К сожалению, да, таковы реальные ограничения, ответил ему человек. И это была проблема COBOL, он был написан несколько десятков лет назад. Что же мы можем сделать? Можно сделать поле побольше, или ещё что-нибудь?, спросил Гуарино. И он сразу же такой нет, здесь ничего не поделаешь! К этому коду на COBOL никто и никогда не собирался прикасаться. У штата даже не было столько денег, чтобы оплатить время, необходимое для изучения этой кодовой базы.

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

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

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

Ошибка Y2K возникла вследствие старого конструктивного решения. Когда первые программисты на COBOL прописывали в своём ПО даты, они использовали две цифры: 1971 год, например, обозначался как 71. Так получилось, потому что у машин в 60-х и 70-х было очень мало памяти. Избавление от двух символов являлось серьёзной экономией. Все программы использовали память очень продуманно был дорог каждый байт, рассказывает мне Томас. Кроме того, кодеры 60-х и 70-х даже не мечтали о том, что их ПО будет использоваться тридцать лет спустя, когда приблизится 2000 год.

Но 2000 год надвигался, и даты из двух цифр превратились в огромную дилемму. В новом тысячелетии ПО на COBOL не будет знать, что означает 00 2000 или 1900. Если банк будет вычислять проценты по вкладу, сделанному в году 01, то он может ошибочно предположить, что вклад был сделан в 1901 году и перечислить клиенту проценты за 99 лет. Огромное количество банковских, розничных и зарплатных транзакций используют даты, поэтому необходимо было обновить миллиарды строк программ. Поскольку 2000 год приближался, банки вызвали своих старичков с пенсии, заплатили им за изучение кодовых баз, нахождение всех мест, где используются даты, и исправление ситуации.

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

Но даже при всём этом банку Томаса не хватило времени на полное решение проблемы. В некоторых случаях банки и фирмы не меняли код, чтобы можно было использовать полную дату из четырёх цифр, например, 2016. Вместо этого они применяли хак: правило сдвига. Они выбирали год далеко в будущем, например, 2045, и делали его новой точкой разрыва. Поэтому если COBOL встречает дату из двух цифр, которая больше 45, то он предполагает, что она относится к 1900-м, то есть, допустим, 87 это 1987 год. А если он встречает число меньше 45, то предполагает, что это 2000-е, то есть 33 означает 2033 год.

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

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

Однажды в понедельник утром нам позвонили: боже, проект встал такой-то работник умер, рассказывает Бейли.

Банкам приходится надеяться, что старички продержатся как можно дольше, потому что сегодня не так много молодёжи, изучающей COBOL.

Нам постоянно звонят компании: у вас есть кто-нибудь с любым опытом в COBOL? Они в отчаянии, рассказывает Мэрилин Цеппетелли, бывшая сотрудница IBM, работавшая на мейнфреймах компании, ныне профессор Marist College.

Marist один из немногих университетов, обучающих COBOL на постоянной основе. Многие учебные программы не рекламируют его. В научных кругах COBOL давно находится в униженном положении. Когда этот язык получил популярность в 70-х, элитные компьютерные учёные скорбели они заявляли, что COBOL стимулирует к выбору ужасных стилей кодирования, которые выходили из моды. Одним из таких примеров является конструкция GOTO: COBOL позволяет приказать программе внезапно перескочить с одной строки на другую, допустим, со строки 899 на строку 217. Честно говоря, компьютерные учёные правы! Подобный стиль кодинга приводит к созданию неряшливих, неупорядоченных программ, которые иногда сложно читать (это так называемый спагетти-код), а языки после COBOL в основном отказались от GOTO. Как бы то ни было, обвинения прилипли к языку. Для людей, серьёзно стремившихся к прогрессу в программировании, COBOL был языком неудачников, застоем.

Работа с COBOL вредит мозгу; следовательно, его преподавание должно считаться уголовным преступлением, так написал в 1975 году знаменитый компьютерный учёный Эдсгер Дейкстра. COBOL был скорее языком рабочего класса, вторжением синих воротничков в святыню кодинга. Кроме того, когда в 80-х появились недорогие настольные PC, они стали привлекательной новой территорией для запуска кода. Купить их мог любой, а для изучения COBOL требовался доступ к огромным мейнфреймам, которые в основном были только у банков или крупных розничных сетей. Когда малые и средние машины получили настоящую популярность, вузы перенесли весь процесс обучения на эти платформы, а мейнфреймы остались немного в стороне, говорит Цеппетелли. В наши дни смартфоны сделали COBOL ещё менее актуальным для студентов: Он просто не выглядит таким же сексуальным, как некоторые другие платформы.

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

Некоторые фирмы, озабоченные тем, что в будущем будет слишком трудно найти адептов COBOL, пытаются переписать всю свою систему на новом языке. Почти всегда это является адской задачей: необходимо продумать каждый аспект задач, выполняемый сложным, создававшимся десятки лет программным обеспечением, и воссоздать каждый малейший шаг на новом языке. Три года назад New York Times переписал систему циркуляции газет с COBOL на Java; попытка оказалась успешной, однако на подтверждение того, что новая система способна на всё то, что и старая, потребовалось неожиданно много времени.

И им ещё повезло. Commonwealth Bank of Australia попробовал переписать ядро системы на новом языке: на проект потратили вдвое больше ожидаемого, 1 миллиард австралийских долларов. Специалист по мейнфреймам с большим опытом Лен Санталусия однажды работал с финансовой организацией DTCC над исследованием возможности перехода с COBOL на Java.

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

Поэтому банки пожимают плечами и говорят: чёрт с ним. Если не сломано, то не надо чинить. Продолжим работать со старым COBOL. Эти программы работали круглосуточно в режиме 24/7 в течение тридцати-сорока лет. Зачем нам их менять?, говорит Томас.

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

Однако проблема банков в том, что несмотря на стабильность их кода на COBOL, ожидания их клиентов могут оказаться не столь постоянными. Как вы могли догадаться, ситуация в финансовой отрасли быстро меняется. Транзакции всё больше происходят в приложениях типа Venmo, позволяющих людям отправлять деньги друзьям; сервисы наподобие Coinbase позволяют покупать криптовалюты; существуют новые приложения для кредитовая наподобие Tala и Upstart. Сегодня люди ожидают максимально простых способов управления своими деньгами через ПО.

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

При этом стартап может делать всё, что угодно. У него нет старых систем. Они находятся в ситуации, которую программисты с любовью называют с чистого листа. Вместо покупки мейнфреймов ценой сотни тысяч долларов для хранения и обработки данных они просто арендуют пространство в облачной системе типа Amazon. Они могут писать код на новом языке, поэтому способны нанять любого энергичного студента компьютерных наук. И им даже не обязательно создавать что-то самостоятельно: когда Showoff пишет новое финтех-приложение, для выполнения сложной задачи она может использовать готовый сервис, например, Stripe для обработки платежей, а не пытаться создать это ПО для себя.

Это снимает с команды большую долю эксплуатационной нагрузки, поэтому она может масштабировать свой продукт, говорит Райан, и работать над продуктом, не особо заботясь об инфраструктуре. Иными словами, им не нужно беспокоиться о COBOL.

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

К 1960 году комитет, изобретавшего COBOL, проработал всего лишь год, однако один из членов комитета, представитель RCA Говард Бромберг, беспокоился, что они движутся слишком медленно. Он утверждал, что если не выпустить COBOL быстрее, то мир бизнеса может уйти вперёд! Изготовители компьютеров выпустят собственные уникальные языки, а в бизнес-программировании возникнет ситуация вавилонского смешения языков.

Поэтому Бромберг в сердцах решил отправить послание главе комитета по COBOL Чарли Филлипсу, работавшему в министерстве обороны. Бромберг привёз могильный камень, увенчанный гранитным знаком жертвенного агнца, с выгравированным словом COBOL. (Что это за имя такое?, спросили его в агентстве ритуальных услуг.)

Бромберг засунул могильный камень в ящик и отправил Филлипсу в Пентагон. По всей отрасли ходят слухи, что COBOL умирает, позже вспоминала Грейс Хоппер.

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

Категории

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

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