Привет, Хабр! Меня зовут Саша, я бэкенд-разработчик в Ozon. Пишу платформу для контента, который генерят пользователи: отзывов, комментов, вопросов, ответов. Раньше я писал на Python. Выбрал его изначально из-за лёгкого синтаксиса и большого количества вакансий для Python-разработчиков — изи катка для входа в профессию. 

В один момент мне написали из Ozon: «П̶с̶с̶,̶ ̶п̶а̶р̶е̶н̶ь̶,̶ ̶п̶о̶к̶о̶д̶и̶т̶ь̶ ̶н̶а̶ ̶G̶o̶ ̶н̶е̶ ̶х̶о̶ч̶е̶ш̶ь̶?̶ Предлагаем переход на Golang с текущего стека, обучение за счёт компании».

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


Среди разработчиков бытует мнение, что язык вторичен. Мол, главное — уметь в computer science, а на чём писать — не так уж важно. Но так считают хардкорные разработчики, они вертят деревья, смотрят на всех свысока и зарабатывают 300кк в наносекунду. Я же программист-полукровка (без высшего технического образования, а ещё мои родители — маглы) и считаю, что смена языка — важный шаг и нужно хорошенько прикинуть, прежде чем в это вписываться. Будем откровенны, если у вас за плечами десять лет на плюсах, вряд ли вам предложат должность senior iOS-разработчика на Swift. Проблема в том, что каждый язык имеет свои особенности и на их изучение требуется время.

Дисклеймер: сравнивая два языка, я не считаю один плохим, а другой — хорошим. У меня также нет цели ответить на вопрос, какой язык лучший (такая постановка в принципе некорректна, ведь каждый язык — для своих задач). Я лишь рассказываю о своём способе мышления и о том, на что я обращал внимание, делая выбор, переезжать или нет.

Python как первая любовь

Обожаю этот язык. По-прежнему слежу за ним и экспериментирую с машинным обучением. Он занял ряд ниш, где правит почти единолично: машинное обучение, аналитика, DevOps, скриптинг для различных нужд, например, датафикса. И, конечно же, Python широко используется в вебе. Рейтинг языков программирования PYPL ставит его на первое место с огромным отрывом.


На Python легко состряпать прототип веб-сервиса и получить proof of concept от рынка. Например, на Django можно за считанные дни собрать MVP — сразу бэкенд и фронтенд. Сильнейшее сообщество Python-разработчиков написало кучу прекрасных библиотек, которые дружат с Django так крепко, что в проект можно добавить какие-то хитрые модули буквально парой строк кода. 

Golang — вечно молодой


В 2005 году перестало работать важное следствие закона Мура: «производительность процессоров должна удваиваться каждые 18 месяцев из-за сочетания роста количества транзисторов и увеличения тактовых частот процессоров». Тактовая частота упёрлась в ограничение, но начало расти количество ядер, используемых в процессоре.


Все основные языки, которые используются в бэкенде, были созданы, когда закон Мура работал и казалось, что так будет всегда: C (1972), C++ (1983), Python (1991), Java (1995), PHP (1995), JavaScript (1995), C# (2000). Дизайн этих языков изначально не подразумевал работу с несколькими ядрами. 

Go — достаточно молодой язык. Ему всего 12 лет, и он сразу делался с оглядкой на то, что код будет выполняться на нескольких ядрах процессора. И в этом его невероятная сила. 

А ещё Go изначально задумывался Робом Пайком, как язык, который ускоряет всё: процесс написания кода и процесс компиляции. Никаких ожиданий — всё под девизом «Make programming fun again».

Плох тот бэкендер, который не хочет писать хайлоад


Я всегда мечтал писать хайлоад — и это было первым аргументом в пользу Go. Вы можете возразить, что на Python тоже есть хайлоад, и будете правы. Когда говорят про Python, всегда упоминают, что на нём написаны Instagram и Dropbox.

На сегодняшний день Instagram — проект с самым большим трафиком, написанный на Python. Чтобы заскейлить горизонтально бэкенд на Python при миллиарде MAU (monthly active users), нужно вложить очень много денег в железо — на десятки процентов больше, чем если бы он был написан на более производительном языке. При таких объёмах затраты на железо бьют по карману. Но есть хорошая новость: такая задача в принципе решаема. Скорее в Instagram используют Python, потому что они заложники легаси-кода, который долго и дорого переписывать. 

Dropbox, изначально написанный на Python, переписали часть бэка именно на Golang. Некоторые микросервисы до сих пор работают на Python, но это периферийная ненагруженная часть, core-функционал переписан на Go. И причина простая — Golang более производительный. 

Основной язык бэкенда компании Lyft — Python. Агрегатор вычислил стоимость железа на одну поездку в такси, заказанную через приложение, — 14 центов. Количество поездок, совершённых в 2018 году, — порядка 650 миллионов. Lyft тратил 100 миллионов долларов в год на инфраструктуру, и сейчас эта сумма ещё больше. Если бы это был Golang стоимость железа могла бы быть меньше на несколько десятков миллионов долларов.

Хайлоаду важен производительный бэкенд — это вопрос экономики. Железо дешевле, чем люди, но на объёмах в сотни миллионов MAU затраты на разработку сравнимы по цене с железом, и выбор непроизводительного языка может привести компанию в ловушку, когда на железо будут сливаться сотни миллионов долларов в год. При этом переписывание бэкенда тоже влетит в копеечку, а, возможно, и вовсе будет невыполнимой задачей.

