DevFM
2.36K subscribers
80 photos
3 videos
469 links
О разработке: технологии, инструменты, system design, процессы, команды

Для связи @sa_bul
Download Telegram
— Без требований программирование представляет собой искусство добавления багов в пустой текстовый файл
— Тесты позволяют улучшать API
— Наличие "и" в описании функции — это плохо
— Магическое число 7
— Важность умения запускать код без IDE
— Мой любимый git add -p

Полезные и не очень советы в статье Чему я научился на своём горьком опыте (за 30 лет в разработке ПО). Какие-то пункты устарели, какие-то не универсальны, с какими-то я не согласен.

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

Кстати, пример с getUserMessage(userId, true) в питоне решается именованным параметром getUserMessage(userId, retrieveFullMessage=true)
#procode
Месяц назад мы обсуждали, что можно сделать с неработающим кодом. Два дня назад своё видение дебага и отладки раскрыл канал Диджитализируй в ролике Кладём баги на лопатки (24 минуты). Он касается следующих тем:
1. локализация проблемы
2. изучение проблемного участка с помощью отладчика или логгирования. Рассматриваете пример логгирования endpoint-а вебсервера
3. тезис "не доверять ни одному фрагменту кода"
4. бан фразы "у меня всё работает"
5. рассуждения о коде как структуре

На разобранном примере кода с добавлением логгинга в связи с нехваткой времени куча недоработок:
— можно настроить, чтобы имя функции само выводилось в логгере, а не вписывать руками
— непонятно, где какие уровни логгера ставить. У него везде debug
— начиная с python 3.8, в f-строках можно писать f"{var=}" вместо f"var = {var}", тогда будет выведено var=значение

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

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

Код быстро становится "чужим". Вернувшись в старый проект, часто только по git blame можно понять, кто писал тот или иной фрагмент. А без readme совершенно невозможно вспомнить, как это всё великолепие запустить.

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

По мотивам беседы с Умпутуном в чате подкаста radio-T

#procode #devfm
Нормальный ли у меня код?

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

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

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

Быстрый по скорости и компактный по данным. Другими словами, код должен быть нормальной вычислительной и пространственной сложности. Тут помогают и интуитивные представления (что-то тормозит), и теория вычислительной сложности (О-нотация). Если вы сортируете записи за O(n^3) и требуете O(n^5) оперативной памяти, то вы делаете что-то не так.

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

Если, конечно, не горят сроки

#procode #devfm
Интеграционные и юнит-тесты — основа стабильности проекта

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

Не забыл автор и про плохие тесты (антипаттерн 5). Тестировать нужно спецификацию, а не реализацию.

Антипаттерн 10 перекликается с трактом баг — задача — ветка — merge request. Если кратко, это работает так. Обнаружили баг — создали задачу (тикет) — создали ветку к этой задаче — реализовали тест, который воспроизводит проблему (и падает, так как проект выдаёт неверное поведение) — починили баг — создали merge request (или pull request в случае github).
#procode
Навигация по каналу

#sudo — наиболее важные посты. Начать знакомство с каналом рекомендуем с них.
#devfm — материалы собственного производства. Не просто аннотации, а наши мысли, статьи и видеоролики.

#python — фокусируемся на самом языке и его библиотеках.
#codereview — разбираем код, находим и устраняем проблемы, превращаем плохой код в хороший.
#procode — о профессиональной разработке и тестировании вне зависимости от языка.
#skills — о смежных с разработкой технических навыках, необходимых для работы и резюме. Инструменты (в том числе git, bash, docker), командная работа, безопасность и прочие фундаментальные вещи.
#systemdesign — проектирование систем и построение архитектуры.
#tools — полезные инструменты для работы.
#edu — полезные нетехнические навыки. Об обучении, продуктивности, английском, умении искать и обосновывать решения.
#youtube — видеоматериалы.
#fun — пятничное развлекательное и культурный код. Обзор художественных фильмов #films, книг #books, комиксов #xkcd и прочего.

#backup — лучшие посты месяца.
Media is too big
VIEW IN TELEGRAM
Sublime Merge — графический git-клиент

Как мы писали раньше, 85% разработчиков работают с git из консоли.

Но для сложного merge c конфликтами рекомендуем использовать sublime merge. На видео демонстрируем, как sublime merge представляет состояние разных веток и позволяет в один клик выбирать нужный код для слияния.

