Костыли и велосипеды программируем как умеем

от admin

Господа программисты! Изобретайте велосипеды! ⁠ ⁠

Я морально готов к тому, что меня заминусуют, плюнут в лицо, наср*т за шиворот и забанят в гугле. И всё-же.

Господа программисты-сисадмины! Изобретайте велосипеды! Если позволяет время, лучше посидите и попытайтесь решить задачу своими силами. После любого результата (даже отрицательного) поищите готовые решения.

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

Пописав «велосипеды» в своё время (да и чего там говорить, до сих пор пишу) и увидев узкие места своего собственного кода, я не раз обращался к готовым решениям. Местами находил «грабли» посерьёзнее моих. В некоторых точно такие же затыки, как и у меня (что приводит к выводу о идентичности решения).

К тому же при постоянно развивающихся технологиях пересмотр старых алгоритмов может иметь место.

Герб Программистов — "Танцы с бубном, велосипеды, костыли и ошибки". Как это выглядит

Одно из самых лучших изображений сущности программирования выглядит так (в ироничной форме отображающее бытие разработчика ПО):

герб программистов

На этом изображении мы можем видеть:

  1. Венок из терния (колючего растения) — означает, что путь программиста тернист, т.е. технически непрост и может быть даже мучительным (особенно, если делать всё тяп-ляп).
  2. Велосипедные колёса (а может и уже готовый велосипед) намекают о том, что в программировании часто изобретают одно и то же. — как символ одного из популярнейших стилей в программировании.
  3. Бубен — напоминает нам, что иногда всё же приходится с ним потанцевать.
  4. «Не баг, а фича» («Это не ошибка, так задумано») — фраза, часто употребляемая как шутка, в случае если обнаружено странное поведение системы, которое обычно (не всегда) действительно является недоработкой. Отвечающий за эту функцию человек может использовать эту фразу, дабы блеснуть знанием фольклора 😉

Что это значит

В силу ироничности, герб во многом напоминает нам о вредных практиках в разработке и, посмеиваясь, предлагает избегать их 😉

Костыль и велосипед

  • Last.FM User
  • Last.FM User
  • Last.FM User

Слушать альбом

Ты что-нибудь знаешь про эту композицию? Написать вики-статью

Похожие теги

Слушать альбом

Представлено на

10Слушать альбом

Представлено на

10Слушать альбом

Велосипеды и костыли: история одной идеи

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

Идея для pet-проекта

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

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

Я хочу качественный продукт

Первое что говорит себе любой разработчик: "Я хочу получить качественный продукт". Почему это проблема?

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

Как только вы хотите делать pet-проект найдите или выделите человека, который будет оценивать результат со стороны пользователя. Скажем, сервер авторизации бесполезен без удобной формы. Функционал пользовательских параметров не нужен, пока UI не учитывает их. И так во всём.

Весь код, который не решает проблемы пользователя — бесполезный для проекта код (на этапе старта). И это надо осознать, иначе вы, как и мы, убьёте огромное количество времени "впустую", т.к. будете решать свои проблемы, а не удовлетворять потребности пользователя.

Я хочу поддерживаемый код

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

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

Я хочу автоматизировать процесс разработки

Бич красивых реализаций, особенно когда вы работает с языками, которые поддерживают меттапрограммирование. Ни один junior не сможет вам провести полноценный debug макроса и кода, который в итоге попал в программу. Это слишком сложно для среднестатистического специалиста!

Так же — чаще всего рутина дешевле. Автоматизировать процесс в разы дороже, чем сделать рутинную задачу.

Я хочу использовать "крутой" стек

"Крутым" можно назвать только тот стек, который позволяет быстро реализовать код, который будет решать реальную задачу. Всё остальное — сказки диванных теоретиков. Рынку нужен продукт, а не красивый исходный код и это вы должны для себя запомнить.

Итоги моих ошибок

Итогом моих попыток сделать крутой продукт стал перевод всего проекта на NodeJS и фреймворка Nest. Пришёл я к этому после того, как отказался от Julia, Racket и CommonLisp.

Использование Dlang + Vibe.d для требовательного API и Ruby + Roda для не нагруженного API

Dlang — очень удобный язык. Это C++ с удобством Python, не плохим пакетным менеджером, компиляцией и возможностью управления памятью. Одним из главных фреймворков Dlang для web'а является Vibe.d. Главный недостаток, который я смог выявить — дикие утечки при компиляции достаточно простого сервиса (сервис авторизации). Почему так — коллега, который занимался DevOps толком не разбирался и просто докинул ресурсов. Возможно у нас просто кривые руки.

Что до Ruby и Roda — отличный стек, но нам казалось, что он будет не достаточно производительным. А количество строк кода немного пугало (ведь у нас везде были микросервисы, что — для команды из 3 человек мягко говоря зря). Вот с Ruby проблем не возникло от слова совсем.

Отказ от Racket

