Журнал

Впечатления от CSS, или как я галерею со смешнявками делал

Disclaimer: Я не фронтендер. И вообще не особо разбираюсь во всем этом. Наверняка специалисты закидают меня субстанциями за то, что сделал что-то криво и вообще “вы не разобрались”. Все впечатления сильно субъективные и зависят от моего круга общения.

Предыстория

Какие-то потуги в сайтостроении у меня были еще в школе, в далеких 2000-х. Статичные сайтики, табличная верстка (sic!), инлайн-стили. Картинка на заднем фоне была тогда вау-эффектом, взаимодействие с пользователем — на грани фантастики, javascript был очень дозированно, jQuery не то что не устарел — его тогда не существовало, а отсупы go <br> <br> <br> <br>.

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

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

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

CSS

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

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

На волне эйфории от создания статического сайта я решил, что буду делать все на чистом CSS. Ну, а что — игры на чистом CSS делают, вот даже есть бродилка от первого лица. Позже выяснилось, что бродилка в себе до фига javascript содержит и ни фига она не pure css, но на хабре была в районе 2014-2015 года была тема про игры на чистом CSS, и там, насколько помню, все было честно. В любом случае на момент старта у меня в голове была картинка, что CSS очень мощный и с ним можно сделать почти все:

Первое, что заметно — очень много материалов. Хорошие доки на Mozilla CDN, если там не понятно — гугл выплюнет 300 тыщ блогпостов, где все написано как для даунов. Становится понятно, откуда так много “вайти в айти” в этой области, хотя скорее тут положительная обратная связь и другие факторы тоже играют. Тем не менее, порог вхождения ощутимо низкий.

Однако это касается простых вещей. Почти все материалы, которые мне попадались — “практические”: сделай так и все. Нет глубокого объяснения, почему это работает так, а не иначе. Нет фундамента, цельной картины, разъяснения внутренних механизмов.

При этом в самом CSS очень много нюансов. Эти нюансы подскажет браузер, но иногда доходит до абсурда: все готово, осталось чуть-чуть поправить, и тут тебе надо поменять несколько свойств у родителя и детей. Или вообще все выбросить и переписать на flexbox, потому что “там проще”. Например, много свойств меняют свое значение или не выполняются, если ты значение display поменяешь.

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

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

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

Тем не менее, я получил большое удовлетворение, когда в итоге у меня получилось сделать галерею на чистом CSS с ленивой подгрузкой изображений. Результаты можно посмотреть тут, а исходники — в developer-tools или тут. Но не могу сказать, что я на 100% уверен, что делает каждая строчка CSS, и что будет, если ее убрать. Особенно с учетом того, что некоторые завязаны друг на друга и меняют свое поведение.

Но были и печальные моменты. Знаете, что было самое сложное? Сделать резиновый квадрат. Да-да, можно делать игры, можно сделать галерею на чистом CSS, будучи нубом как я, но резиновый квадрат это уже сложно. Хотя, по идее, именно ради этого CSS и должен был развиваться, разве нет? Я задал вопрос на StackOverflow, спросил у нескольких фронтендеров — никто не знает, как это решить. Есть конечно нюансы, но это хороший пример того, что в CSS не все в порядке. Подробнее можете прочитать в вопросе. Особенно меня позабавил чувак, который предложил jQuery для решения этой проблемы (“верните мой 2007”, как говорится).

Знатно у меня пригорело от того, что простые вещи делаются сложнее “сложных”. У меня реально существенно больше времени ушло на верстку превьюшек (хотя казалось бы, чего там сложного — накидал квадратиков, да картинки в них), чем на элементы управления (стрелки, переход на следующую картинку и т.п.). Я думал, все эти смешнявки про то, как тяжело добиться нужного форматирования в CSS — это приколы из серии “ты забыл точку с запятой и компилятор тебе выдал 1000 ошибок, ну ты и нуб”, а это оказалось суровой реальностью.

Итого

А итог получается смешанный. С одной стороны — на CSS можно сделать много всего, но сложно. Или нельзя, и надо добавить чуть-чуть JS. Или генерации на сервере. А потом удивляемся, почему страницы там медленно грузятся и статичные read-only страницы не отображаются нормально или крашат браузер. Реальная история: с моего старого планшета почти невозможно читать какие-нибудь фэндом-вики на *.fandom.com — постоянные вылеты при скролле. Кто-то скажет “купи новый планшет”, но черт побери, там сложность контента как на википедии — простыня текста да пара картинок. С учетом всего этого многие выбирают простой путь и генерят все фреймворками на JS. И вообще, какой смысл от стилей, если все равно сервак нужен? Можно тогда джаваскриптом получать viewport размеры и на сервере по пикселям все генерить? И не удивлюсь, что где-то так и есть.

