Comments 116
А что вы хотите, индийцам тоже нужно чем-то на хлеб зарабатывать.
+27
Палец вверх за goto!
(Пародия на this is horosho)
-14
В коде ядра Linux тоже есть goto. Все дело в том каким образом в коде на Си конкретное goto применяется и по умолчанию goto в Си говнокодом не является.
+22
Если судить по другим мейнстрим языкам, то проблемы goto обходяться другими алгоритмичискими решениями. Согласитесь, все же 1 goto на 60 строк — это многовато…
+5
Если он даёт прирост производительности и не создаёт уязвимостей, то говнокод это использование громоздких, медленных и тяжёлых конструкций вместо лаконичного и шустрого goto
+15
еще пример где без goto не обойтись (по крайней мере я не вижу как) — выход из трех вложенных циклов. Зачем делать три вложенных цикла — другая тема)
+3
Например завести флаг и проверять его в каждом цикле.
-2
Конечно если эти три вложенных цикла не размазаны на 2000 строк кода…
+3
Я скорее соглашусь с вами. Но я кода не видел этого проекта. Может быть что каждые 60 строк кода им надо освобождать ресурсы и это удобно делать в Си с goto. А за 60 строчек можно много ресурсов наполучать от ОС…
Кстати можно было бы в вашем анализаторе более детальный анализ goto сделать. А то для проектов на Си это актуально. По крайней мере в обзоре было написано только что их много, а еще бы хотелось знать где и почему в коде проекта они используются неправильно.
Кстати можно было бы в вашем анализаторе более детальный анализ goto сделать. А то для проектов на Си это актуально. По крайней мере в обзоре было написано только что их много, а еще бы хотелось знать где и почему в коде проекта они используются неправильно.
+3
Правил с goto сейчас в PVS-Studio нет, так как сам по себе goto ошибкой не является. Конечно, легко сделать ошибку в запутанном коде с переходами туда-сюда. Но там даже человеку сложно понять, что задумывал автор и что получилось. Так что пока идей, как код с goto анализировать не было.
То что много goto, это я просто глазами заметил. А потом посчитал и вообще удивился.
То что много goto, это я просто глазами заметил. А потом посчитал и вообще удивился.
+1
Знаете, я тоже думал, что использование goto — плохой тон. Но потом понял, что это, пожалуй, самый изящный способ выхода из вложенных циклов в большинстве языков.
0
Точно. Вот в питоне нет goto – вместо него пользуют страшное… Exception'ы. Но это даёт ещё один уровень вложенности, что не есть хорошо.
+1
А что-нить типа break(3)?
0
Он там для обработки ошибок вроде используется, т.к. в Си нет исключений. Фактически goto может пригодиться только для досрочного выхода из блока. Все остальные способы использования goto можно назвать быдлокодом.
+2
>Он там для обработки ошибок вроде используется, т.к. в Си нет исключений.
в основном — да, для обработки ошибок, но на самом деле — где надо, там и используется, а не где мама разрешила.
и возврат в начало функции бывает и выход из цикла чуть дальше его конца (аналог for-break-else) и просто пропуск нескольких блоков когда.
хотя бы потому, что три уровня вложенности в функции на 20 строк — намного больший ппц, чем красивый локальный переход. вобщем, имеющий grep. да увидит.
в основном — да, для обработки ошибок, но на самом деле — где надо, там и используется, а не где мама разрешила.
и возврат в начало функции бывает и выход из цикла чуть дальше его конца (аналог for-break-else) и просто пропуск нескольких блоков когда.
хотя бы потому, что три уровня вложенности в функции на 20 строк — намного больший ппц, чем красивый локальный переход. вобщем, имеющий grep. да увидит.
+3
UFO just landed and posted this here
если я не ошибаюсь — то это богиня покровительствующая программированию, одно упоминание ее имени в коде защищает проект от багов/лагов примерно на 75%
+26
UFO just landed and posted this here
Средняя курсовая работа студента поместиться в одну такую :)
0
В исходниках Андроид — Java классы по 10000 строк кода. И это скорее всего нормально. Просто люди не хотят создавать лишних абстракций, лишних подпрограмм (функций) там где они не нужны. Они написали класс-сервис на 10К строк, который делает законченную вещь. Внутри его делить и абстрагировать видимо нет смысла. Экономия времени и денег на лицо.
0
Java классы по 10000 строк кода? Или класс, число строк в методах которого превышает 10000?
В первом случае, мне кажется, что это классический big ball of mud, хотя если они этими классами умудряются не нарушить SRP и ISP, то это здорово (хотя я думаю, что если программист решился на такое, то это подразумевает, что больше никто и никогда в этот класс не полезет).
Во втором случае — это абсолютно нечитабельное самоубийство. Потребуется немало времени тому, кто попытается разобраться, что же там происходит. И, думаю, что в этом случае это метод почти наверняка не покрыт тестами, так как тестировать такой метод невероятно сложно.
В первом случае, мне кажется, что это классический big ball of mud, хотя если они этими классами умудряются не нарушить SRP и ISP, то это здорово (хотя я думаю, что если программист решился на такое, то это подразумевает, что больше никто и никогда в этот класс не полезет).
Во втором случае — это абсолютно нечитабельное самоубийство. Потребуется немало времени тому, кто попытается разобраться, что же там происходит. И, думаю, что в этом случае это метод почти наверняка не покрыт тестами, так как тестировать такой метод невероятно сложно.
+5
> Или класс, число строк в методах которого превышает 10000?
Я имел в виду «число строк каждого из методов».
Я имел в виду «число строк каждого из методов».
+1
Вобщем там все хорошо разделено в коде по ответственности классов. Например: имеем сервис окружения ОС, например activity manager. Он выполняется в виде класса, который позволяет делать RFC вызовы к себе, а внутри него уже 10к строк. Точно название класса я сейчас не скажу, в котором это видел. Но это точно правда. Там вобще все файлы с кодом «большие», не зависимо Java 'то или C/C++. Тесты там тоже есть. Например есть большой compatability suit (вроде так называется), его можно собрать вместе с самим Android и запустить на девайсе. Он тестирует все подсистемы окружения ОС, написанные на всех языках. Если все подсистемы отработали по тестам — то девайс совместимый.
Также можно заметить что в исходном коде окружения Андроид ничего не указывает на то что он писался с использованием каких-либо IDE. То есть файлов какой-либо IDE или автоматически сгенерированных ими замечено не было. Там еще в системе сборке (написанной на make, руками) есть генераторы разных файлов.
Также можно заметить что в исходном коде окружения Андроид ничего не указывает на то что он писался с использованием каких-либо IDE. То есть файлов какой-либо IDE или автоматически сгенерированных ими замечено не было. Там еще в системе сборке (написанной на make, руками) есть генераторы разных файлов.
0
бох мой — думаю, эти классы в 10 000 кода и не тестируются. У меня был проект, где правые английские парни наколбасили таких классов под 20 000 строк. Мы с командой такой проект два года переделывали, чтобы он хоть как то тестировался автоматизированно.
+2
Попробуйте их посопровождать.
+4
UFO just landed and posted this here
Тоже верно. Но там всего 75к строчек кода и из них 5 функций по 2 и более тыщи строк. Т.е. получается, что в этих функциях помещается не много не мало но 1/7 часть кода.
+1
Часто после такого разбиения ради разбиения ВНЕЗАПНО оказывается что код можно использовать во многих местах. А потом его ещё раз разбиват, и ещё. Выделяют из него классы, хелперы… И оказывается что этот код на 2000 строк состоит из цепочки 3-х методов по 15 строк, и 10 сторонних методов и классов, которые используются в разных местах системы и в общем-то универсальны. Это и называется рефакторинг.
+14
UFO just landed and posted this here
«Большой switch плюс куча ifdef» почти всегда красиво решаются правильной иерархией классов и полиморфизмом.
0
UFO just landed and posted this here
А кто говорил, что использование ОО кода будет простым и интуитивным? :) Бонус ООП скорее в гибкости и упрощении разработки/поддержки кода, а не в простоте его использования. А для упрощения уже каждый может добавить обёрток под свои нужды.
Больших switch и т.п. иногда можно избежать с помощью более высокоуровневых инструментов, например, вместо ручного парсера использовать генераторы. Опять же, не факт что получившийся код будет проще, но его будет легче писать и поддерживать за счёт более ограниченного и чистого синтаксиса.
Больших switch и т.п. иногда можно избежать с помощью более высокоуровневых инструментов, например, вместо ручного парсера использовать генераторы. Опять же, не факт что получившийся код будет проще, но его будет легче писать и поддерживать за счёт более ограниченного и чистого синтаксиса.
0
UFO just landed and posted this here
Я же специально разделяю внутреннюю поддержку кода и его внешнее использование. На вашем примере restkit, снаружи было бы проще позвать одну функцию с нужными параметрами, чем городить непонятные объекты. Изнутри же более удобно этим управлять, когда код чётко разделён по классам, плюс это более гибко.
Парсер я привёл лишь в качестве примера высокоуровневого инструмента, упрощающего код для конкретной задачи, ничто ведь не мешает использовать что-то аналогичное и для выполнения байткода.
Парсер я привёл лишь в качестве примера высокоуровневого инструмента, упрощающего код для конкретной задачи, ничто ведь не мешает использовать что-то аналогичное и для выполнения байткода.
0
На счёт ifdef и парамаетров компиляции. Приведу пример: у нас есть класс CFileOperations с десятком методов по копированию, удалению, перемещению файлов и т.д. Класс должен работать под Linux, Windows и MacOS. Поэтому в каждом методе есть 3 блока вида «ifdef WIN… end if». Класс огромен и страшно читаем.
В тоже время можно было бы создать интерфейс IFileOperations и 3 реализации под разные ОС. В итоге:
-у нас на весь код был бы только 1 блок «ifdef»
-при добавлении методов количество «ifdef» не увеличивается
-в каждом классе собран код только под одну ОС — читать легко и приятно
-если мы забудем после добавления очередного метода написать реализацию под определенную ОС — нам об этом скажет компилятор, а не загадочное поведение на рантайме.
Я, конечно, не отрицаю случаев когда такой подход не катит, но частенько — катит.
В тоже время можно было бы создать интерфейс IFileOperations и 3 реализации под разные ОС. В итоге:
-у нас на весь код был бы только 1 блок «ifdef»
-при добавлении методов количество «ifdef» не увеличивается
-в каждом классе собран код только под одну ОС — читать легко и приятно
-если мы забудем после добавления очередного метода написать реализацию под определенную ОС — нам об этом скажет компилятор, а не загадочное поведение на рантайме.
Я, конечно, не отрицаю случаев когда такой подход не катит, но частенько — катит.
+1
Чем больше функция — тем сложнее понять что она делает. Чем сложнее понять — тем больше вероятность, что при ее изменении будет допущена ошибка. Разбивать имеет смысл хотя бы ради этого.
+1
Чтобы было легче ориентироваться в этих функциях, они используют заметные комментарии :-)
/* @@@@@ @@ @@@@ @@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@@@ @ @@@@@@ @ @@@ @ @ @ @ @ @ @ @ @ @ @@@@ @@@@@ */ //------------------------------------------------------------------------- // @ @ @ @@@@@ @ @ // @ @ @ @@ @ @ @ @ // @@@ @ @ @ @ @ // @ @ @ @ @@ @ @ @ // @ @ @ @@@@@ @ @ //-------------------------------------------------------------------------
+16
Супер :)
/* @ @ @@ @@@@@ @ @ @ @@@@ @@@@ @@@@@ @@ @@ @ @ @ @@ @ @ @ @ @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@@@@ @ @ @@@@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ @@@@@ @ @ @@@@@@ @@@@ @@@@ @ */
+20
UFO just landed and posted this here
в первую очередь своим размером
0
А еще попробуйте нанимать не неё юнит-тест. :)
0
Наверное тем, что цикломатическая сложность высокая, и как следствие гарантировать отсутствие багов в ней невозможно
0
А вот могу тоже привести кусочек C#-говнокода, обнаруженный в коде реального проекта компании, известной и старой не менее, чем Интел.
И это не прикол — это реальный код.
И это не прикол — это реальный код.
string s = Request.QueryString["action"];
// s may have "remove" or "restore" values
switch (s[2])
{
case 's':
case 'S':
..............
break;
case 'm':
case 'M':
..............
break;
}
+5
Надеюсь вы НЕ отправили баг-репорт! Индийский говнокод вряд ли что исправит, а вы только нарушите тонкий баланс ошибок позволяющий программе работать.
+6
Без этой картинки, пост был бы не полноценным :)
+16
Разработчики Intel тоже говнокодят
-1
Разработчики Intel тоже говнокодят
… можно было бы завести для этого вот такой макрос "#define STRLEN(s) (sizeof(s) / sizeof(*s) — 1)"
Стоит ожидать топика?
Разработчики PVS-Studio тоже говнокодят
+20
+1
-1
Прошу, попробуйте написать вычисление длины пути хорошо. А мы обсудим Ваше решение.
+2
Легко. Вот смотрите:
CString str = _T(«Тут:\Какой-то\путь»);
int len = str.GetLength();
CString str = _T(«Тут:\Какой-то\путь»);
int len = str.GetLength();
0
По-вашему терминальный нуль просто так придумали?
-5
Что?
+4
Мы на разных языках говорим? Зачем по-вашему '\0' помещают в конец строки?
-3
А как Вы думаете, почему Ваши вопросы минусуют, а мои плюсуют? И еще подумайте, что должен возвращать макрос, называющийся STRLEN.
+2
По-моему вы чересчур преисполнены самомнением для человека, который для вычисления длины строки советует использовать макрос
Для препроцессоров компиляторов C/C++ строка является единым токеном, поэтому невозможно вычислить длину строки at compile time, используя macro expansion trickery.
Как бы я реализовал «вычисление длины пути хорошо»? Пожалуй что так.
#define STRLEN(s) (sizeof(s)/sizeof(*s) — 1)Я поясню свои вопросы: (Null Terminator) '\0' — маркер окончания последовательности символов, хранящихся в памяти и составляющих данную строку. Таким образом длину строки, хранящейся в памяти, можно определить как разность смещений начала строки и терминального нуля, относящегося к данной строке.
Для препроцессоров компиляторов C/C++ строка является единым токеном, поэтому невозможно вычислить длину строки at compile time, используя macro expansion trickery.
Как бы я реализовал «вычисление длины пути хорошо»? Пожалуй что так.
0
Написали бы сразу, что не знаете как работает оператор sizeof.
+1
Ещё один. С чего вы взяли, что я не знаю как работает оператор sizeof?
Суть моих ответов сводится к тому, что НЕВОЗМОЖНО определить длину строки at compile time, что безуспешно пытается сделать уважаемый Andrey2008. Это можно сделать только at run-time, к примеру, с помощью strlen.
Суть моих ответов сводится к тому, что НЕВОЗМОЖНО определить длину строки at compile time, что безуспешно пытается сделать уважаемый Andrey2008. Это можно сделать только at run-time, к примеру, с помощью strlen.
-2
Это же строковая константа, и её размер компилятор знает.
+1
Было бы невозможно, вот только уважаемый Andrey2008 успешно определяет длинну константной строки:
#define PL_FOLDER_STRING "C:\\productivity_link"
#define PL_PATH_SEPARATOR_STRING "\\"
#define PL_APPLICATION_NAME_SEPARATOR_STRING "_"
+1
Господа, приношу свои извинения! В заблуждение ввело то, каким образом могут обрабатываться строковые литералы:
Отдельные извинения для Andrey2008! Не сразу обратил внимание, что приведённый макрос предлагается использовать для литералов.
char c = *"Brown fox jumps over the lazy dog";
Отдельные извинения для Andrey2008! Не сразу обратил внимание, что приведённый макрос предлагается использовать для литералов.
0
Отнюдь не пытаясь оправдаться за весь Интел, упомяну, что данный SDK взят со страницы whatif.intel.com. Как известно, там лежат даже не прототипы, а так — программки, некоторым из которых когда-либо суждено стать прототипами. Многие наброски и черновики сделаны студентами-практикантами, которых уже вычеркнули из книги судеб.
Там кстати специально написано: «What if software were like this?» ;)
Впрочем, это ничуть не меняет сути поста: код должен быть красивым, надежным, и в обязательном порядке проверенным статическим анализатором. Например, Klocwork или PVS Studio :)
Там кстати специально написано: «What if software were like this?» ;)
Впрочем, это ничуть не меняет сути поста: код должен быть красивым, надежным, и в обязательном порядке проверенным статическим анализатором. Например, Klocwork или PVS Studio :)
+2
Я понимаю, что это полупрототипы. И что присматриваться к ним не стоит. Но не удержался, и изучил «Intel Energy Checker SDK Primary Technology Contacts»:
Jamel Tayeb, Applications Engineer. Jamel Tayeb is a software engineer in Intel's Software and Services Group. He has held a variety of engineering, marketing and PR roles over his 10 years at Intel.
Kevin Bross, Modular Systems Architect. I've worked for Intel for 20 years in a variety of engineering, marketing, and architectural roles.
Действительно, студенты-практиканты. :-)
Jamel Tayeb, Applications Engineer. Jamel Tayeb is a software engineer in Intel's Software and Services Group. He has held a variety of engineering, marketing and PR roles over his 10 years at Intel.
Kevin Bross, Modular Systems Architect. I've worked for Intel for 20 years in a variety of engineering, marketing, and architectural roles.
Действительно, студенты-практиканты. :-)
+1
Я почти угадал — загадывал тысячу, ибо на C очень удобно обрабатывать ошибки, исползуя код вида:
int somefunc(...)
{
if (something_erroneus)
goto ERROR;
...
do_something();
...
if (something_else_failed)
goto ERROR;
return 0;
ERROR:
... // free the resources, null the output arguments etc.
return ERROR_CODE;
}
+4
Все таки лучше иметь один return в функции.
-3
Стоп-стоп, без фанатизма. Не стоит воспринимать рекомендации буквально: если говорят, что лучше иметь одну точку выхода из функции, это ещё не значит, что нужно весь код писать так, чтобы в каждой функции был ровно один return. Правила, придуманные человеком, в этом смысле не эквивалентны законам физики, ибо есть возможность выбора, и ей нужно пользоваться с умом. Ну, и учитывайте специфику языков: что позволено Юпитеру C'шнику, то не позволено плюсовику, так сказать.
+3
Как раз в обработке ошибок это удобно, так как можно выполнить действия по выходу из функции, файл закрыть или мьютекс разблокировать.
0
я вот недавно в книжно магазине видел экземпляр, полный догматический заявлений типа «Не возвращайте из функции NULL, кидайте эксепшны» и мотивировалось это только тем, что NULL нужно будет обработать(как будто на эксепшн можно забить). Жаль название и автора не запомнил.
0
А я загадал 1200 и таки почти угадал =)
А все потому, что по долгу службы мне приходится работать с одним адским проектом, который содержит в своем череве некоторого монстра, реализующего транспортный уровень. За все мои 15 лет айтишного прошлого я не видел говнокода страшнее и махровее чем там. Лес дефайнов, километры свичей, частокол goto — все можно найти там. Коду 20 лет и его приходится поддерживать и портировать на разные платформы. И это ужасно, скажу я вам. А еще ужаснее то, что заказчик считает что все прекрасно и ничего страшного в этом нет. Just a business…
А все потому, что по долгу службы мне приходится работать с одним адским проектом, который содержит в своем череве некоторого монстра, реализующего транспортный уровень. За все мои 15 лет айтишного прошлого я не видел говнокода страшнее и махровее чем там. Лес дефайнов, километры свичей, частокол goto — все можно найти там. Коду 20 лет и его приходится поддерживать и портировать на разные платформы. И это ужасно, скажу я вам. А еще ужаснее то, что заказчик считает что все прекрасно и ничего страшного в этом нет. Just a business…
+3
Если описанный вами проект написан на C++, то я вам очень сочувствую: на предыдущей работе я попал в точно такую же ситуацию — и goto, и switch на три-четыре экрана, макросы, «ООП» на структурах (коих по одной на сущность — никаких абстракций и наследования), функции на сотни строк (а одна была даже на ~3800). Будущее выглядело примерно таким же. Но мы нашли общий язык с начальством: я написал заявление, и больше там не работаю, а занимаюсь любимым делом, ха-ха (:
0
Че-то Вы быстро сдались. Я тоже в подобных ситуациях бывал. Тут все просто — свой кусок начинаешь писать правильно, в тех местах, где он примыкает к другому коду — подправляешь тот код. Тесты, конечно же, контроль версий и т.д. Так тихой сапой за полгодика можно привести в чувство самый мертвый код.
0
Рефакторить без тестов получается не всегда, а тесты, в свою очередь, не всегда можно повесить на хреновый код(отличный тут пример — в веб приложениях каша из логики и представления). Так и работаешь, постоянно восстанавливая тонкий баланс проекта, после каждой добавленной строчки кода. Чувствуешь себя той самой бабочкой, взмах крыла которой, несет катастрофы, мор, отчаяние.
0
Да, именно так и делается. Ощущаешь себя мужиком с мачете в джунглях =) Вгрызаешься в код, начинаешь окультуривать. Ну и так потихонечку-полегонечку. Вот только все равно нет никакой гарантии что не вылезет какая-нибудь очередная бяка за день до релиза.
+1
У меня не было полгода времени и столько же терпения. Тем более, это был fatality, а предпосылки были ещё до этого чудо-проекта.
0
Да, именно на С++. Но много и реликтовых кусков на Си. Причем «аффтар» сего творения до сих пор не упокоился работает в команде и продолжает писать. Самое жуткое, что вот я смотрю на его код 20-летней давности и тот, что он написал буквально вчера… ну никакой разницы! Он прямо начинает излагать говнокодом, представляете? Ладно, если вся эта трехомудия образуется в результате патчинга-перепатчинга и затыкания ошибок… но в данном случае она зарождается прямо изначально. Это выше моего понимания.
Мне страшно оттого, что некоторые люди за \d0 лет практики продолжают как ни в чем ни бывало писать ТАКОЕ. Поэтому я очень осторожно отношусь к стажу («он еще в 80х начинал»), как показателю опыта и качества.
Мне страшно оттого, что некоторые люди за \d0 лет практики продолжают как ни в чем ни бывало писать ТАКОЕ. Поэтому я очень осторожно отношусь к стажу («он еще в 80х начинал»), как показателю опыта и качества.
+2
Зато у вас есть работа. Может быть, еще на 20 лет…
0
К слову, в славной платформе Samsung Bada обработка «исключений» осуществляется именно так.
Найдите отличия: developer.bada.com/help/topic/com.osp.devguide.help/html/basics_bada_programming/exception_propagation.htm
Найдите отличия: developer.bada.com/help/topic/com.osp.devguide.help/html/basics_bada_programming/exception_propagation.htm
0
Вообще, кто кодит на ASM и на C одновременно, их должно не покидать чувство, что это очень близкие языки. И C хорош тем, что он ближе к ASMу, чем, например, Pascal или Java. Так вот чем ближе к машинному коду, тем уместнее конструкция GOTO, ведь процессор по сути то и делает, что «прыгает» от инструкции к инструкции, с помощью тех самых GOTO (безусловных JMP и условных JE, JNE...). Очевидно, что во многих случаях GOTO позволяет сократить кол-во строк кода и соответственно процессорное время.
+3
0
Да ладно, все говнокодят. Другое дело, что одни потом свой говнокод исправляют, а другие нет. И ещё одни учатся писать правильно, а другие так и говнокодят до конца жизни.
+1
UFO just landed and posted this here
Нет ничего удивительного, что в гигантах есть места для инду/ази/говно кода.
В своё время тоже сильно удивлялся способностям Senior' ов из Редмонда (название организации додумайте сами). Программируя на C++ люди, которые были ответствененны за инфрасруктуру, копировали refernces на объекты параметры методов, отчего в определённые моменты, например, падали ВСЕ тесты в проекте.
Потом весь TFS вставал на уши.
Вся эта история может быть вызвана как бюрократией (мы направляем Васю Пупкина на этот проект и всё тут) так и сложностью с людскими ресурсами ресурсами, так и проблемой условного апгрейда кадров (Вася Пупкин работает уже 20 лет у нас, давайте дадим ему Senior и отправим на проект с неизвестными ему технологиями).
В своё время тоже сильно удивлялся способностям Senior' ов из Редмонда (название организации додумайте сами). Программируя на C++ люди, которые были ответствененны за инфрасруктуру, копировали refernces на объекты параметры методов, отчего в определённые моменты, например, падали ВСЕ тесты в проекте.
Потом весь TFS вставал на уши.
Вся эта история может быть вызвана как бюрократией (мы направляем Васю Пупкина на этот проект и всё тут) так и сложностью с людскими ресурсами ресурсами, так и проблемой условного апгрейда кадров (Вася Пупкин работает уже 20 лет у нас, давайте дадим ему Senior и отправим на проект с неизвестными ему технологиями).
+1
350 — в три раза ошибся.
0
Sign up to leave a comment.
Разработчики Intel тоже говнокодят