Wiki для пет-проектов

от автора

Программисты пишут свои вики в obsidian, в org файлах и других инструментах. С ними готовятся к собеседованию, ищут команды для баша, хранят информацию про их сервер и много чего еще.

Почему бы не использовать такой подход для кода, а не только для текста? У меня хранится куча вспомогательных скриптов, я пробую новые библиотеки и часто начинаю новые пет-проекты. Даже если проект никогда не зарелизится, все может остаться в такой “вики”, и потом это можно переиспользовать.

Можно сделать персональный монорепозиторий, в котором будет храниться весь код, как вики, но для кода.

Стек

Весь код я пишу на Clojure и ClojureScript, так как REPL позволяет много пробовать, на динамическом языке легче писать скрипты, и дизайн языка правильно шейпит проблемы в переиспользуемые компоненты.

Вместо Clojure можно использовать Python и JS, и в целом использовать несколько языков одновременно.

Для архитектуры я выбрал Polylith, это структура монорепозитория, которая помогает выделять отдельные решения в компоненты – отдельные директории – и переиспользовать их в каждом новом проекте.

Технические детали

Верхнеуровнево Polylith репозитории устроены так:

$ tree -L 1.├── bases├── components├── development├── projects├── build.clj├── deps.edn├── Makefile├── package.json├── readme.md└── workspace.edn

У Polylith’a есть CLI-утилита, которая помогает правильно организовать репозиторий и создавать компоненты, однако без нее можно обойтись: Polylith – это прежде всего архитектура монорепозитория, и ее можно создать и поддерживать вручную.

Базово Polylith репозитории состоят из четырех директорий:

  • в components находятся все компоненты, которые проекты могут переиспользовать, например:

    • компонент авторизации,

    • логов,

    • интерфейс работы с базой данных,

    • интерфейс работы с 3rd party сервисами,

    • доменные сущности: если в нескольких проектах используется модель user с устоявшимся интерфейсом, то можно вынести ее из проектов и сделать отдельным компонентом.

  • В bases находятся базовые блоки для проектов. Каждый блок определяет интерфейс какого-то публичного API, имплементирует его с помощью REST, RPC, командной строки и вызывает один или больше компонентов для функционала.

    • Можно сказать, что это тонкий мост между реальным миром и бизнес-логикой из компонентов.

  • Каждый проект в projects обычно состоит из одного файла с перечислением всех нужных компонент из components и одного базового блока из bases.

  • Для экспериментов и локальной разработки также есть директория development, в которой хранятся файлы-черновики (ниже объясню подробнее).

Еще в корне репозитория находятся служебные файлы, например, deps.edn описывает все компоненты и базовые блоки для локальной разработки, build.clj и Makefile хранят вспомогательные команды для компиляции проектов и для тестирования.

Так как помимо Clojure я использую ClojureScript – диалект Clojure, который компилируется в Javascript, а не в JVM – в корне проекта находится package.json с dev-зависимостями ClojureScript’а.

В workspace.edn хранятся метаданные для Polylith CLI-команды, которую я практически не использую.

Примеры проектов и миграция репозиториев в монорепозиторий

Мигрировать все проекты в монорепозиторий можно постепенно. Сначала можно создать базовый блок и скопировать весь код туда. Это называется fat base – “толстый” базовый блок.

Затем, когда будет желание или потребность, можно начать выделять переиспользуемые компоненты по одному из блока и добавлять их в components директорию.

Пока что я не выделял компоненты и только недавно закончил миграцию всех проектов в монорепозиторий, поэтому компонентов у меня практически нет:

$ tree -L 1 components./components└── postman

В директории postman лежит аналог curl или postman для работы с интересующими меня сервисами в REPL.

В bases находятся толстые базы пет-проектов, которые существовали еще до монорепозитория.

$ tree -L 1 ./bases./bases├── clj_scraper├── clj_tasks├── doodle_editor├── fe_experiments└── truth_or_dare_clj

Например, в fe_experiments лежит сайт на ClojureScript, там я изучал DOM и как им манипулировать. Если это понадобится еще раз, выделю утилиты для DOM в отдельный компонент или хотя бы grep’ну примеры использования.

В clj_tasks находится сборная солянка из разных задач: Advent of Code разных лет, leetcode (его просмотр помогает при подготовке к собеседованиям), тестирование jupyter notebook’ов с JS рантаймом Deno и даже презентация про мемы про горилл на аналоге jupyter notebook в Clojure — библиотеки clerk:

