jsunderhood Profile picture
Apr 28, 2021 70 tweets 14 min read Read on X
А сегодня поговорим о том, как сделать код читаемым и тестируемым ^_^

Расскажите о своих приёмах, как вы улучшаете кодовую базу на проектах? Какие применяете методы, принципы, эвристики?

Я пока начну 🧶
Самое простое (и одновременно сложное 😃) — это нейминг.

Хорошие и внятные имена для переменных и функций — это очень мощный инструмент.

(Первая книга на тему, которую я прочёл — это «Читаемый код» Фаучера:
bespoyasov.ru/blog/the-art-o… )
Хорошее имя для сущности: короткое, но полное и описательное.

На Гитхабе есть классный чеклист по неймингу сущностей:
github.com/kettanaito/nam…
Я люблю проверять имена всех экспортируемых сущностей на понятность со стороны пользователя.

«Если я буду импортировать эту функцию из модуля, я пойму, что она делает? какова область её ответственности? как её использовать?»
Например, внутри модуля пользователя функция `create` выглядит органично, не дублирует контекст, короткая, описывает действие: Image
Но если функцию импортировать и начать использовать, то уже не так очевидно: Image
А вот тут — наоборот: Image
Годный шаблон для названий можно вот тут посмотреть:
github.com/kettanaito/nam…
Шаблон A/HC/LC:
prefix? + action (A) + high context (HC) + low context? (LC)

В идеале по названию переменной должно быть понятно, функция это, булево значение или что-то ещё. Image
Для булевых значений можно использовать префиксы:
should, is, has, will, did.

Для функций — первым словом лучше поставить глагол действия:
get, set, update, remove, delete, push…
Иногда (редко) от шаблона можно отойти, если двусмысленность получается исключить:

`let mounted = false` — тут сложно подумать, что mounted что-то кроме boolean.
Моё любимое — не используйте аббревиатуры, пожалуйста 😃


(А если используете, обязательно документируйте.)
Всё настраиваемое лучше сразу выносить в конфиги. Пусть даже по началу это будет объект в том же файле.

Это и читаемость улучшит, и тестируемость повысит (о тестах дальше).
Используйте паттерны проектирования.

Есть замечательная подборка паттернов на том же Гитхабе, очень советую посмотреть:
github.com/kamranahmedse/…
Как правило, большую часть наших задач уже решили за нас до нас 😃

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

Мы с twitter.com/dex_157 в нашей книжке о принципах SOLID добавляли разделы с паттернами под каждый принцип.

ota-solid.vercel.app
Чаще рефакторите код, но без фанатизма ¯\_(ツ)_/¯
Писать код и рефакторить — это как «писать» и «редактировать статью», сложно делать одновременно.

Если кусок кода попался большой, стоит дать ему «отлежаться». Когда вы начнёте работать с ним на свежую голову, то будет гораздо проще начать думать о коде с точки зрения читателя.
А ещё (самое сложное для меня) надо купировать перфекционизм 😅

Отрефакторить до идеала сложно, а чаще всего не нужно.

Пользуемся правилом 20/80 — 20% усилий должны приносить 80% результата.

ru.wikipedia.org/wiki/Закон_Пар…
Чтобы рефакторить безопасно, пишите тесты.

Физерс в «Эффективной работе с легаси» писал о том, как можно рефакторить старый неповоротливый комбайн:

bespoyasov.ru/blog/working-e…
Он предлагает искать швы — места, в которых можно относительно безопасно «распилить» комбайн на части.

Покрыть швы тестами, а уже потом начинать рефакторинг.

Я пробовал, это и правда работает.
Как найти хороший шов?

Обычно шов — это место, где мы можем заменить одно поведение другим: месте соединения модулей.

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

Типа, как заменить зависимость:
- одну зависимость за раз;
- определить, какую зависимость хотим поменять;
- покрыть шов тестами;
- вынести текущий код в отдельный класс;
- заменить класс на другой.
Раз уж мы о книгах заговорили, то ещё «Чистый код» Мартина могу посоветовать:
bespoyasov.ru/blog/clean-cod…

Хотя и считаю, что «Эффективная работа с легаси» шире, глубже и практичнее ¯\_(ツ)_/¯
bespoyasov.ru/blog/working-e…
Ещё офигенная и практичная книга — “Debug it!” («Отдебажь это!», простите за кустарный перевод):
bespoyasov.ru/blog/debug-it/

Она вся состоит из рецептов, как работать с багами из-за непонятного кода.
Там даже содержание — это уже рецепт! 😃

Глава 1 — Исследовать обстановку
Глава 2 — Воспроизвести проблему
3 — Определить причину
4 — Исправить
5 — Как не допустить такой ошибки в будущем
...и т. д.
Внутри каждой главы есть списки действий под ситуацию. Короче, рекомендую.
От читаемости к тестированию! 🦸
Используйте TDD 🙂
TDD мне экономит кучу времени. К нему надо привыкнуть, потому что сперва приходится «вывернуть мозги», но он быстро окупается.
С ним:
- Исчезает проблема «дополнительной работы»
- Писать тесты и рефакторить входит в привычку
- Рефакторить безопаснее
- Видно сущности, делающие слишком много
- API проектируется до реализации, и-за чего становится удобнее
Я недавно делал доклад о TDD на Frontend-crew:
-
- bespoyasov.ru/talks/podlodka…

