2014-02-23

О PlantUML

Все любят диаграммы. Все архитекторы/менеджеры/начальники рисуют диаграммы. Стандартными диаграммами являются диаграммы на UML.

Чтобы рисовать диаграммы (как UML, так и другие) существует множество инструментов: MS Visio, DiayEd. Даже LibreOffice/OpenOffice.org Draw или Google Drive Drawing могут быть полезны. Ну а под Андроид есть великолепный DrawExpress Diagram, где диаграммы можно рисовать прямо пальцем.

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


Но есть и другие инструменты. Где вы описываете логическую структуру диаграммы: какие должны быть блоки и с каким текстом, каким образом они должны быть связаны. А визуализация этого описания делается автоматически. Таким является Graphviz для визуализации графов. Вы описываете структуру графа на специальном языке в текстовом виде, а затем генерируете изображение, растровое или векторное.

Графы — это хорошо. Но нам нужны диаграммы. Поэтому существует PlantUML (технически — это надстройка над Graphviz). Эта штука позволяет описывать UML диаграммы текстом (на своем языке для разных типов диаграмм) и генерировать картинки. Это не единственный инструмент подобного рода, но самый популярный и функциональный. PlantUML встраивается в вики-движки (включая Confluence и MediaWiki), в IDE и текстовые редакторы (включая Eclipse и IDEA). Но самый простой способ попробовать PlantUML действии — запустить сервлет у себя или же воспользоваться уже работающим сервером plantuml.com.


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

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

Диаграмма последовательностей. Самая простая диаграмма. Используйте её, если нужно показать порядок взаимодействия компонент. Я предпочитаю сначала перечислить все компоненты, задав тем самым порядок их расположения на диаграмме, а потом рисовать сообщения. Мне больше нравится явно писать слова actor, participantdatabase, чем полагаться на нотации типа :actor:.

 actor Twilight  
 boundary Spike  
 actor Celestia  
   
 Twilight -> Spike : letter  
 activate Spike  
 Spike -> Spike : fire-breath  
 Spike --> Celestia : letter  
 deactivate Spike  
 activate Celestia  
 ...  
 Celestia --> Spike : answer  
 deactivate Celestia  
 activate Spike  
 Spike -> Spike : belch  
 Spike -> Twilight : answer  
 deactivate Spike  

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

Диаграмма классов. Очень полезная диаграмма. Можно описать классы, методы, свойства, связи, включая наследование и композицию, с указанием ролей в связи. Можно объединять классы в пакеты. Чаще я описываю не классы языка программирования, а классы предметной области: что куда входит и как друг с другом соотносится. А еще чаще использую для описания структуры БД. Обычно сначала я описываю классы и их вхождение в пакеты, а потом расставляю связи.

 package MLP <<cloud>> {  
   
  class Pony {  
   head  
   body  
   legs  
   tail  
  }  
   
  class Unicorn {  
   corn  
  }  
   
  class Pegasus {  
   wings  
  }  
   
  class Alicorn  
   
 }  
   
 Pony <|-- Unicorn  
 Pony <|-- Pegasus  
 Unicorn <|-- Alicorn  
 Pegasus <|-- Alicorn  
   
 Pony ..> Pony : friend of >  

Диаграмма деятельности. По сути — блок-схема. В PlantUML есть два варианта синтаксиса для этих диаграмм. Первый подразумевает явное описание всех стрелочек-переходов. Второй (еще в бете) больше похож на язык программирования, со всякими if-else-endif. Я не люблю эту диаграмму, потому что даже псевдокод исходника диаграммы для меня понятнее, чем блок-схема. Хотя некоторые разработчики находят диаграмму деятельности полезной и понятной.

