Компоновка
Jan. 28th, 2016 05:23 amМеня внезапно осенило, почему лишь очень небольшая часть хороших программистов в состоянии делать хорошие юзер-интерфейсы.
Дело в том, что хороший интерфейс – это система, в которой основную роль играет качество компоновки, а не качество реализации внутренних подсистем как таковых. В отличие от, скажем, программ командной строки или серверных решений, экспортирующих API.
В UI компоновка не просто определяет качество решения, компоновка и есть решение. Причём ограничения на компоновку весьма жесткие, не в пример суровей типипчных ограничений на компоновку программ без графического UI. Я думаю, тут уместна будет аналогия с компоновкой летательных аппаратов, там тоже внешние обводы и диапазон центровки – непреодолимые ограничение, любые подвижки в которых очень тяжело даются.
Типичная программа редко приближается к ограничениям окружения – обычно имеется существенный резерв, нередко на порядки. Да и самих ограничений рантайма не так и много: CPU, IO, RAM, overall response time.
С UI всё совсем не так – ограничения на время отклика отдельных частей, геометрию, да просто количество элементов, на связность и сгруппированность их, очень жесткие, никаких «туда-сюда на порядок» там нет в помине. Плюс целевые системы могут очень отличаться по производительности и, например, размерам экрана.
Задачи на связность и группировки, если их решать в лоб, моментально дают комбинаторный взрыв – а компоновка как раз такая задача.
Наилучший способ решения таких задач – это использовать мозги для того, что они (нейронные сети) умеют лучше всего, и это совсем не логическое мышление. Это навигация по ландшафту. В этом случае, ландшафту вариантов решений, образованных этим самым комбинаторным взрывом.
Сначала выбирается стратегия, а затем итеративно строится путь, причём возврат «назад», к началу, стóит существенно дороже, чем движение вперёд. Примерно так мы играем в шахматы. Примерно так выглядит процесс обучения, когда мы учимся самостоятельно.
Компоновка – это игра, а программисты нередко слишком серьёзные )
Дело в том, что хороший интерфейс – это система, в которой основную роль играет качество компоновки, а не качество реализации внутренних подсистем как таковых. В отличие от, скажем, программ командной строки или серверных решений, экспортирующих API.
В UI компоновка не просто определяет качество решения, компоновка и есть решение. Причём ограничения на компоновку весьма жесткие, не в пример суровей типипчных ограничений на компоновку программ без графического UI. Я думаю, тут уместна будет аналогия с компоновкой летательных аппаратов, там тоже внешние обводы и диапазон центровки – непреодолимые ограничение, любые подвижки в которых очень тяжело даются.
Типичная программа редко приближается к ограничениям окружения – обычно имеется существенный резерв, нередко на порядки. Да и самих ограничений рантайма не так и много: CPU, IO, RAM, overall response time.
С UI всё совсем не так – ограничения на время отклика отдельных частей, геометрию, да просто количество элементов, на связность и сгруппированность их, очень жесткие, никаких «туда-сюда на порядок» там нет в помине. Плюс целевые системы могут очень отличаться по производительности и, например, размерам экрана.
Задачи на связность и группировки, если их решать в лоб, моментально дают комбинаторный взрыв – а компоновка как раз такая задача.
Наилучший способ решения таких задач – это использовать мозги для того, что они (нейронные сети) умеют лучше всего, и это совсем не логическое мышление. Это навигация по ландшафту. В этом случае, ландшафту вариантов решений, образованных этим самым комбинаторным взрывом.
Сначала выбирается стратегия, а затем итеративно строится путь, причём возврат «назад», к началу, стóит существенно дороже, чем движение вперёд. Примерно так мы играем в шахматы. Примерно так выглядит процесс обучения, когда мы учимся самостоятельно.
Компоновка – это игра, а программисты нередко слишком серьёзные )
no subject
Date: 2016-01-28 03:28 am (UTC)Добавлю ещё один момент, который завязан на incentives, поэтому его невозможно починить, просто если отдельные инженеры приобретут нужные скиллы. Его нужно чинить через структуру организации, более дальновидных менеджеров, принициально новые процессы разработки и т.п.
Момент вот в чём: даже у очень хороших программистов нередко бардак с таксономией и каталогизацией. Отвратительные названия элементов, тянущиеся с разных предыдущих версий проекта, родственные вещи не всегда названы одинаково, из названий не всегда понятно, что это. Это происходит в любой более-менее крупной системе, где более 1 программиста работает над фичами в проекте.
Причины у этого, на мой взгляд, такие:
1. Награждают за фичи. Таксономия и организация - это приниципально таск, трогающий много фич, он трогает все фичи, но лежит вне зоны owernship'а отдельных фич. Этот таск просто НЕ КОМУ делать, кроме редких энтузиастов, которые не стремятся именно за это получить награду. (Кстати, такие редкие энтузиасты, если они ещё и достаточно продуктивны, чтобы выкатывать правильные фичи, чтобы их не выгнали с работы за безделье, и становятся потом крупными архитекторами, technical fellows, etc, но таких ребят заметно меньше, чем нужно.)
2. Таксономия и организация - это принциально таск, не поддающийся планированию заранее. Его нужно делать, когда фичи уже есть, занимаясь рефакторингом. Ты не можешь организовать вещи на своём рабочем столе, пока у тебя нет вещей - надо сначала свалить вещи на стол, а потом организовывать. Так же и здесь: фичи должны быть уже написаны, чтобы можно было сделать best job в отношении их организации. Сегодняшние инженерные процессы, best-практики и инструменты всё ещё очень слабы, чтобы позволить делать рефакторинг достаточно дёшево. Настоящее, по-честному, переименование ОДНОГО элемента везде в коде, с учётом всего необходимого тестирования, может легко вылиться в проект на неделю, за который никто не заплатит 25% месячной зарплаты (ну, эквивалент недели работы). Дополнительно осложняет п.1, т.к. новые названия лепят как попало owner'ы своих фич, им совершенно не нужно знать и видеть "всю картину", чтобы своя фича работала, соответственно, это название их не напрягает.
3. Психология. Это сложная задача, сложные задачи могут делать опытные люди, не новички. Чем более опытен человек, тем больше он привыкает к той кривоте, что есть, и в какой-то момент его уже невозможно убедить, что это говно нужно зарефакторить, переименовать, реорганизовать. Такой неприятный замкнутый круг.
no subject
Date: 2016-01-28 04:07 am (UTC)У меня таких задач серьёзного масштаба, кажется, не было и я сейчас понял, что не смог сформулировать быстро критерий, по которому я бы мог решить, что код пора рефакторить.
У тебя больше опыта, поделись соображением, зачем и когда это надо делать?
no subject
Date: 2016-01-28 05:17 am (UTC)Совсем навскидку, я бы сказал, что надо тратить как минимум 25% ресурсов (время, деньги) на "cleanup", что включает в себя рефакторинг, удаление дедкода, починку annoying-but-not-critical багов, fit-and-finish, переделывание UX на более удобный и т.п. Рефакторинг это большой диапазон - от переименования, до переиначивания интерфейсов, переписывание сложных компонент на простые и т.п.
Рефакторинг не всегда надо делать. Например, если на ближнем горизонте есть создание новой компоненты, которая *целиком* заменит старую, это может быть лучшее решение.
Симптомы, когда рефакторинг делать (давно было) пора, это когда интуитивно тривиальную фичу нельзя сделать за пару часов. Надо день (или ДНИ) ковыряться и искать, где её сделать, потом неделю чинить всё, что сломалось из-за того, что ты её сделал, или НЕОБХОДИМО разговаривать с людьми, а не просто посмотреть в код и в тесты.
Ещё один сиптом, что рефакторинг надо делать обязательно - это если даже простой рефакторинг делать очень дорого по причине плохого покрытия юнит-тестами. В этом случае первым шагом повышаешь coverage до максимума, а вторым уже делаешь рефакторинг.
Рефакторинг решает проблемы maintenance, стоимость которого постепенно повышается со временем. Если не надо maintain'ить или reuse-ать, то и не надо рефакторить. Чем больше ты делаешь одно или другое, тем важнее оно становится.
Когда я делал маленькие проекты чисто для себя, я рефакторил итеративно, 50% времени. Мне намного комфортнее, когда всё идеально организовано, это даёт какой-то полёт фантазии, свободу творчества (*крайне легко* экспериментировать с новыми фичами). На производстве 50% никто не делает. Делают 5-35%. Причём чем выше уровнем ребята, тем больший процент делают, т.к. это стратегический investment в продуктивность всех остальных. Но за это не принято отдельно reward'ить. Это как волонтёрская нагрузка такая.
Я нередко включаю рефакторинг старого говна в свои estimates, когда делаю фичу, как-то трогающую или использующую это говно. Собственно, это ещё один критерий: если ты видишь, что говно и тебе нужно потратить день, чтобы с ним разобраться или написать work-around'ы, чтобы обойти грабли - лучше потрать два дня, чтобы это говно зарефакторить. Когда это один день vs два, выбор очевиден в пользу второго. Когда это один день vs десять, уже не так очевиден. Когда ещё больше, то х.з.
Я про чуть отдалённое скажу, но оно связано. Вот, например, мне нужно регулярно делать какую-то последовательность действий в командной строке, или строить очень длинную строку параметров. Я не буду это делать 4 раза. После 3-го отвращение переваливает через край, и я буду писать скрипт, который автоматизирует, или займусь упрощением самого workflow. Примерно так же с рефакторингом - если противное чувство по поводу куска кода X возникло 3 раза, не надо позволять ему возникнуть в 4-й раз. Иначе привыкнешь к говну и понизишь свой стандарт.
no subject
Date: 2016-01-28 05:19 am (UTC)no subject
Date: 2016-01-28 05:26 am (UTC)В общем, я к тому, что всё грустно и хорошего решения нет.
no subject
Date: 2016-01-28 05:36 am (UTC)Это неплохая логика, только если действительно сделать это, а не использовать эту идею как отмазку, чтобы отказываться от clean-up'а, при этом длительное время поддерживая жизнь старой системе.
no subject
Date: 2016-01-29 01:41 am (UTC)Это очень хорошая и глубокая аналогия неожиданно. Я денёк подумал и решил, что тут можно по-разному смотреть.
Как-то раз мне коллега предъявила претензию, что дескать я живу какими-то мечтами несбыточными – и меня это чрезвычайно задело, потому что эти мои «несбыточные мечты», скажем, пятилетней давности все вполне в реальности теперь существуют.
Всё таки можно организовать вещи на своём рабочем столе, когда у тебя ещё нет этих вещей. Когда ты это делаешь, ты придумываешь им место, а значит, придумываешь какими будут эти вещи.
Если ещё точнее, ты определяешь их внешние контуры, то-есть границы где «вещь» заканчивается и начинается внешний мир. «Вещи» с миром взаимодействуют в основном именно на границе.
Я ещё это додумаю.
no subject
Date: 2016-01-29 09:42 am (UTC)Навскидку, несколько таких "разделений":
1.
Что такое waterfall? Проект -> реализация -> результат.
Хорошая такая логическая последовательность.
Отсутствует разделение во времени. Вносим разделение по времени с сразу становится лучше: проект (p) разделяем на изначальный проект (p0) и на "рабочий проект, в который регулярно вносятся изменения" (p1, p2, ... pN). Результат result = pLast, а не p0. Но при этом ломаются разные предположения, которые были сделаны в простейшей модели, где N=0.
Например предположение об оценке стоимости, что cost(p) = cost(result) уже заведомо неверно :) Потому что cost(p) = cost(pLast). При этом pLast неизвестен, если не пройти все предыдущие шаги, которые к нему привели.
Приходит осознание, что в достаточно сложной системе хорошие estimates проекта целиком либо невозможны, либо возможны, но каким-то совершенно иным способом, а не, как в наивном случае казалось бы, деталировкой и оценкой изначального проекта. Например, более правильный вариант - искать похожие проекты в past performance, вспоминать, сколько реально времени заняло, и брать это число в качестве estimate.
no subject
Date: 2016-01-29 09:42 am (UTC)Есть проект, есть результат.
Всё, что не результат (не осязаемый результат работы), то проект (задумка).
Разделям проект по ролям. Важно не что он из себя представляет, а какой цели он служит. Под таким углом сразу возникает разделение: например, проект как мотивационная задумка - он мотивирует к реализации, к созданию первого прототипа, именно он используется для бизнес-анализа, вообще стоит ли в этот проект вкладываться. Другое - это проект как ориентир, он направляет, с ним ты сверяешься, чтобы корректировать курс. Ориентир - это другое, потому что это динамика. Он "годен" только для очередной итерации, а потом он сталкивается с реальностью, которая обнаруживается после реализации, и реальность вносит свои коррективы. Ориентир версии N+1 учитывает опыт версии N, изменившиеся требования сегодняшнего дня (во времени t = N+1), а также эскиз желаемого на сегодня конечного результата.
Например, твоя изначальная мечта - это мотивационный инструмент. Он good enough, чтобы начать работу. Если бы ты её сразу продумал во всех деталях ты был бы вынужден проходиться по ней напильником. Но ты, возможно, намеренно сформулировал её тезисно, в общих чертах, чтобы она могла дольше мотивировать. Но вот такую тезисную абстрактную формулировку уже не назовёшь рабочим проектом ведь, правильно?
Чем хорошо подобное разделение - оно позволяет сосуществовать разным идеям о "проекте", без необходимости пытаться их противопоставлять и решать, всё-таки идеи о проекте живут от начала до конца или же вынуждены меняться со временем. Правильный ответ по-моему, в том, что некоторые идеи могут жить от начала до конца, а некоторые меняются со временем. Это разные идеи, хоть и сильно взаимосвязанные.
no subject
Date: 2016-01-29 09:42 am (UTC)Возвращаемся к аналогии со столом. Давай предположим такой волшебный стол, на который вообще физически нельзя ничего положить, не подготовив место для вещи. Т.е. есть два типа объектов - тип "полезная вещь", и тип "место для неё". Карандаш и пенал. Мышка и коврик для мышки. Просто на голый стол мышь нельзя класть. Создать место - дёшево и быстро, но при этом про экологию (взаимодействие мест, эстетику их взаимного расположения) ты можешь думать только в совокупности, когда смотришь на весь стол и меняешь все места сразу. При работе с вещами ты смотришь только на её место, и тебе плевать на другие вещи и их места - они не видны - ты нагнулся к столу и видишь только эту одну вещь.
Теперь смотри. Вот ты делаешь этот изначальный эскиз - придумываешь места вещам. Всё офигенно, красиво, правильно. Теперь тебе нужно добыть вещи, которые в эти места влезут.
Тут, whoops, возникают накладки:
1. Ты добыл карандаш, а он слишком толстый для этого пенала. Тебе нужно срочно пенал побольше. Взял - заменил. Но помни: ты работаешь с одним объектом, карандашом, поэтому всей картины ты не видишь. Ты не видишь, что из-за более толстого пенала теперь всё стало чуток по уродски и вразнобой по стилю. Ты это увидишь только потом, если заново окинешь взглядом весь стол (вот он, рефакторинг).
2. Ты подготовил отличную подставку для твоего айфона, а вдруг выяснилось, что ты ЗАБЫЛ про зарядку. Нужен шнур. Нужен USB-хаб. Нужна зарядка для USB-хаба. Нужен удлинитель питания, чтобы воткнуть эту зарядку. Для всех этих вещей срочно нужно место. Ты его по-бырику создаёшь и добываешь все эти дополнительные dependencies. Но помни: ты всё ещё держишь в руках айфон, который нужно срочно воткнуть, и смотришь только на то, что касается айфона. В этот момент ты не видишь, что теперь на столе опять стало некрасиво, неэргономично и т.п. Мало того, ты даже не видишь, что у тебя уже была вебкамера, воткнутая в свой USB-хаб, и вполне можно использовать один на двоих. Но этого не видно, поэтому ты купил новый. Только рефакторинг позволит избавиться от лишнего хаба.
"ЗАБЫЛ" - ключевая вещь. Это не ошибка. Это неизбежность. В достаточно сложных системах совершенно стопудово нельзя подумать обо всём. Скажем, если проект имеет размер больше одного человеко-года (скажем, на троих-четверых на квартал), то 50% work item'ов вылезают в процессе. Их невозможно включить в изначальный estimate, просто потому что о них никто не подумал. Опытный разработчик делал проекты подобного размера раньше и просто знает, что estimate необходимо уможить на 2 ТОЛЬКО ЛИШЬ чтобы учесть этот фактор - удвоенное количество работы из-за достаточно большой сложности и невозможности обо всём подумать заранее. Но единственный способ, как он может обосновать такой estimate начальству - это объяснить, что список work item'ов содержит только половину того, что нужно, а про вторую половину мы узнаем только в процессе. Доверьтесь моему опыту. Если же чисто идти по списку, то такой 2x буфер обосновать не удастся. Многие инженеры пытаюся идти этим вторым путём, интуитивно таки пытаются вставить этот 2x, т.к. жопой чувствуют, что надо, но сформулировать не могут. Умные (но неопытные) менеджеры и коллеги их ловят, уменьшают estimates до реалистичных, дальше всё суммируется, и получается неправильный estimate, который как минимуму вдвое off. Whoops :) Вот она неспособность признать невозможность достаточной дальновидности, чтобы всё сразу детально запланировать.
no subject
Date: 2016-01-29 04:10 pm (UTC)> либо возможны, но каким-то совершенно иным способом,
> а не, как в наивном случае казалось бы, деталировкой
> и оценкой изначального проекта
О, да. Я утверждаю, что для грубого эстимэйта достаточно представлять внешний периметр системы, в широком смысле. «Померяв» его в условных попугаях типа количества интерфейсов или методов в API, можно неплохо прикинуть кратность против трудозатрат на условную единицу.
По моим не очень многолетним наблюдениям там зависимость похожа на степенную функцию со степенную между 1 и 2, скажем, 3/2. Степень в самом деле зависит от глубина подсчёта начальной цифры. Чем больше глубина начальной оценки, тем меньше степень.
Математический смысл такого рода эстимэйта предлагаю додумать самому, дробные размерности сразу подсказывают, где искать, и почему эта механика оценки имеет право на жизнь )
> "ЗАБЫЛ" - ключевая вещь. Это не ошибка. Это неизбежность.
Это вообще нормальный подход, сразу делать это «забыл» намеренно, не думать очень вглубь.
no subject
Date: 2016-01-29 09:50 am (UTC)no subject
Date: 2016-01-29 09:53 am (UTC)Новенький, кстати, не может сразу увидеть айфон на столе. Он смотрит на весь стол, видит отвратительный бардак, ему противно и тяжело, у него куча вопросов "зачем оно всё так", и он потратит время, прежде, чем идентифицировать отдельные компоненты, их роли и их взаимодействие. Вместо красивого расположения предметов, которое своим видом подсказывает архитектурную диаграмму, он видит нагромождение проводов и гаджетов. Он не может быстро отличить важную ключевую компоненту от вспомогательного гаджета (скажем, айфон, USB хаб, зарядка и удлинитель - всё это может казаться одного калибра вещами, когда, на самом деле, айфон ключевой, а остальное вспомогательное).
no subject
Date: 2016-01-28 03:30 am (UTC)no subject
Date: 2016-01-28 04:02 am (UTC)Это очень сильно зависит от того, что именно ты понимаешь под словом «план».
«План» может быть:
1. TODO-списком, работает хреново.
2. Списком майлстоунов, работает получше.
3. Начальной грубой итерацией и бинарной «функцией»-индикатором достижения цели, причём само описание такой функции обычно хорошо выделяет главные стратегические ориентиры – работает совсем хорошо, но не обязательно с первого раза.
Третий тип «планов» всё таки поддаётся грубой априорной оценке. Так как я это делал, для меня главным параметром выступает объем сырых данных в parseable текстовом формате.
> Будет криво.
Не обязательно. Вероятно, будет не самый оптимум, но при таких размерностях решений хрен ты вообще найдёшь этот «самый» оптимум.
no subject
Date: 2016-01-28 05:29 am (UTC)no subject
Date: 2016-01-28 05:33 am (UTC)