Там рассказываю об этом подробнее:
- как внедрить на проекте;
- как использовать;
- как сделать тесты проще.
Как упростить тесты при работе по TDD:

- Чаще использовать чистые функции
- Обращать внимание на зацепление кода
- Тестировать только свой код
- Использовать удобные инструменты
- Потратить время на удобную инфраструктуру
С TDD можно искать пахнущий код. Код пахнет, если:

- Тестов слишком много по сравнению с другими модулями
- Описание ссылается на несвязанные вещи
- Ожидание от теста оформлено невнятно
- Подготовка теста слишком сложная
- Тест проверяет детали реализации
- Тест всегда зелёный
Там же я рассказываю, как помочь увидеть пользу от TDD (и тестов вообще) руководству.

Коротко: говорим только об измеряемых параметрах, проводим исследования, сравниваем.
Я ещё когда-то написал книжку с примером разработки крестиков-ноликов: bespoyasov.ru/ttt-tdd/

Там показываю, как переходить по циклу TDD, в какой момент приступать к рефакторингу, на что обращать внимание.
TDD можно использовать и при работе с React тоже.

Недавно я проводил воркшоп об использовании TDD при разработке React-приложений:
bespoyasov.ru/talks/?full#1

Он длинный, около 5 часов, но там я прохожусь по всем основным концепциям, а именно, как тестировать:
- ...Функций бизнес-логики.
- Функции, возвращающие случайные значения.
- Простые компоненты.
- Кастомные хуки, их связь с компонентами.
- Работу со стором.
- Асинхронные функции и вызовы API.
- Пользовательские действия: клик, клавиатура.
Так-с, пора работать!
Продолжим во время обеда 😃
Продолжим!

Чем же так хорош TDD для тестируемости и читаемости?

Он сразу поставит нас в ситуацию, когда сперва придётся думать о тестируемости. Писать код, который будет неудобно тестировать, по TDD — очень сложно 😃
Напомню стандартный цикл разработки по TDD. В нём 3 этапа:

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

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

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

Рефакторить код становится безопасно: если мы что-то по пути поломаем, мы узнаем об этом мгновенно по покрасневшим тестам.
А ещё TDD — это единственный способ безопасно (или даже вообще хоть как-то) отрефакторить легаси 😃
Кроме TDD тестируемость улучшит Dependency Injection.

Вместо того, чтобы мокать всё подряд, можно использовать DI, и подмешивать во время тестов нужные зависимости.
DI — это не обязательно контейнеры и всё такое страшное, можно использовать кустарный DI через объект с зависимостями в конце.

Об этом я тоже недавно писал пост:
bespoyasov.ru/blog/di-ts-in-…
Ещё, кстати, в TypeScript я стараюсь описывать тип-аргументы в дженериках не одной буквой, а нормальными названиями, когда это имеет смысл 😅

Мне нравится руководство по тип-аргументам в C#:
docs.microsoft.com/en-us/dotnet/c…

(Если в проекте не принято иначе, конечно.)
Часть треда снова ускакала не туда, простите :–/
Дальше, чтобы код был понятнее, его должно быть как можно меньше 😃

Всё, что можно может сделать браузер, лучше отдать ему — он сделает это лучше, оптимальнее и быстрее.
Нужно сериализовать форму?
Используем FormData:
developer.mozilla.org/en-US/docs/Web…

Там есть конечно трудности со всякими кастомными контролами, но процентов 80 случаев можно покрыть только ей.
Нужно сделать ленивую загрузку картинок?
Испольуем `loading="lazy"`:
developer.mozilla.org/en-US/docs/Web…

Опять же, полифилим только для тех, у кого это не работает и только если надо 🙂
Ну вы поняли 😃
Кнопки — кнопками, короче.
Непонятное стороннее API лучше прятать за фасадом, чтобы намерение было выразительнее:
github.com/kamranahmedse/…
Если язык позволяет, то для выбора из нескольких вариантов используйте pattern matching:
- docs.microsoft.com/en-us/dotnet/c…

(Это ещё и безопаснее в некоторых случаях.)

Для TS тоже есть реализации!
- github.com/nrdlab/pattern…
Снова перерыв на работу 🙂
Вечером продолжим!
Продолжим!

Ещё один приём, который улучшает читаемость — CQS, command-query separation.

- bespoyasov.ru/blog/commands-…
Запрос — функция, которая возвращает результат и не имеет сайд-эффектов.

Команда — функция, которая меняет состояние системы и ничего не возвращает.
На чтение такого кода уходит меньше сил, потому что ожидания при чтении оправдываются, и мы можем строить прогнозы относительно функций, с которыми работаем.
Ещё один приём рефакторинга, который улучшает читаемость — вынесение деталей в метод или функцию:
- bespoyasov.ru/blog/missing-a…

