Archive for the ‘Эффективность’ Category

Хороший или Гениальный программист.

Tuesday, January 19th, 2010

Мысль из сегодня обсуждения… Плюс навеяно обсуждением книги Good to Great.

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

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

Слава богу, за свой опыт я не слишком часто с такими сталкивался :)

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

Подведем простой математический расчет. Естественно все цифры взятые с потолка, если вам они не нравятся, подставьте свои.

а) Не ходить на работу и пользовать это время на программирование. (+10% дополнительного времени).

б) не писать документации (+5% дополнительного времени).

в) не обучать других программистов (+10% дополнительного времени)

г) не менеджить других программистов (+15% дополнительного времени)

д) отсутствие отчетности отчетности (+5% времени)

е) отсутствие необходимости согласовывать решения (+5% времени)

е) не присутствовать на всех митингах (+10% дополнительного времени)

д) отсутствие переключения между задачи (+30% эффективности).

е) отсутствие прерываний работы другими людьми (+30% эффективности)

Итого уже накапало 50% дополнительного времени и 60% увеличение эффективности. То бишь, продуктивность возросла в 2.4 раза. Думаю, если покопаться и выписать более полный список того, что НЕ делает гениальный программист, то вполне разность продуктивности может получаться и в 3-4 раза (отсюда и выходит, что хороший программист делает 3 месяца, а гениальный вадает за 4 недели).

А теперь на секунду остановитесь и задумайтесь. Хорошо ли для компании такое увеличение продуктивности в 4 раза? Если вы ответили “Да”, то вам двойка в четверти по бизнесоведению :)

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

Есть такая штука “technical debt”. Это когда написал код на скорую руку и решил, что потом его приведешь в божеский вид, но потом как-то не наступило и код продолжает пользоваться/изменяться/дополняться. С каждым днем его становится все сложнее поменять и все больше времени уходит на его поддержание. То есть мы сделали что-то быстрее взяв в долг, но долг не выплатили и него начали расти проценты.

Аналогично,  можно, ввести понятие “process debt”, когда что-то делается без учета уже действующих правил (а-ля документирование, обучения людей и т.п.).  Взять такой долг на короткое время вполне можно, но постепенно он накапливается (остальные разработчики не знаю тех частей, которые написаны гениальным программистом, не возможно предсказать даты выпуска продукта, так как нету никакой отчетности, время поддержки кода растет из-за отсутствия документации, обсуждений по ходу разработки и т.п).

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

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

P.P.S. В еще нескольких комментариях началось обсуждение, как называют этих программистов. Предложенные варианты “Гики”, “Хакеры”. В целом, пусть хоть горшками (не чайниками) называют. Важно то, что с точки зрения множество людей в компании эти гении/гики/хакеры более (или по крайней мере не менее) полезны чем хорошие программисты выполняющие гораздо больший спектр задач.

Agile и автоматизированное тестирование

Thursday, December 3rd, 2009

После года работы в Agile режиме (в нескольких их видоизменениях) осознал, что Agile НЕ может нормально работать для без автоматизированного тестирования.

Пойду доказательством от противного. Пусть мы работаем в Agile и у нас нету автоматизированного тестирования. У нас есть пять user story, которые мы делаем. Внутри каждой из них у нас есть какие-то разумные критерии по которой мы определяем, что user story сделана – они обычно включают ручное тестирование измененной/новой функциональности.

По окончанию скажем 6 Sprint’ов, мы решает, что пора выпустить новую версию и тут мы обнаруживаем, что оттестированная каждая user story не значит оттестированный весь продукт. Соответственно, мы стартуем большое регрессионное тестирование всего продукта. Исправляем баги (для них можно даже сделать отдельные user story), потом опять проходимся и делаем регрессионное тестирование (так как исправленные баги могли внести другие). Надеюсь это знакомо? Итого, нам понадобилось еще 3 Sprint’а, чтобы привести все к виду, в котором можно выпускать.

Итого, на самом деле мы похерили пару принципов из Agile
а) То что продукт постоянно близок по качеству к выпускаемому.
На самом деле, чем дольше мы работаем без release тем общее качество у него будет хуже и хуже
б) То, что заранее НЕ нужно планировать отдельные фазы (разработка/тестирование).
Теперь нам заранее надо таки планировать, что на каждые 6 sprint’ов разработки – 3 sprint’а тестирования и стабилизации.
Ой… А это случайно мы не в waterfall возвращаемся, когда мы делаем сначала разработку, а потом тестирование?
в) То, что по ходу, можно и нужно улучшать код рефакторингом.
Рефакторинг кода, становится делать опасно, так как зачастую только через месяца (во время большего тестирования) становится видно какие проблемы он добавил.

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

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

Не давайте бензопилу детям.

Monday, August 31st, 2009

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

Ну и по ходу у нас вышло вот такое обсуждение:

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

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

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

Сначала небольшая аналогия, а потом к делу. Представим себе, что у нас есть Linux машина и мы всем раздаем root права. И администратор говорит, ну что же поделаешь, что пользователи могут к черту все системы разнести, я приду починю, пожурю, объясню в следующий раз как надо и как не надо делать.

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