$ tree -L 1 ./bases/clj_tasks/src/snyssfx/clj_tasks/./bases/clj_tasks/src/snyssfx/clj_tasks/├── aoc2024├── aoc2025├── clojure_camp├── common├── curl_command.go├── di├── external_ip.go├── gm.clj├── gm.sql├── gorillas.clj├── hs.clj├── leetcode├── logic├── lucky_tickets.clj├── mb├── our_mathematical_universe├── other├── portal.clj├── test_deno.ipynb└── vi.clj

В projects находится несколько проектов, например, проект для сайта выглядит вот так:

$ tree -L 1 ./projects/fe_experiments./projects/fe_experiments├── deps.edn├── node_modules├── package.json├── package-lock.json├── shadow-cljs.edn└── target

Тут описание JS и CLJS зависимостей, node_modules с этими зависимостями, а также забандленный сайт в target, который достаточно скопировать на сервер.

Пример локальной разработки на Clojure

Директория development выглядит так:

$ tree -L 1 ./development/src./development/src├── doodle_fiddle.clj├── fe_experiments_repl.clj├── fe_experiments_repl.cljs├── fiddle.clj├── fp_slides.clj├── skia_fiddle.clj└── truth_or_dare_repl.clj

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

Есть 2 сценария как добавлять код в репозиторий: писать новый код или переиспользовать старый.

Эксперименты с новым кодом в Clojure

Допустим, я хочу попробовать новый функциональный GUI для Clojure membrane.

Я открою туториал с примерами использования, и в ./development создам новый файл membrane_fiddle.clj, и открою его:

Затем запущу REPL в редакторе (в Emacs это одна команда cider-jack-in) и проверю, что REPL работает, сложив 2 и 2:

В Clojure принято использовать REPL-driven подход, когда вы вводите текст в редакторе и по горячей клавише выполняете его. Ничего не надо переписывать руками, редактор уже подключен к терминалу. Нужно только навести курсор на конец строки и (в случае Emacs) нажать Ctrl+C Ctrl+E.

Чтобы добавить библиотеку в зависимости, тоже можно использовать REPL:

Затем копирую пример из туториала и выполняю его, и сразу увеличиваю шрифт и указываю размер окна:

Вместо Hello World хочется вывести хорошую шутку, поэтому нахожу сайт с шутками и сразу выполню этот запрос в REPL. Библиотеку для http-запросов я использую во всех проектах, если что-то забуду, то смогу загреппать примеры использования:

Помимо шутки вернулось много вспомогательных данных, и разбираться в них довольно сложно. В такие моменты я открываю portal – удобный viewer структур данных для Clojure. Он уже настроен в этом репозитории, так что открывается по одной команде Emacs:

Теперь понятно: нужно взять body, распарсить json и оттуда взять поле “joke”. Сделаем это и выполним код еще раз. Для парсинга json’a я использую одну библиотеку во всем репозитории, так что это довольно просто:

Добавлю это выражение вместо Hello World и выполню весь код еще раз:

К сожалению, хорошую шутку вывести не удалось, однако какую-то шутку удалось вывести. Успех!

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

Так я использую Clojure REPL и вместо командной строки, и визуализируя маленькие команды и тексты, и для написания веб-серверов в пет-проектах.

Использование существующего кода

Помимо общих библиотек можно выделять отдельные компоненты. Например, для application-сервера я везде использую одну и ту же связку из библиотеки для роутинга reitit, библиотеки-интерфейса ring, а также функций для старта и остановки сервера из REPL. Все это можно было бы выделить в отдельный компонент server и импортировать в новые проекты.

Заключение

Мне очень удобно держать все проекты в одном месте и строить новые проекты на наработках из старых. Динамический live язык программирования с REPL’ом помогает. Интересно, как бы это выглядело на более live языках типа SmallTalk.

Из следующих шагов думаю объединить этот репозиторий с вики, а также закинуть dotfiles, в котором лежат скрипты для настройки компьютера.

С LLM-агентами это тоже работает, так как не надо каждый раз генерировать весь код для нового проекта, достаточно переиспользовать старый.

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

ссылка на оригинал статьи https://habr.com/ru/articles/1066118/