Также sublime merge может быть полезен тем, кто только начинает осваивать git. Он дает наглядное представление об устройстве репозитория и взаимосвязях между ветками.
#procode
Магия CORS

При разработке веб-приложения в консоли браузера можно увидеть не очень информативную ошибку:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ..

В результате беглого гугления глаза разбегаются от количества разных объяснений и костылей для фикса. И часто решение сводится к "забил и поставил хедер Access-Control-Allow-Origin: *".

К сожалению, нельзя дать простое и быстрое решение этой проблемы. Мы рекомендуем статью Deep dive in CORS (перевод), где подробно, с картинками излагается история и причины возникновения CORS, где и как они применяются, и почему решение выше — плохое. В конце статьи приводятся практические советы по настройке CORS.
#procode
Git исход

Удивительные факты:
— при разработке Linux системы контроля версий (СКВ) уже существовали*, но долгое время не использовались. Все изменения приходили по email в виде набора патчей Линусу.
— BitKeeper стал первой СКВ, которую начали использовать при разработке Linux. Ирония в том, что для символа open source использовали СКВ с закрытым исходным кодом и очень ограничивающей лицензией.

А git так возможно и не появился бы, если не одно НО. Об этом можно почитать в захватывающей статье со скучным названием A Git Origin Story.

*Linux был опубликован в 1991. Первая СКВ была создана в 1982 году — RCS. В 2000 появился BitKeeper.
#procode
Технический долг

В статье Мартина Фаулера TechnicalDebt (перевод) описана проблема технического долга. Когда быстро сделал костыль здесь и сейчас для решения задачи, то есть словно взял кредит. Через полгода из-за этого решения потратил время на поиск бага — выплатил долг по кредиту.

Будьте внимательны! Нередко написать правильно и без технического долга по времени занимает столько же, сколько написать неправильно. Например, начинающие разработчики часто полагают, что юнит-тесты отнимают у них время, не осознавая, сколько потом времени тратится на отладку.
#procode
Pre-commit — must have утилита любого проекта

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

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

Для решения всех обозначенных проблем есть замечательная утилита — pre-commit. Один раз в конфиге прописываете, какие анализаторы кода нужно запускать, и далее при любом коммите они будут запускаться автоматически. С этого момента код будет опрятным и шелковистым. Вы просто не сможете сделать коммит, если у анализатора есть вопросики к коду.
#devfm #procode
Делаем код мягким и шелковистым

Мы уже говорили об утилите pre-commit, которая автоматизирует рутинный запуск анализаторов кода и не позволяет сделать коммит, пока проблемы не будут исправлены.

Теперь расскажем о тех утилитах, которые применяются в каждом нашем проекте:
flake8 — статический анализатор кода с поддержкой очень большого количества плагинов
black форматирует и приводит код в общему виду
mypy проверяет аннотации типов
reorder-python-imports единообразно организует импорты
autoflake удаляет неиспользуемые импорты и переменные
pyupgrade обновляет синтаксис до текущей версии python
yesqa удаляет ненужные #noqa

Применение всех утилит с настройками по умолчанию скорее вредно, поэтому вот несколько советов:
— настройте в pre-commit опцию exclude — список каталогов, для которых не применять анализатор
— в flake8 настройте игнорирование особо душных замечаний
— в autoflake настройте автоматическое исправление замечаний
— при использовании mypy совместно с pre-commit нужно пользоваться специальной версией

Применение перечисленных утилит в командной работе облегчит проведение code review. Никто не будет тратить время на то, что может поправить машина.

Расскажите, если на практике используете другие анализаторы кода.
#devfm #procode
Зачем нужны юнит-тесты

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

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

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

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

Занятная рабочая история про пользу юнит-тестов — в канале Борис опять. Рекомендуем.

Полезно вспомнить про антипаттерны тестирования ПО.
#devfm #procode
Доработать нельзя переписать

Где поставить запятую в заголовке?

Стратегия "Доработать нельзя, переписать" свойственна молодым и неопытным разработчикам. Проще выкинуть старый код и написать новый, хороший и правильный.

Более опытные разработчики скажут "Доработать, нельзя переписать". Они знают цену кода, который уже кто-то написал и отладил. Предыдущий автор уже через боль и страдания создал работающее решение. Костыли в этом коде появились не просто так.

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

