- PVSM.RU - https://www.pvsm.ru -
Программисты пишут свои вики [1] в obsidian [2], в org файлах [3] и других инструментах. С ними готовятся к собеседованию, ищут команды для баша, хранят информацию про их сервер и много чего еще.
Почему бы не использовать такой подход для кода, а не только для текста? У меня хранится куча вспомогательных скриптов, я пробую новые библиотеки и часто начинаю новые пет-проекты. Даже если проект никогда не зарелизится, все может остаться в такой “вики”, и потом это можно переиспользовать.
Можно сделать персональный монорепозиторий, в котором будет храниться весь код, как вики, но для кода.
Весь код я пишу на Clojure [4] и ClojureScript [5], так как REPL [6] позволяет много пробовать, на динамическом языке легче писать скрипты, и дизайн языка правильно шейпит проблемы в переиспользуемые компоненты.
Вместо Clojure можно использовать Python и JS, и в целом использовать несколько языков одновременно.
Для архитектуры я выбрал Polylith [7], это структура монорепозитория, которая помогает выделять отдельные решения в компоненты – отдельные директории – и переиспользовать их в каждом новом проекте.
Верхнеуровнево 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 лежит сайт [8] на ClojureScript, там я изучал DOM и как им манипулировать. Если это понадобится еще раз, выделю утилиты для DOM в отдельный компонент или хотя бы grep’ну примеры использования.
В clj_tasks находится сборная солянка из разных задач: Advent of Code разных лет, leetcode (его просмотр помогает при подготовке к собеседованиям), тестирование jupyter notebook’ов с JS рантаймом Deno и даже презентация про мемы про горилл на аналоге jupyter notebook в Clojure - библиотеки clerk [9]:
$ 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, который достаточно скопировать на сервер.
Директория 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 сценария как добавлять код в репозиторий: писать новый код или переиспользовать старый.
Допустим, я хочу попробовать новый функциональный GUI для Clojure membrane [10].
Я открою туториал [11] с примерами использования, и в ./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 [12] – удобный viewer структур данных для Clojure. Он уже настроен в этом репозитории, так что открывается по одной команде Emacs:

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

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

К сожалению, хорошую шутку вывести не удалось, однако какую-то шутку удалось вывести. Успех!
После того как мы поизучали библиотеку, можно вынести ее в отдельный компонент и заиспользовать в следующих проектах.
Так я использую Clojure REPL и вместо командной строки, и визуализируя маленькие команды и тексты, и для написания веб-серверов в пет-проектах.
Помимо общих библиотек можно выделять отдельные компоненты. Например, для application-сервера я везде использую одну и ту же связку из библиотеки для роутинга reitit, библиотеки-интерфейса ring, а также функций для старта и остановки сервера из REPL. Все это можно было бы выделить в отдельный компонент server и импортировать в новые проекты.
Мне очень удобно держать все проекты в одном месте и строить новые проекты на наработках из старых. Динамический live язык программирования с REPL’ом помогает. Интересно, как бы это выглядело на более live языках типа SmallTalk.
Из следующих шагов думаю объединить этот репозиторий с вики, а также закинуть dotfiles [13], в котором лежат скрипты для настройки компьютера.
С LLM-агентами это тоже работает, так как не надо каждый раз генерировать весь код для нового проекта, достаточно переиспользовать старый.
Если захочется что-то выложить в open-source или подключить другого человека к разработке, то можно этот репозиторий скопировать в публичный и удалить все ненужные и приватные части.
Автор: snyssfx
Источник [14]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/clojure/455939
Ссылки в тексте:
[1] вики: https://en.wikipedia.org/wiki/Zettelkasten
[2] obsidian: https://obsidian.md/
[3] org файлах: https://orgmode.org/
[4] Clojure: https://clojure.org/
[5] ClojureScript: https://clojurescript.org/
[6] REPL: https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop
[7] Polylith: https://polylith.gitbook.io/polylith
[8] сайт: https://snyssfx.com/
[9] clerk: https://github.com/nextjournal/clerk
[10] membrane: https://github.com/phronmophobic/membrane/tree/master
[11] туториал: https://github.com/phronmophobic/membrane/blob/master/docs/tutorial.md
[12] portal: https://github.com/djblue/portal
[13] dotfiles: https://wiki.archlinux.org/title/Dotfiles
[14] Источник: https://habr.com/ru/articles/1066118/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1066118
Нажмите здесь для печати.