В прошлой статье От инженера до руководителя. Часть 1: Чувство справедливости я рассказывал о чувстве справедливости. Возвращаясь к ней, хочу повториться, что чувство справедливости является основополагающим моментом. И если мне вздумалось о чём-то рассказать, то каждая моя неточность, а тем более ложь, неподкреплённое фактами мнение, орфографическая ошибка и агитация нашли бы своих недовольных. Что, собственно, можно наблюдать и тут и в жизни ежедневно. Одно дело придерживаться конкретной стороны в холиваре (парадигме, стандарте, процессе), получая тумаки от одних и поддержку от других; и совсем другое дело — описывать и следовать своей собственной точке зрения, опыту и выдерживая свою стилистику. Это — сродне минному полю, где известны правила игры, но за всё, что делаешь, несёшь сам ответственность. Такая же разница существует между исполнителем и руководителем, где последний при своей ошибке получит пинок из-за проявленой “несправедливости” и набьёт немало шишек сам, если будет ошибаться, хотя и спасая этим идущих за ним. Поэтому в моём понимании лучше набивать шишки загодя, с уровня сотрудника, ощупывая путь мягкими частями тела, не получая дополнительных пинков сзади — главное не отставать и не идти против руководителя, впрочем, если он не до конца неправ и не ведёт всех на обрыв. В противном случае, попридержите коней, ведь вы — рабочая лошадка — в одной упряжке. О том, как как поставить правильную цель и как исполнять работу совместно с другими и пойдёт речь в этой статье.
Friendship is magic!
Однажды, непогожим осенним деньком, меня вызвали в другую страну решать чужие проблемы с чужим проектом. Помимо языкового барьера необходимо было разрешить и барьер технологический. Я особо не представлял, что же за проект мне достался, как всё в нём работает и что надо делать. Мне сказали только одно — тебя пригласили сдать набор модулей в срок с должным качеством и по установленным стандартам. Поставленная задача для меня была совершенно обычной и понятной, мой опыт и знания позволяли это легко сделать, а чего нехватало можно было подтянуть, прочитав мануалы и даташиты, не сильно теряя по времени — пара дней, не более. Но вот что же собственно надо писать в коде, как понять и как читать спецификацию и какую же на основе её архитектуру строить, и как она связана с тем каркасом, что я увидел в UML-модели, я не имел понятия.
К счастью для разрешения этой неувязки меня познакомили с Senior Developer’ом, которого звали Майнул. Майнул происходил, по-видимому из рода брахманов, и был очень приветливым и натасканным, просветлённым человеком из Индии. Мне впервые пришлось встретиться в работе рука об руку с индусом. Не где-то удалённо, где Бангалоре за кусок лепёшки клепался код и тесты, и откуда приходили невнятные результаты с магическими числами 3, 9 и 24 от очередного Раманамамы Шакати, а с элитным представителем своей профессии здесь и сейчас вживую.
— Привет, как дела?
— Нормально, разбираюсь. Как мне писать? Есть наработки? Могу ли я добавлять что-то в модель?
На что он мне ответил, что, мол, надо писать, вот так… И принёс мне свой код, где для ознакомления он предложил мне реализовать ещё пару десятков функций. Что ж, сказать, что его код был индийским — ничего не сказать. В нём хотя и имелся некий налёт от текущих требований, но конструкции следующего вида ужасали:
sint16_t Temperature = nInTemp;
bool_t Sign = TRUE;
Sign = Temperature >> 15;
if (Sign = TRUE) // do something
// and do a little bit more
else if (Sign == FALSE) { // do something }
Некоторые вещи вообще не компилировались. Я написал, что недоставало, подправил небольшие его косяки, чтобы это хотя бы не нарушало стандартов и компилировалось. Затем были разночтения спецификации у меня и у него, и он так же предлагал индусские подходы. Я увидел индусский код в действии! Он всё делал, чтобы я я уверовал в предрассудки об индийских кодерах, разве что не танцевал при этом у меня под носом. Но вопросы-то возникали и накапливались, а индийские решения меня не устраивали. Поэтому в один из дней я сказал ему: “У нас проблема, надо решать”. И тут произошло следующее — Майнул стал приводить ко мне специалистов, словно они были взяты руками Шивы: тех, кто писал и трактовал спецификацию, тех, кто паял микроконтроллеры, тех, кто прогонял математические модели. И они давали мне ответы. Он умел находить людей, которые знали, как это должно работать. Людей из одной упряжки. Я же попросту не знал тысячной армии из всего штата, и подойти к кому бы то ни было, в-общем, было сложно и объяснения заняли бы немало времени. И тут я понял. Я передавал ему свои проблемы и обязанности — найти устройство механизма людей, а он покрывал свои — написание правильного кода. У нас всё заработало и проект мы закрыли за 3 недели с интеграцией из 4-ех выделенных.
В отношении руководителя есть правило — делегируйте. Но, на самом деле, даже попав не в отлаженный коллектив, где у каждого своё место, как в часах, вы можете так же делегировать свои обязанности тем, кто с этим хорошо справится. Даже с позиции рядового сотрудника. Это работает эффективно, но не значит, что бесплатно. Отдавая поручения, вы берёте на себя и определённые затраты и обязанности, иногда даже дополнительные на этапе планирования. Проявляйте инициативу и прислушивайтесь к людям, вне зависимости от той парадигмы, взглядов, что у вас есть.
Со стороны руководителя — доверяйте своим сотрудникам, они обычно лучше проинформированы, лучше знают кому дать задачу, знают кто проявит большее рвение. Делегируйте выбор, делегируйте ответственность. Не бойтесь работать на сотрудников, пускай они скажут, что же важно в данный момент и как это исправить, пусть они дают вам советы не на собраниях в овальном кабинете, а в рабочем процессе на рабочих же местах. Не делайте чужую работу, а распределяйте её так, как всем будет выгодно. Если вдруг компетентности у сотрудника не хватает, спросите его мнение, как он бы решил эту проблему, что нужно по его мнению для разрешения проблем. Слушайте больше, чем говорите. И лишь потом предлагайте своё, жёсткое, единственное решение. Единожды делегировав, оценивайте важность вклада в работу, мотивацию, дайте возможность развернуться и показать себя. Ищите людей и методы, способы замотивировать исполнение задач, поощряйте самостоятельность — хоть леденцом «театральным» или переходящим плюшевым талисманом успеха — и помогайте, не оставляйте подопечных один на один с проблемой, если им нужна помощь, используйте свои связи, чтобы привлечь нужные ресурсы. Посоветуйте книгу, посоветуйте сходить проветриться, но не лезьте никогда ни при каких условиях в код и в работу сотрудника, не тормозите “достаточно хорошими” критериями, позвольте ему самому быть мерилом хорошего кода в поставленные временные рамки.
Постановка цели
Чтобы правильно дать совет, ориентировку и выделить решение как руководителю, так и сформулировать задание разработчику, надо правильно ставить задачи. Но как правильно поставить задачу? Во-первых, есть методология SMART. Во-вторых, чтобы задание было понятным, его должен понять и оппонент. Это означает, что тот, кто ставит задачу, должен так же и проконтролировать правильность понимания, в простейшем случае — повтор постановки задачи от исполнителя. Чтобы задание было наиболее простым для понимания и его возможно было воспроизвести без ошибок, оно должно быть максимально простым, т.е. разбитым при необходимости на элементарные части и шаги, и наглядным, чтобы, если структура задания комплексная, его можно было легко изобразить на клочке бумаги.
В моей практике был случай, когда мне поставили очень важную и критичную задачу для всего проекта следующим образом: “Нам нужен набор критичных мониторов. Попробуй разобраться с этим”. Окей, пришлось уточнить, правда, что за список мониторов нужен. На всё остальное было исключительно моё design decision. Что ж, за неделю с небольшим я продумал всю структуру классов, сделал быстрые методы и систему оповещения. У меня был готовый модуль, который был связан с готовыми интерфейсами. Однако за день до сдачи мне пришлось всё переделывать. Так как после код ревью, грозная начальница, которая своим авторитетом была втрое шире и выше меня, сообщила мне, что ошибки и мониторы должны быть реализованы в разных классах (даже если в ошибках одно поле), да и не надо использовать спид-хаки, вообще не нужны битовые операции и упаковка ошибок в биты одного слова, а надо использовать кучу наглядных switchif c отдельным int под каждую ошибку. И вообще, было сообщено мне, что в концерне давно решена проблема с мониторингом, и моё дело повторить (sic!) готовый код. Интерфейс, правда, отличается, но удалить ненужные методы, а интерфейс поглушить нулями, где это можно. Я в принципе не против рефакторинга, когда он уместен, но когда задача преобразуется из “yours design decision” в “use old damn code” от несопоставимого проекта — это как-то смущает на этапе исполнения за 1 оставшийся день. И самое интересное, что в этом был не прав и я. Надо было определить правила игры.
Техническая спецификация
Самая лучшая идея — это составить спецификацию из следующих соображений:
- Спецификация должна содержать детерминированный набор действий и условий
- Требование спецификации должно прослеживаться как вверх к родительской задаче, так и вниз, к подзадачам (действиям) при необходимости
- Каждое действие должно быть элементарным и не допускать различных трактовок.
- Спецификация должна содержать сценарий или схему взаимосвязи, которую легко описать на бумаге или псевдографикой.
- Спецификация не должна быть псевдокодом или последовательностью действий и инструкций, оставляя вопросы дизайна и инструментария разработчику.
- В идеале одно действие должно покрываться одним тестом (или проверкой, если речь не идёт о программном коде)
- Следствием предыдущего является отсутствие в спецификации не подлежащих проверки не-функциональных требований (описания инструментария).
Хорошая спецификация:
11. RAM Functionality
[Requirement ID 111]
[Actual Baseline 1 Iteration 8]
[Parent requirement 11]
The functionality of the RAM devices shall be tested by a write / read test of at least all used memory locations.
[Requirement ID 112]
[Actual Baseline 1 Iteration 8]
[Parent requirement 11]
In case of detecting a RAM fault, the unit shall be halted.
Т.е. мы можем понять место своей спецификации в архитектуре и мы имеем набор действий, которые можно покрыть двумя тестами для двух действий (выполнить сценарий):
- случай без ошибки
- случай с ошибкой
Почему мы не расписываем как должен проходить readwrite тест и как должен останавливаться CPU? Во-первых, позвольте решить разработчику, как это сделать. Во-вторых, подумайте о тестировщиках — как это протестировать, да ещё по желанию одним тестом? Или, что хуже, как протестировать особенность выключения CPU? Не всегда углубляться в детали — хорошо. Даже при низкоуровневых требованиях сохраняйте уровень абстракции, который сделает возможным создание тестов, а не будет ограничиваться код ревью с пометкой none-testable*. (*Впрочем, зачастую принцип чёрного ящика для тех же Unit test'ов не всегда выполним, поэтому имеет смысл давать ссылку на даташиты или описывать нефункциональные требования из разряда layout или необходимости применения ассемблерных инструкций, например остановки ISR на время записи EEPROM, чтобы перестраховаться. По возможности следует избегать уточнений, какая же из десяти функций аппроксимации должна использоваться в этой реализации — это должно логично следовать из спецификации).
Плохая спецификация:
Fault machine
[Requirement 31]
[Actual Baseline 1 Iteration 8]
[Parent requirement: 3, 5, 7, 8, 9, datasheet 23 page 5]
System shall catch and store all errors in None-Volatile Memory.
Errors shall be stored as follows:
1. Set IE to OFF;
2. Write Date;
3. Write Time;
4. Write Error ID;
5. Write ‘’;
6. Set IE to ON;
Обратный пример. Какие ошибки? Вот overflow — это ошибка? Превышения порога датчика — ошибка? У нас есть лист ошибок в конце концов? Отлавливать как? Когда? Есть сообщения, приходящие раз в секунду — нужно ли проверять каждое из них? А во время ожидания получения? Потом детализация сохранения. Оно должно описывать формат — это хорошо, но вы составляете уже алгоритм, превращая разработчика в кодера — оно вам надо — делать двойную работу? Кроме того, как тестировщик протестирует последовательность действий? Особенно установки регистров. Дебаггером? А как же black-box? Да вы шутите!
Спецификация — не такая уж и однобокая штука, которой должны заниматься архитекторы. Со стороны разработчиков — это описание функционала и поведения кода для тех же тестировщиков. Со стороны тестировщиков — сценарий, во время исполнения которого произошёл сбой, или нарушение стандарта, отсылка к которому будет понятна разработчику, в конце-концов это — описание тест-кейса и окружения. Со стороны руководителя — чёткая постановка технической задачи иили описания тех. процесса. Стремитесь изъяснять мысли правильно: структурировано, чтобы их можно было разделить при необходимости на подэтапы, и стилистически, для описания сценария и вашего отношения. Если это crap, то следует и писать прямо: «rework this pile of crap», конечно, с условием, если вы правы и ваша позиция весомо аргументирована. Для написания хорошей спецификации потренируйтесь в письме. Пишите тексты, хотя бы по полстраницы в день. В блог, в сообщества, в стол. Пишите и старайтесь излагать свои мысли так, чтобы они были услышаны и восприняты с исходным посылом. Хоть о сложности изготовления змеевика в домашних условиях, хоть о мягкой гриве пони. Проверяйте и зачитывайте тесты своей кошке, развешивайте план субботника в подъезде или в интернет — и не того он успел натерпеться. Ваша задача — научиться доносить мысли максимально просто и правильно. Раз. Два. Три. В моём случае посыл — это не готовый набор «наилучшей практики», а призыв поразмышлять со мной и посмотреть на обычные процессы, на примеры из собственного опыта глазами разработчика, попавшего в новые условия.
Постановка задач
Ладно, если техническая спецификация близка к коду, то что касается постановки задач?
Плохое задание:
Create input monitoring functionality urgently.
Хороший пример должен содержать последовательность действий или вовсе описываться дорожной картой:
Try to integrate last application release (from SVN) till Friday 13 in the following way:
1) Build new framework
2) Build new application with the new framework
3) Run tests
4) Run all benchmarks
5) Report to Jira
If one of the steps breaks, try to re-check its default settings, and if all is correct, then report status.
Хорошее задание должно быть:
S) конкретным (что именно мы делаем)
M) детальным, а следовательно измеримым (в данном примере можно посмотреть, на каком этапе находится задание)
A) понятным и достаточным (в данном примере мы используем последние исходники с SVN, сборку в конкретной среде)
R) Каждое действие актуально, если предыдущий этап был успешно завершён. Нет смысла тестировать производительность, если неожиданно упали тесты. И уж тем более нечего тестировать, если не прошла сборка.
T) ограниченным во времени (для соблюдения сроков и синхронизации проекта).
Т.е. для моего случая поручение Майнулу было таким: «Надо узнать сегодня (T), как обрабатывать буфер ARINC сообщений (S) для интерфейса A429 уровня приложения (A), когда приходят два сообщения с одинаковым идентификатором (M1) и в случае когда буфер переполнен (M2). Это может быть связано с ошибками тайминга, пожалуйста, узнай поведение железки ®.»
Всё это, в конечном итоге, структурирует и логирует диалог между исполнителем и руководителем (или заказчиком). Если вам непонятно задание, спрашивайте до тех пор, пока его не перефразируете на атомарные фразы. Если вы хотите быть понятым, спрашивайте, насколько понятно это задание. Какие решения и предложения разработчик хочет использовать и какое место он видит заданию в архитектурепроекте. Согласуйте до того, как приняться за дело, планируйте. Это во-первых, позволит избежать недовольства в конечном итоге, так как обе стороны приняли правила игры. Это избавит от бесконечных переделок и обид, что код уже “достаточно хорош”. Это избавит от вопросов, т.к. все правила и ответы есть в мануалах, вики, и всём, что описано в задании или в тех процессе. Это, в конце концов, приведёт к понятным отчётам, которые будут как защитой исполнителя, так и аргументом руководителя.
О пользе отчётов и вообще зачем они нужны речь пойдёт в следующий раз.
Автор: wwakabobik