Комментарии 104
Я писал на многих языках за свою карьеру. В частности когда-то давно на C, Asm, PHP, Perl. Сейчас пишу на C#, Python, JavaScript. Пытался несколько раз изучить Golang: ставил компилятор, открывал таториал, cмотрел на код... и в ужасе закрывал таториал до следующей попытки. Кровь из глаз и всё такое. Впрочем, я и Java не смог полюбить, хотя казалось бы она похожа на C#. Но Golang - это нечто, конечно. Буду ещё пытаться.
Я на Го писал лет 7, сейчас ушёл с него. Сложно понять, что же может там вызывать кровь из глаз - он же простой как пятка. Эдакий Си с прибамбасами.
Простой, но синтаксис какой-то не человеческий. Питон вот мне сразу зашёл, даже несмотря на списковые сокращения и прочее такое. В основном там всё же синтаксис понятный. А в Go как-то не интуитивно код выглядит.
Вот именно 'Си с прибамбасами'. Какую ценность он приносит совершенно непонятно, а раз непонятно не вижу причин использовать го вместе джавы например
Ну, если вы не разобрались в особенностях языка, то это не значит что в нём нет смысла.
Мы тут про синтаксис говорим, а не про язык в целом.
Ну, например, он компилируется напрямую в бинарник, что даёт лучшую производительность, чем джава и при этом он более высокоуровневый, чем Си, что облегчает написание программ. А также имеет легковесные горутины, которые потребляют намного меньше ресурсов, чем использование потоков напрямую в джаве, что позволяет хорошо масштабироваться. Вот вам как минимум 2 причины, когда его можно выбрать вместо джавы.
Юзкейс самый что ни на есть очевидный: Тебе нужно куча производительности и малое потребление памяти, но ты не хочешь управлять памятью вручную/писать Rust. Если тебе нужен высокопроизводительный бэкенд, Го - это лучший вариант, но если ты можешь тратить часы работы серверов только на запуск рантайма - флаг тебе в руки.
Так вы таториал открывали, а надо было tutorial - и глаза остались бы целы :)
ну если изучить историю почему его сделали таким и для каких задач,то я думаю вопросы отпадут
Вывод: нужно помнить, что подходы, принятые в Golang, чаще всего зависят от контекста. И когда вы слышите, что «тут так принято», лучше не верить на слово, а загуглить и разобраться в вопросе.
Принято что именно? Короткие имена переменных? Если человек придумал абсолютно тупейшее название toUniqueViolationError, тут как переменные не назови, лучше уже не станет. Ну а переменной m вообще нет нигде, я не знаю, что именно вы хотели этим примером показать.
p.s. почему код в картинках то?
Нет фреймворков, но есть огромная куча библиотек как std, так и сообщества. Писать велосипеды не надо.
Ну тут же смотря с какой стороны смотреть. Статья (как и изначальный доклад) больше про то, какой боли ожидать при переходе именно php-шникам. А по отзывам, для многих php-шников, которые привыкли к огромным фреймворкам и всяким доктринам, половина кода go-шных сервисов выглядит как велосипеды.
Попроси человека, который десять лет пишет на пхп с использованием Symfony, написать небольшое приложение-API на го хотя бы с использованием Gin, и можно запасаться попкорном. Symfony - комбайн, в то время как Gin - просто серп, позволяющий не рвать зерно руками.
Да в целом ничего сложного, как раз мой случай )) если конечно человек прям "Симфонист", на "голом" PHP почти не писал и т.д., тогда да, тогда возможно он долго будет недоумевать, почему всякий миддлвейр надо самому тащить. Хотя приходилось и объяснять людям, почему приложению на Go не нужен апач и "где у него интерпретатор" ))
Из самого необычного я бы выделил 2 пункта, к которым так и не смог привыкнуть до конца:
1. Имя пакета - часть имени сущности. И если в пакете auth надо создать какой-то сервис аутентификации, то надо назвать его просто Service, а не AuthService. Из-за этого местами непривычно читать код, так как часть имени любой сущности написана в самом верху кода (пруф)
2. В именах пакетов не должно быть подчёркиваний, тире и пр. Только одно слово (пруф)
Из-за этого лично мне иногда не особо понятно, по какому принципу надо расположить и назвать модули и папки в проекте. А если ты решил поменять эту структуру папок, то по идее надо и имена сущностей менять.
В остальном язык приятный. Мне очень нравится, что в сообществе нет огромных фреймворков. Это даёт возможность выбирать небольшие библиотеки под каждую задачу, а не тащить в проект одного большого монстра, который за тебя всё решил.
Ну и местами смущает работа с ошибками. Например, функция http.ListenAndServe всегда возвращает ошибку, что вроде как укладывается в концепцию go, но лично меня интуитивно такое отталкивает. И простого if err != nil иногда не хватает (см. rows.Err() в либах для работы с БД).
А вообще обидно, что аналогов у языка нет. Если ты хочешь язык общего назначения со статической типизацией без современного ООП (как в Java, C#, C++), то выбора особо и нет. Kotlin больше под мобилки заточен, Swift явно создавался для экосистемы Apple. Так что популярность go вполне себе логична
Блин, зачем я стал читать это
Nullable columns are annoying and lead to a lot of ugly code. If you can, avoid them. I
Очередное "мы в это не можем и поэтому объявим плохим".
Раст - вполне неплохая замена без лишних нагромождений ООП.
"Swift явно создавался для экосистемы Apple." — да, но сейчас он работает под Линукс, есть несколько вебфреймворков и на нем пишут веб сервисы. Вообще Swift самый красивый и логичный язык, который я когда либо видел.
А вообще обидно, что аналогов у языка нет.
Есть KPHP - статическая типизация с "авто" выводом типов, и сборкой в нативный бинарник. (под капотом C++)
Совместимость с PHP обеспечивает быстрые проверки, а после сборки - быстрое исполнение.
kphp - это довольно специфичное решение, которое за пределами ВК не существует
По ссылке https://habr.com/ru/articles/686496/ про "не существует".
То, что у обычных PHP разработчиков нет проблемы скорости и защиты - это не проблема самого решения.
В любом случае это не PHP, а лишь подобие с похожим синтаксисом.
Почему "подобие"? У нас приложение одинаково хорошо работает на PHP и KPHP. KPHP относится к PHP также, как Mono к .NET. Просто другой рантайм для такого же кода.
Каких-то фич может и нет (рефлексия и что с ней связано), но это вопрос уровня поддержки рантаймом.
Если занять позицию "аналогов нет, и для быстрых сервисов давай менять язык", то конечно ничего не подойдёт. Нужна производительность но не хочется менять язык, то KPHP очень даже.
К тому же разводить зоопарк языков на проекте так себе идея.
А у вас приложение на Symfony, Laravel, Laminas или может быть Slim с Doctrine? Или всё же "просто другой рантайм" оказался не таким уж и "простым" и всё пришлось написать с нуля, т.к. язык всё же другой с похожим синтаксисом, верно?
P.S. У меня позиция, что для быстрых приложений достаточно воткнуть Swoole/RoadRunner/etc, а менять его на какой-нибудь Rust/C/Go уже только в случае критически важных секций, коих на практике оказывается обычно ровно 0.
У нас исторически свой ORM и свой роутер запросов.
KPHP - Это просто другой рантайм. Если код написан без магии на рефлексии и с нормальной типизацией (как последние версии рекомендуют), то все собирается просто. Поэтому ничего не пришлось писать с нуля для миграции кроме обеспечения рефлексии (нужна для ORM). Для которых библиотек пришлось найти реализации на чистом PHP или подключить FFI.
Рантайм (как реализация исполнения языка и всех основных функций) не обязан обеспечивать поддержку монструозных платформ которые используют "магию".
А PHP модули - у каждого рантайма свои. Я же не буду говорить "Ой, Mono не поддерживает WPF, это не правильная реализация, их язык только похож". Просто в реализации Mono нет этого модуля. Но с точки зрения реализации исполнения языка - всё хорошо.
KPHP вполне собирает код на PHP 7.4.
И хотя есть свои дополнения, но они не мешают исполнению в PHP режиме.
Ну т.е. у вас всё своё, нет ни единой библиотеки на PHP из композера, т.к. сам композер не работает, но при этом "это чуть-чуть другой рантайм", я правильно понял?)))
P.S. И с каких пор конструкции, полностью поддерживающиеся IDE, с работой автокомплита, с покрытием стат.анализатором, без каких либо инструментов метапрограммирования (get/set/call) и прочим называется "магией"?
KPHP вполне собирает код на PHP 7.4.
И хотя есть свои дополнения, но они не мешают исполнению в PHP режиме.
А теперь, финт ушами. Языком PHP может называться язык, соответствующий спецификации PHP (1) https://phplang.org/ и (2) https://github.com/php/php-langspec
Откройте, например, часть про выражения: https://github.com/php/php-langspec/blob/master/spec/10-expressions.md и скажите, KPHP всё поддерживает? Даже eval? Если нет, то о чём речь? Это не PHP и не является им. Это не "другой рантайм". Это лишь диалект/подмножество.
А другой рантайм - это вот: https://github.com/ircmaxell/php-llvm легковесный бекенд поверх llvm.
У нас есть что-то из композера. И композер работает. и KPHP даже работает с PSR структурой файлов.
Про выражения - не понял вопроса. У нас не возникло никаких проблем с ними, и с лямбдами тоже, если Вы про них.
А вот eval() не положен по безопасности. Такое возможно только в интерпретаторах.
P.S. И с каких пор конструкции, полностью поддерживающиеся IDE, с работой автокомплита, с покрытием стат.анализатором, без каких либо инструментов метапрограммирования (get/set/call) и прочим называется "магией"?
В затягиваемых внешних библиотеках часто применяются магические методы. Если в Ваших фреймворках такого нет, и нет смены типа переменных "на лету", то такая библиотека может быть собрана в KPHP и Вы сможете получить преимущества от сборки в исполняемый платформозависимый файл.
Если Вам это не надо, нет проблем. Ниже Вы указали "Хочу узнать о переезде", я дал ссылку на статью про переезд без переезда.
А какую метрику увеличили перейдя с PHP на Go?
Команду разработки и зарплаты :)
Не отвечу за Lamoda, но в проектах где мне довелось участвовать в миграциях с PHP основным мотивом было снижение ресурсов по памяти и CPU. И это удавалось сделать, хотя процесс перевода не был быстр и сам требовал много ресурсов на разработку. Например, из личного опыта, Lazada как пример большого проекта практически съехавшего с PHP, переезд занял несколько лет. Сейчас работаю в компании где монолит на PHP тоже имеет место и бизнес не готов его убрать совсем и вижу в сравнении, что на Go-сервисах в той же инфраструктуре потребление ресурсов ниже. Но вложение в переезд может обойтись еще дороже, чем поддержка того что есть.
В подавляющем большинстве случаев, снижение потребления ресурсов и тем более CPU обуславливается обычным рефакторингом. Причём как показывает та же практика - PHP на порядки эффективнее в утилизации памяти, нежели Go из-за специфики своей работы (что взял, то моментально отдал).
Учитывая это, образно, тачка с 500 метрами может держать хоть 1000 сервисов на PHP с пиковым потреблением в эти самые 500 метров, а в случае Go это крайне проблематично, если не невозможно.
Ну чтобы не быть голословным: Обычный рефакторинг кода на PHP, который ранее отвечал за ~0.300с сейчас у меня отвечает за 0.004с - 0.007с. Тупо рефакторинг куска бизнес-логики с выносом в отдельный сервис без смены стека.
Hidden text