Так вот, я выступаю за то, чтобы разграничить права на использование инструментов, как разграничены права пользования в Linux. Условно говоря, главный программист может делать любые действия. Средний программист не имеет права изменения критических существующих интерфейсов и ядра системы. Младший программист не имеет правда добавления новых файлов, copypaste и т.п. Еще раз… Это всего лишь пример. У меня сейчас нет в кармане полного списка прав и как все разделить и разграничивать.

Естественно задача проблемного кода решается даже без введения ограничения на инструментарий путем review кода до сommit’а его в SVN. Но даже на этом уровне, получается, что доступ к более опасным tool’ам будет съедать время главного программиста, так как ему придется объяснять, что Вася, не бери бензопилу, не меняй интерфейсы в классах.

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

Да, кстати, localstorm пишет в своем ЖЖ о управление проектами, программировании и других IT радостях жизни. Рекомендую почитать – хорошие и взвешенные мысли.

P.S. В многих комментариях проскочила одна и та же мысль. Можно же откатиться по системе контроля, так что ничего страшного. Я согласен, что откат уменьшает в десятки раз нанесенный ущерб. Но! Пусть нам нужно скажем сделать какой-то модуль. Мы можем это сделать двумя методами – даем неопытному его дизайн, он его делает плохо, мы объясняем как делать хорошо, он переделывает. Или мы сначала объяняем как делать хорошо и он сразу делает хорошо. Второй путь по большему счету более эффективный (с точки зрения компании).  Замечу, “откатить” плохой дизайн мы можем мгновенно, но дизайн все равно придется второй раз делать.  Поэтому, когда мы заранее знаем где лежат грабли, имеет смысл действовать проактивно, то есть решать проблему ДО того, когда она стукнула тебя по голове.


Теория планного программирования.

Monday, August 10th, 2009

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

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

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

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

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

И в целом, если объектоориентированное программирование крутится вокруг идеи выделения объекта и методов, чего можно делать с объектом. То аспектноориентированое программирование крутиться вокруг выделения идеи аспекта и собирания его из размазанного вида в одно место. Так, что вместо бегания по 100 функциям разных объектов и copy/paste одного  того же куска, мы можем сказать – делать то-то при входе в функции таких-то классов.

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

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

Да, поясню слово вклиниваться. Я имею в виду, то, что мы не пишем никакого кода в определенном месте программы, но тем не менее есть код (на самом деле компилятор), который “знает”, что в этом месте надо выполниться.

Похожим образом дело обстоит с полиморфизмом.Хотя код и есть, но тем не менее, в отличие от процедурного программирования, когда мы говорим – исполни функцию A (и четко известно, что это за функция A и с чем ее едят), то тут мы вполне можем говорить – исполни метод A, объекта. И на момент исполнения неизвестно, какой будет объект и соответственно какой код вызовется.  Сразу соглашусь, да это чуть другое чем с деструктором. Там мы не видим кода, но знаем что он сработает. Тут мы код видим, но не знаем что сработает конкретно.

Но, это все так, отклонения от общей темы.

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

Так, вот, возвращаясь к планному программированию. Моя идея следующая – херим все сущности, которые были введены раньше (функции, объекты, аспекты, модули, namespac’ы, templat’ы и т.п.) и вместо всего оставляем совмещая все это получаем коцепцию плана.

Итак, что такое план?

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

Основная идея, это построить их так, чтобы сделать код гораздо более податливым. Если сейчас код находится в классе X, похожий код в классе Y и Z, то начинается целая свистопляска, чтобы его объединить, или хотя бы свести в одно место (как с логированием).

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

Чтобы не углубиться окончательно в теоретические изъяснения, приведу пример. Хотим мы написать адресную  книгу. Как мы это делаем?

- Начинаем с того, что для любой программы есть стандартный пустой план – application, который содержит все остальные планы.

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

- Мы обнаружили, что наш MainWindow план стал слишком жирным, выделяем Storage план, переносим туда весь код по работе с файлом (чтение, запись, временное сохранение измененных записей).

Пока звучит, точно как объектноориетированное, а то и процедурное? Согласен, но движемся дальше.

- Мы решаем, что нам надо добавить 5 полей. Начинаем их добавлять, и обнаруживаем, что мы нам нужно будет тупо copy-paste работу с полями для того, чтобы их отрисовывать в  MainWindow и сохранять, считывать в Storage. И вот тут наступает самое интересное, мы вводим новый план ContactField в который составляем из куска Storage по работе с полем и куска MainWindow по работе с полем.

Замечу, что ContactFiled содежит не копию кода, а зеркало кода. То есть, редактируя в ContactFiled оно редактируется в MainWindow или Storage. Итак, это первая выгода, что мы теперь можем просматривать код в том виде, который нас действительно интересует – либо с точки зрения UI (смотрим в MainWindow) или Storage или с точки зрения Field. На самом деле, теперь мы можем в этот план включать любые другие места где наше поле используется, передается, храниться и обрабатывается. То есть, всегда есть место где централизованно можно  видеть сущность.

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

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

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

Самое важное, что я осознал, во время написания статьи, фактически планное программирование – это две вещи:

а) Мощная среда разработки, которая умеет делать много разных отображений кода и работы с этими отображениями.

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

Надеюсь, кого-нибудь зацепила эта писанина. :) Жду, комментариев, критики и горячих споров.

P.S. Насчет Mix-in и Closure. И то и другое продолжает решать проблему дублирования кода, но все таки не так радикально как предложено у меня :)

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