Ездил на прошлой неделе в Австралию на ларакон. Увидел свой продукт как спонсора на плакате. Был доволен как ребенок. Michael Dyrynda со сцены еще отдельно выделил, что плагин помог им сделать сайт и приложение для конфы.
P.S. Австралия - классное место. Конкретно город Брисбен утопает в зелени и парках. На каждом шагу публичные библиотеки, где можно посидеть с ноутом сколько хочешь. Я там захотел жить.
JetBrains недавно переделал отчеты по продажам плагинов и они стали в разы более информативные. Вот этот, например, показывает новые покупки или продления лицензий Laravel Idea.
Синим наверху показаны продления и видно, что на данный момент я денег со старых верных клиентов получаю почти в два раза больше, чем с новых. Наверно, можно сделать какие-то выводы. В год я теряю примерно 10-15% юзеров, что весьма неплохой показатель. А вот продажи новых за последний год не растут. Скорее всего это связано с совместной рекламной кампанией с JetBrains. Она мощно стартанула прошлым летом и с тех пор сложно переплюнуть приток новых.
Там есть еще график покупок персональных и корпоративных лицензий. И там до сих пор от людей, покупающих напрямую, чуток больше денег идет, чем от организаций. Где-то слышал, что у JetBrains ситуация сильно склонена в сторону корпоративных лицензий. Значит, я тут сильно недорабатываю. И действительно, весь мой неумелый маркетинг направлен напрямую в юзеров. Надо что-то менять.
Зарелизил таки новый плагин. Пока без лишнего шума. Но если у кого есть локализации в Laravel приложении, вполне могут посмотреть и некий фидбек оставить(буду благодарен).
https://plugins.jetbrains.com/plugin/24979-locale-sphere
Пока весь UI - это toolwindow справа на экране. Над плагином идет активная работа, как по фреймворкам(симфони на подходе), так и по фичам. Ссылка на багтрекер есть внизу окошечка.
На прошлой неделе ностальгировал по вебу и погрузился в доработку новой версии https://laravel-idea.com/ - можете посмотреть кстати, там наверняка еще осталось пару косяков, но уже более-менее.
Один товарищ почти сделал ее еще полгода назад. Но там было куча деталей, которые в итоге пришлось самому дописывать. Я даже руками svg иконку правил! Понравился концепт. Ты пишешь почти все, что надо в markdown-файлах. Что необходимо - дописываешь Vue-компонентами. Просишь собрать и оно собирает все в одну папку с html/js/css, которую можно просто положить на сервер и оно работает. Полноценное SPA, причем браузер получает уже срендеренный для каждой страницы HTML, как при SSR. Но собирается это все хоть на машине разработчика еще(хотя правильнее на CI), и на сервере лежит уже в html-файлах. Для не особо динамических сайтов самое то. Главная страница, документация, ну и блог продукта небольшой там же будет. Инструмент называется Vitepress от создателя Vue.
Но проблема всех этих глубоких компиляций разных яваскриптов друг в друга почти всегда одна и та же: если вдруг совершаешь небольшую ошибку, то придется долго гадать что явилось ей причиной. Stacktrace не поможет. Он может выглядеть примерно так и вообще ничего не даст.
Error: Transform failed with 2 errors:
inline-script.js:1:0: ERROR: The JSX syntax extension is not currently enabled
inline-script.js:2:25: ERROR: Expected "}" but found ";"
at failureErrorWithLog (/Users/adel/test/laravel-idea/node_modules/esbuild/lib/main.js:1651:15)
at /Users/adel/test/laravel-idea/node_modules/esbuild/lib/main.js:849:29
at responseCallbacks.<computed> (/Users/adel/test/laravel-idea/node_modules/esbuild/lib/main.js:704:9)
at handleIncomingPacket (/Users/adel/test/laravel-idea/node_modules/esbuild/lib/main.js:764:9)
at Socket.readFromStdout (/Users/adel/test/laravel-idea/node_modules/esbuild/lib/main.js:680:7)
at Socket.emit (node:events:513:28)
at addChunk (node:internal/streams/readable:324:12)
at readableAddChunk (node:internal/streams/readable:297:9)
at Readable.push (node:internal/streams/readable:234:10)
at Pipe.onStreamRead (node:internal/stream_base_commons:190:23)
Давно не запускал полный набор тестов в плагине на локальном компе. На Continuous Integration сервере запускаются после каждого git push, поэтому не парился. Но недавно запустив, осознал, что уже больше недели там есть тест, который падает. А на CI работает!
Причина банальная: когда на компьютере часовой пояс UTC, то тест нормально работает, а когда отличается - падает.
Вообще, такой ситуации быть не должно, и CI должен ловить такое, но что сделать для этого? Банально поставить ему другой часовой пояс? Крупные компании решают эту проблему как либо?
В твиттере высветился пост одного стартапера и меня порадовала концовка. Он там рассказывает как делал десятки стартапов. Вон 21 стартап за 2 года.
Ничего толком не приносило денег до тех пор пока... он не выпустил сервис для быстрой выкатки стартапов! "The NextJS boilerplate with all you need to build your SaaS, AI tool, or any other web app and make your first $ online fast."
Это теперь приносит ему $75k в месяц. Типичный пример когда человек не смог копать золото сам, но стал продавать лопаты другим)
Код Laravel Idea потихоньку приходит к канонам. К слоям. Я начал разделять его на отдельные проекты.
* Проект с общими хелперами, которые пригодятся другому плагину(сюда уедут процентов 10 кода).
* Проект с всей логикой Laravel Idea(тут будет 85% кода).
* Проект для связки Laravel Idea с PhpStorm.
* Проект для связки Laravel idea с Qodana.
Это все равно когда-нибудь произошло бы. Но теперь понадобилось для двух целей - специальной версии плагина для Qodana(CI тулзы от JetBrains) и нового плагина, который уже давно хотел начать, но вот сейчас только руки дошли.
Когда начинал писать плагин, думал что проект совсем иной и общего с веб-проектами с их слоями Application layer и другими нет. Тут очень все завязано на внутреннюю структуру IDE. Но чем дальше в лес, тем больше приходит понимание, что структура там тоже важна. И вырисовывались слои. А новые требования быстро помогли осознать, что пора их отделить уже физически.
Когда-то давно одним из основных продуктов JetBrains был Resharper. Умный плагин для C# разработки в Visual Studio от Microsoft. Долгое время спустя, убедившись, что они умеют делать собственные успешные IDE для кучи ЯП, они решили сделать Rider - среду программирования для C# (или всех .NET языков, тут не знаю). Но была проблема: тот умный решарпер, который понимал C# и на основе которого планировалось делать Rider, был написан на C#.
Решили это таким образом: появился некий бекэнд(решарпер), который понимает код, генерит подсказки и подсвечивает ошибки, и фронтэнд - сама оболочка, UI. Общаются они по внутреннему протоколу. JB ходили на конфы и рассказывали как сложно им оптимизировать это, чтобы работало с приемлемой скоростью, но с годами проблемы были решены, и насколько мне известно Rider потихоньку отвоевывает рынок у Visual Studio, хотя цифр конкретных у меня нет.
Архитектура Rider вынужденно повторила архитектуру современных SPA веб-приложений. И вроде бы получился некий уродец с кучей проблем. Тут и перформанс, и плагин нормальный написать - тоже надо и фронтэнд часть и бэкэнд. Но кто-то в JB решил, что это не баг, а фича!
И появился Fleet - "Next-generation IDE by JetBrains". Архитектура точно такая же - интерфейс это фронтэнд, а вся умнота(там это называется Smart mode) - бекэнд. А фича в том, что бэк можно разместить как на компе юзера(и тогда получим более-менее обычную JetBrains IDE), а можно на облачном сервере от JetBrains, Space! И это решит одну из главных жалоб юзеров - потребление кучи ресурсов(памяти и проца).
Я сегодня таки поставил и пощупал. Котлин оно понимает весьма неплохо. Интерфейс модный-молодежный. Как у VSCode. Настройки хранятся в json-файле. Как у VSCode. Да и smart mode напоминает language server протоколы, которые тоже популярны в вскоде. Но это JetBrains! Они умеют делать не только хорошо, а лучше всех. Надо еще подождать и посмотреть. Настроек очень мало. Плагинов пока никаких нет и апишки к ним не опубликованы.
fun getSpatieToolPackageName(methodReference: MethodReference): String? {
return getNameMethodCall(methodReference)
?.getParameter(0)
?.getStaticStringValue()
?.removePrefix("laravel-")
}
fun String.removePrefix(s: String) { return this.что-то там... }
В PHP-коммунити новый холивар. Пакет Unfinalize, который проходится по всем классам библиотек в vendor и убирает ключевое слово final из всех классов и методов. Разумеется, пакет получил большую поддержку и кучу благодарностей.
Испанский стыд - это самое точное описание того, что чувствую.
О трудностях разработки плагинов. Я начинаю поддержку новой мажорной версии шторма сразу как только выходит EAP. Соответственно ориентируюсь на ранние релизы. Но где-то между 2023.1 и 2023.1.5 они переместили классы в другой неймспейс) Это нормально сделать между мажорными версиями, поскольку эти классы помечены как экспериментальные, и что они могут меняться. Но не между минорными же! Такое очень сложно поддерживать...
Здесь пришлось просто не использовать эти классы. Отложить их до 2023.2 и надеяться, что такого не случится еще разок
https://gist.github.com/adelf/bf9e8d6d094e5c01c24689e4c92ce9c5
Простой пример реального Value Object на Котлин(из кода Laravel Idea). Скрывает свое содержание. При сравнении не учитывает регистр. Ну и еще пару фич в нем. Раньше все в строке хранил в формате "схема.таблица", но стало сложно управляться. Теперь намного проще.
Благодаря equals&hashCode, Java/Kotlin умеет их использовать в качестве ключей массива(Map) и таблички в разных регистрах спокойно работают. Удобно.
Пятничная мелкая тактика. Сделал тут мини-рефакторинг и решил выложить. Вначале присвоение isEnabledAndVisible
казалось незаметной мелочью и проблемы видно не было. Но когда метод подрос, стало понятно, что это все-таки две логики: посчитать нужно ли показывать экшен и "присвоить" результат. Из-за лаконичности Котлина второе не казалось чем-то важным.
Вообще, isEnabledAndVisible
это котлиновский сахар. На самом деле он превращается в вызов Java-метода setEnabledAndVisible
, который сам по себе просто сахар над вызовом двух методов.public void setEnabledAndVisible(boolean enabled) {
setEnabled(enabled);
setVisible(enabled);
}
Иногда крайне сложно разглядеть такую мелкую "логику". Но буквально минуты через две как вынес этот метод, я нашел, что правильнее делать это не через метод update
, а по-другому, и вынесенный метод сразу же пригодился там.
Мораль: разной логике(даже мелкой) лучше лежать в разных местах. Оно облегчает изменения и улучшает будущий код.
PHP-народ сегодня активно обсуждал нововведение в PHP 8.3 - #[Override]
атрибут. Такой же как в Java. Он позволяет указать тот факт, что этот метод перекрывает метод у класса-родителя. Подробнее по ссылке - /channel/sergei_predvoditelev/33
В 8.3 это будет просто полезная для стат-анализаторов или больших проектов штука, которая поможет отследить, если в классе-родители метод удалили, а наследники будут продолжать думать, что они чтото переопределяют у родителя.
Поговаривают, что в 9 или 10 версиях это может стать обязательным. Но обязательным оно наверняка станет одновременно и с обязанностью в классе-родителе указывать то, что метод может быть переопределен. Как в Котлине, Яве и куче других языков. В котлин если метод неабстрактный, но может быть переопределен в потомках, надо указать ключевое слово open. open fun method()
и тогда в классе-потомке можно будет его переопределить override fun method()
- тот самый override.
Для чего, кроме строгости, это нужно? Для оптимизации вызовов этих методов. Методы, про которые мы знаем точно, что их никто не перекрывает, вызывать легко. В давние стародавние времена я разбирался с тем, как выглядит вызов обычного и виртуального(так назывались те, которые можно перекрыть) методов в C++ коде(от C++ Builder компилятора). Ассемблер код вызова простого метода(забьем на параметры) - call method <aдрес где сидит код того метода>
Вызов динамического - найти в обьекте ссылку на таблицу виртуальных методов, там найти указатель на нужный метод, и вызвать метод. Для тех времен это была серьезная оптимизация, поэтому виртуальными старались делать только те методы, которые надо.
В PHP все методы виртуальные - их можно перекрыть. Но если ввести обязательное указание виртуальности метода(open/override или както иначе), то можно получить более оптимальный опкод. Насколько большим будет прирост на реальных проектах судить не берусь. Понимаю только, что это очень серьезный BC - ломает совместимость прям сильно. Сомневаюсь, что на это пойдут.
В первый раз попросил ChatGPT реальный код написать для меня. Аккуратно ему описал что нужно и он с первого раза дал то, что надо. Тестов я, конечно, написал сам. Когда попросил его написать тест - он написал прям плохой. Без краевых случаев. Очередное доказательство, что он ничего не понимает.
Такие простые задачки для него идеальны. На самом низком уровне написать алгоритм, на который он 100% натыкался, когда анализировал какой-нибудь stack-overflow(если даже на другом языке - он наверняка справится). Я пробовал для прикола просить у него что-нибудь более высоко-уровневое - бесполезно. С генерацией текстов для git-коммитов - тоже самое. Если в коде немного абстракции - сообщения он генерит абсолютно бесполезные.
Вывод: помощник он неплохой. Позволяет хорошо сократить время на что-либо, но надо понимать ограничения.
Заглянул таки в это окошко в IDEA своей. Неудивительно, что в большом проекте, даже писанном почти полностью своими руками, Navigation-фичи начинают вылезать на первые места. Больше ходишь по проекту и читаешь код, чем пишешь.
У меня еще не такой большой. Средний. Чуть больше 4 мегабайтов кода. Котлин весьма компактный язык.
Recent Files popup - это Cmd(Ctrl)-E. Удобно вместо табов, ибо они не нужны.
Я сьездил на LaraCon американский. Пообщался с кучей людей, которых знал только в онлайне. Но как-то все быстро и наскоком. Зато много общался с ребятами из команды шторма. Обсудили кучу вещей, много полезного узнал. Только ради этого разговора стоило туда ехать.
Там анонсировали плагин для VSCode от команды Тейлора. First-party, как говорится. Не сказать, что много крутых фич показали, но это определенно лучше массы странных малополезных плагинов, которые сейчас там есть. Мотивацию добавило делать побольше разных крутых фишек, чтобы отличаться еще сильнее.
В Java Swing есть много разных способов размещать компоненты внутри окошек. В давние стародавние времена я выбрал некий GroupLayout, который давал максимальный контроль. Потом я узнал, что его не рекомендуется использовать вручную и он вообще для этих редакторов, где мышкой интерфейс набрасывают, ну да ладно. До сих пор использую его, но там, чтобы указать как именно себя должен вести компонент, надо указать в правильном порядке комбинацию из трех параметров.
Логика какая-то наверняка в этом всем есть, но я никогда ее не осознаю и я никак не могу понять по этим параметрам как компонент будет отображаться: растянутым на всю ширину(высоту), средненько или будет занимать минимальную ширину(высоту). Поэтому давно еще создал методы-расширения, чтобы было более понятно. addComponentShrink - этот будет ужат до минимума. addComponentNotResize - этот будет нормально отображаться, но не будет растягиваться. Теперь решил подчистить весь код, чтобы не осталось ни одного вызова с этими тремя параметрами.
Вывод 1: далеко не всегда понятно, что код, который вот щас по гайдам из интернета сделан, будет непонятен при прочтении в будущем. Надо постоянно следить и скрывать такую непонятную логику в методах с нормальным названием. На картинке видна разница.
Вывод 2: методы-расширения весьма полезны в таких вот Fluent-вызовах методов, в которых return this
. Поэтому я все больше и больше начинаю уважать ларавелевский ::macro()
, который позволяет делать тоже самое в PHP, в котором нет поддержки методов-расширений.
Читаю Аркадия Морейниса. Не каждым постом, но регулярно выдает что-нибудь точное. Вот примерное обьяснение почему мне крайне не хочется делать что-то для VS Code - /channel/temno/6239
Среди людей, выбирающих бесплатное(вскод вместо шторма), будет крайне мало тех, кто готов платить за плагин для Laravel.
Мой маленький CI для сборки Laravel Idea выглядит так. Простой HTML как в бородатые года. Никакого CSS. (Сегодня ночью собирался 8.1.4 релиз, уже доступен на маркетплейсе)
Как думаете сколько памяти в Хроме занимает эта страница? 57 Мб!! Что там? Как? Для чего? Очень поразила меня эта инфа...
«Своя игра» по PHP на канале CutCode
Вы можете задать сейчас вопросы участникам викторины.
Самые интересные вопросы будут разобраны на игре, а их авторы получат возможность поучаствовать в следующей игре и получить мерч!
🕛 13 марта в 19:00 (мск)
🔹 Александр Макаров
🔹 Кирилл Несмеянов
🔹 Адель Файзрахманов
🎬 https://www.youtube.com/watch?v=WNIAO0kEk7U
/channel/tg_5minphp/1306
А я бы спать не мог спокойно, зная что такое дерьмо у меня в коде...
Upd: в комментариях подсказали, что Олег Фогель это один из руководителей разработки в 1С) это прекрасно
Когда внезапно осознаешь, что твое приложение - многопоточное.
Прилетает в sentry такой эксепшен:
java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 0
at java.util.ArrayList.add(ArrayList.java:455)
at java.util.ArrayList.add(ArrayList.java:467)
at com.laravel_idea.plugin.translation.fetching.b.b.a(b.java:24)
java.lang.ClassCastException: class java.util.HashMap$Node cannot be cast to class java.util.HashMap$TreeNode (java.util.HashMap$Node and java.util.HashMap$TreeNode are in module java.base of loader 'bootstrap')
at java.util.HashMap$TreeNode.moveRootToFront(HashMap.java:1986)
at java.util.HashMap$TreeNode.treeify(HashMap.java:2102)
at java.util.HashMap.treeifyBin(HashMap.java:770)
at java.util.HashMap.putVal(HashMap.java:642)
at java.util.HashMap.put(HashMap.java:610)
at com.laravel_idea.plugin.translation.fetching.b.b.a(b.java:16)
Читать полностью…
synchronized(this) {
vendorData.add(data)
}
Это примерный уровень понимания всеми этими AI кода, в котором хоть какая-то крупица абстракции есть.
Код, который они генерируют, соответственно, тоже плоский, без глубины. Ибо настоящего понимания нет и долго еще не будет.
Технологическая сингулярность все еще далека.
class FractalTransformerGenerationTest: BaseAppModuleSystemTestCase() {
init {
addPackageDependency<FractalPackage>()
addDependency(EloquentModelsDependency())
}
У меня сильно разрослись тесты и понадобились некоторые бест-практис, чтобы навести порядок. Тестам часто нужна какая-то инициализация. Она выполняется в setUp/tearDown. Если тестов с общей инициализацией много - то убирают дублирование путем выделения базового класса и вынесения этой инициализации туда.
До определенного момента я так и делал, а потом понял, что иногда нужны разные инициализации. Вот здесь мне нужно подключить определенный пакет(Fractal) и некоторые Eloquent модели к проекту. Плюс там BaseAppModuleSystemTestCase тоже не просто так стоит)
Пора применять композицию вместо наследования. Сделал простой интерфейсinterface TestDependency {
fun setUp(fixture: CodeInsightTestFixture)
fun tearDown(fixture: CodeInsightTestFixture) {}
}
ну а в самом базовом тестовом классе:private val dependencies =
mutableListOf<TestDependency>()
...
fun addDependency(dependency: TestDependency) = dependencies.add(dependency)
override fun setUp() {
super.setUp()
forEach { it
dependencies..setUp(myFixture)
}}
forEach { it
override fun tearDown() {
dependencies..tearDown(myFixture)
}
super.tearDown()
}
Очень удобно стало. Намного меньше мусора теперь в тестах. По хорошему надо и этот BaseAppModuleSystemTestCase превратить в тестовую зависимость, но пока лень.
Сегодня приснился кошмар. Я попал на работу в крутую компанию. Начал писать какой-то функционал и написал тест. Менеджер начал мне обьяснять как добавить этот тест в их систему. Надо взять бланк, описать название теста, на каких кейсах его запускать. Потом понести этот бланк на другой этаж к админам! Получить от них одобрение, инвентарный номер теста(!!!), поставить подписи и все! Менеджер всю дорогу хвастался насколько удобен этот процесс)
Я сидел там у админов и не верил в происходящее. Номер мой тест получил 347. А система огромная! Значит тестов почти не пишут. Понятно почему)
Мимо проходил знакомый, мы поздоровались и я сказал "Вот коллегой твоим стал, но видимо ненадолго"
Недавно сделал рефакторинг, который выглядит дико даже для меня, но в итоге, подумав, вполне нормальный.
Был класс, который размещал два компонента в окошке. Обычно это label и поле к нему. Но иногда там был только label и тогда field было null. И наоборот могло быть тоже. В итоге была огромная конструкция где проверялись все возможные варианты - кто null, кто не null. А когда добавились еще параметров стало просто невмоготу.
Что я сделал? Вместо одного класса FieldPair, в котором оба поля могли быть null, я сделал три. OnlyLabel, OnlyField and FieldPair. И в каждом только ненулевые компоненты. И все стало так сильно проще! Я даже мелкий баг нашел сразу же.
Некоторые уже в курсе, что мы с JetBrains сделали общий bundle, в котором пользователи могут купить шторм вместе с плагином со скидкой. За подробностями сюда.
Картинку взял с корейского обьявления. Там логотип Laravel Idea кажется самым главным)
Давно хотел перевести книгу https://github.com/adelf/acwa_book_ru обратно на английский и нашел идеального переводчика - ChatGPT. После него править почти не надо. Решил еще разок проревьюить её - добавить всякие новые фичи, которые появились в PHP за эти 3-4 года. Ну и дописать пару мыслей. Приведу тут пару отрывков нового.
То, что я описал здесь, очень похоже на архитектурный шаблон Гексагональная архитектура, или Луковая архитектура, или еще десятки подобных. Неудивительно, поскольку они решают ровно те же задачи. Однако, гораздо полезнее для развития разработчика самому осознать причины выделения кода в отдельные классы, почувствовать какие части кода могут работать с базой данных или данными веб-запроса. Самому увидеть как участки кода с похожими обязанностями и потребностями выстраиваются в некое подобие слоев.
Получив подобный опыт и набив руку, можно ознакомиться с данными шаблонами и, возможно, принять один из них как стандарт на каком-либо проекте, полностью осознавая, что данный шаблон подходит проекту, не являясь микроскопом, коим забивают гвозди или пушкой, которой стреляют по воробьям.
Выделение слоя приложения - весьма ответственный шаг и причина должна быть серьезной. Их всего две:
1. Разные интерфейсы к одним и тем же действиям (Web, API, Console, различные боты). Тут вынесение общей логики просится само.
2. Разросшиеся и сложные логики обработки веб-запроса, например, и бизнес-логики. В данном случае разделение логик по разным местам может заметно улучшить связность кода. Как правило, это ведет к повышенной багостойкости кода.
Наше PHP-коммунити составило списки интересных каналов. Можно добавить сразу пачку и они добавятся в вашу телегу отдельной папкой и даже не будут содержаться в основном списке(будут заархивированы по умолчанию, удобно)
PHP (Rus) — /channel/addlist/R7i3akzlB2kwZTMy
PHP (Eng) — /channel/addlist/yNksksLVT6M5NGEy