Так что лично я крайне скептически отношусь к таким аргументам, т.к. в таких случаях никогда не приводятся сравнения БЕЗ рефакторинга кода. И никогда не сравнивается результат в целом: Был стек из 10 железок в кластере -> стал стек из 1000 железок или наоборот, 1 железка.
Более того, практика того же Avito, что переезд с PHP на Go с параллельным нарезанием на микросервисы всего и вся, показывает, что потребление всех ресурсов (включая человеческие) и стоимость поддержки в таком варианте возрастает кратно (пруф: https://www.youtube.com/watch?v=yLrSp174yc0).
Короче, нужно смотреть из ситуации. Не говорю что Go не несёт никаких профитов, однако, имхо, эти "профиты" зачастую или переоценены, или наоборот оцениваются некорректно и в результате плюсы только с точки зрения поддержки отрефакторенного (sic!) кода.
Как я понял, они там распределённые монолит сделали. Так что пример не самый показательный
Увы, я других примеров не знаю, где бы реально оценивалась стоимость поддержки 1 монолита на PHP до и 1000 микросервисов на Go после (железо + человеческие ресурсы). И желательно без переписывания всего кода, а копирование предметной области 1 в 1.
Так что я могу ориентироваться лишь по косвенным докладам подобным и собственным опытом.
P.S. Было бы неплохо услышать доклад/статью про "мы переехали с Go на PHP и вот что получилось". Вангую, там бы тоже получили ускорение и удешевление.
P.S. Было бы неплохо услышать доклад/статью про "мы переехали с Go на PHP и вот что получилось". Вангую, там бы тоже получили ускорение и удешевление.
Есть "Мы переехали с PHP на KPHP",
https://habr.com/ru/articles/686496/
ускорение точно есть и по опыту нагрузочных тестов - удешевление инфраструктуры.
Мы когда из интеграционных тестов небольшие скрипты через PHP-FPM дергали, то память расходовалась сильно. Добавили эти части в состав основного KPHP-сервера приложения - стало все идеально.
PHP на порядки эффективнее в утилизации памяти, нежели Go из-за специфики своей работы (что взял, то моментально отдал)
Но есть нюанс. В Go, в отличие от Java и C#, не происходит выделения всего в куче.
Компилятор может сгенерировать код так, что "взял-отдал" будет происходить на стеке и тогда GC вообще не будет принимать участие в этом движе.
Плюс даже в куче Go использует отдельные пулы для мелких и больших объектов, поэтому фрагментация довольно неплохо контролируется.
Поэтому в целом Go не то что бы плох в плане менеджмента памяти.
Это если переменные в каком-нибудь локальном скоупе, тогда думаю да. Но это скорее оптимизации процесса всё же. То что компилятор эффективнее оптимизирует потребление памяти на работу внутри функции - это, думаю, скорее всего очевидно (хотя есть конечно приёмы, требующие JIT с рантаймом).
Но код на Go в основном пишется не в функциональном стиле, а наличие структур уже говорит о каком-то стейте. А стейт придётся и так и так хранить где-то, в отличие от PHP, у которого в большинстве случаев стейта нет, а всё что он может хранить - это байткод в mmap/sysv.
P.S. Под эффективностью менеджмента я имел ввиду утилизацию в ноль, т.к. в GC у пыха вообще дефрагментации нет, например, о какой эффективности (кроме скорости работы) можно говорить.
в отличие от ... C#, не происходит выделения всего в куче.
Ну не прям все. В C# есть значимые типы, которые выделяются на стеке. И дженерики умеют в мономорфизацию.
Но есть нюанс
На Go программист не управляет размещением объектов в памяти. Доктор Компилятор сказал в хип, значит в хип.
В C# есть значимые типы, которые выделяются на стеке.
Чтобы использовать их преимущества, точно так же надо проектировать программу с учётом особенностей этих типов и компромиссов, которые придется принять.
Как минимум стек в C# (был?) не резиновый, в отличие от го.
На Go программист не управляет размещением объектов в памяти.
Плохо разве? Хорошо, меньше выбора - проще код писать.
Явно конечно не управляет, но неявно - вполне можно, хоть и сложно.
Компилятор сказал в хип, значит в хип.
Ну что ж вы компилятор выставляете тираном :) Как минимум есть правила escape analysis, можно влиять на это.
Плюс даже в куче Go использует отдельные пулы для мелких и больших объектов, поэтому фрагментация довольно неплохо контролируется.
А кто не использует-то?
Как выглядел рефакторинг ? Переход на Swoole, отказ от больших фреймворков, грузящихся на каждый запрос?
Ну просто взялся классический монолит и от него запилился сервис с отдельной бизнес-логикой на PHP 8.3 + Symfony 7.1 (изначально 6.4) с Доктриной, JMS, DDD, гекасгоналкой и прочим. Ну т.е. дефолтный стек вообще весь. В качестве аналогии по коду/архитектуре могу привести в пример вот эту репку с наброском сервиса https://github.com/SerafimArts/packagist.app/tree/master/app
Единственный "тюнинг" - это просто вместо FPM установлен RR (благо в симфе любой сервер 1 кнопкой меняется), да и то не для скорости, а просто чтобы весь сервис в один контейнер можно было собрать (ради удобства деплоя), вместо двух nginx+fpm.
Т.е. рефакторинг выглядел как рефакторинг, а скорость - просто сайд-эффект. Случайно получилось.
RR это RoadRunner ? Так и есть, это полноценный живущий cgi как Swoole, без постоянной инициализации на каждый запрос, а не умирающий после каждого запроса FPM.
RR и Swoole принципиально разные инструменты для совершенно разных задач. Одно тупо альтернатива FPM почти 1 в 1 с такой же однопоточной и блокирующей работой, а другое набор инструментов для написания асинхронных и многопоточных приложений, где просто есть в наличии API для работы с HTTP. Но не будем об этом...
Да, RR -- RoadRunner, просто полноценно живущий CGI, причём не самый быстрый, прошу заметить. Его преимущество только в том, что можно взять FPM и заменить на RR без каких-либо доработок и наоборот.
Но на всякий случай повторюсь, не используя всяких глобальных переменных, всяких статических шляп, синглтонов и прочего, ну т.е. запиливая обыкновенный адекватный код уровня миддла - рантаймы можно как перчатки менять. Примерно такие же правила хорошего тона, как "не завязываться на Apache". Вроде не рокет сайнс.
Учитывая это, образно, тачка с 500 метрами может держать хоть 1000 сервисов на PHP с пиковым потреблением в эти самые 500 метров, а в случае Go это крайне проблематично, если не невозможно.
Честно говоря верится с трудом. Хотя возможно речь про небольшой рпс в системе, тогда такое возможно. В любом случае если бы вы написали статью с методикой сравнения, с удовольствием бы почитал и изменил свое мнение.
P.s. могу даже помочь с написанием гошных сервисов
Можно и на С++ настолько плохо написать, что будут жраться ресурсы как не в себя. В 2004 году я переписывал счётчик HotLog (если кто-то помнит такой), Была производительность 100 хитов/секунду, стало 5000 хитов в секунду. Причина: применялась "копирка" c Perl с коллекциями (хеши). После рефакоринга на правильные типы данные и правильные коллекции (rbtree) скорость заметно выросла и количество необходимых серверов уменьшилось.