Это чем-то похоже на фасад, но над своим кодом:
- github.com/kamranahmedse/… ImageImage
В этом случае мы как бы прячем детали за названием функции.

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

Детали мы будем смотреть уже отдельно, если потребуется. А смысла функции отражён в названии.
Ещё одна важная часть читаемого кода — это ошибки и стек-трейс.

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

- bespoyasov.ru/blog/make-erro…
Чем чище и понятнее ошибки, тем проще дебажить код ¯\_(ツ)_/¯
Кстати, вот мы говорили про вынесение кода в функцию или метод.

Можно делать это не руками, а встроенными инструментами рефакторинга в IDE.

Например, вот для VS Code инструкция:
- code.visualstudio.com/docs/editor/re…
Ну и конечно — документация!

В ней нам стоит описывать не «как оно работает», а «почему оно работает именно так».

Ответ на вопрос «почему?» — это важный кусок контекста задачи, который может потеряться. Лучше его зафиксировать в документации.
А на вопрос «как оно работает», считаю, должны отвечать тесты 🙂
Подведём итоги за сегодня 🙂

- Хорошая читаемость снижает когнитивную нагрузку при чтении кода.
- Паттерны проектирования, эвристики и рефакторинг помогают улучшить читаемость.
- Тесты помогают рефакторить безопасно.
- Чтобы рефакторить легаси, удобно использовать швы.
- ...
- ...Грамотная архитектура улучшает тестируемость.
- TDD также улучшает тестируемость и помогает разбивать легаси на модули.
- DI — не обязательно контейнеры, можно проще.
- React тормозит на глубоких деревьях 😅
Завтра поговорим о том, как расти в разработке. Обсудим, что лучше:

- учиться в университете,
- окунуться в боевую разработку,
- найти ментора,
- читать книги,
- участвовать в опен-сорсе.

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with jsunderhood

jsunderhood Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @jsunderhood

Jun 29, 2021
Прочитала с грустью и без удивления, к сожалению. В айти как то слишком часто стали такие «практики» распространяться.
Если говорить о тестовых. Я против них, особенно до технического собеса. Ты тратишь свои силы, время, ресурсы и твоя работа может просто быть выброшена и не оценена. Собственно как было выше
Я предпочитаю лайв кодинг. Это хороший и рабочий способ провести собес.
За все время я написала одно тестовое и то в страхе, что меня просто никуда не возьмут. Это была первая работа.
Read 5 tweets
Jun 29, 2021
Всем доброе утро!
Ночью решила, что хочу и этим с вами поделиться 🙌🏻 если по ходу чтения есть вопросы, смело задавайте, возможно я что-то полезное и интересное для вас опускаю
Сказ о том, как я имела три работы, а потом три работы имели меня 👇🏻
Если коротко: херовая идея конечно
Предыстория. Все детство пока девочки играли в куклы, я играла в школу и жить без этого не могла. Поэтому с первого курса бакалавриата я стала репетитором по математике. Вообще вышло это случайно и я не планировала во время учебы на мехе чем то заниматься, кроме учебы
Read 24 tweets
Jun 28, 2021
Если говорить о специфике работы, то для себя я поняла, что с большей долей вероятности я не пойду делать бек. Я люблю UI, люблю верстать, люблю думать над клиентской логикой.
На первой работе у меня были достаточно сложные и глубокие вещи, просто так в которые не въедешь. Я тратила все ресурсы и силы, чтобы родить что-то годное.
Сейчас же я занимаюсь социальной частью сайта. Я делаю очень много клиентской логики и это мне доставляет.
Read 10 tweets
Jun 28, 2021
Ну как и положено, начну я с рассказа почему я стала программистом
Ещё в 10 классе я поняла, что мне интересна математика и мой очень близкий друг/одноклассник/одногруппник/однопартник предложил пойти с ним в мат школу при мехмате. (Никита, если ты это читаешь, я держусь, чтобы не тегать тебя!)
Ну я короче прониклась в то, как мы решали примеры 1 курса и перестала себя видеть в чём-то другом, кроме программирования и математики.
Read 12 tweets
Jun 28, 2021
Ну вот и я дорвалась в этот аккаунт на недельку! Будем знакомиться 🙂
Меня зовут Татьяна Гулякина, я – фронтенд разработчик вот тут @tradingview и почти магистр комп.наук вот тут @mmcs_sfedu. Мне 22 года и живу я в городе Ростове-на-Дону 🙋‍♀️
Правда какого-то конкретного плана на неделю у меня нет, есть только пара заметок на интересные темы, некоторые из которых подкинули мне мои друзья для обсуждения здесь
Read 5 tweets
Jun 20, 2021
Ну, что ж, моя неделя подошла к концу, было интересно! Спасибо всем за вопросы и обсуждения.
Вот что успели обсудить:
— про меня
— преподавательские мудрости
— чего не знают мидлы
— про сишарперов и фронтенд
— про архитектуру
Холивар про реактивность в двух частях: и
Холивар про чистые функции
— Про людей, не знающих «фундаментальные вещи»
– Про кругозор
— Чистый код
— Новые стеки
— Ошибки саморазвития
— Соревнования
Read 5 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(