Ссылка

Как убить на мелкий скрипт кучу времени или история одного пулл-реквеста

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

Понадобилось мне дублировать информацию из markdown-заметок в телеграм-канал. Казалось быть, что тут рассусоливать — Ctrl+C и Ctrl+V в помощь. Однако выяснился маленький нюанс: markdown в телеге не совсем полноценный и ссылки в таком формате [text](http://example.com) клиент не поддерживает. Ладно, подумал я, попробуем что-то с этим сделать.

Пытаюсь победить врукопашную

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

Может, получится копированием из браузера или текстового редактора? Снова облом и еще один баг в телеге: при использовании буфера обмена форматирование сообщения теряется. Я заинтересовался, почему же так произошло.

В этом нам поможет утилита xclip, которая позволяет инспектировать содержимое буфера обмена. Для сообщения телеги она показывает такие доступные форматы:

text/plain
UTF8_STRING
STRING
TEXT
application/x-td-field-text
application/x-td-field-tags

Сравните это с теми, что предоставляет Firefox:

text/html
text/_moz_htmlcontext
text/_moz_htmlinfo
UTF8_STRING
COMPOUND_TEXT
TEXT
STRING
text/plain;charset=utf-8
text/plain
text/x-moz-url-priv

или какой-нибудь Libre Office Writer:

application/x-openoffice-embed-source-xml;windows_formatname="Star Embed Source (XML)"
text/rtf
text/richtext
text/html
text/plain;charset=utf-16
application/x-openoffice-objectdescriptor-xml;windows_formatname="Star Object Descriptor (XML)";classname="8BC6B165-B1B2-4EDD-aa47-dae2ee689dd6";typename="LibreOffice 6.0 Text Document";viewaspect="1";width="16999";height="2995";posx="0";posy="0"
text/plain;charset=utf-8
UTF8_STRING
STRING
application/x-libreoffice-internal-id-5387

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

В любой непонятной ситуации пиши скрипт

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

Ок, нам нужен скрипт, который прочитает файлик и отправить его сообщением в телегу. Какие библиотеки для этого есть? На PyPI 31 страница с результатами по запросу “telegram”. Выбрал Telethon — на хабре статьи с ним есть, и вообще, стильно, модно, молодежно, асинхронно, чистый питон, MTProto и так далее.

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

from telethon.sync import TelegramClient
import socks

api_id = 11111
api_hash = '...'
proxy=(socks.SOCKS4, '127.0.0.1', 9050)

with TelegramClient('anon', api_id, api_hash, proxy=proxy, timeout=60) as client:
	print(client.get_me().stringify())

Запускаем скрипт… и он не работает из-за того, что не может подключиться к телеге. Конечно же, я не HACKERMAN, чтобы он с первого раза заработал. Наверно, проблема с прокси.

We need to do deeper

Ок, проверим, работает ли Tor, который исполняет роль прокси:

$ curl -XGET httpbin.org/ip --socks5 localhost:9050

Получаем ответ с адресом, который не мой — все ок. Значит, проблема в том, как работает прокси в Telethon. Какую библиотеку он для этого использует? PySocks. Отлично, с этой библиотекой работал давным-давно. Там есть простенький скрипт, которым в том числе можно проверить работу прокси. Запускаем и получаем…

Traceback (most recent call last):
  File "test.py", line 110, in <module>
    print("HTTP: " + opener.open("http://httpbin.org/ip").read().decode())
  File "/usr/lib/python3.6/urllib/request.py", line 526, in open
    response = self._open(req, data)
  File "/usr/lib/python3.6/urllib/request.py", line 544, in _open
    '_open', req)
  File "/usr/lib/python3.6/urllib/request.py", line 504, in _call_chain
    result = func(*args)
  File "test.py", line 94, in http_open
    return self.do_open(build, req)
  File "/usr/lib/python3.6/urllib/request.py", line 1318, in do_open
    encode_chunked=req.has_header('Transfer-encoding'))
  File "/usr/lib/python3.6/http/client.py", line 1254, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib/python3.6/http/client.py", line 1300, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.6/http/client.py", line 1249, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.6/http/client.py", line 1036, in _send_output
    self.send(msg)
  File "/usr/lib/python3.6/http/client.py", line 974, in send
    self.connect()
  File "test.py", line 55, in connect
    ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),))
  File "/usr/lib/python3/dist-packages/socks.py", line 200, in create_connection
    proxy_username, proxy_password)
  File "/usr/lib/python3/dist-packages/socks.py", line 322, in set_proxy
    username.encode() if username else None,