У Python есть три фундаментальные проблемы:

  1. GIL (global interpreter lock) съедает профит от concurrency. GIL нужен для того, чтобы синхронизировать потоки для работы сборщика мусора. При изменении количества ссылок на переменные GIL блочит все потоки и даёт исполняться только одному. Те, кто пишут код на Python, давно просят разработчиков языка убрать GIL, но он пустил свои корни так глубоко, что, скорее всего, останется с нами навсегда.
  2. Перед выполнением любых операций с любыми объектами Python проверяет их тип. Это боль языков с динамической типизацией, которая съедает производительность.
  3. Почти все объекты в Python аллоцируются на куче, а не на стеке. Неиспользуемая память на куче освобождается медленнее.

Тут нужно оговориться, что невысокая скорость Python — это не баг, а фича. Это расплата за лёгкий синтаксис, с которым можно не заботиться о памяти. Возможность создавать резиновые списки, hashmap’ы, set’ы и т. д. не может быть бесплатной.

Golang же идеален для написания хайлоада. Распараллелить флоу программы на несколько ядер и собрать результат воедино можно без танцев с бубном, а с помощью синтаксиса из коробки. Тут стоит упомянуть, что многоядерность — это не панацея и что она работает до определённого предела. 

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


Ещё одним преимуществом Go являются горутины. Это легковесные потоки исполнения программы. В отличие от тредов в операционной системе горутины: 

  • быстро стартуют;
  • имеют расширяемый стек памяти;
  • могут эффективно коммуницировать друг с другом через пакет sync и каналы;
  • менеджерятся планировщиком от Go и не завязываются на ОС.

Читаемость и предсказуемость кода


На одно написание кода приходится в среднем пять его прочтений. А некоторые критические места любого сервиса перечитываются десятки раз. В Zen of Python есть такие строки:

Должен существовать один — и, желательно, только один — очевидный способ сделать что-то

На деле мне как-то на собеседовании задали вопрос про то, сколько существует способов развернуть список.

lst = [1, 2, 3, 4, 5]

# 1, разворачивает список in-place, возвращает None
lst.reverse() 
# 2, возвращает reversed object, который надо завернуть в list()
new_lst = list(reversed(lst))
# 3, разворачивает список с использованием слайса, возвращает новый объект списка
new_lst = lst[::-1]

Очень легко запутаться и забыть, какой способ что возвращает. Это порождает ошибки. 

Python пошёл по пути добавления синтаксического сахара. В какой-то момент его стало чересчур много. Например, объединить два хешмапа до версии 3.9 можно было вот таким способом:

new_dict = {**dict1, **dict2}

Но в 3.9 для этой операции появился ещё один вариант:

new_dict = dict1 | dict2

На вопрос: «А как же Zen of Python?» — в стандарте дан ответ: «Ну мы же говорим про один очевидный способ». А какой из них очевидный-то?

Там же в стандарте кодеры ругаются, что синтаксический сахар затрудняет чтение. 

В Golang нет built-in-способа объединить два хешмапа. Приходится делать так:

for k, v := range b {
    a[k] = v 
}

Ужасно? Но я сразу понимаю замысел программиста, который писал этот код. 

Синтаксический сахар порой становится миной замедленного действия. Например, в Python есть прекрасное средство для быстрого создания списков — list comprehension.

lst = [i for i in range(5)] # [0, 1, 2, 3, 4]

Но если такое средство есть, то в один момент ты встречаешь в чужом коде вот такую конструкцию.

init_data_struct = [{'a': 10, 'b': 20}, {'p': 10, 'u': 100}]

# нам нужно объединить два словаря внутри списка, выкинув оттуда ключи 'b','u'
new_data_struct = [[(k,v) for k,v in d.items() if k not in ['b','u']] for d in init_data_struct]

# [[('a', 10)], [('p', 10)]]

И чип начинает искриться. А что делает эта конструкция? Что хотел сказать автор? Сложность кода и так растёт экспоненциально. А если это часть какой-нибудь функции-портянки строк на 50, то восприятие теряется, ты снова и снова утыкаешься в это место и не понимаешь, что происходит. Я наблюдал, как среди разработчиков иногда начинается гонка, как уложить код в как можно меньшее количество строк. В итоге получается огромная цепочка вызовов в рамках строки — и никто не знает, как она работает.

В Golang реально есть только один очевидный способ сделать что-то. Без дзенов. Там просто нет синтаксического сахара. На написание любой конструкции требуется больше кода, чем в Python, но зато паттерны видны сразу, постоянно встречаются одни и те же конструкции, которые упрощают коммуникацию разработчиков через код.

Статическая vs динамическая типизация


У Python динамическая типизация — это значит, что тип переменной определяется в рантайме. У Go тип переменной определяется при компиляции.

a := "UpdateReviewStatus"

b := 1
c := a + b

Этот код в Golang просто не скомпилируется. Возможно, дело даже не дойдёт до компиляции, так как IDE начнёт ругаться и разработчик заметит это. Проект на Python с подобным кодом запустится, но выдаст ошибку в рантайме (хотя и тут IDE поругается и укажет на ошибки). За это Go-разработчики расплачиваются тем, что приходится писать похожие функции для разных типов. Кажется, небольшая плата за баги, отловленные на моменте компиляции. Если код на Go скомпилировался, то он рабочий в подавляющем большинстве случаев.

В версии Python 3.6 появились аннотации типов. Они подсказывают разработчику, какой тип заходит в функцию и что возвращается. Аннотации типов сделаны для того, чтобы подсказать разработчику, как функция работает. Даже если передать в функцию с аннотированными типами значения с другими типами, код запустится и будет работать.

def add(x: int, y: int) -> int:
    return x + y

add("a", "b") 
# ab

На аннотацию типов немного поругается IDE, а в остальном интерпретатор будет безразличен. Многие разработчики считают, что аннотация типов — это не Pythonic way. Хотя лично я считаю аннотации обязательными: с ними код читается гораздо легче, хоть и нет никаких гарантий, что в функцию зайдёт нужный тип.

Где больше денег, Лебовски?


