Программисты пишут свои вики в 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 или подключить другого человека к разработке, то можно этот репозиторий скопировать в публичный и удалить все ненужные и приватные части.