AttributeError: 'int' object has no attribute 'encode'

Ах ты ж… Какого черта? Я ведь сам туда хэндлер для https добавлял, должно работать! Что могло там поменяться за… 6 лет? Какой я старый :(

15-20 минут на клонирование репы и принт-деббагинг приводят к ответу, что проблема в неправильном порядке аргументов — в основной библиотеке поменяли аргументы, а во вспомогательном скрипте забыли поправить. Хорошо хоть пулл-реквеcт на фикс этой проблемы есть, и скрипт оттуда работает. Мораль проста: если список аргументов длинный, лучше использовать именованные параметры.

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

DEBUG:telethon.network.mtprotosender:Connection attempt 2...
WARNING:telethon.network.mtprotosender:Attempt 2 at connecting failed: ProxyConnectionError: Error connecting to SOCKS4 proxy 127.0.0.1:9050: [Errno 115] Operation now in progress

О чем же говорит нам эта ошибка? По номеру находим код EINPROGRESS, а про него читаем уже маны ядра по функции connect. Если общими словами, то проблема заключается в том, что у нас неблокирующая операция (в нашем случае установка соединения) не успела выполнится мгновенно и намекает нам, что она все еще выполняется. Похоже на правду: Tor не очень быстр. Не ожидал конечно, что ради 10-строчного скрипта на питоне, который в наши времена любой школотрон пишет за 5 минут с закрытыми глазами, мне придется опуститься до уровня системых вызовов и вспоминать особенности работы с сокетами…

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

Пора уже делать что-то полезное

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

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

- s.setblocking(False)
+ s.settimeout(timeout)

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

Возвращаемся назад

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

Как нормально передать параметры api_key и api_hash, которые нельзя отозвать/поменять, но можно заабузить? Посмотрел, как это делают в примерах Telethon — через переменные окружения. Неплохо, но где хранить тогда эти переменные окружения? В конфиге, чтобы подгружать их? А что если кто-то получит доступ к конфигу?

Тут я немного начал параноить (видимо сказывается профдеформация) и начал думать в сторону всяких Vault, шифрованных разделов, аппаратных ключей, начал смотреть про их уязвимости… Решил даже спросить совета у хорошего знакомого, который лучше разбирается в ИБ, чем я. Рассказал ему о проблеме. И тут он спрашивает про мой скрипт:

А зачем ему энтерпрайз левел секурити?)

… подумал я и впихнул чтение через keyring. Не plain-text, ничего разворачивать не надо, даже ставить не надо. Для моих целей действительно сойдет.

Заключение

Я надеюсь, что мне удалось на примере еще раз показать, что оценка времени на работу — непростое занятие, что для программиста иметь кругозор — хорошо, и что надо знать, когда стоит остановиться. Скрипт разросся до 50 строк, из которых полезны только 10, остальное — парсинг параметров, импорты да отступы. А пулл-реквест в итоге приняли на следующий день после небольшой дискуссии.

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

Опрос: оправдана ли трата нескольких часов на этот скрипт?

  • Да
  • Зависит, от ситуации, но скорее да
  • Зависит, от ситуации, но скорее нет
  • Точно нет
СсылкаКомментировать

Программист — существо социальное

Работа в команде

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

Взаимодействие с другими отделами

Частая причина “войн” отделов — непонимание. И это замкнутый круг: из-за непонимания возникают проблемы, ответственность сваливают друг на друга, и оборот вражды делает ещё один круг. Чтобы разорвать порочный круг, надо просто сесть и поговорить, наладить общение (даже если ненавидите этих уродов людей), понять, какие у них трудности и в чем их проблемы. “Возлюби ближнего своего”. Можно разработать сотни регламентов, но если вас на том конце будут ненавидеть, то и качество будет “на отвали” (итальянская забастовка).

Развитие себя и своей команды

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

Лучшие практики из мировой истории

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

Прокачка навыков тимлида

Обратите внимание на тимлидские конференции. Про что там доклады? Про общение, налаживание связей, софт-скиллы, онбординг, мотивацию, выгорание и не только. Встречаются выступления про процессы, «залётные» доклады про декомпозицию задач и планирование, но самые интересные всегда про людей. Характер людей менять тяжело, их понять-то не всегда просто, а без этого никак. По этому поводу, кроме докладов с конференций (например, TeamleadConf), не лишним будет прочитать книги Дж. Ханка Рейнвотера «Как пасти котов. Наставление для программистов, руководящих другими программистами» и Марка Гоулстона «Как разговаривать с мудаками. Что делать с неадекватными и невыносимыми людьми в вашей жизни». Первая довольно легкая, вторая более фундаментальна, но не забывайте про разумный скепсис.