В одно утро я нашёл достаточно молодой фреймворка Vela для Racket. Понравилось то, что код выходил очень компактным, а сам язык поддерживал прекрасную возможность использовать макросы для генерации исходного кода сервера. Почему-то нам идея это понравилась. Мы хотели описывать декларативно в YAML-файлах под сервера и логики API (если она была на уровне CRUD) с возможностью подключения handler'ов там где нужно что-то сложнее.

Сами же handler'ы мы планировали реализовывать на DSL (так мы хотели решить процесс вливания junior-разработчика), который должен был быть чем-то вроде python, с ограниченными возможностями и стандартной библиотекой для взаимодействия с платформой.

Начали длительный процесс реализации идеи.

Сразу хочу сказать, что от Racket я отказался исключительно потому, что инструменты выбранные мной просто плохо работали (скажем, пришлось делать доработку драйвера для MongoDB для того, чтобы я мог использовать необходимый мне функционал). Язык Racket является одним из наиболее красивых из стеков с которыми я поработал (и именно в этот момент я пожалел, что не стал использовать Clojure), но количество батареек удручает. Хотя, если бы я не пытался писать универсальное нечто — было бы всё намного проще и удобнее. В свою очередь хочется сказать спасибо всему сообществу языка Racket за отзывчивость. При работе над платформой на основе Racket, MongDB, Vela я получил бесценный опыт, который останется со мной навсегда. Всем советую хотя бы раз попробовать сделать такой продукт.

Отказ от Racket в сторону Common Lisp и работа под Woo

В какой-то момент я устал бороться с недостатком батареек под Racket и посмотрел в сторону Common Lisp. Требовалось не так много для переноса кода с одного диалекта на другой. Аналог Vela можно было реализовать достаточно быстро самому. В качестве сервера был выбран не блокирующий Woo . В качестве реализации был взять Steel Bank Common Lisp (SBCL).

Начался новый цикл переноса кодовой базы. Он шёл достаточно быстро и просто (благо Emacs в качестве IDE мне нравится, а REPL для CL просто прекрасен). Почему ушли? В момент, когда сервер научился собирать из YAML-файлов базовый сервер я понял, что я просто не найду людей на поддержку продукта. В одиночку — уже не вытягивал. Как раз прошло серьёзное повышение по работе, которое отъело много свободного времени (освоение новых областей ответственности).

Следовательно мне было надо передавать часть работы junior'у, который до этого просто разбирался с базовыми принципами архитектуры. И тут я могу сказать — порог вхождения в Common Lisp выше, чем в Racket. Просто потому, что для Racket есть очень удобная и качественная, современная документация. Даже сам Emacs как IDE стал проблемой.

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

Читать:
Gr16 500 что это

Julia и проблемы ФП

И после работы над попыткой написать платформу на достаточно функциональном, но в тоже самое время гибком язык Julia могу сказать — мне не понравилось. Julia. привкус python в нём силён на столько, что возникают вопросы, а почему не Python?

Решалась работа junior'a тут просто, кстати. Julia умеет вызывать Python и получать из него результат выполнения кода. Да только мы так и не добрались до этой фичи, т.к. погрязли в куче разных доработок функционального кода. Почему так вышло?

Именно с переходом на Julia и получения быстрого начального результата мы задумались о бизнес-сценариях и поняли, что кодовая база перенесённая с Lisp просто не выдерживает никакой критики. Мы делали продукт красиво выглядящий для программиста-теоретика и отвратительный для прикладника. Решения, которые мы предлагали — было очень сложным, выглядело монструозным. А ещё — именно тут мы осознали на сколько же в бизнес-кейсах удобно ООП. Без него мы были вынуждены придумывать массу сложных обёрток и действий, которые позволяли получить нам нужный результат. В итоге фича которая делается в ООП языках за счёт простого наследования превращалась в новую задачу по продумыванию архитектуры. Не спорю, что возможно просто я плохой ФП программист или начал переносить на Julia код из Lisp "в лоб" (там я проблему наследования легко решал макросами).

В итоге, мы взяли паузу и начали переносить кода на NodeJS. И итогом стала тестовая архитектура и свой фреймворк Raccoon, которые сейчас болтаются в NPM.

NodeJS, Raccoon, Nest

В итоге, когда был готов фреймворк в версии применимой для разработки мы смогли выделить для себя понятные критерии качества и поняли. я в одиночку не смогу поддерживать фреймворк и делать работу по написанию кода продуктового. Мне нужно было и реализовать и заиспользовать фичу. С одной стороны удобно было то, что я понимал любой шаг обработки запроса и мог сделать то, что мне надо в текущем моменте, не прибегая к большим костылям. С другой — это время. Год 2020 заканчивался, а продукта не было.

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

Итоге — за 3 недели была перенесена вся кодовая база. Микросервисы переехали в монолит, а в основу легла луковичная архитектура. На данный момент кодовая база растёт, а фичи — просто начинают работать после того, как появилась потребность.

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

Итоги