По данным исследования Stack Overflow, медианная зарплата Go-разработчиков выше, чем у разработчиков на Python, на 27%: $75 669 против $59 454. В России, по данным Хабр Карьеры, разрыв ещё больше — 38%: 180 000 рублей против 130 000 рублей. 

Здесь стоит упомянуть, что на Python, например, пишутся парсеры и прочие скриптовые истории, — они тянут медиану зарплат вниз. Но если взять 90-ый процентиль, то есть 10% самых дорогих специалистов, то разница между самыми дорогими разработчиками на Go и на Python составит примерно 10%. Для меня это был приятный бонус при переезде. 

Тут стоит упомянуть, что переход на другую технологию почти всегда сопровождается падением по зарплате. Рынок верит в то, что опыт работы на конкретном языке важнее, чем опыт в целом. Но когда тебя хантят, то тебе компенсируют упущенную выгоду на твоём языке. Ты можешь учиться за счёт работодателя, что является приятным бенефитом.

Вывод


Отрефлексировав свой опыт, я убедился в правильности своего решения о переезде на Go:

  1. Во многих технологических компаниях он стал языком номер один. Если всё будет хорошо, то Golang станет стандартом высоконагруженного бэкенда — кажется, к этому всё и идёт.
  2. На Go пишется хайлоад, а это другой уровень задач и их разнообразия. В Ozon в период осенних распродаж держали планку в 5000 заказов в минуту.
  3. У Go прозрачный синтаксис и статическая типизация, что улучшает читаемость кода и уменьшает количество просаженных багов.
  4. Go-разработчикам больше платят. В России они вообще входят в топ-3 по зарплатам. 

Но и Python я забывать не планирую. Я внимательно слежу за ним и продолжаю решать на нём свои задачи.

