Заблуждение строго типизированных языков
11 января 2023 г.Сегодня я хочу поделиться одним открытием, которое поразило меня в этом году. Я должен упомянуть, что на протяжении большей части своей карьеры я работал разработчиком Java, и поэтому это может быть характерно для областей, где используются строго типизированные языки, а не для всей отрасли в целом. Java — это статически типизированный язык, такой как C#, Golang или Typescript, и для работы с данными мы должны объявить формы и структуру объектов, которые мы будем использовать. Как я понял, это лишь иногда лучший подход в программировании.
Несколько лет назад мне довелось стать частью системы, большая часть которой написана на NodeJS, языке с динамической типизацией. Эта система была написана очень быстро и поэтому представляла собой абсолютный беспорядок. Работать с ним было больно, и я бы не сказал, что мне это нравилось. Мало того, что у него была огромная кодовая база, но необходимость отлаживать что-то и понимать, как работают функции, была головной болью и проблемой.
Одной из основных причин, по которой систему было трудно понять, было отсутствие информации об объектах, передаваемых в коде. Когда я перешел к файлу с необходимой логикой, я не мог понять, какие структуры обрабатывались, откуда они брались или даже что это были за поля — числа, строки или объекты. Из этой случайной части кода я не мог бы получить это знание, даже если бы вы проследили происхождение потока кода — какой-то контроллер или внешний вызов. Код обычно выглядел так.
let answers = {}
(request) => {
if (request.questions) {
answers = callExternalSystem(request.questions.sections)
}
if (!answers.detailed) {
answers = callAnotherExternalSystem(request.questionsSetId)
}
return answers
}
Можете ли вы понять, каким будет результат этой функции? Что будет отправлено во внешние системы? Что делать, если вам нужно добавить дополнительную информацию к ответу этого метода? Каждый раз, когда мне приходилось иметь дело почти со всем кодом NodeJS, меня настигало это разочарование. Но было бы то же самое, если бы это был python без типов или любой другой язык. Мне не нравилось, что я всегда оказывался в ситуации, когда я не знал, что происходит, и чтобы немного понять, мне приходилось связываться с автором или идти и попытать счастья с поиском этих внешних API-интерфейсов служб.
Я не мог понять, как авторы работали с такими расплывчатыми инструментами и информацией в потоке данных. Не лучше ли было бы даже авторам, если бы через 6 месяцев, когда они ничего не помнят о системе, типы помогали им видеть используемые данные прямо в момент чтения кода?
В той же компании, где я работал, меня попросили написать сервис, который бы проксировал запросы к нескольким нижестоящим сервисам. Это должно было стать временным решением для мультирегиональной поддержки вызывающей службы. Основываясь на нескольких полях, мне нужно было решить, какую нижестоящую службу вызывать. Потребовалось около двух недель, чтобы собрать модели каждого сервиса, внедрить красивые DTO и очистить поток данных, а также решить все вопросы, касающиеся API, с авторами нижестоящих сервисов. Все было четко и ясно, пока при реализации сервиса я не понял, что использовал всего 3 поля для маршрутизации потока запросов, и всю эту работу можно было бы пропустить. После нескольких дней мучений по поводу этого факта я смирился с горькой реальностью, что было бы лучше и для кода, и для бизнеса, если бы я не реализовывал все это. Это было бы даже лучше для бизнеса, так как они сэкономили бы время разработчика. С точки зрения кода все исследования DTO и моделей данных были бесполезны.
С этого момента я начал сомневаться в минимальном объеме работы, который мне нужно сделать, и в том, что на самом деле нужно бизнесу — быстрое решение их проблемы, долгосрочное решение или какой-то компромисс между этими двумя факторами.
В одном из следующих проектов я решил реализовать функцию, и, просматривая кодовую базу, я заметил комментарий о том, что этот код реализует шаблон проектирования Tolerant Reader. Почитав немного о том, что это такое, стало ясно, что это было решение, которое мне нужно было реализовать тогда в этом мультирегиональном прокси. Оказалось, что дизайн, в котором приложение не заботится о моделях данных, которые оно принимает, за исключением фактических полей, которые оно использует, довольно распространено. Более того, этот шаблон является стандартным программным подходом к обработке и передаче данных в приложениях NodeJS. Они знали это! По какой-то причине в мире NodeJS не тратят время на моделирование всего, если данные нужно только отправить в какое-то другое место или если бизнес-логика требует всего несколько полей из запроса. Перфекционистское инженерное эго снова было оскорблено и вступило в бой с осознанием того, что все приемы, которым научились с дженериками, стиранием типов во время выполнения, схемами и так далее, во многих случаях действительно бесполезны. Но мне нужно было двигаться дальше.
Через несколько месяцев я присоединился к новой компании, и меня попросили исправить проблему с одной нижестоящей службой, которую мы использовали неправильно. Одна из важных вещей, о которых я знал, заключалась в том, что через неделю нас ожидал высокий сезон продаж, и это исправление было критически важным для бизнеса. Я был в стрессе, так как на расследование, чтение всего кода и понимание того, что происходит, у меня было всего 3 дня. К моему удивлению, когда я увидел код NodeJS, похожий на тот, что я показывал раньше, я не испугался и не почувствовал никакого гнева. Теперь я злорадствовал по поводу того, что это был бестиповый код. В первый раз я сказал себе - ага! Я знаю, что ничего не знаю, но мне это и не нужно! Мне нужно будет только добавить в запрос несколько проверок и дополнительное поле! Это было именно так, и я потратил около 2 часов на внедрение и отправку исправления в производство. Все были счастливы, даже я.
Со временем я понял, что использование бессхемных баз данных или хранилищ типа «ключ-значение», таких как MongoDB или DynamoDB, — это тот же подход и ценность по тем же причинам. Отсутствие необходимости думать и работать со схемой данных и свобода решать проблемы по мере их появления позволяют нам быстрее получать обратную связь и быстрее внедрять решения. Сочетание этих двух подходов к проектированию — толерантного шаблона чтения и баз данных без схемы значительно ускоряет и упрощает разработку.
Когда я создаю новый сервис сегодня, я задаю себе следующие вопросы. Как скоро бизнесу понадобится эта услуга? Какой период обслуживания будет для него? Должен ли он быть производительным или обрабатывать много данных? Насколько устойчива требуемая логика к сбоям и ошибкам? Ответы на эти вопросы помогают мне ограничить сложность восходящей службы. Моделирование данных внутри службы — трудоемкая задача. Использование базы данных со схемой требует гораздо большего планирования и исследования, даже до начальной загрузки кода. Даже добавление тестов может быть излишним для некоторых сценариев. В конце концов, любой бизнес нуждается в работающем решении как можно скорее. С тех пор экономия времени и снижение сложности стали моими основными критериями при выборе устройств для реализации.
Что вы думаете? Дай мне знать в комментариях.
Удачного программирования и более простых решений для всех вас!
Оригинал