Диаграмма компонент. Моя любимая диаграмма. Тут можно разрисовать и логическую структуру системы: кто с кем по каким протоколам взаимодействует. И физическую структуру того, как оно все развернуто: на каком узле какие процессы запущены и в каких датацентрах сами узлы находятся. Компоненты прекрасно объединяются в узлы (блок node) и облака (cloud).

 cloud Equestria {  
  frame Canterlot {  
   actor Celestia  
  }  
  frame Ponyville {  
   actor Twilight  
  }  
  frame "Everfree Forest" {  
   actor Zecora  
  }  
 }  
 cloud "Crystal Empire" {  
  actor Cadance  
 }  
   
 Celestia - Twilight  
 Twilight - Zecora  
   
 Celestia - Cadance  
 Twilight - Cadance  

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

 state "Twilight Sparkle" as Twily  
 Twily : unicorn, Princess Celestia's student  
   
 state "Princess Twilight Sparkle" as Princess  
 Princess : alicorn, princess of Equestria  
   
 [*] --> Twily  
 Twily --> Princess : solve Star Swirl's spell  

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

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

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

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


2014-02-08

О Mercurial

Я люблю Mercurial.

Вообще-то это про кроссовки.
Из систем контроля версий я имел дело с Visual SourceSafe (VSS, в его старой реинкарнации в виде файлопомойки на сетевом диске), с Perforce (P4), Subversion (SVN), Mercurial (Hg) и Git.

Про VSS и P4 вообще вспоминать не буду. Эти серверные блокировки — тот еще ад при совместной разработке. А вот Subversion был первым действительно удобным и крутым инструментом контроля версий. Я сразу же поднял репозиторий на localhost и засунул в него все свои локальные проекты (типа исходников сайтов).


Потом наступила эра распределенных DVCS. Я как раз подумывал о том, что выкладывать финальные сборки ПО на сайт в архивах — это некошерно, нужно заводить публичные репозитории. И захотел попробовать распределенные системы и найти хостинг. Sourceforge уже тогда выглядел загибающимся. GitHub тогда еще не существовал. А на гуглокоде поддерживались только Subversion и Mercurial. Так я стал пользоваться Mercurial. И до того он мне понравился, что локальный Subversion репозиторий быстренько превратился в кучку меркуриаловых (тогда это было не так тривиально). С тех пор публичные мои проекты так и живут на гуглокоде, а более менее приватные наброски обосновались на Bitbucket. Почти все на Mercurial (хотя немного грешу и GitHubом).


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

Не хватает трекинга каталогов. Ни Git, ни Mercurial этого не умеют. В результате, если где-то нужен какой-нибудь пустой каталог, в нем появляется какой-нибудь readme, в котором объясняется, зачем этот каталог тут есть и почему он пустой. Как в старом добром CVS.

Не хватает чудесных и мощнейших properties. Иметь произвольный набор метаданных (включая, в конце концов, mime type) для каждого файла — это круто. Жаль, что никто из DVCS этого не сделал. Тогда бы не понадобились странные файлы .gitignore и .hgignore.

Теги — это все же (постоянные) указатели на ревизии. В Subversion это так (как мы помним, копия какого-нибудь trunk в подкаталог в tags). В Git это тоже ссылка (refs). А вот в Mercurial зачем-то изобрели .hgtags. Это хорошо в том смысле, что все изменения тегов (ежели такие имеют место быть) записаны в историю (изменений этого файла). Я, правда, не совсем понимаю, как быть с изменением этого файла в разных ветках. Получается так, что, находясь в разных ветках, будешь видеть разный набор тегов?


Таки чем же Mercurial (и любая DVCS) лучше Subversion (и любой централизованной VCS)?

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

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

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

Надежность. Это больше для сисадминов. Больше не нужно нежно оберегать Subversion репозиторий, делать его бэкапы. Ибо полная (ну почти) история проекта есть на машине каждого разработчика. Потерять что-то становится почти нереально. Правда, возрастает риск "утечек", если кто-то что и унесет, то унесет всю историю, включая все фейлы и случайно закоммиченные пароли. От форка не отмажешься.

Локальный репозиторий. Сделать его для чего угодно — одна команда. В Subversionе же нужно еще мучаться с file:// urlами.


Чем Mercurial лучше, чем Git? Буду холиварить.