Будь позитивен

Ссылка

Лучшие практики и с чем их едят

Стоит понимать, что общие принципы фундаментальны и особо не зависят не то что от языка, но даже и от парадигмы программирования. На этих принципах уже строятся более конкретные вещи. И по мере того, как программист становится матерым, он из этих фундаментальных принципов может выводить уже прикладные решения. Поэтому всякие трюки “специально для Java” или “специально для Python”, конечно, полезны тем, что дают вам знание на уровне “а что, так можно было?” или “о, так это так работает”, но лучше все-таки более системно подходить к этому вопросу. Языки меняются, «кишки» — тоже дело переходящее, фундаментальные принципы — редко.

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

Через призму такого зрения возникнет вопрос: а как писать хорошо на новом языке программирования и понять его философию? Самый простой ответ — посмотреть существующий код из плюс-минус доверенных источников, желательно, разумеется, от создателей языка. Но большинство мейнстримовых языков сейчас мультипарадигменные и довольно мощные, поэтому в одном языке можно написать одни и те же вещи очень по-разному, и, опять же, на это могут сильно влиять окружение проекта, его цели и т. п., например, код на Kotlin для Android, для Enterprise-бэкэнда, для IOS и для фронта могут выглядеть настолько по-разному, что как будто написаны на разных языках. И “лучшая практика” для IOS-кода может быть тем еще костылем в мире энтерпрайза и наоборот. Поэтому надо затачивать практики под свое окружение и проверять их, а не делать из них культ карго.

Стоит еще вопрос задать — а зачем вам практики нужны? Что конкретно плохо в текущем коде? Как бы вы это решили в хорошо знакомом языке? Можно ли так же решить в этом? Да, возможно, неидиоматично получится, но почему это вас должно волновать? Код решает конкретную проблему, к нему есть требования по качеству. Идиоматичность — приятно, но совсем не главное. Узкие вещи по языку лучше изучать планомерно и глубоко, а не на основе коротышей с поверхностными трюками.

Ссылка

Teamlead: постановка задач

Исполнитель должен понимать контекст задачи

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

Надо хорошо понимать исполнителя

Если вы в одном контексте, и исполнитель хорошо знает тему, то и описание ему можно дать достаточно короткое. “Петрович, дай ту фиговину, чтобы я ее прихреначил на эту приблуду”. Петрович в контексте, он и так знает, что надо без 1000 слов. А если вместо Петровича придет стереотипная Софочка, то ей придется долго рассказывать про фиговины, хреначение и приблуды. А если подавать фиговины больше некому - что ж, лучше потратить время на то, чтобы Софочку ввести в контекст, писать первое время подробно все и разжевывать, а потом постепенно уменьшать уровень подробностей. В идеале Софочка превратиться в Петровну и без проблем сможет подавать даже хреновины для прифигачивания, а не только фиговины для прихреначивания. Чем опытнее исполнитель, тем больше свободы ему стоит давать (и меньше лишних подробностей).

Еще один аспект про понимание исполнителя

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

Адекватно декомпозируйте задачи

Зависит от многих вещей, но в среднем по больнице задача дольше дня уже должна быть декомпозирована. Не обязательно отдельным тикетом, в формате TODO-шек в тикете и/или чеклистов тоже сойдет, зависит от вашего workflow. И декомпозировать может не только руководитель, но и исполнитель. Ежедневные agile-митинги примерно про это - что сделал, какие проблемы и т.п. (но они не панацея, и не всегда нужны). Это нужно, чтобы держать руку на пульсе и перенаправить исполнителя в нужное русло. Иначе получится, что человек неделю варился в собственном соку и на выходе совсем не то. Если будет привита практика регулярного отписывания по тикетам - то там и варианты решения можно обсудить на ранних стадиях.

Исполнитель должен знать неявные условия задачи

Окружение, проект и его цели, стек технологий, сложившиеся практики, стиль кода и т.п.. Чтобы это все было как данность и незамусоривало сам тикет. Отпадут проблемы когда весь стек на java, а чувак написал сервис на rust просто потому что (“ну а че, в тикете написано же не было”). Как вы определяете, что “сделано не то”?

Определите, по каким критериям будет приниматься задача. Знает ли их исполнитель? А он как раз должен. Определите, что критично - напишите в тикет это. Подумайте о definition of ready (DOR) и definition of done (DOD) (а если не знаете, что это такое - стоит почитать, например, тут).

И помните, что garbage in => garbage out.

Ссылка