Комментарии (105)


  1. kalombo
    28.12.2021 16:48
    +3

    При этом некоторые компании отключают GIL на уровне интерпретатора. Например, Wargaming пишет свою систему оплаты в World of Tanks на Python, и он отключил GIL в угоду производительности.

    Сходил по ссылке, там говорится, что они Garbage collector отключают, а не gil. Gil можно отключить в расширениях на си, только придется писать на си)


    1. JJOSKEY Автор
      28.12.2021 16:59

      Спасибо огромное, действительно неточность. Убрал эту штуку из статьи.


  1. MentalBlood
    28.12.2021 16:56
    +11

    new_data_struct = [[(k,v) for k,v in d.items() if k not in ['b','u']] for d in init_data_struct]

    Можно использовать форматирование:


    new_data_struct = [
        [
            (k, v)
            for k,v in d.items()
            if k not in ['b','u']
        ]
        for d in init_data_struct
    ]


    1. JJOSKEY Автор
      28.12.2021 17:00
      +3

      Да, вы правы. Но, кажется, с форматированием, код не стал понятнее. Хотя это моя вкусовщина, но я бы точно поставил nitpick, когда ревьювил код.


      1. VPryadchenko
        28.12.2021 23:01
        +1

        Лёгкость понимания таких конструкций дело привычки, а форматировать их не можно, а нужно )


    1. perestroika
      29.12.2021 12:34
      +7

      Тогда уж лучше просто:

      new_data_struct = []
      for d in init_data_struct:
          for k, v in d.items():
              if k in ['b', 'u']:
                new_data_struct.append((k, v)


      1. AnthonyMikh
        30.12.2021 17:31

        Ииииии вы инвертировали условие для добавления ключа в new_data_struct


  1. dixoNich
    28.12.2021 17:04

    Спасибо


  1. OGR_kha
    28.12.2021 17:23
    +3

    # нам нужно объединить два словаря внутри списка, выкинув оттуда ключи 'b','u'

    [[('a', 10)], [('p', 10)]]

    Так задача не выполнена. Получилось не объединение словарей внутри списка, а список списков кортежей


    1. JJOSKEY Автор
      30.12.2021 14:41

      Хотел хитрый пример найти, но, видимо, не получилось ( замечание полностью валидно, сорян (


  1. katletmedown
    28.12.2021 17:26
    +2

    Те, кто пишут код на Python, давно просят разработчиков языка убрать GIL

    Вы драматизируете, с этим вполне можно жить не слишком страдая.


    1. RC_Cat
      28.12.2021 17:30
      +2

      Вы драматизируете, с этим вполне можно жить не слишком страдая.

      Но зачем жить и страдать. Тоже как автор перекатился на Go.


      1. katletmedown
        28.12.2021 17:33
        +3

        Тогда поподробнее, в чем заключались ваши страдания?


        1. RC_Cat
          28.12.2021 20:24

          • GIL

          • Настройка venv для каждого проекта

          • Отсутствие типизации


          1. katletmedown
            28.12.2021 20:54
            +6

            Ещё конкретнее) GIL вам мешал в ситуации, когда... Я не издеваюсь, просто пишу конкурентные программы, и мне не мешает, вдруг я что-то не так делаю.


            1. igrishaev
              29.12.2021 15:53
              +1

              Не совсем про GIL, но все же: была задача распарсить лог апача на 10 гигов и собрать стату. Программа на питоне делала это час, программа на си — пять минут.


              1. masai
                29.12.2021 16:39
                +1

                Так речь же о GIL. Просто его очень часто упоминают как недостаток, но потом оказывается, что не особо он и мешает в большинстве случаев.


          1. masai
            29.12.2021 16:50
            +1

            Создание venv — одноразовый процесс, который легко автоматизируется с помощью того же Poetry.

            Про GIL было бы интересно послушать, как именно он мешал. Какие-то экзотические задачи были?

            Отсутствие статической проверки типов — тут соглашусь, в больших проектах не хватает. Mypy, увы не очень удобен.


            1. RC_Cat
              30.12.2021 16:20

              Мне нужно скачать 100k файлов с разных ресурсов и записвать в файлы. Может я что-то делаю не так, но работает это не очень быстро.


              1. masai
                30.12.2021 22:43

                Это как раз типичная I/O-bound задача. Почему Вы думаете, что тормозит из-за GIL? Тут он как раз не очень мешает.

                Посмотрите презентацию с EuroPython 2019. Там миллиард файлов скачивали примерно за 8 часов (синхронный код работал 48 дней). Использовали комбинированный подход: asyncio + потоки + процессы.


    1. JJOSKEY Автор
      29.12.2021 15:56
      +1

      Съедается выгода от асинхронщины. Потому что весь флоу исполняется в один поток. Я не старался драматизировать, но многие говорят об этом. При этом, прекрасно понимаю, что не каждому сервису это нужно. Как я уже сказал в статье, Python решает свои задачи, и делает это хорошо.


      1. masai
        29.12.2021 16:47
        +4

        Асинхронщина и многопоточность — вещи ортогональные. Она и без GIL однопоточная, если сами поток не запустите.

        GIL мешает только если вы параллельно делаете вычисления на чистом Python (что бывает очень редко), то есть у вас CPU-bound задача. А большинство задач на чистом Python I/O-bound, и тут GIL не мешает вовсе.

        К тому же в расширениях GIL можно отпускать, что и делают всякие числодробильные библиотеки (например, Numpy) — они многопоточные внутри. Можно в Cython легко отпускать его, если не хочется связываться с другими языками.

        Разработчики давно говорят, что убрали бы GIL, если кто-то предложит реализацию, которая не замедлит однопоточные скрипты. Пока что такой реализации не видно. Даже в недавно попытке выпиливания GIL прирост скорости от оптимизаций, а не от убирания GIL.

        Ну и наконец, есть реализации Python без GIL. Это особенность интерпретатора, а не языка.


        1. JJOSKEY Автор
          29.12.2021 18:59

          Позвольте порассуждаю, если не прав, поправьте. Реально, хочу разобраться.

          Работа какого-нибудь апи метода - это не на 100% i/o-bound задача. Там всегда есть cpu-bound составляющая. Парсинг запроса и json'а, формирование запроса в бд, манипуляция с полученными данными, какая-то дополнительная логика, формирование ответа и так далее. Это всё cpu-bound. Значит, что при каких-то понятных нагрузках, в сервисе всегда будут cpu-bound задачи, то есть вся система перейдёт в работу в один поток.


          1. masai
            29.12.2021 19:50
            +2

            Запрос в БД — это I/O bound задача и она на порядок или два дольше, чем всё остальное.

            Парсинг JSON — это код, вызывающий библиотеку, написанную на C или C++, и она, скорее всего, отпускает GIL, так что может работать в фоне.

            Манипуляция с данными и формирование ответа — это маленький кусочек времени.

            Да, на Go код будет работать быстрее, спорить смысла нет, но это больше заслуга того, что Go — компилируемый язык.

            Ну и никто не отменял запуск отдельных процессов-воркеров. На процессы GIL никак не влияет.

            Я не говорю, что GIL полезен. Нет, если бы можно было без него, то конечно было бы лучше. К сожалению, он появился не просто так и от него не так просто избавиться.

            Я лишь хочу сказать, что GIL часто называют среди главных недостатков Python (точнее CPython). У языка много недостатков, но GIL далеко не главный, и Python медленный в типовых задачах вовсе не из-за него.


            1. vtb_k
              29.12.2021 21:13

              Парсинг JSON — это код, вызывающий библиотеку, написанную на C или C++, и она, скорее всего, отпускает GIL, так что может работать в фоне.

              Парсинг json'a в недавнем проекте съедал 40% всего времени работы в профайлере


              1. masai
                29.12.2021 21:47
                +1

                Я не говорил, что это быстро, я лишь утверждал, что это делается внутри библиотеки, которая теоретически может отпускать GIL. Впрочем, я подумал, там, скорее всего GIL всё же будет, так как внутри строится дерево питоновских объектов.

                40 % выглядит как довольно большая доля. Не пользуетесь базой или у вас JSON такие большие?

                В любом случае решение нужно принимать исходя из проекта. Если у вас нагрузка — запрос в минуту и 40 % — это процент от 10 мс, которые устраивают пользователя, то всё прекрасно.

                Если вы не можете использовать процессы по каким-то причинам и нужны именно потоки, и упирается всё именно в то, что декодирование JSON не отпускает GIL, тогда Python не для вас.

                Я вовсе не пропагандирую Python сейчас.


                1. vtb_k
                  30.12.2021 01:07
                  +1

                  В любом случае решение нужно принимать исходя из проекта. Если у вас нагрузка — запрос в минуту и 40 % — это процент от 10 мс, которые устраивают пользователя, то всё прекрасно.

                  Я просто перекладываю json'ы из кафки в базу. И чем больше база, тем дольше перекладывать. Занимало в среднем 24-32 часов для базы ~500гиг. Переписали на го и теперь 12-14 часов. Json'ы сложные, многоуровневые документы


              1. pesh1983
                30.12.2021 10:02
                +2

                Тут часто время зависит от того, какую библиотеку вы для этого использовали. Стандартная медленная, попробуйте https://github.com/ijl/orjson.


                1. vtb_k
                  30.12.2021 10:51

                  Пробовали, не пошла, уже даже не помню почему.


            1. JJOSKEY Автор
              30.12.2021 14:42
              +1

              Спасибо большое, что объяснили, очень ценная инфа


  1. Terras
    28.12.2021 17:30
    +10

    «большого количества вакансий для Python-разработчиков — изи катка для входа в профессию» — это ложь, на бек на Python вакансий меньше, чем на C#/Java/PHP и там требуется сразу адекатные ребята с прямыми руками.

    Лучше скажи автор честно — насмотрелся инфоцыган и повелся на хайп.


    1. JekaMas
      28.12.2021 18:00
      +3

      12 лет golang... Какой тут хайп? Вы о чём?


      1. Almas016
        29.12.2021 12:35

        Ну типа многие сейчас говорят о golang. Как я сменил языки и тд


    1. JJOSKEY Автор
      29.12.2021 16:03

      Иду прямо сейчас на hh.ru, выбираю Москва. Ищу только Junior вакансии:
      "python junior": 264 вакансии;
      "java junior": 192;
      "php junior": 93;
      "php junior": 93.

      Ну и я сам нашёл работу джуном без проблем. Кажется, ваш поинт невалиден )


      1. Terras
        29.12.2021 17:32
        +1

        Может быть со знанием Django можно пойти на:

        Product/Data Analyst (Junior)
        Junior Data Scientist
        Разработчик ботов (Python & JS)
        Junior ML Engineer
        Инженер поддержки (Junior)
        Младший специалист по верификации RTL (Junior)
        Стажёр-инженер АСУ / Automation junior engineer

        И прочие непрофильные сферы, которые показываются по этому запросу? Или инфоцыгане научили, что на Питон вакансий куча, а то что, ты со знанием Django не сможешь пойти в условный ML или QA-Automation (так как это другая сфера и нужен другой стек знанаий) как-то забыли объяснить? Или вот Junior Devops тоже проходит как python junior?


        1. JJOSKEY Автор
          29.12.2021 18:43

          Не понимаю, при чём здесь инфоцыгане? Я даже курс не покупал, когда начинал, учился сам. Вы хотите оспорить тезис про популярность языка Python? Пыху он точно подвинет. C# и Java, кажется, для других задач, Python им тоже не конкурент.


  1. almaz1c
    28.12.2021 18:00

    Есть ли в Go, какой-нибудь условно типовой стек наподобие Питоновского: Flask + SQL-Alchemy + Alembic? Было бы интересно узнать, насколько сложно было перейти с Python на Go в бэкенде, заменив один стек на другой, оставив при этом применяемые и тут и там: Docker, Postgersql и Postman?


    1. Ommonick
      28.12.2021 18:24

      Scratch, ptorobuf


    1. valis
      28.12.2021 19:07
      +2

      Я не юзаю пока как таковые веб фреймворки. Мне роутера Gorilla/Mux за глаза.

      С докером вообще круто - юзаешь самый минимальный контейнер после билда.

      Я даже на Golang реализовывал немного машинного обучения (распознание лиц)


      1. Sly_tom_cat
        28.12.2021 19:18

        В докер go вообще можно собирать FROM SCRATCH (если cgo не нужен)


        1. vp7
          28.12.2021 23:28

          Кстати, спасибо за напоминание. Удивительно, но я как-то умудрился про такое забыть.


          1. QeqReh
            30.12.2021 09:24
            +2

            Не забудьте помимо бинарника скопировать корневые сертификаты, если делаете внешние запросы)


    1. rinat_crone
      28.12.2021 20:01
      +1

      Почитайте вот эту статью: habr.com/ru/company/agima/blog/591435. Для новичка в Го она будет неплохим стартом для погружения.


    1. random1st
      28.12.2021 20:30

      Там конкретного стека нет, но на Go и не пишут монолиты. Есть какие-то простейшие фреймворки - отдельно для работы с базой, отдельно для HTTP API (если встроенной недостаточно) и прочее. Попытки наворотить что-то наподобие Django есть - но это совсем не тру.


    1. JJOSKEY Автор
      29.12.2021 16:07
      +2

      В Go нет ORM. Точнее есть, но сообщество пришло к тому, что это не Go way.

      По поводу фреймворка, они есть, но Golang используют в основном в крупных корпорациях, и там юзают свою обвязку на Golang.

      Так что в Go фреймворк вторичен, нужно учить сам язык и синтаксис. Также советую посмотреть вот этот видосик, он прольёт больше света по проблеме, которую вы озвучили: https://youtu.be/XR9NiwuiK-k


  1. OlegZH
    28.12.2021 18:24
    +2

    Сам по себе переход на другой язык программирование — это сама по себе хорошая идея. Надо меняться.


  1. itsoft
    28.12.2021 18:34

    А я недавно себе книжек по пайтону накупил.

    Пойду теперь по Go покупать. :) Мне понравилось, что там код более понятный.


    1. Ommonick
      29.12.2021 11:03
      +14

      Обменяйте, скажите что сюжет не понравился.


  1. valis
    28.12.2021 19:08
    +4

    Сам перешел и не жалею! Это по моему мнению самый логичный язык программирования из всех, что я только учил (помимо Python JS,C#, Dart, Visual Basic, Delphi, Lua)


  1. GBR-613
    28.12.2021 19:10
    +1

    Интересно, что народ думает про Юлю? (Она же Julia.) Её разработчики утверждают, что она проста как Питон и при этом быстра как С, но у меня что-то нет особого впечатления, что ей кто-то пользуется. Почему?


    1. omxela
      28.12.2021 22:53
      +1

      Чтобы переползти на новый язык, нужна мотивация и немного любопытства. Юлия столь же молода, что и GoLang. При этом её изначально позиционировали как вычислительный язык. Ну, типа, фортран сегодня. Вернее, Матлаб, но быстрый. Вот название русскоязычного руководства: "Язык программирования математических вычислений JULIA". Это сильно сбивает, на самом деле. Язык современный, действительно очень простой, но при этом подключает питоновские библиотеки, например. И фортрановские. И сишные. Параллельный не хуже Go. Но вот кому он нужен и где используется - вопрос. Мне хорошо - я одиночка, программирую физические задачки, сделал пару проектов на Юлии просто из чистого любопытства. Поставил галочку - и вернулся обратно. Не думаю, что у этого чудесного языка есть какие-то широкие перспективы, кроме его узкой ниши. Ну, отрежет он что-то у R. Что-то у Питона (биг дату немножко). У Матлаба, безусловно (если он кому-то нужен). Это не тот размах, имхо.


    1. z0ic
      29.12.2021 00:10
      +2

      Julia это достойная (и бесплатная) замена таким системам, как Matlab или R. Последнее время в неё вливаются колоссальные деньги https://juliacomputing.com/media/2021/07/series-a/. Но это не язык общего назначения, у неё очень тяжеловесная среда, примерно как Anaconda.


      1. GBR-613
        29.12.2021 08:09

        Если программу можно откомпилировать, кому потом помешает тяжеловесная среда?

        В чём заключается это "не язык общего назначения"? Нет средств для HTML? GUI? Это вопрос наличия библиотек, а не языка.


    1. masai
      29.12.2021 16:56

      Идея у языка интересная (хоть и есть странности в дизайне), но нет экосистемы, а без неё никто на Julia переходить не будет.


  1. worldmind
    28.12.2021 20:22
    +6

    Вот что-то никогда го меня не возбуждал (хотя может я просто не понял чего, не углублялся):

    1. Exceptions убрали, но ничего взамен для error propagation не предложили, возвращай кол ошибки как в сяз, зотя именно из-за этого эксепшены и появились. Как понимаю в расте слелали нормально.

    2. Компилятор не ловит null-pointer exception, можно получить его в проде, расты/хаскелы ловят на сталии компиляции.

    3. Нет дженериков, надо или копипастить код или кодогенерацией заниматься, не аккуратно как-то. Везде считай есть

    4. Нет механизма чистки языка от легаси, помню был какой-то косяк со строками, который видимо никогда не пофиксят из-за обратной совместимости. В расте вроде решили.


    1. icecube092
      28.12.2021 23:24
      +2

      В расте все хорошо. Только попробуй найти работу на нем :)

      А дженерики через пару месяцев завезут, но их отсутствие не проблема


      1. worldmind
        29.12.2021 00:59
        +1

        Да, работы нет.

        Ну поглядим, давно говорят, если дженерики заведут, то по идее и null-pointer проблему можно пофиксить, но опять же на практике обратная совместимость не позволит.


        1. QeqReh
          29.12.2021 07:17
          -1

          обратная совместимость не позволит.

          Для этого есть абстрактный Go2)


          1. worldmind
            29.12.2021 10:27

            Да вот пока нет его, ждём-с


        1. WASD1
          29.12.2021 15:56
          +2

          Не понял, а как дженерики связаны с NPE?
          NPE - это же завезти "засахаренную ad-hoc монаду Maybe" + поломать обратную совместимость (в Java / С# вон уже сколько лет пытаются - не выходит аленький цветочке).


          1. worldmind
            29.12.2021 16:32

            Да, согласен, просто без дженериков надо Maybe пол каждый тип.


          1. marshinov
            30.12.2021 09:22
            +1

            Ну в c# получилось (почти) для новых проектов (хотя для value и reference сделано по разному), а в Java получился Kotlin. Стало получше. Но в общем случае вы правы, надо вообще к херам систему типов ломать, чтобы совсем null победить


            1. AnthonyMikh
              30.12.2021 17:35

              Но в общем случае вы правы, надо вообще к херам систему типов ломать, чтобы совсем null победить

              Нет, нет, нет. Само наличие null в системе типов — это баг, который пошёл ещё от Алгола. Убрать null — это не сломать систему типов, а, наоборот, починить.


              1. marshinov
                30.12.2021 17:58

                Починить, поломав обратную совместимость :)


    1. QeqReh
      29.12.2021 07:16
      -1

      Компилятор не ловит null-pointer exception, можно получить его в проде, расты/хаскелы ловят на сталии компиляции.

      Существует линтер: https://staticcheck.io/docs/checks#SA5011


      1. worldmind
        29.12.2021 10:26
        +1

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


        1. QeqReh
          30.12.2021 09:37

          1. Линтеры приучают писать корректный и правильный код.

          2. Код надо покрывать тестами.

          И уже научившись писать правильно у тебя не будет проблем с nil-pointer.


          1. worldmind
            30.12.2021 10:45

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

            Насколько знаю го появился как раз потому что умные и талантливые гуглеры (знающие почему люки круглые и решающие весь letitcode за вечер) не осиливали писать правильный код.


    1. 143672
      29.12.2021 12:35

      В 18 версии дженерики. Я либу пишу, чтоб перенести Option, Result, Either в Go. Конечно enum типа нет и размер структуры будет из за этого больше, но проблема с nil pointer будет решена, как в скале. Если все просто начнут использовать option и явно его обрабатывать


      1. worldmind
        29.12.2021 14:50
        +2

        Не, надо чтобы по умолчанию nil был невалидным значением для типа указатель.


    1. dikey_0ficial
      29.12.2021 13:47

      вместо экзепшнов есть panic, генерики уже в бете, легаси потихоньку чистят. вот нулпоинтеры это грустно, пару раз попадался вроде бы рабочий код, но с неинициализированными мапами, в итоге всё ломалось


      1. worldmind
        29.12.2021 14:55
        +3

        Panic это не замена для всех исключений.


    1. JJOSKEY Автор
      29.12.2021 16:11
      +1

      1. Да, по началу самого бесили эти постоянные if err != nil, но сейчас привык, и мне нравится. Перехват ошибки может осуществляться где-то в непонятном месте, что увеличивает когнитивную сложность системы, а тут ты ошибки протягиваешь через цепочку исполнения и явно видишь их.

      2. Проблема, согласен.

      3. Дженерики завозят скоро.

      4. Всё-таки, к этому можно по-разному относится. Для кого-то это преимущество, и возможность без проблем обновлять систему на новую версию языка.


      1. worldmind
        29.12.2021 16:41

        1. Так понятно что исключения это разновидность go to, но не руками же пробрасывать коды ошибок по всему стэку, в расте вроде макрос для этого есть.

        1. Ну в реальном мире сходу идеально никто не умеет делать, надо развивать и фиксить, обратная совместимость тормозит и даже блокирует развитие, для беспробоемного обновления нужны другие механизмы, вроде фиксации версии языка, могу наврать, но вроде в расте так сделали.


      1. masai
        29.12.2021 16:58
        +7

        сейчас привык, и мне нравится

        Стокгольмский синдром. :)


  1. random1st
    28.12.2021 20:34
    +10

    Какой смысл ограничиваться одним языком вообще? Я знаю что на питоне я смогу написать быстро сложную бизнес-логику с тяжелыми зависимостями, а Go использовать для оптимизации отдельных частей. Если понадобится CMS - я возьму Wordpress, просто потому что в код мне придется заглядывать крайне редко. Еще что-то - Java тоже рабочий инструмент. Go как раз тем и хорош, что можно обвешаться линтерами, помимо уже имеющихся ограничений, и писать по крайней мере читабельный код. Но это не значит, что он лучше Python, это значит, что в него порог входа ниже. На самом деле после питона на голанг пишешь и страдаешь. Возможно, выходцам из PHP он лучше заходит :)


    1. Stas911
      29.12.2021 05:08
      +1

      "На самом деле, после питона на голанг пишешь и страдаешь" - все очень индивидуально, у меня как раз ровно обратное впечатление :)


      1. random1st
        29.12.2021 13:02
        +3

        ну не знаю, там где на питоне обходился тремя строчками на го пишу простыню


    1. QeqReh
      29.12.2021 07:19
      -1

      Поддерживаю. Языки программирвоания это по сути инструменты со своей экосистемой, и все наилучше подходят для разных задач. Нет универсальных языков.


    1. dzaytsev91
      29.12.2021 16:26
      +1

      У меня ровно обратные ощущения после перехода на го, на питон смотреть больно. А вообще изучение новых языков сильно помогает развить кругозор, замыкаясь в одном языке перестаешь видеть его проблемы, например питонисты до сих пор считают что GIL не проблема или что питон быстрый язык. В целом не надо быть рокстар программистом чтобы понимать что разные языки программирования подходят под разные ситуации и не нужно микроскопом забивать гвозди


      1. masai
        29.12.2021 17:04

        например питонисты до сих пор считают что GIL не проблема

        Приведите, пожалуйста, какой-то конкретный пример из Вашей практики, когда GIL сильно мешал и ничего нельзя было сделать.

        или что питон быстрый язык.

        Это где такие утверждения? Питонисты так не считают.


  1. maksim_skorynin
    28.12.2021 22:01
    -1

    Спасибо за статью ????


  1. uldashev
    28.12.2021 23:11
    +3

    Dropbox, изначально написанный на Python, переписали часть бэка именно на Golang.

    А я помню эту историю, когда пошел хайп, переписывать все на Go, хипстеры из Dropbox тутже кинулись перписывать все с python на golang, конференции, статьи, много шума, закончилось тем, что часть они таки перписали, но вот производительность была не на много выше чем у python, а тут rust пошел на хайп и что? правильно - перпишем все на rust. И в результате, пока google drive, onedrive? да тот же yandex пилили новые фичи, команда dropbox занималась перписыванием старых, кончилось все закономерно, а ведь когда то dropbox был №1 в мире облачных хранилищ.


  1. z0ic
    29.12.2021 00:19
    +1

    Чтобы чип не искрился при виде [[(k,v) for k,v in d.items() if k not in ['b','u']] for d in init_data_struct] нужно было учить Хаскель. Кстати, на нём даже веб-сервисы можно программировать (наверное).


    1. RarogCmex
      30.12.2021 21:17

      Можно, я пробовал WAI/Warp. Проблема в том, что хаскель -- это такой C++ от мира функциональщины, и я в один момент начал болеть и перестал справляться с его сложностью. А ещё Servant -- фреймворк не для начинающих, проверено на личном опыте.

      В тему с пояснениями, почему хаскель рано или поздно должен сдохнуть, кастуется @0xd34df00d


  1. warlock13
    29.12.2021 06:00
    +4

    На написание любой конструкции требуется больше кода, чем в Python, но зато паттерны видны сразу, постоянно встречаются одни и те же конструкции, которые упрощают коммуникацию разработчиков через код.

    Вот как раз существование постоянно повторяемых паттернов, которые видны сразу, означает, что когда вы читаете код, то вы воспринимаете эти самые конструкции-паттерны как целое, как элементы синтаксиса некоего "Го с сахаром", существующего неявно в головах раработчиков на Го. Всё отличие от языков с сахаром только в том, что записываются эти конструкции излишне громоздко, что повышает сложность чтения. То есть простота Го для в меру опытных Го-разработчиков - липовая, по факту она отрицательная.

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

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


    1. QeqReh
      29.12.2021 07:37
      +1

      Не согласен. Благодаря простоте Go, его код легко читаем. Из-за знакомых конструкций глаз сразу цепляется за нужные места и в целом чтение кода похоже на чтение книги.

      Тогда как в языках с сахаром часто приходится ломать голову. Особенно если ты только познакомился с каким-то языком на базовом уровне и начинаешь разбираться в коде какого-то проекта или библиотеки. И такие конструкции часто сложно как-то загуглить.

      В итоге тратится больше времени разработчика и в целом увеличивается сложность кода, что чревато ошибками и трудно выявляемыми багами, которые скрыты за магией сахара.


  1. QeqReh
    29.12.2021 07:07
    +2

    Go — достаточно молодой язык. Ему всего 12 лет, и он сразу делался с
    оглядкой на то, что код будет выполняться на нескольких ядрах
    процессора. И в этом его невероятная сила.

    Тут я с вами немножко поспорю. Go это результат опыта полученного от работ над такими языками как Alef и Limbo, а так же платформой plan9. В первых версиях Go был полностью основан на plan9. А это внимание: 1989 год!

    Настоятельно рекомендую посмотреть доклад Филиппа Кулина: https://www.youtube.com/watch?v=ql-uncsqoAU

    Так что истоки у Go очень древние. Это кстати объясняет подход к работе с ошибками, так как тогда ещё подход с exceptions был не столь популярным среди языков.

    медианная зарплата Go-разработчиков выше, чем у разработчиков на Python

    Есть предположение, что в этом виноват финтех.


    1. Gizmich
      30.12.2021 08:42
      +2

      Что странно, в го работать с финансовыми данными не удобно, есть пропозал на добавление нативного decimal64 и decimal128, но это не так модно как дженерики и поэтому движения в эту сторону никакого нету. Кстати кому не лень найдите и лайкните )


      1. worldmind
        30.12.2021 10:55

        Язык-то для сетевых микросервисов, на универсальность не претендовал вроде.

        Кстати, пример пхп показывает, что идея языка под задачу не работает в этом мире.


        1. Gizmich
          30.12.2021 11:13

          Даже сетевые микросервисы ходят за данными в базу, где денежнве величины хранятся в decimal


          1. worldmind
            30.12.2021 11:17

            Видимо у гугла таких нет.


  1. bbc_69
    29.12.2021 11:10
    +4

    Вот не понимаю я этого гонева на GIL. Всё равно в проде запускаешь всё через какой-нибудь uwsgi с кучей воркеров, которые рассаживаются по разным ядрам. Концепция многопоточности может и не идеальная, но вполне стройная и понятная. Т.е. по факту проигрыш в скорости не такой уж и значительный. Особенно, если запариться над оптимизацией некоторых моментов.


    1. JJOSKEY Автор
      29.12.2021 16:16

      Но в рамках воркера с async/await синтаксисом всё исполняется в рамках одного треда.


      1. bbc_69
        29.12.2021 16:20
        +1

        И? Процессоры-то все утилизированы. А то, что сделано не так, как в другом языке... Ну тык языки-то разные!


      1. masai
        29.12.2021 17:08

        Но в рамках воркера с async/await синтаксисом всё исполняется в рамках одного треда.

        Если это какой-то веб-сервис, который большую часть времени сидит в ожидании I/O, то в чём проблема? Ожидание не блокирует исполнение.


    1. vwvw
      29.12.2021 18:46

      Как я понял, вот откуда "гонево" на GIL:

      Основной язык бэкенда компании Lyft — Python. Агрегатор вычислил
      стоимость железа на одну поездку в такси, заказанную через приложение, —
      14 центов. Количество поездок, совершённых в 2018 году, — порядка 650 миллионов.
      Lyft тратил 100 миллионов долларов в год на инфраструктуру, и сейчас
      эта сумма ещё больше. Если бы это был Golang стоимость железа могла бы
      быть меньше на несколько десятков миллионов долларов.


      1. masai
        29.12.2021 19:36

        А где в цитате GIL? Тут о Python в целом. Python менее производительный как минимум потому, что он интерпретируемый, и это не исправить убрав GIL.

        Речь не о том, быстрый Python или нет. Очевидно, он проигрывает комилируемым языкам. Речь о том, что если убрать GIL, то код вообще может замедлится.

        Ну и ко всему прочему You are not Google.


      1. bbc_69
        29.12.2021 21:17

        Я даже не про конкретно эту статью, а в целом. Любят у питона GIL покритиковать. Есть что-то стереотипное в этом наезде.


  1. marshinov
    30.12.2021 09:31
    +2

    Докину 5 копеек. В C# быстрее всего поняли, что постоянно трактовая частота расти не будет и завезли async/await, который затем победоносно завезли в JS и Python почти один в один. Плюс у .NET'а сразу был мощный интероп, unsafe и value type. А с появлением .NET 6 с pgo и кучей оптимизаций asp.net вообще ворвался в топ самых быстрых фреймворков по версии tech empower. И все это с дженериками, орм, исключением и возможностью писать коротко. Так что аргумент go моложе, там по-умолчанию все круче, чем в старших собратьях - ну такое:)


  1. yulchurin
    30.12.2021 11:06
    +1

    Тоже подумывал перейти с PHP на Go, потом посмотрел сравнение производительности Go и PHP8 и передумал.

    Может начну изучать Rust...


    1. Gizmich
      30.12.2021 11:39
      +1

      А в php8 уже можно как-то хранить состояние ? В реальном пииложении выигрыш го дает за счет того что однажды проинициализированные переменные уже существуют в памяти, а php строит все при каждом обращении насколько я знаю...


    1. olafars
      30.12.2021 16:26

      Позвольте возразить.
      Вы сравниваете несравниваемое. Два совершенно разных языка для совершенно разных задач.
      Как человек, некогда писавший на PHP и перешедший на Go, говорю Вам об этом.


  1. OkunevPY
    31.12.2021 08:55
    +1

    Свои пять копеек.

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

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

    Мы у себя лабаем на чём бог послал пока формируем mvp, потом выделяем критические секции и переписываем на том, что в данном конкретном сценарии покажет лучший результат. Мероприятие крайне затратное, но без этого никогда не получить максимально эффективное решение


  1. mondzucker
    31.12.2021 13:50
    +1

    Согласен почти со всем! Особенно про неявность в Python.
    Предрекали что он уничтожит неявность, а не будет её плодить.

    Тоже перешёл на Golang в качестве основного. Спустя пару лет некоторых вещей всё-таки не хватает.