Благодаря которым вы станете лучше как программист
Привычки, относящиеся к написанию кода, есть у каждого разработчика — и вредные, и полезные. Но если завести правильные привычки, это поможет серьезно повысить эффективность работы с кодом. Такие привычки повлияют не только на вас, но, скорее всего, и на ваших коллег.
Как сказал Денис Уэйтли, «нельзя избавиться от вредной привычки, но можно заменить ее полезной». Поэтому я предлагаю вам список из семи привычек, которые, как мне кажется, помогут вам стать лучше как программист.
Переведено в Alconost
1. Не повторяйтесь
Наверняка у вас не однажды бывало такое: вы смотрите на кусок кода и видите, что он очень похож на фрагмент, который вы уже когда-то писали.
Это — плохая примета: повторения следует избежать. Дублирование кода — плохой тон, ведь такой код со временем становится всё труднее поддерживать, так как исправления приходится вносить в нескольких местах. В итоге повышается вероятность того, что в код закрадутся ошибки.
Хорошим тоном считается следовать принципу «НЕ ПОВТОРЯЙСЯ», или DRY (англ. «Don’t repeat yourself»): если вы начинаете писать код, который уже есть в другой части системы, то, скорее всего, нужно провести рефакторинг. Разделите код и логику на более мелкие повторно используемые блоки и вызывайте их там, где нужно.
2. Написал — отрефакторь
Большинство программистов, особенно те, у кого опыта немного, думают, что если поставленная задача выполнена, это значит, что код работает так, как задумано. Но чтобы «выполнить» задачу, недостаточно просто набросать код, реализующий новую функцию.
Но если код работает, чего ещё нужно?
Ну да, работает. Но прежде чем переходить к следующей задаче, нужно провести рефакторинг: это улучшит читабельность кода. Ведь наверняка этот фрагмент — не самый аккуратный и очевидный из написанных вами: что-то может быть с ходу понятно вам, но как этот код будет видеть другие? Будьте критичны к своему творению.
Кроме того, рефакторинг помогает снизить сложность кода, что упрощает сопровождение. И в перспективе это окупится.
3. Сосредоточьтесь на задачах бизнеса
Разработчики, как правило, настолько погружены в изучение технологического стека, что упускают из виду задачи бизнеса — однако о них забывать нельзя. Вспомните, какая цель у разрабатываемого вами проекта?
Некоторые разработчики интересуются лишь техническими аспектами своей работы. Их не волнуют деловые и экономические факторы, наличие которых, по сути, и дает им работу.
На что уходит ваше рабочее время? Прино?сите ли вы пользу бизнесу — или слишком много времени тратите на то, что по факту не очень-то и важно? Задавайте себе этот вопрос почаще.
4. Небольшие коммиты
Если охват у коммита небольшой, это позволяет дать ему внятное описание. Все же понимают, что «исправлено пару ошибок» — это плохое описание, да?
Небольшие коммиты упрощают отладку: можно откатиться до предыдущего коммита и проверить, была ли ошибка в нем. А если в коммите кода немного, то и ошибку искать будет легче.
Если же коммиты большие, следствием могут быть различные неприятности и неудобства. Если изменения в одном коммите затрагивают много всего, то становится непонятно, что конкретно изменилось.
А ведь кому-то придется делать проверку этого кода, и ему (или ей) будет страшновато делать слияние, потому что слишком многое в коммите может потенциально нарушить работу остального кода.
Поэтому в случае небольших коммитов проверка кода существенно упрощается: проверяя одно изменение за раз, легко разобраться, что изменилось фактически.
5. Главное — единообразие
Если вы решили именовать переменные в Верблюжьем Стиле, не отступайте от этого правила. Хотите использовать пробелы вместо табуляции? Вперед! Но что бы вы ни выбрали, придерживайтесь этого везде.
С течением времени разобраться в написанном ПО становится всё сложнее: чем дольше существует проект и чем больше людей над ним работает, тем больше в коде беспорядка. Поэтому отсутствие единообразия порождает проблемы.
Что же предпринять, чтобы сохранить единообразие?
Во-первых, нужно выбрать определенный стиль оформления и придерживаться его. Можно использовать анализаторы кода — линтеры, — который будут проверять написанное на соответствие выбранному стилю.
Во-вторых — придерживаться единообразного именования переменных, методов и классов. Подробнее о том, как давать хорошие имена, — в моей статье.
Главное — помните: удобство сопровождения кодовой базы очень сильно зависит от единообразия кода!
6. Не оставляйте «на потом»
«Ладно, это я поправлю позже», — звучит знакомо? Все мы знаем, что такое «позже» часто превращается в «никогда». Если вы видите комментарий «TODO», это значит, что кто-то оставил что-то на неопределенное «потом».
Работайте над фрагментом кода или пользовательской историей с нуля и до полного завершения.
Но что значит «до завершения»?
В первую очередь это означает, что код прошел рефакторинг, как говорилось выше. Кроме того, код нужно протестировать. А тестирование для большинства разработчиков — пожалуй, самая нелюбимая часть работы. При этом недостаточно пройти по сценарию, включающему лишь предусмотренные действия — обязательно проверяйте и другие варианты. А можно приложить чуть больше усилий и написать парочку автоматических тестов.
И последнее — документация. Нужна ли для этой конкретной функции документация? Объяснили ли вы тестировщику, как проверять эту функцию? Нужно ли ему (или ей) знать о каких-то предварительных условиях?
7. Не переставайте учиться
Новые технологии появляются каждый день. Иногда может показаться, что успевать за всеми тенденциями невозможно. Тем не менее, нельзя прекращать изучать новое: как только вы перестанете учиться, вы перестанете расти профессионально.
Изучение нового — единственный способ оставаться актуальным на рынке труде в нашу технологическую эпоху постоянных перемен.
«Вы не будете расти, если не будете пытаться совершить что-то за пределами того, что уже знаете в совершенстве»
— Ральф Уолдо
О переводчике
Перевод статьи выполнен в Alconost.
Alconost занимается локализацией игр, приложений и сайтов на 70 языков. Переводчики-носители языка, лингвистическое тестирование, облачная платформа с API, непрерывная локализация, менеджеры проектов 24/7, любые форматы строковых ресурсов.
Мы также делаем рекламные и обучающие видеоролики — для сайтов, продающие, имиджевые, рекламные, обучающие, тизеры, эксплейнеры, трейлеры для Google Play и App Store.
> Подробнее
arkamax
В рамках предложения: 1.5. Написал, собрался рефакторить — сначала напиши тесты. Да, я все еще встречаю людей, которые рефакторят код без тестов.
JustDont
Всегда так делал и буду делать.
Когда у вас есть статические типы, тесты при рефакторинге вам по большому счету нафиг не нужны.
arkamax
Простите, не понял. Если вы рефакторите логику, разве нельзя ее сломать, при этом не поломав типов?
JustDont
Зависит от типов. Если типы достаточно выразительны — нет, нельзя. Но да, на практике такие вещи не слишком распространены; впрочем и «обычных» типов хватает для того, чтоб вынести за скобки необходимость держать в голове большую часть кода и следить за сохранностью логики в ней.
Ну и именно потому, что всё можно сломать — рефакторинг и делает программист, а не нейронка. Вне зависимости от того, есть ли у вас тесты или нет. Тесты — не гарантия того, что вы ничего не сломали. Как максимум это гарантия того, что вы не сломали тесты (да и то не всегда).
vsb
Например я использую Java и Idea. Она поддерживает очень много рефакторингов, которые практически гарантированно не меняют семантику кода. А более сложные уже привыкаешь делать составляя их из простых. В итоге рефакторинг получается достаточно безопасный и без тестов. Конечно если это тупо переписывание куска кода, это другой вопрос.
arkamax
Согласен про Idea, в моем случае PhpStorm, и тоже использую эти фичи постоянно. Я имел в виду именно переписывание кусков кода, когда нужно, к примеру, переписать логику, запросы к ORM/БД, и подобные вещи.
Finesse
А если вам на ревью пришёл PR с рефакторингом, как вы со спокойной совестью поставите Approve? В этом случае сильно помогает автоматизация, в том числе тестирования.
ViceCily
Это не рефакторинг, а переименования и перемещения. Рефакторинг — это когда значительно меняется подход к реализации какого-либо слоя логики без изменения ее функциональности.
vsb
Это у вас своё определение уже. Слово Refactoring популяризировал Мартин Фаулер и в его одноименной книге переименование это один из видов рефакторинга. Про "значительно" там ничего нет. Рефакторинг это изменение кода без изменения его функционала. Значительное оно или не значительное — не важно. https://refactoring.com/catalog/ тут даже целый каталог есть.
0xd34df00d
О, смена пришла.
JustDont
Скорее наоборот ?\_(?)_/?
chapuza
Обожаю диванных экспертов. Расскажите, как в библиотеке, которая парсит маркдаун, хоть в чем-то помогут типы.
JustDont
Сразу после того, как вы нам расскажете, как вы покроете библиотеку, парсящую маркдаун, такими тестами, чтоб можно было обосновать, что она корректно парсит любой ввод.
chapuza
В корректные контрпримеры надо уметь. Существуют наборы тестов для маркдауна, которые покрывают ожидаемый ввод, гарантируя, что когда вы добавлете экстеншн, существующие конструкции не поломались.
Типы там вообще не помогут буквально ничем.
JustDont
Конечно существуют.
Что будем делать с не ожидаемым?
Но на самом деле наверное даже не стоило влезать в общение с человеком, который ради пачки комментариев намеренно проигнорировал то, что заглавный комментарий ветки не содержит в себе повального обобщения.
GreyStrannik
Ничего. TDD требует отказа от тестирования тех сценариев, которые использоваться не будут, и покрытия тестами только тех, которые будут.
Более того, давно уже известно, что написание тестов полезно не только и не столько постоянной проверкой результата исполнения кода, сколько качеством получающейся структуры кода за счёт более глубокого продумывания при написании тестов.
JustDont
А, ну теперь-то всё понятно. Нужно просто использовать ПО только так, как это было задумано; а как не было задумано — не использовать. Мы-то, тупые, тут чё-то сокрушаемся, что код порой не работает, потому что разработчики не всё предусмотрели. А умные парни всё уже давно решили.
А знаете, что еще повышает качество структуры кода, помимо продумывания при написании тестов?
Продумывание собственно самой структуры кода перед её написанием.
0xd34df00d
Офигенно подходит для библиотек, которые потребляют хоть какой-то пользовательский ввод.
Исключительно по личному опыту: написание типов перед написанием реализаций помогает продумать структуру кода куда лучше.
Хотя, наверное, мне стоило написать «давно известно», а не «по личному опыту».
chapuza
Это не вариант.
Что там от кого требует TDD — такое же точно идолопоклонничество, как «типы нас спасут от ядерного взрыва».
Неожиданный ввод может перекладываться в аккумулятор, как есть. Может выбрасывать исключение. Может сразу возвращать пустую строку. Может возвращать разметку до позиции, в которой мы нашли что-то неожиданное.
Зависит от варианта использования.
0xd34df00d
Ну вот типы это и помогут выразить.
chapuza
Что именно выразить-то? Чувствую, в воздухе явственно запахло монадами.
Мне лично тут не нужны помощники, чтобы что-то выразить. Все выражено до меня.
0xd34df00d
Все выразить.
Как функция обрабатывает неожиданный ввод, кидает ли она экзепшоны, есть ли элемент в типе-перечислении для неожиданного ввода, и так далее.
chapuza
А почему это нельзя прямо кодом выразить? Буквально, кинуть эксепшн?
0xd34df00d
Потому что для того, чтобы работать с вашей функцией парсинга маркдауна, мне придется читать ее реализацию, а не тип.
chapuza
Ах вон оно что!
Нет, не придется. Вам придется читать документацию, в которой вы увидите и тип, и примеры использования, и как начать с ней работать.
Посмотрите, как мы в Elixir к документации подходим. Я, к примеру, функции без спецификации или документации, заворачиваю на код-ревью не глядя. Такая культура сложилась, и люди втянулись.
Ну вот, например, библиотека параллельного вычисления — даже не корка языка. У меня весь мой код такой же, и в OSS, и под NDA.
Автоматически генерируется вот в такое.
0xd34df00d
Ну да, конечно, документация, которая может устареть и не проверяется компилятором — это куда лучше, чем типы!
chapuza
Конечно. И да, доктесты придумали как раз для проверок документации компилятором.
0xd34df00d
Лишь бы не типы, они прокажённые!
chapuza
Нет, дело не в этом. Документация лучше по очень многим причинам, главная: в силу формата, она априори позволяет рассказать читателю больше, чем тесты. Документация для эрланга включает в себя типы, если автор кода не совсем тупой. А если тупой — типы тоже не спасут.
И она не может устареть; эта страшилка из девяностых — сама устарела, с введением современных правил разработки. CR проверяет и документацию тоже. С тем же упехом можно заявить, что код может устареть: ведь у нас теперь расширился список обрабатываемых вариантов для параметра
x
, а в коде это не поправили (и типы тут не помогут тоже).Типы не напишут за меня документацию, не сделают ненужными тесты, и не являются серебряной пулей с осиновым наконечником, которая спасет нас от диавола. Вот и все.
0xd34df00d
Ну, для начала они могут помочь доказать, что парсер всегда завершается и никогда не входит в бесконечный цикл. Жду проверки этого свойства тестами.
chapuza
Во-первых, мне это неочевидно. Во-вторых John Hughes для такого типа задач придумал property testing. В-третьих, один последний clause для парсящей ввод из стрима функции, который откусывает один символ если ничего не заматчилось, и перекладывает в аккумулятор, делает утверждение очевидным. В-пятых, потенциальная возможность обработки бесконечного стрима делает доказательство невозможным.
0xd34df00d
Ну, не знаю, что тут сказать. Посмотрите статью «Total parser combinators» для примера — с мобильника трудно дать ссылку.
Ну сформулируйте свойство «всегда завершается» для начала (это можно сделать, но не очень тривиально).
А, во-вторых, вот те свойства, которые вы пишете в property-based testing, вполне могут быть выражены в типах.
Про левую рекурсию не слышали?
Тем интереснее!
В языках, где бесконечные стримы выразимы, понятие завершимости заменяется более общим понятием продуктивности — что ваша функция за конечное время выдаст следующую порцию выходного стрима.
chapuza
Пункт «во-первых» там присутствовал ради последних двух пунктов.
Не могу придумать, зачем бы это мне (кроме академического интереса похвастаться коллегам).
Наверняка их можно выразить даже в сферических совах на глобусе, только зачем? Там один входной тип: поток байтов.
Зачем в этой задаче левая рекурсия? Больше баззвордов, пока все в комнате не начнут выглядеть некомпетентными болванами?
Бесконечные стримы выразимы во всех без исключения полных по Тюрингу языках, даже если они не знают, что называются красивым словом «выразимы». Любой язык умеет читать из сокета.
А вот это уже вредно для конечного продукта. Например, работа с XMPP протоколом в общем случае не удовлетворяет этому условию:
ejabberd
в прямом смысле запустит поток и будет висеть бесконечно, ожидая следующей станзы, пока клиент не отключится.Ему не надо никого оповещать о таймаутах и каноничекую продуктивность такого процесса не доказать (потому что ее нет). И ничего, мир не рухнул.
0xd34df00d
Чтобы ваш парсер, например, не зависал на каком-то входе, триггерящем редкий кусок грамматики.
Чтобы проверять их компилятором формально на всём множестве входов, а не тестами на десятке-сотне примеров.
Я никогда не парсил маркдаун, но парсил кое-какие другие языки, и на необходимость устранения левой рекурсии руками натыкался.
Во-первых, распространённые ныне зависимые типы не полны по Тьюрингу (так как завершимость там — это как раз доказуемое свойство системы типов).
Во-вторых, речь о, например, потоке данных, представленном уже в языке в виде, скажем, плюсовых итераторов или хаскелевского бесконечного списка. Просто в более продвинутых языках данные и коданные различают (последние могут быть бесконечными, первые — нет), и с коданными мы пока умеем работать очень плохо. Например, доказать это у меня не получилось даже с помощью зала.
Вы неправильно понимаете продуктивность (или я не до конца формально её выражаю). Если вкратце и не ударятся в десятки страниц матана, то нет никаких проблем с выражением серверов или repl'ов или тому подобных вещей в терминах продуктивности.
chapuza
Вообще, мне кажется, что у вас легкая форма профессиональной деформации, вызванная прыжком из языка, где можно в WORD по указателю положить достаточно короткую строку, в язык со строгими типами (еще я удивлен, почему вы все-таки предпочитаете Хаскель — Идрису, тот вроде, идеологически еще концептуальнее).
Без обид, исключительно, как повод задуматься, если вдруг увидите в моих словах рациональное зерно.
Мир просто не черно-белый. В том же эрланге есть чудесный способ получить все плюшки, не связываясь с типами (продуманный pattern matching и guards). Кому не хватает — есть статический анализатор кода, который умеет выводить типы. И хотя сам Вирдинг утверждает, что типы — это круто, и их нет в эрланге только потому, что язык изначально проектировался для hot code reload, я не думаю, что они бы что-то всерьез улучшили.
0xd34df00d
Я все ещё пишу на плюсах :) Правда, теперь только на работе, а не как раньше.
И это не то что деформация, просто мне действительно нравится мир, где есть чуть более строгие гарантии, и где не нужно тратить непродуктивное время на тесты. А чем больше я про все это говорю и пропагандирую, тем больше людей про это знает, а чем больше людей про это знает, тем выше шансы жить в таком мире.
А предпочтения зависят. На хаскеле можно код в продакшен писать, ибо в идрисе даже вон пакетного менеджера и репозиториев не завезли толком (да и скоро Idris 2 будет с ещё более лучшей системой типов). На идрисе хорошо играться с завтипами и доказывать, что x + 0 = x. Но да, я очень жду завтипы в хаскеле и даже сам начал немножко это ковырять.
chapuza
Это-то мне понятно, и такая позиция мне крайне близка. Просто чем больше вы начинаете говорить о типах, как о панацее — тем дешевле становятся их преимущества (которых тоже, конечно, вагон).
IMSO, конечно.
0xd34df00d
А тут неоднозначность разбора, хехе. Дешевле в каком смысле?
chapuza
«Дешевле» — в смысле «доказательная наука» превращается в «религию».
Я повторяю: я согласен, что есть ситуации, в которых типы помогают. Но когда мне начинают говорить, что типы лучше документации, типы делают ненужным тестирование и типы спасут мир — это уже начинает выглядеть как «на все — Воля Божья».
arkamax
ИМХО одно другому не мешает. Представьте себе донельзя упрощенный вариант:
int sum(int a, int b) {
return a+b;
}
А потом кто-то впопыхах берет и меняет плюс на минус, и это проходит code review (лично видел не раз). Строгая типизация удовлетворена, компилятор счастлив, но
самолетпродакшн таки падает. YMMV, но у меня таких примеров в работе море.0xd34df00d
Она просто недостаточно строгая. Вот если там рядом будут теоремки вроде
?x. sum x 0 = x
и? x. sum 0 x = x
...arkamax
Прошу прощения, мне не знаком данный синтаксис в контексте языков программирования (математически понятно). Подскажите, какой это язык? В любом случае, мой комментарий касался более классических ЯП, где подобное отсутствует. Ну и такой момент — если эти теоремки будут рядом, всегда есть искушение «поправить» и их заодно с рефакторингом. Понятно, что от полных идиотов не спасет ничто, но если тесты вообще в другом файле, то очумелые ручки доберутся до них с чуть меньшей вероятностью. Все ИМХО.
JustDont
А уж если их в блокчейн положить…
Я в принципе нисколько не против, чтоб люди упарывались, вы просто когда упарываетесь — то имейте в виду, что выраженные типами теоремы о том, как должен работать тот или иной код — это по сути почти то же самое, что и полное покрытие любого ввода через property-based тесты, только без тестов.
(Почти — потому что типы базируются на математике, а property-based тесты таки вынуждены выполнять ваш код и смотреть на результат)
0xd34df00d
Это гибрид из агды и идриса. На идрисе я что-то сделал, например, здесь или здесь или здесь.
На агде я пока ничего не сделал, кроме упражнений к книжке по ней, но на буйство уникода можно глянуть, например, здесь.
chapuza
Пф. Вы переписали код вычиления суммы с языка
Foo
на языкBar
. Теперь, по сути, сама функцияsum
не очень-то нужна, потому что вы выразили ее полностью в типах.Смена аксиоматики не может сделать что-то понятнее, точнее, или правильнее.
0xd34df00d
Нет, я не выразил её в типах. Я в типах выразил её свойства, не более.
kwolfy
В целом согласен, но с оговоркой что тесты должны быть интеграционными. От юнит тестов при более-менее большом рефакторинге толку ноль