Учитывая это, образно, тачка с 500 метрами может держать хоть 1000 сервисов на PHP с пиковым потреблением в эти самые 500 метров, а в случае Go это крайне проблематично, если не невозможно.
Отвечу за Ламоду
В первую очередь нас заботила ТТМ. Мы довольно большие и у нас много сервисов, и средняя скорость разработки в старых PHP монолитах заметно меньше, чем в новых перенесенных на Go. Тут конечно не только стек решает, но и сам рефакторинг. Очень позитивно сказывается на онбординге новичков, тут тоже помогает микросервисная архитектура, а Go для этого и создан. По техническим метрикам разница значительная в некоторых параметрах десятки раз. Память Go из коробки расходует скромнее и стабильнее) Еще одна не маловажная метрика это счастье разработчиков. И 90% перешедших безусловно не хотят возвращаться. И при прочих "равных" тут явный перевес)
"Подводные камни" в высшей степени странные. ИМХО чистый субъективизм.
Я немного знаю PHP, но основные языки для меня - C/C++, поэтому появление Go и возможность использовать его в вебе вместо PHP/Python для меня был как глоток свежего воздуха.
Имена переменных можно делать любой длины, как и во всех языках. Главное чтобы было понятно.
То что в Go не принято использовать все эти ваши фреймворки - это же прекрасно! Язык программирования (любой, не только Go) по опеделению проще и надежнее, чем фреймворк. Проще в изучении, проще в использовании, содержит на порядки меньше ошибок (вообще ошибки в компиляторах это суперэкзотика). И то что называется "написание велосипедов" на самом деле лично я воспринимаю как просто обычное классическое программирование. А вот продирание сквозь дебри фреймворков в попытке сделать что-то чуть нестандартное воспринимается лично мной не иначе как танцы с бубном (но это конечно тоже субъективная оценка).
А вот важнейшее на мой возгял преимущество Go перед PHP/Python - это даже не статическая типизация (хотя она тоже очень важна), а то что в языке нормально, ЯВНО объявляются переменные. Т.е. переменная явно объявляется один раз (оператором var или :=), а все остальные упоминания имени - это ее использование. В случае опечатки в имени не возникнет новая переменная, а будет ошибка компиляции.
Конечно субъективизм, никто и не спорит. Это топ жалоб именно php-шников, тут вся статья рождена из доклада на внутреннее php-комьюнити на тему "какой боли ждать и к чему вам готовиться морально".
Безусловно, у тех, кто переходит из C, ощущения будут совсем другие.
явная декларация не гарантирует ничего
func main() {
a := 1
func() {
// ошибочка вышла - опечатались := вместо =
a := 2
doSmth(a)
}()
if a == 2 {
println("ok")
} else {
println("obosrams")
}
}что выведет эта програмка на "безопасном" го?
вопрос решается сторонними тулзами (линтерами), которые на php/python есть
а для го есть?
Ещё в го нормальная система сборки и крайне лёгкий деплой.
камон, у го даже нет разделяемых библиотек
т.е. вы обречены на каждый чих компилять, компилять и компилять
и вот это уже реальная проблема, в отличие от шелухи описанной в статье
Как по мне это плюс)
Учит понимать что делает программа и не смотреть логи на каждый чих)
Что конкретно компилять? На какой каждый чих? То, что Го собирает статические бинарники по дефолту, как по мне, просто отлично. Деплоить проще, зависимостей нет.
Ну а если нужно, то через CGO можно .so подключить
Если надо с разделяемыми библиотеками, то можно и так. Если есть нужда использовать модель плагинов - есть поддержка https://pkg.go.dev/plugin. Команда go build -buildmode=plugin myplug.go. Думаю это используют, но мне проекты такие не попадались. Компиляция достаточно быстрая. Деплоить 1 бинарь удобно, чем тянуть 100500 зависимостей.
Проблема с переменной в пхп надуманная. Так сделать, конечно, можно и язык это позволит. Но косячить можно вообще на всем, были бы способности. Если мы говорим о коммерческом коде, то любое пыховское приложение на этапе сборки прогоняется через статический анализатор после чего запускаются юнит и e2e тесты. На десятке проектов за последние надцать лет я не видел ни одной подобной проблемы. За тот же период времени количество 500х, которые попадались в проде могу по пальцам одной руки пересчитать. Тут не совсем в языке дело.
Я вообще PHP-шник, но начал писать приложение на Python для обработки большого количества событий, получаемых из сети по WebSocket.
Я упёрся в проблему, когда даже асинхронные подходы и даже с параллельными выполнениями в разных процессорах не давали необходимую скорость.
Я попробовал в этот момент GoLang, и он оказался очень быстрым. Мне понравилась его философия, и могу сказать, что в своей нише это очень классный язык.
Все, что касается i/o задач он очень хорош. Но вот работа с мютексами немного нервы потрепала.
После php это вообще другой мир. Но я бы не стал переносить на него все. Только там где логика не очень сложная, и нужна скорость
Ну так Golang я так понимаю прямо заточен под многопоточность, быстрые сервисы и всё вот это вот. А Python изначально скриптовый язык, и то, что теперь это универсальный комбайн, на котором хочешь в дата сайенс играйся, хочешь сервисы пиши, хочешь GUI изображай, не делает его лучше приспособленным для каких-то таких вещей, где важна каждая миллисекунда. Я бы высоконагруженные сервисы на Python писать не стал, хотя и люблю его в целом. Каждому языку своё предназначение.
Но вот работа с мютексами немного нервы потрепала.
Инкапсулируйте код.
Асинхронный код и методы синхронизации должны находиться на одном слое - читай в рамках одного объекта. Если что-то вылазит наружу - у вас проблема с архитектурой.

