Перехід з С++ (not web related) в Web Backend (Java/C#/Python)
Всім привіт.
Передісторія:
Працював на C++. Писав різного роду backend логіку, чи то для системних процесів, чи то для десктоп аплікацій, одного разу навіть навіть веб сервіса довелось писати (як виявляється для цього в нас плюси не часто використовують). Одним словом спеціалізації в чомусь не було. Вирішив щось міняти. Думати довго не довелось. Web на ринку праці України найбільш розвинений.
Мені ближче тай цікавіше backend, от і вирішив цим займатись.
Після інвестигації використовуваних технологій/мов/фреймворків зупинився на Java (Spring), Python (django) і C# (.Net/.Net Core).
Питання:
Проблем в тому що вони всі маю свої плюси, свої мінуси. Не можу визначитись що буде більш актуально/ використовуване на ринку праці.
Буду вдячний за ваші думки нарахунок цього.
141 коментар
Додати коментар Підписатись на коментаріВідписатись від коментарівКоментар порушує правила спільноти і видалений модераторами.
-
Посмотрите в сторону golang. Это тот же С с синтаксическим сахарком и приправленный аппетитным toolchain.
да вот только к Го еще очень много хотят из микросервисов, знания пачки продуктов Hashicorp, kubernetes, docker; со знаниями одного только чистого Го практически не берут
На других языках у работодателей аналогичные хотелки.
дажи на php такое щас требуют :)
Не знаю, на счёт Java и Python, но в .Net, в большинстве случаев, чистый back-end разработчик бизнесу не интересен.
Поэтому, для .Net, добавляем ещё сверху JavaScript, TypeScript, Node.js, JQuery, Angular, React+Redux, Vue.
Скажите пожалуйста, вы рассматривали Go?
1. Не впевнений, але мені здається що з точки зору бізнесу чистий бекенд девелопер не цікавий напевно і в Java з Python також.
2. Ні, Go не розглядав.
дык можно найти работу, где есть плюсы и еще что-то, так и перейдешь за счет работодателя
Був колись С—, але не витримав конкуренції)
Та да, було б непогано.
Справа ваша, але поки я собі кодив на плюсах, у вебі й мобайлі все десять разів встигло помінятись. Бекенд, звісно, також потроху еволюціонує, але переливання з пустого в порожнє там якось менше. Можна мати20-річний досвід Linux, досвід PalmOS чи WinMobile ні в кого й до 10 не дотягнув.
лет 8 назад перешел с C++ бекенд на Java бекенд. Из-за денег :-( . на Java в тот момент были более денежные вакансии и выбор больше. вообще в языках с С-подобным синтаксисом (java, C#, kotlin, scala) много общего, после одного изучить другой — несложно. Python в связке с machine learning сейчас очень хайповая тема и можно денег поднять
А хіба в цепепе програмістів ЗП не більші ніж в веб-макак?
Сильно залежить — широка область і мало люду
Как то я ходил на собеседование на джуна c++. Перед этим сделал тестовое. Во время собеседования меня спросили сколько я хочу, я ответил что 800 дол после испытательного. Мне сказали ну опыт у вас есть судя по тестовому, но владелец(который в германии) после многих студентов принятих за 400 дол будет предлагать 600 дол максимум. А мой друг недавно джуном веб программером на 1.1 к устроился. Вот Вам и выше сложность c++ и «больше» зп.
Было это в Киеве года 2 назад.
ну у меня лет 8 назад с 1.5к на с++ получилось перейти на 4к на пшп
даже 2г назад за плюсы 800 это ни о чем вообще. На пыхе можно спокойно говно месить начиная от 1К. Надо было больше контор попробовать
це ніхто не знає, по великому рахунку усе буде, хоча із наведеної трійки я думаю у python більше веб майбутнього (хоча мені django не дуже сподобалася, але там наче не тільки django є) — просто тому що динамічно типізована мова , що для веба відносно важливо (на c#/java один і той ж саме функціонал буде банально мати більше байтів у коді, чим на python)
можна ще дивитися на ruby та nodejs, воно теж буде, але я б рекомендував дивитися як мова йде, бо я наприклад, відчував себе не дуже комфортно у python, вже звик до фігурних дужок
п.с. десь років 10+ тому уходив із с++, але мені треба було щоб remoting був і щоб без майкрософт (хоча c# дуже красива мова), після деяких поневірянь (і java була і python) вийшов на highload php, мова найбільш органічно «лягла» після c/c++ ну і в базах, оптимізаціях я знався , навіть міг якийсь extension пофіксити і перезібрати
Ну власне напевно, буде усе.
Просто цікавила думка людей, які мають досвід з Web Backend.
Наразі складається враження, що потрібно обрати будь-яку з них і просто почати роботу з Web Backend (те заради чого ця розмова і почалась).
Проте схиляюсь більше до Java.
Чомусь не покидає враження що Python не надто advanced мова, через що і не можу зрозуміти чому вона краща для цих потреб.
Python надзвичайно зручний та гнучкий порівняно з іншими старими мовами. Там навіть є синдром звикання, коли після кількох місяців на пітоні ніщо інше не кодиться. І писати на ньому в рази швидше та компактніше.
Есть подозрение что читаемость и соответствие стандартам зависят от культуры в компании, а не от языка.
Тоді усе потрібно переводити на Раст.
откуда ти узнав?
Якщо писати на Расті — то більшості там не буде, а хто залишиться — ті не зможуть писати гнучкий код, бо мова негнучка)
это как?
ну ти можеш:
1) перегрузити усі оператори
2) перегрузити виклик метода, щоб перед чи після нього викликалась кастомна функція
3) перегрузити доступ до поля об’єкта щоб замість прямого доступа до даних викликались методи-аксесори
4) перетворити об’єкт на клас, щоб він робив інші об’єкти
5) multiple inheritance з діамантами в 1 рядок коду
і ще купа всього, що я не пам’ятаю
тобто, якщо тобі заборонена арифметика з пойнтерами, то це
гг
і без діаманта
напоркуа?
WTF
тіпо рефлексія?
крім юніт тестів в джабі (де тре через опу рвати гланди) не бачив потреби
Невзирая на всю свою скромность, я думаю что могу написать говнокод на любом языке. К тому же, уже практически везде стоят линтеры и используют CI/CD, если код не соответствует стандарту — можно просто ронять билд. Я все равно считаю, что вопрос выбора языка для поддержки и качества кода — это как выбирать между теплым и мягким, когда тебе нужно круглое.
Компилер должен отлавливать часть проблем с обращением к несуществующим членам классов либо опечаток. Интерпретатор этого не может, так как интерпретирует в момент выполнения ту строчку, которую выполняет. Линтер тоже не может если есть exec с динамическим созданием кода. Либо если взять монстра с перегрузкой всего типа Джанго — думаю, там распарсить происходящее тоже будет грустно.
для цього є тести
Бачив 100% покриття логіки тестами?
А якщо поле об’єкту динамічно створюється в одному місці коду, а використовується — в іншому? Якими тестами покриєш: юніт- чи інтеграційними? Інтеграційні тести пишуть усі програмісти коли змінюють щось в своєму коді?
коли паблік апі — так, якщо не тестами , то use cases
ну і взагалі, у реалі майже не зустрічав помилок, про які ви пишете, тобто вони виникають (і то, з усіма цими code completion дуже рідко) , але навіть на етапах попередньої перевірки розробником проявляють себе , ну тобто я написав щось, запустив, отримав помилку — виправив. у прод і далі таке не попадає
але запускати ви будете завжди, просто помилка виявиться на іншому етапі, а за рахунок відсутності компіляції, та додаткових витрат часу на прописування типів — помилка банально виявиться швидше :)
якщо ви пишете документацію до методів, то там і буде ця інформація , навіть більш детально, ніж в декларації метода
отож. у нетипізованих мовах вона виявиться раніше
для програміста, це може бути не просто int, а int в діапазоні (наприклад), або не просто строка, а json
enum зветься
візьми розпарсь зразу при отриманні в окремий тип. нащо тобі нерозпаршений json?
ну наприклад нещодавно ключ від gcloud так передавався, сенс його парсити, якщо він так і передається в апі
cloud.google.com/...ging-service-account-keys
Оберни його в окремий клас, щоб ніхто не почав його міняти. В С++ є ще typedef
typedef const string AccountKey;
це те, про що я кажу. типізовані мови вимагають більше кода, ніяк не підвищуючи якість кода
наразі це не є загальновживаною думкою, бо не усе перейшло на пітон
бо інший розробник , який визиває цю функцію, буде знати що там передавати. і ніякий компілятор тут не допоможе
Сенс в тому, щоб коли Гугл змінить формат ключа, чи коли контора переїде з Гугла на Мікрософт, не переписувати весь код, котрий в Гугла зберігав чи приймав рядок, а в Мікрософта — інт64.
Коли треба переїхати на інший тип, добре, коли цей тип в одному місці названий окремою назвою (typedef). Тоді весь код, де він трапляється, не потрібно переписувати, а просто один раз змінити typedef.
ех, були часи коли іноді після такої простої зміни хедера, потім дві години чекав поки воно перекомпілюється :)
чтоб нельзя было сунуть произвольную строку например
ну може щось змінилося, але раніше enum такі речі, як наприклад вік людини, або zip не дуже :)
а який ренж у віку?
typedef знову ж
вже з’явився компілятор, який має тип age ? :)
Ти не повіриш... але компілятору не обов’язково «мати» тип, достатньо щоб система типів була достатньо гнучкою щоб його описати.
type Age = Not[Less[0]] And Less[150]type AgeInt = Int Refined Age// це скомпілюється
val age1: AgeInt = 0val age2: AgeInt = 10// а це — ні
val age3: AgeInt = -1val age4: AgeInt = 150і навіть
val age5: AgeInt = request.input('price').toAge();скомпілюється, але потрібен буде запуск програми і тести, щоб впевнитися що все працює
я ж про те, що те, що успішна компіляція ніяк не доводить що програма без помилок
я про те що компілятор допомагає не там, де воно реально потрібно
Валідація може буть кривою.
І тому бойлерплейт типу
взагалі непотрібно писати. Він має автоматично генеруватися з моделі даних. При чому інфа з рефайнед типу буде використана для валідації.
Передбачаючі наступний аргумент — якщо в моделі у вас написано, що
price: Age— то тут дійсно ніякий компілятор не врятує. Так саме як і коли знайдеться 150 річний юзер :) Але це вже баги не у програмі а у спеціфікації.дякую. весь цей тред був присвячений відповіді на
Як раз від SQL injection — компілятор може і захистити ... якщо ліба для доступу до бази не дозволяє пихати в себе квері у вигляді RAW строк — тільки типізовану структуру, у якій всі змінювані будуть правильно заескейплені перед відправкою у базу.
Ну... либа должна ещё поддерживать все фичи базы, может я захочу какой-нить хинт оптимизатору прокинуть... Также грустно, когда уязвимость находится в самой либе...
Взагалі будь-який динамічно типізований код — можно описати за допомогою якоїсь сістеми типів. З різною деталізацією в залежності від того — що ця система типів дозволяє описати. Просто у динамічній мові ніхто ці типи не бачить.
Можна сказати, що виконання динамічно типізованної програми — це теж тайпчекінг тих типів ... але він виконується у рантаймі та тільки для тих гілок, що таки були виконані. Ну і баги у якійсь рідко використовуваній гілці, яка чомусь :) не була покрита тестами ловить вже користувач.
ця проблема існує без залежності від типізованості мови
так ці ліби не те що давно вже існують, а обов’язкові для використання як для типізованих, так і нетипизованих мов
отакої, я про це і кажу вже десять коментів
Если вот прямо проект аж дохреналиард строк обмазанных магией — то для такого есть mypy, пишешь себе аннотации и оно проверит. Вопрос в том, сколько вообще существует таких проектов, где это необходимо.
Наверное, их на Питоне потому и нет, что неудобно. На Джаве овердофига, на С++ тоже много.
Та ну. Прототипи, скрипти для парсінгу логів, сторінки інтернет-магазинів, скрипти в іграшках чи 3Д-рендерерах...
Усе, що при падінні не звалить дорогу систему, і що не має біжати 24/7
Сама типізація займає час. Думаю, що скрипт на 1000 рядків Пітона перетвориться на 2000 чи 3000 у джаві чи С++. Тому таке швидше й зручніше писати на Пітоні, і якщо нормально написане — то й розібратись не проблема буде незалежно мови.
Тут не про розмір проекта, а про мову. Мова дозволяє перевантажувати усе, від операторів до доступа до змінних об’єкту до перетворення об’єкту на клас. І підтримує множинне наслідування в один рядок «з коробки».
А те, що великі проекти треба писати на компільованій мові — то усі діти знають)
Тому на Пітоні здебільшого пишуть вспоміжні скрипти та інтернет-магазини. Джанго розробили для швидкого додавання кастомних сторінок з новинами, здається. В ентерпрайз, звичайно, його не пхають.
Але коли требе якийсь прототип — навряд чи буде варіант написати швидше на іншій мові.
Инстаграм и дискас вроде как не согласен, что джангу в прод не пихают.
Кстати аналитика на миллиарды событий в каком-то из сервисов фейсбука написана на питоне и asyncio.
upd. Bitbucket тоже юзает джангу
Джанга в проді з свого народження.
Я про те, що коли проект від мільйону рядків коду, то туди треба компілятор, бо якщо десь не та літера стоїть, і такий монстр через це навернеться — то буде дорого коштувати.
я теж цього боявся, але виявилося що такої проблеми немає, і не має bloated синтакса, де третина коду присвячена тому, щоб розповісти коп’ютеру що строку треба зберігати як строку, а масив — як масив :) а потім ще й явно преобразовувати усе у строки (якщо ми про веб)
так. це трохи полегшує, але у декларації функцій чи преобразуваннях все одне треба писати, toString чи що там
для public методів — так, я так і роблю через type hinting (у php), але private методи, або якісь ну зовсім зрозумілі параметри — id, name ....
ну коли закінчуються логічні доводи, починається ... гг
ну я на php останні 10 років, а до цього лише на типизованих мовах, паскаль, c++, с#. і (передаючи вітання даннінгу крюгеру) раніше вважав так як ви
так що знайдіть час для свого розвитку, подивіться що у світі (розробки) відбувається, воно корисно для мізків.
По моему с версии 3.6 в питошке — спокойно пихаешь аннотации и кормишь это все в mypy. И получаешь свои декларации. Только в очевидных или не критичных местах можно не писать тайп хинты.
А есть пример где вот без типипизации апи выглядит прямо ужасно?
джаві або Сшарпу
А що дуже велике на ньому написане?
Го вже в ентерпрайзі?
в виде микросервисов — вполне. А здоровый монолит на нем писать — чуть лучше, чем на С
docker, почти все продукты www.hashicorp.com, kubernetes... Энтерпрайзные решения
вже давно люди користуються усякими тулзами, які перевіряють код на стандарти і рідабіліті (навіть, я вибачаюсь, у php гг)
От доречі, пітон це єдина популярна мова, де є обов’язкові відступи і не напишеш весь файл 1 стрічкою :)
мені до речі це і не сподобалося в python — не там додав пусту строку — усе, логіка зіпсувалася (я не пам’ятаю деталей, але щось таке було)
ну java це усякий банківський софт і таке інше — дороге і корпоративне, якщо ви себе там бачите — то тоді java, hibernate і фабрики-фабрик-фабрик :)
Неймовірно адвансед. з величезною екосистемою. В усіх топах за популярністю. Раджу.
Сам свічнувся з с,с++ колись.
Ты уверен что питон скриптовый язык, а не интерпретируемый?
в чому мінус для Сника в пітоні?
За шарп и джаву ничего не могу сказать, но если ты решишь врываться в питон, то посмотри повнимательней — сейчас волна хайпа на асинхронные фреймворки(хотя джанга с версии 3.0 вроде тоже должна будет поддерживать, но там пока мутно все) и DS/ML, так что чистой джанги не так и много по моему мнению.
Зачем какие-то асинхронные приблуды вроде ноды, когда есть нормальный многопоточный Go, с самой простой реализацией многопоточности.
Раз ею пользуются — значит кому-то или для каких-то задач она удобнее.
Не знаю не пишу на ноде.*Все дальше сказанное исключительно субъективное мнение*. Не знаю на счет ноды, питон тупо удобней и количество реальных задач с которыми я сталкиваюсь и в которых го показал бы значимый прирост в производительности стремятся к нулю. Переписать какой-то «высоконагруженный» сервис на го — я бы лучше взял раст чисто из-за хайпа.
Да, нода это подыхающий труп, в который всеми силами пытаются вкрутить пародию на многопоточность с кучей костылей в виде промисов, колбеков, воркеров с генераторами впридачу
В том то и дело, что асинхронность это уход от многопоточности. Ты получаешь выполнение в один поток с переключнием контекста, поэтому синхронизировать выполнение становиться в разы проще. Это очень удобно, особенно когда 90% нагрузки всё равно ляжет на базу.
И это ужасно и отвратительно, когда начинаются длинные сценарии с доступом к общим ресурсам, имеющим состояние. В результате нужно продумывать все возможные пересечения запросов к и нотификаций от ресурса, обрывающие асинхронный сценарий, и откат всех ранее проведенных в сценарии изменений.
Асинхронность тут не причём, в таком духе легко можно писать и в многопоточных приложениях. Основное отличие примерно такое: в многопоточным приложениях может выполняться одновременно разные участки кода на разных ядрах, в то время как в асинхронном код выполняется строго в один поток. Также переключение контекста в многопоточном приложении может произойти в любой точке кода, в то время как в асинхронном приложении в строго определенных местах. Обычно такие места даже помечены в коде через await.
Соответственно, при асинхронности у нас меньше забот с синхронизацией, нам не надо думать, какой мьютекс, атомарную операцию или ещё что заюзать, чтобы вставить элемент в общий список или дикт, потому что мы твёрдо уверены в том, что никто другой в этот момент не может с ним работать. Ну а минус, что мы не можем использовать одновременно всю мощь ядер. Впрочем этот минус иногда даже плюс, потому что при интенсивной работе с одним и тем же глобальным состоянием, даже при отключённой синхронизации у меня был случай, когда два потока у меня работали в полтора раза медленее, чем один, а восемь потоков — раз в пять медленнее. Всё из-за конфликта кэшей ядер процессора при доступе к одной странице.
Поэтому асинхронность сейчас очень популярна — куда меньше проблем с синхронизацией, а производительности часто и так хватает, либо ботлнэк в другом месте.
Есть простой подход позволяющий избежать лишней синхронизации: создавать только константные объекты (read-only). Если проанализировать код, в подавляющем большинстве случаев результат вычисления выражений — в дальнейшем не меняется, за исключением разве что счётчиков циклов, которые можно менять атомарно. Если нужно изменить объект — создаётся новый на основании предыдущего. Кроме того, можно работать с каналами, и писать код который выглядит как асинхронный. Хотя реально на самом деле операции могут выполняться многопоточно на уровне ядра или библиотеки.
Скорее два канала позволяют реализовать концепцию сопрограммы (короутины). А вот асинхроность ближе к кооперативной многозадачности.
Ну... Например, у нас есть сервис, который считает число запросов и трафик от определённого пользователя. После чего читает запрос с другого сервера и возвращает ответ обратно. Время отвремени статистика агрегируется в базу.
В асинхронном случае нам не надо беспокоиться о том, что доступ к
при агрегации статистики, а нам немного придёться вспомнить о синхронизации при записи в базу:stat.request_countиstat.in_bytesбыл как-то дополнительно защищён. Мы спокойно напишемsql = f'INSERT INTO stats(request_count, in_bytes) VALUES ({stat.request_count}, {stat.in_bytes})' stat.request_count = 0 stat.in_bytes = 0 await execute_sql(sql)Тут главное не обнулить статистику после выполнения запроса, потому как в этот момент может прозойти переключение контекстаawaitпридёт новый запрос, которые не попадёт в статистику.Без асинхронности нам не обойтись без блокировки, ИМХО:
{ lock() defer unlock() stat.request_count += 1 stat.in_bytes += request.content_size }и
request_count, in_bytes := func() { lock() defer unlock() request_count := stat.request_count in_bytes := stat.in_bytes stat.request_count = 0 stat.in_bytes = 0 return request_count, in_bytes }() sql := fmt.Sprintf("NSERT INTO stats(%d, %d) VALUES ({stat.request_count}, {stat.in_bytes})", request_count, in_bytes)" // execute sqlА как ты предлагаешь?
Вышеприведённая схема с локами — это самый простой и эффективный способ, здесь придумывать что-то ещё — это лишнее. Разве что INSERT с форматированием запроса отправить в отдельной горутине. Соптимизировать можно следующим образом: преобразовать логику так, чтоб изменялось только 1 число, которое можно апдейтить атомарно, и поскольку запросы с обнулением выполняются редко, то реально потоки лочиться не будут. Эта схема будет иметь смысл для высоконагруженного сервиса с обработкой bigdata, иначе можно действительно не заморачиваться с локами, если это сложно, и писать асинхронный код под ноду.
Ну... одно число надо упаковать, распаковать. Надо атомарно обнулять, тут уже будет атомарное сравнение с обменом в цикле. Это плюс дополнительный неочевидный код. Плюс думай, лучше нам блокировку свою на пользователя, или эскалировать до общей для всех. Причём ошибиться достаточно легко, а поймать ошибку сложно. Тут особняком стоит Rust, который просто не позволит скомпилировать ошибочный код. В любом случае это дополнительные вопросы, которые требуют решения, квалификации программиста, в отличие от «написал и забыл».
Опять же для нагруженного сервиса, наверное, стоит рассмотреть вариант уникальных статов для каждого потока, а запись в базу будет агрегировать данные по всем потокам.
Что-то слишком сложно расписано. Упаковывать-распаковывать ничего не нужно, здесь напрямую компилируемый язык, без виртуальной машины, в которой нужно беспокоиться о целостности состояния, операции над элементарными числами. Получать значение и обнулять — нужно под мьютексом, а не атомарно, если точность данных имеет принципиальное значение. Если между чтением и обнулением допустим разрыв — можно конечно вообще ничего не лочить. Мьютекс тут можно объявить локальный, невидимый извне, и поскольку в этом коде других нет, никаких дидлоков не будет.
Насчёт подсчёта в разных потоках если можно полностью разбить задачу — отличный вариант, и по возможности именно так и делается. Но если данные приходят асинхронно, тогда только вариант с локами.
«зеленими нитками», ти про це?
Заставь го работать только на одном ядре, будет тоже самое
.
Для django треба розуміти HTML+CSS — бо власне веб сторінки він також створює. На Джаві є шанс потрапити в ентерпрайз і взагалі не мати справу з вебом, а займатись лише бізнес логікою.
Для джанги не нужно понимать html и css, понимать фронт хотя бы на элементарном уровне нужно в принципе, независимо от фреймворка. На джангу одной командой накатывается рест фреймворк и можно забыть о хтмл и цсс, отдавая эту дичь полностью на фронт.
Зачем вам фронт, если от вас требуется обычное вэб апи? Лучше уделить больше времени на стандартизацию воркфлоу, предсказуемость сигнатур и удобство моделей.