Команды Mercurial гораздо более понятны. Они делают ровно одну вещь и выдают вполне внятные и краткие сообщения об ошибках. А в Git тот же pull на самом деле делает fetch и merge. А вот pull --rebase делает совсем другое: fetch и rebase. А банальная попытка сделать clone локального Git репозитория в соседний каталог, с последующим пушем в него (что вполне обычный кейс для Mercurial), выдала целый экран предупреждений и ошибок, оказывается, не bare репозиторий в Git по умолчанию не принимает пуши и требуется явное его конфигурирование.

Хотел сказать, что у Mercurial лучше документация. У Mercurial есть своя книга (как и у Subversion). Но Git тоже обзавелся книгой. Ничья.

Из GUI самым лучшим является черепашка (TortoiseSVN, TortoiseGit, TortoiseHg). Вот только черепашка для Subversion и Git — только под Windows (для Git это вообще самый безболезненный способ установки и использования под Windows). Нормального полноценного GUI для Git для Linux я так и не нашел. Есть неплохие штуки для коммитов и просмотра истории, но это далеко не весь функционал. А вот TortoiseHg (спасибо кросплатформенным Python и Qt) — функционален, удобен и одинаков на всех платформах.

Ветки. Тому, что в Mercurial называется branch нет аналогов в других системах контроля версий. И это мощная штука. В Mercurial название ветки является свойством каждого коммита. В результате целая группа коммитов, неважно, сколько над ними работало разработчиков и как переплелась история, является объединенной в ветку. В Git такого нет. В Git ветка — это лишь указатель на голову репозитория. Лишь одну голову. В результате возникает практика использования rebase, чтобы искуственно выровнять естественно запутанную историю. Чтобы уменьшить количество ветвлений и голов. Потому что иначе, при наличии лишь указателей на верхушку, нельзя понять, к чему относится коммит посередине. Rebase же — это изменение истории. Пусть и локального репозитория. Изменение нескольких коммитов в прошлом. Меня это пугает. Ибо сам смысл существования систем контроля версий заключается в том, что история зафиксирована навсегда. К тому же в Git эти указатели на головы/ветки/теги являются метаинформацией где-то сбоку от основной истории изменения файлов. Сам видел однажды, как они тупо терялись, и репозиторий превращался в лапшу. Ветки Mercurial надежно зашиты в историю, в каждый коммит, и это хорошо.

Не понимаю, почему Git является гораздо более популярным, чем Mercurial. Неужели мнение Линуса так ценно? И ведь в Mercurial даже пошли навстречу: добавили bookmarks — полный аналог указателей-веток в Git.

Кстати, Mercurial — наиболее универсальная система контроля версий, он умеет работать и с Git репозиториями, и с Subversion. А еще Mercurial (спасибо Python), имеет кучу разных расширений. А еще в Mercurial есть встроенный http сервер, что бывает полезно для расшаривания репозитория в локальной сети.


2014-02-02

Об архитекторе

Поговорим об архитекторах.


Обычно все вспоминают вот этого бородатого дядьку.


Ну ладно. Сархитекторил свою матрицу.

А лично мне из киношных архитекторов больше нравится этот герой в исполнении Тома Хэнкса из «Неспящих в Сиэттле».


Это не архитектор программных систем, а архитектор домов. Но очень душевный архитектор.

А вообще, я иногда представляю работу архитектора аналогичной работе этого персонажа (крайний справа), в замечательном исполнении Харви Кейтеля, из "Криминального чтива".


Он придет и молча поправит все,
Человек из Кемерова.
(c) БГ

Из ИТ классики вспоминается "Мифический человеко-месяц".

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

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

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



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

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

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

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

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


И вообще, как мы выяснили на HappyDev, хорошим выбором вполне является и такой выбор, чтобы отложить выборы на потом. А идеальная архитектура — которой (еще) нет.

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

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

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

Архитектор — это лишь роль. И исполнять её могут совсем разные люди. Это — коллективный труд.


З.Ы. Спасибо ребятам, которые здорово играли и пели на гитаре, пока я это писал.