В классической статье Грабли, на которые не стоит наступать (оригинал) Джоел Спольски рассуждает о вопросе выше. Он вспоминает о браузере Netscape, который умер в результате переписывания. Одной из причин желания всё переписать Джоел видит сложность чтения кода. С этим тяжело не согласиться. Остальные детали в статье.

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

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

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

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

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

Теперь о ридми. У нас на проектах ридми состоит из следующих блоков:
— Вводная часть, где тезисно указываем общее назначение сервиса. Если у сервиса чётко выраженное назначение, то описываем общий алгоритм работы.

— Установка и запуск. В этой части указываем набор команд, которые необходимо выполнить, чтобы запустить проект.
Подробность описания должна быть такая, чтобы при первом знакомстве с проектом разработчик скопировал из ридми команды и без матерных выражений запустил проект.

И, конечно, мы всё запускаем в докере. О важности докера у нас есть отдельный пост.

В этом же разделе также важно описать, как запускать тесты. Очень важно.

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

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

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

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

И ещё мысль по поводу документации:
Считаем вредным советом или требованием писать документацию в каких-то сторонних системах, таких как конфлюенс (да, порой и такое требуют). Практика показала, что поддержание документации в актуальном виде в стороннем сервисе, мягко говоря, не работает. Очень сложно убедить и даже заставить разработчика поддерживать доку где-то в сторонней приблуде. С ридми проще — можно писать, не отходя от кассы. И контролировать легче, проверяя изменение доки в merge request.
#devfm #procode
ООП на простых примерах

В 40-минутном видео аккуратно объясняют три кита объектно-ориентированного программирования. Примеры даны на TypeScript, но понятны любому разработчику. Автор аккуратно иллюстрирует необходимость ООП, рассказывает про инкапсуляцию, наследование и полиморфизм. Покрыты даже относительно сложные вещи вроде параметрического и ad-hoc полиморфизма.

На трёх китах ООП автор не заканчивает. Вторая половина ролика повествует о композиции и агрегации на примере автомобиля с двигателем и колёсами, об абстрактных классах и интерфейсах, и даже немного о дженериках. Завершает изложение реализация паттернов Dependency Injection и Singleton. При этом Singleton во многих случаях является антипаттерном и мы не рекомендуем его применять.

Обратите внимание, как автор умело использует IDE для автогенерации сеттеров и геттеров. Не забывайте, что IDE — ваш добрый друг, который много чего умеет.

Про нюансы getattr и setattr в питоне мы делали отдельные посты.

#sudo #youtube #procode
Делай нейминг как сеньор

Все мы знаем, что нужно правильно и понятно именовать переменные. Правильно, понятно – а это как?

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

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

Неправильно именование приводит к неправильному пониманию, а дальше – к некорректному использованию.

Чтобы детальнее разобраться, автор выделяет несколько распространенных проблем: слишком общие название, избыточные названия, названия без контекста, названия с некорректным переводом или калькой со своего языка, абстрактные названия. Каждая из проблем сопровождается примерами.

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

Заканчивается статья набором конкретных практических советов, которые можно брать и применять.

#procode
Вариантность типов

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

Простой пример: тип Natural является подтипом Integer и Positive. И все трое одновременно являются подтипами Real. А тип Prime является подтипом всех вышеперечисленных.

Есть у нас функция, которая в качестве параметра принимает на вход определённый тип данных. А можем ли мы передать в качестве параметра подтип или надтип исходного типа данных? За это как раз отвечает вариантность типов.

Контравариантность, ковариантность, инвариантность — в статье все эти замечательные термины рассматриваются на конкретных понятных примерах.

Также прочтение статьи позволит более глубокого понять принцип подстановки Барбары Лисков (LSP), который фигурирует в известной аббревиатуре soLid.

В конце рассматривается реализация вариантности в различных языках программирования –TypeScript, C#, Java, C++.

А на тему вариантности в python у Диджи было замечательное видео.

#procode
Приоритизация технического долга

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

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

— Если мы ничего не будем предпринимать, усугубится ли проблема, или всё в целом останется на прежнем уровне, или вообще всё будет двигаться в сторону улучшения?

— Проблема относится к той части системы, которая активно развивается и используется или находится просто на обслуживании?

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

#procode
Асинхронное взаимодействие сервисов с применением Kafka

Практическая статья, демонстрирующая, как организовать асинхронное взаимодействие сервисов на Python с использованием Kafka. Автор коротенько даёт вводные, описывает архитектуру и переходит к делу.

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

#python #procode