Чтобы узнать, как это условие сработает, нужно понимать, как именно PHP будет конвертировать строку в boolean-значение
Вот как раз если знать, то такого в жизни не напишешь. Например, если прилетит "0", то код под if не выполнится. И вообще это поведение может меняться от версии к версии.
Чудеса динамической типизации... всегда удивляло как люди умудряются большие проекты тянуть на языках вроде PHP или Питона - там же куча тестов нужна чтобы вот подобные вещи только ловить, не говоря уже о бизнес-логике
всегда удивляло как люди умудряются большие проекты тянуть на языках вроде PHP или Питона
Да нормально. У меня у самого немаленький проект на РНР - хорошо за миллион строк кода. И начинался когда там ещё никакой типизации, кроме динамической, и в мыслях не было. Это скорее вопрос культуры кодинга, внутренних стандартов. Если так писать, то в крупном проекте действительно упаришься баги ловить.
На культуре, к сожалению, можно выехать только в маленькой команде как мне кажется.
А в остальном остаётся надеяться на тесты и IDE, которых часто бывает недостаточно.
По крайней мере по моему опыту разработки на Питоне часто вылезали какие-то ошибки которые кроме как в рантайме (или тестах) не поймаешь, IDE и прочие анализаторы их не видят.
Со статической типизацией же всё просто: накосячил - не собралось. А в каком-нибудь Расте не собралось и ещё по куче более сложных причин)
...прочие анализаторы их не видят.
Со статической типизацией же всё просто: накосячил - не собралось.
Так статические анализаторы полностью заменяют/реализуют это поведение, т.к. и реализуют компилятор по факту. Тот же вывод типов, то же построение CFG, всякие анализы DCE и проч. Лично я не могу представить, где бы наличие строгой типизации в языке решило бы проблему, которую не решают статанализаторы. Скорее наоборот, т.к. в PHP нет ни алгебраических типов (пользовательских имею ввиду), ни зависимых, ни шейпов, ни каких-либо более узких скаляров (class-string, non-empty-string, int<0, 2>, etc), ни чего-то ещё. Плюс taint-анализ...
А пример можно привести косяков статанализа? Потому что что-то не могу такого представить.
А IDE... Ну IDE (если мы говорим про PHPStorm) довольно примитивный анализ реализует. Да и в CI не воткнуть её в отличие от PHPStan/Psalm.
А пример можно привести косяков статанализа? Потому что что-то не могу такого представить.
Анализатор показывает ошибку там, где её нет, и приходиться переписывать код под анализатор. У меня было такое с Resharper'ом.
Ну IDE (если мы говорим про PHPStorm) довольно примитивный анализ реализует. Да и в CI не воткнуть её в отличие от PHPStan/Psalm.
Это взаимодополняющие инструменты, оба должны использоваться. Код, не прошедший статику, должно быть невозможно вмержить в рабочую ветку.
На культуре, к сожалению, можно выехать только в маленькой команде как мне кажется.
Маленькая – это сколько? У меня 25 человек только непосредственно на разработке – справляемся. В конторах FAANG класса опыта нет, но думаю, там процессы контроля кода ещё жёстче прописаны. А если программист знает, что за такой код можно получить по башке, то и писать будет аккуратнее.
Ну это плохой код. Обычно, разработчики миддл+ и выше так не пишут, как минимум потому что статический анализатор такое не пропустит.
такой код порицается сообществом, заворачивается на ревью, а автора очень ласково просят так больше никогда не делать.
на php очень приятно и удобно писать сложную бизнес логику, пользуясь всеми прелестями ооп, можных фреймворков, довольно развитого тулинга и экосистемы в целом.
на php очень приятно и удобно писать сложную бизнес логику, пользуясь всеми прелестями ооп, можных фреймворков, довольно развитого тулинга и экосистемы в целом.
РНР в последнее время сильно усовершенствовался, особенно начиная с 8.х. Если сейчас начинать на нём новый проект, так совсем хорошо. Ещё бы дженерики нативные завезли, вообще отлично бы стало. Правда, это если и случится, то нескоро. Но как ни крути - сейчас подавляющее большинство проектов лютое легаси, куда новые фичи лезут с трудом. Но такой код, как в примере автора, писать всё равно не нужно 🙂 Да и сравнивать с Го, по-моему, тоже - это два разных инструмента для разных задач.
Большие проекты люди такими условиями как на скриншоте не пишут. Понимают что может и приведёт к ошибкам.
Ну а ноги растут из самого C. Типизация в C++ более строгая, чем в C, но именно на последнем написаны Python, PHP и много ещё чего.
Само собой, на скриншоте просто крайний случай. Но в динамической типизации полно других более тонких мест где можно накосячить.
А насчёт строгой типизации - не очень мне понятно как язык, на котором написан интерпретатор, влияет на типизацию того языка, который интерпретируется.
Следуя вашему примеру - С++ был изначально написан на С. Ну и С является подмножеством С++, так что я не сказал бы что в общем смысле С++ более строгий.
Это не гипотезы или предположения.
1 пример, про void*
#include <stdio.h>
int main() {
int a = 10;
void* p = &a;
double* d = p;
printf("%f\n", *d);
return 0;
}C допускает неявное преобразование void* в любой другой тип указателя. Программа скомпилируется.
C++ требует использовать явное приведение. Программа не скомпилируется.
2 пример, про const
#include <stdio.h>
void modify(const int* ptr) {
int* modifiable = (int*)ptr;
*modifiable = 42;
}
int main() {
const int x = 10;
modify(&x);
printf("%d\n", x);
return 0;
}Результат будет разным: в С будет 42, а в C++ 10.
3 пример, про void
#include <stdio.h>
void func() {
printf("Hello\n");
}
int main() {
func(42);
return 0;
}В C скомпилируется, т.к. передать. А в C++ нет.
В C функция, объявленная как void func(), означает, что она принимает произвольное количество аргументов.
C++ строго проверяет соответствие сигнатуры функции.
Вот несколько примеров. Так что я не согласен что C++ не более строгий.
Ну, "может" только в теории. Пока планов на изменение правил кастинга нет. А так - да, тяжелое наследие царского режима. Можно настроить статический анализатор, чтобы не пропускал.
Я на go перешел с чистого C, переход очень плавный получился, мне просто завезли чего не хватало плюс немного чистой архетиктуры, я после на C стал писать в Го стиле
С тезисом, что поначалу собственный код на Го кажется уродливым убожеством, полностью соглашусь. Добавлю только, что спустя четыре года работы на языке полюбить его я смог, а вот писать на нём код, который мне нравится визуально, так и не сумел.
Я спустя 8 лет и полюбить не смог, скорее сила привычки.
Ага, понимаю, кодинг по расчёту :)
Ох тыж. Ору в голос! ) И ведь в этом есть смысл. Одна из проблем того же пыха - проекты, которые вместе с ним приезжают. Чаще всего это что-то максимально неинтересное или плохо написанное. И тут хочешь не хочешь, а задумаешься о смене языка, чтобы просто сменить проекты.
Почему я убежал в Go из PHP так, что аж пятки сверкали:
Простой язык порождает простой код. Читать и понимать программу на Go легко, даже если проект впервые видишь
Настоящая строгая типизация, а не эта поделка в PHP, которая позволяет нерадивым и ленивым на неё просто забить. По этой причине специфики в Go на порядки меньше, чем в динамических языках. Запомнить несколько особенностей мап, слайсов и интерфейсов не так сложно.
Дешёвая многозадачность. Любая функция может стать горутиной. Вы ничего не окрашиваете бестолковыми async. Все race conditions в начале тупо обмазываете мьютексами. Из-за относительно высокой производительности самого языка этого в 90% случаев достаточно. Если нет, то потом оптимизируете.
Не перегруженная система типов. Да, сначала может показаться, что этого недостаточно, но пока ребята на Typescript и Rust ублажают себя изысканными типами, способными решить задачу трёх тел, ребята на Go выпускают проект на рынок.
Утиная типизация плюс интерфейсы и новые дженерики позволяют сделать ваши слои проекта настолько независимыми, что даже умные книжки про чистый код обзавидуются.
DI - это хорошо. DI контейнер - это зло! Не ведитесь на эту лживую "простоту". Внедряйте зависимости вручную. Поверьте, километровую простыню ручного внедрения зависимостей дебажить легко, как дышать в сосновом лесу утром, а дебажить проблемы, возникшие из-за непонимания, как оно всё связано в проекте с контейнером - это ад.
Стандартная библиотека и инструменты из коробки выше всяческих похвал. В моих проектах число внешних зависимостей чаще всего не превышает количество пальцев на одной руке старого фрезеровщика)
И т.д. и т.п.
Согласен. По 6 пункту (DI) - еще применяется кодогенерация, благо не сложно это реализовать. Вот например небольшой пример https://habr.com/ru/companies/omprussia/articles/558690/
Выскажу личное мнение. Нормально все в нем после php, нет никаких болей кроме одной - if err != nil. Когда на 20 строк бизнесового кода пишется 10 проверок и код тонет в этом. В такие моменты с тоской вспоминается замечательный принцип "fail fast!", который в продуктовом коде просто бесценен.
Причем все аргументы и наезды на обычные exception, которые мне попадались на глаза в холиварах, базировались только на том, что если накосячить в куске кода, который ловит ошибки, будут проблемы. Да, это так, причем очень большие. Ну дак накосячить можно на чем угодно, были бы способности к этому. Можно и канал закрыть в куске кода, который из него читает. Дак и что, каналы из Go нужно убрать? А если допустить, что отлов ошибок реализован нормально?
Есть ощущение, что когда достали компилятор из Plan 9 и отряхнули с него пыль стало понятно, что языки за это время ушли вперед. Что во всех популярных языках катаются на тех же exception. Но воткнуть обработку исключений в этот компилятор не прокатит чисто технически. Как мне кажется это и есть причина, а не вот это вот все светлое и молодежное. Поэтому была придумана красивая легенда про ясность.
В остальном язык просто прет! Простой как табуретка синтаксис, свой планировщик, дешевые горутины и принцип с каналами вызывают восхищение.
if err != nil.
Причём был недавно пропозал же чтобы это говнище упростить до уровня Раста или что-то в таком духе. Даже не упростить, по моему, а обернуть в синтаксический сахар просто. Но его то ли не приняли, то ли отложили к сожалению.
И хорошо что не приняли.
Говнище, это когда пишут:
if err != nil {
return err
}А надо:
if err != nil {
return fmt.Errorf("some do, some_var=%s: %w", someVar, err)
}А лучше так:
if err != nil {
return someError{err: err, v: someVar}
}И тогда все будет норм.
Меняйте подход в голове. Почитайте статьи и книги про работу с ошибками в го.
Я писал на Go лет семь, так что примерно понимаю как там ошибки работают.
И вот эта череда if-ов выглядит ужасным boilerplate, по сравнению даже с Rust:
let request = Request::new().context("unable to create request")?;
let response = self
.client
.execute(request)
.await
.context("unable to execute HTTP request")?;Я на PHP писал лет 15 и меня всё это время раздражало пхпэшное сообщество со своими "не изобретай велосипед".
Во первых если бы люди не изобретали, то мы так бы и жили в каменном веке.
А во вторых лучше написать небольшой велосипед созданный под конкретную задачу, чем ставить готовый бандл в котором 90% существующих умелок тебе не нужно, а 90% умелок которые нужны твоему бизнесу там нет. В итоге это всё надо как-то наследовать, расширять, магия магией погоняет, неповоротливо и нестабильно.
Сообщество в го говорит "лучше небольшой копипаст, чем небольшая зависимость". В го приходят не за удобным кодом, в котором должно быть всё предусмотрено наперёд, в го приходят за скоростью и возможностями. В го не боятся произносить вслух слово "рефакторинг", как например в php, если ты собрался рефакторить, а не наследовать, значит ты плохой архитектор.
А отказ от доктрины это отдельный кайф.
Когда ты сам управляешь своими запросами и не чешешь голову почему база не нагружена, а приложение уже еле дышит (потому что orm само по себе выжирает всё что можно).
При компиляции лишний код и так будет отброшен, поэтому угар по велосипедостроению экономия на исходниках не оправдывает. Есть подозрение, что причина в том, что Go сам по себе не особо располагает к написанию универсального кода.
Вот о таком сообществе я и говорю.
Написание идеального инструмента под задачу бизнеса назвали угаром по вилосипедостроению.
Поставили в систему готовый модуль, который перегружен ненужными для задачи функциями понадеявшись на опкэш и прочую подкапотную магию.
И наконец оправдали всё это ненужной универсальностью.
Универсальность нужна уже затем, что готовый код по очевидным причинам дешевле в разработке и поддержке, чем идеальный, который не написан и не протестирован. В типичном проекте лучший код - это самый дешевый, выполняющий задачу.
И, по моему скромному мнению, проблему объема кода вы сильно преувеличиваете, учитывая, что его не умещают в мизерную память МК и не отправляют по мобильной сети клиентское устройство.
Выглядит что код бы просто отрефакторили и все. Переписали бы на новом php, скажем 8.4, было бы и быстрее и дешевле, а возможно и приятнее, так как есть библиотеки для всего. Php проигравает в скорости, но в вэбе не так много задач на скорость. Их можно вынести в очереди и jobы. Единственно где php проигрывает это go рутины и паралельные вычисленя. Это есть но на значительно более низком уровне. Но это и не очень php нужно.
Php безусловно приятнее. Go с точки зрения архитектора кажется сильно кастрированным в сравнении с php. Тут соглашусь.
А что касается скоростей в вебе это неправда. Сейчас всё на вебе завязано, все сайты, все приложения, весь онлайн бэкэнд требует скорости. И в случае с php мы рано или поздно упираемся в производительность и закупку дополнительных серверов. Напомню, что я говорю не про сам php, а про идеологию сообщества - поставить друпал, наставить на него модулей, а потом как-то в хуках костылями допиливать проект под требования ТЗ.
Ну в вэбе остновная нагрузка ложится на базу, кэш, движек полнотекстного поиска. Логика где надо сделать миллион вычислений без образещения к внешним источникам - редкий сценарий. По крайней мере в моих проектах так. Хотя я видел тормозащие системы на PHP , например оч популярный коммерческий магаз Shopware.
касательно Drupal или Wordpress, там плагин кэша поставил, и все летает если простой вабсайт. Но вообще иметь опцию использовать готовое решение дорогого стоит, например тот же Magento, Opencart. С точки зрения намного дешевле запилить магаз на PHP опенсоурс чем писать там что то с нуля на GO и потом это суппортить.
На мой взгляд основная сила PHP не в языке, а софте который уже есть и работает. Хотя мне и ООП PHP ближе чем обрезок Go или Rust.

Переход на Go глазами PHP-разработчика: 5 подводных камней