Итогов работы над этим pet-проектов на самом деле несколько. Какие? Я их разбил для себя по разным годам. И ниже привожу выводу по каждому.

Сама статья была написана в уже далеком 2021 году (а дополнена только сейчас в 2023 и бережно перенесена с Яндекс.Дзен), когда казалось, что реализация "мечты" уже вот-вот, очень близко.

Итоги 2020 года

Закончился 2020 год и мы отказались от своего pet-проекта. Сейчас он трансформировался в работу над 2 более простыми сервисами без замашек на разработку универсальной платформы. С одной стороны — это очень печальный опыт. С другой — мы наелись много кактусов и получили большой опыт в разработки по настоящему сложного продукта очень малой командой.

Выводы которые мы сделали для себя:

    маленькая команда должна брать монолитную архитектуру, но делать из неё луковицу. Таким образом не будут тратиться ресурсы на связывание микросервисов с тобой и проще выпустить нужный функционал. С точки зрения DevOps монолит — самый простой вариант в принципе. сложные языки (Lisp, Haskell) не просто так не стали популярными. Порог вхождения в них очень высок, в то же время — программистов способных их освоить для написания полезного кода не так много. Беря такой стек — вы обрекаете проект на проблемы с поддержкой и трудностями в разработке (батареек для них в разы меньше). компилируемые языки в любом случае потребуют вменяемый build-сервер, иначе чехарда с контейнерами будет достаточно узким местом. К тому же — в реальных задачах скорее всего вашим узким местом станет канал или СУБД, а не сервер приложения. Количество же запросов дешевле решать горизонтальным масштабированием. не стоит изобретать велосипед на не популярном стеке. Стек не просто так не получил распространения, вероятнее всего где-то есть проблемы и связаны они именно с реализацией бизнес-задач. применимость стека можно понять только при работе над бизнес-задачами. В остальных случаях вы работаете со "сферическим конём в вакууме" и с ним всё будет хорошо. пишите тесты. Только написание тестов позволило нам быстро переносить код со стека на стек и не терять функциональность.

Удачи вам в разработке pet-проектов! Надеюсь эта заметка будет кому-то полезна.

Итоги 2021 года

Почти весь 2021 год прошел под эгидой переосмысления проекта. С основным идеологом проекта мы выкинули все не нужное (а его накопилось очень много, начали заново прокладывать бизнес-процессы). И. я продолжил собирать pet-проект на NodeJS/Nest.

Моей основной ошибкой было делать монолит, да еще и на MongoDB. Практической пользы от подхода Nest же оказалось очень мало при этом — куча модулей, не нужного кода, сложных манипуляцией для того, чтобы добавить простейший CRUD на вложенный документ. Что в таких случая делают? Правильно — меняют идею или стек.

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

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

В этот момент на backend появился Clojure и на frontend стал использоваться Svelte/SvelteKit. Да, от них проект в итоге ушел. Но мне этот год дал многое. В частности — позволило по новому посмотреть на архитектуру тем, что я занимался.

После того, как я сделал какое-то представление в виде монолита на Clojure — была пора отдохнуть и пересмотреть проект еще раз. Но уже в рамках выпуска реального приложения.

Итоги 2022-2023 года

В этот момент у pet-проекта появилась настоящая команда разработки. Именно организованная и понятная.

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

За, примерно, месяц я перенес все наработки Clojure на Julia (да, я опять вернулся к ней из-за простоты и превосходного инструментария), а базу сменил MongoDB на PosgreeSQL, так как выбрал для реализации — Genie, приятный фреймворк имевший свою ORM, но не работающий с MongoDB.

Примерно после переноса 2/3 кодовой базы с Clojure был найден backaend разработчик и frontend программист (он подхватил Svelte). Чуть позже к проекту подключился второй разработчик, который хотел освоить Pure C. Проект был бы и сейчас на этом стеке, если бы не одна проблема — новая версия Genie была не совместима с плюшками предыдущей версии мы экстренно начали искать замену.

Попробовали NodeJS — он пошел плохо, так как второй разработчик был Python-программист и NodeJS шел не очень. Тем более — возник спор по поводу использования TypeScript, противником которого я являюсь (это тема отдельной статьи, которую я когда-нибудь напишу. Правда-правда, обещаю!). Параллельно я экспериментировал с CommonLisp и вылился в генератор микросервисов (поставлен на паузу, пока нет времени, каюсь — второй ребенок стал подрастать и теперь у меня два замечательных и любознательных сына, которым все интересно).

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

Общий вывод

А его у меня нет для вас. Статья — скорее попытка делиться опытом, в первую очередь негативным. А так же — fail story одного проекта, который после 5 (!) лет ментаний принял наконец очертания, понятные бизнес-цели и понятные задачи. И только сейчас я могу сказать — у этой идеи есть будущее. И да, теперь мы даже не думаем о глупой идеи сделать свой Steam. Это будет нечто другое. То, что будет полезно людям. Но о бо всем это позже.

Похожие публикации