Разговор, о котором я думал в последнее время, – это разговор Гэри Бернхардта под названием Идеология . Я настоятельно рекомендую вам пойти посмотреть его сейчас, если хотите, я подожду здесь. В противном случае, вот tl;dr: энтузиасты динамической типизации любят говорить, что им не нужен компилятор, так как у них есть тесты; энтузиасты статической типизации любят говорить, что им не нужны тесты, так как у них есть компилятор.
По моему опыту, первое утверждение встречается гораздо чаще, чем второе, редко можно услышать, как люди, которым нравятся статические типы, говорят, что им не нужны модульные тесты. Возможно, в прошлом все было по-другому; в какой-то момент истории инженеры Google утверждали, что модульные тесты необходимы только для плохих программистов, с тех пор они были доказаны неправильно, и теперь Google усиленно применяет тестирование для всего своего кода. То, что я иногда слышу от энтузиастов таких языков, как Node.js, Python или Ruby-это то, что им действительно не нужна безопасность и структура, предоставляемые компилятором, поскольку у них есть наборы модульных тестов, которые улавливают ошибки.
Это вредная философия. Это не страшно в некоторых случаях, когда ошибки на самом деле не имеют значения: в стартапах на ранней стадии ошибки оказывают ограниченное влияние, потому что у вас либо вообще нет клиентов, либо клиенты терпимы к проблемам. Вы можете позволить себе, чтобы ошибки проскальзывали, так как преимущество быстрого перемещения и изменения вещей перевешивает стоимость доставки ошибок. К сожалению, это меняется по мере роста стартапа, а ошибки с ошибками кода и регрессии напрямую приводят к оттоку клиентов. Это усугубляется тем, что по мере усложнения программного обеспечения и увеличения размеров команд вероятность создания ошибок очень быстро возрастает. Если вы ничего с этим не сделаете, команды в конечном итоге окажутся в положении, когда они просто борются с пожарами, а не создают продукт.
“Но если бы у вас был хороший набор тестов, этого бы не случилось!” – утверждают многие разработчики. Разве тестирование недостаточно хорошо и не поймает мои ошибки, если они есть? Есть много способов, которыми ошибки могут проскользнуть мимо тестов, вот несколько примеров. Каждый из них я видел в профессиональной обстановке, в том числе в Google, где очень строгие стандарты кодирования:
- Тесты, которые ничего не проверяют. Тест может выполнить код и получить 100% покрытие кода, но если он не проверяет правильность условий, то это не очень хороший тест.
- Во-первых, их никто не писал. Иногда, когда возникает необходимость что-то исправить, люди пропускают написание тестов. Многие компании имеют культуру, которая фокусируется на доставке как можно быстрее, что не способствует написанию надежного программного обеспечения.
- Не проверяю важные дела. Я видел тесты, которые просто проверяют счастливый путь, не проверяя ни один из сценариев ошибок – что делать, если вы не можете подключиться к базе данных или соединение прерывается? Что, если файла, который вы пытаетесь прочитать, там нет?
- Интеграционные/функциональные тесты. По своей природе интеграционные тесты сложнее писать и поддерживать, а комбинация входных данных взрывается экспоненциально, поэтому очень редко можно иметь солидный набор интеграционных тестов за пределами абсолютно важных проектов.
- Никто не проводил тесты и не смотрел на результаты, прежде чем нажать. Это происходит в настройках, в которых нет никаких процессов (я смотрю на вас, хакеры-стартапы), которые проваливали тесты, блокировали коммиты/слияния/толчки.
Для этого есть несколько решений. Некоторые из них довольно просты и широко распространены: обзор кода, непрерывная интеграция, крючки предварительной фиксации и строгие разрешения на главные ветви-это лишь некоторые из них.
Более глубокое решение, которое уменьшает множество головных болей, – это использовать какие-то подсказки типа. Причина, по которой я рекомендую это, заключается в том, что если вы используете его последовательно, он удаляет большую часть человеческой ошибки из уравнения – компилятор будет всегда помнить проверять все, где даже лучшие люди иногда ошибаются и что-то забывают. Компилятор также намного быстрее находит ошибки, когда запуск наборов интеграционных тестов может занять очень много времени – как правило, я не блокирую коммиты на интеграционных тестах, иначе внесение изменений может занять несколько часов. Это также автоматически: вам не нужно писать тесты. Меньше работы-это плюс в моих книгах.
Но должен ли я использовать для этого один из этих раздражающих языков, таких как C++ или Java, где я провожу все свое время, пытаясь удовлетворить компилятор, а не выполняя реальную работу? Нет! Вы можете использовать это в Node.js с помощью TypeScript или компилятора закрытия Google в Python вы можете использовать объявления типов Python 3, а в Ruby вы можете использовать что-то вроде RDL (отказ от ответственности, я никогда его не использовал). Существуют также современные статически типизированные языки, такие как Go, Rust, Swift и Kotlin, которые гораздо приятнее использовать, чем предыдущие поколения.
Для связи между различными системами вы можете использовать JSON со схемой JSON или буферами протоколов (которые при необходимости могут быть сериализованы как JSON). Ужасная история из прошлого: моя команда строила распределенную систему с Node.js и Python, и различные узлы в системе будут передавать сообщения через JSON. Кто – то внес изменения в одну систему, которые изменили тип поля в одном из сообщений, но прошло несколько прыжков, прежде чем что-то действительно попыталось получить доступ к этому полю. Система, которая пыталась получить к нему доступ, очевидно, потерпела крах, и отслеживание того, где это поле было изменено, потребовало изрядной детективной работы (в дополнение к тому, что вызвало сбой производства). К счастью для нас, сбой происходил в системе Python, поэтому у нас была ключевая ошибка и трассировка стека, чтобы, по крайней мере, иметь представление о том, что происходит, если это произошло в одном из Node.js системы, которые он просто произвел бы неопределенный
и продолжил бы свой путь. Если бы мы использовали какие-то проверки типов сообщений, это
Я настоятельно рекомендую, чтобы, если вы занимаетесь какой-либо разработкой серверной части или инфраструктуры и еще не используете некоторые из этих методов, вы начали. Это избавит вас от головной боли позже.