Pull to refresh

Comments 119

Заполняйте код комментариями вроде /* прибавить 1 к i */, но никогда не указывайте в коде такие общие вещи, как назначение модуля или метода.

Был у меня как-то коллега, который действовал именно так. Разбирать полгода спустя его код бывало трудновато
Мне кажется некоторых этому учат в университете.
До слез! Приходится сейчас поддерживать код, в котором большинство пунктов в точности соблюдены.
Если бы он использовал все пункты, то до сих пор работал бы с этим кодом, а не вы вместо него :)
Небось, пошел вверх по карьерной лестнице просто :)
Поищите пособия по программированию, чьи авторы чаще пишут книги, чем программы. Можете зайти в местный книжный магазин и отобрать книги, в которых есть масса сложных диаграмм и ни одного примера написания кода. Бегло просмотрите эти книги и выберите в них чересчур умные слова. Эти слова можно использовать для того, чтобы отпугнуть самонадеянных новичков, претендующих на ваше место.


Вот это — просто прекрасно. С одной стороны, мысль неочевидная (в отличие от большинства других высказанных), а с другой, иногда создается впечатление, что процентов восемьдесят разработчиков Enterprise-уровня этому вредному совету неукоснительно следуют.
>«вы сможете гарантировать себе пожизненное рабочее место, так как никто кроме вас не сможет разобраться в вашем коде.»

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

а. У остальных сотрудников достаточная квалификация, чтобы распознать его, пока еще не поздно
б. У вас достаточно средств, чтобы просто взять и выкинуть из бюджета пару сотен человеко-часов
в. У вас достаточно времени и средств, чтобы найти новых людей и потратить время на их обучение и введение в проект)
+ Используйте кириличные аналоги латинских букв в названиях своих переменных и функций. Это сделает вас неповторимым и загадочным разработчиком.
Кстати, да. Давно жажду, чтобы в популярных ide все символы, не входящие в ASCII, но написанные за пределами комментариев и строковых литералов, как-то особо помечались. Даже в компилирующем языке, порой, можно свести себя с ума, написав «с» или «а».
Простое решение: шрифт, в котором есть только ASCII, а остальные символы будут тогда отображаться другим. Сразу будет бросаться в глаза.
И, по-моему, в IDEA можно указать другой (например, bold того же шрифта) для таких символов.
Вот. Давно хотел спросить, но стеснялся, как правильно называть переменную, обрабатывающую сообщения об ошибке?
  1. jopa
  2. gopa
  3. zhoopa

?
В статье же написали — в разных местах по-разному. Главное, сделать вид, что эти различия на что-то влияют.
Очевидно же — использовать язык программирования, который позволяет указывать идентификаторы в UTF-8. Тогда можно написать просто и незатейливо: 臀部
В JetBrains ReSharper у Extension методов первый аргумент зовётся this, где i — украинская буква.
Так легко же заметить… если в остальном слова правильные… ну и если установлена проверка орфографии. :)
Половине из этих советов я и так следую. Второй половине следовать не могу, поскольку не пишу на Java.
Особенно приятно, что в проекте перемешаны углы, выраженные в радианах, градусах, угловых секундах, оборотах и условных единицах, равных 360/2^28 градуса. Иногда также встречаются угловые минуты и устаревшие условные единицы (360/2^15 градуса). Избавиться от них нельзя — они попали в формат одного из конфигурационных файлов…
Кстати, по поводу пункта 22 — как должен называться единственный открытый статический метод класса, выполняющий всю работу (после рефакторинга Method to Class) — Do или Work?
Принято называть InitInstance.
Так он ничего не Init — он вызывает конструктор (private), потом одну-две функции у полученного объекта, потом возвращает результат — а объект уничтожается.
Стоит задуматься о том, чтобы руками и в коде выполнить вот эту операцию:
объект уничтожается


А метод перенести в другой класс, где он нужен.
Смысл метода — что-то вычислить. Скажем, посчитать какую-нибудь сложную характеристику списка точек в пространстве. Вызываться он может из разных мест. Как считает — никому не важно, кроме него самого. Объект нужен, потому что есть много данных, использующися в разных функциях этого вычисления. Но снаружи никто о существовании объекта знать не должен. Так что что-то куда-то выносить, вроде бы, незачем.
Т.е. не соответствует названию. Что и требовалось, коллега.
Согласен, хорошее обоснование.
Run было бы хорошей идеей, если бы вычисление было асинхронным и запускало отдельный поток.
Ещё есть Compute.
Запуск асинхронной операции — это Start или Begin. Run — это именно что проведение операции в текущем потоке…
Назовите его destroy(). Вот потеха-то будет!
Ну с таким подходом программист сам свалит через пару лет, ибо поддерживать данный код даже у автора не будет никаких сил.
Вы — оптимист. Увы, есть гуманоиды с очень особым строением мозга, способные не только сгенерировать нечто, неподвластное представителю homo sapiens, но даже и поддерживать, развивать это и продвигать долго. В мелкой конторе такой человек действительно способен стать «серым кардиналом», которого боится даже учредитель.

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

Под этим углом считать программирование искусством не следует :)
Наверное, у них когда-то были хорошие результаты на олимпиадах.
На любой сколь-либо серъезной олимпиада писать приходится аккуратно и понятно, потому что у вас нет времени вчитываться и пытаться понять.
Если на эту олимпиаду отведено несколько дней, то возможно. Но, в любом случае, олимпиадное программирование — спортивная дисциплина. А в спорте главная задача — прийти первым. В частности — первым выдать верное решение задачи. Любой ценой. Цель «написать красивый код» не ставится. Причем в отличие от коммерческой разработки, где красивый код — вклад в будущее, потому что он упрощает последующую поддержку, в олимпиаде, где вы получите свой приз и навсегда выкините программу, которая вам его принесла, писать код можно как угодно.

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

Таким образом мы получаем олимпиадника — человека, способного в одиночку ркализовать очень сложный алгоритм и пишущего код по принципу «как можно короче/быстрее». То есть человека, генерирующего сложные алгоритмы в режиме «write-only».
Проблема в том, что если писать как указанно в статье, то ты даже отладить не сможешь в случае чего. Или объяснить сокоманднику что ты там сделал. И с определенного уровня для успеха надо писать аккуратно. Масштаб задач такой, что там для достаточной понятности не требуется декомпозиции, архитектурных решений и прочего такого.
У меня есть достаточный опыт с людьми у которых «когда-то были хорошие результаты на олимпиадах» (да и сам я какой-то результат имел) и у меня нет проблем с пониманием их кода.
Да нет… Увы. Если бы человек, который пишет такой код, сразу был неспособен его отладить, он бы очень быстро бы понял, что делает что-то не так.

Большая часть информации — ключ к пониманию принципов работы кода — остается в голове автора. Это происходит само собой, вы даже не замечаете. Обращали ведь внимание на то, что иногда ошибку в собственной программе находите даже не сидя в этот момент перед монитором. Просто вы помните, что написали.

Я лично прекрасно помню момент, когда начал впервые задумываться о стиле кодирования. Это произошло в школе, когда мне пришлось дорабатывать прогу, которую я написал для отца за полгода до этого. И там я увидел что-то типа
for i := 1 to 12 do
begin
a[i] := b[i] div 2;
end;
Form1.Visible := !Form2.Visible;
Label12.Text:="Количество";

(код был на Delphi)

Никаких отступов, комментариев, вообще ничего. Стена из вот такого вот…

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

Самое интересное, что когда я только создал этого «монстрика», я его прекрасно отлаживал. Я отлично помнил, что значат все эти Form5 и Label126. Так что единственный способ объяснить такому «гению», где именно он неправ — заставить его вернуться к его собственный код полугодовой давности. Или, по меньшей мере, заставить его в течение полугода развивать и поддерживать любое его собственное решение сложнее простого скрипта.

К сожалению, этот метод дорогостоящий.
Насколько я помню ACM, человеку страдающему перфекционизмом, в исходные коды лучше не смотреть. Там всё тяп-ляп и на скорость. Как только код проходит тесты (даже если это происходит вопреки ожиданиям), про задачу команда забывает и моментально переходит на следующую. В самом же коде, как правило, никаких классов, наследований и пр. радостей жизни. Просто простыня с методами, если оные оказались нужны. А то и вовсе без них. Например набор одно-буквенных переменных, пяток циклов, чтение и запись файла.
Как только код проходит тесты (даже если это происходит вопреки ожиданиям), про задачу команда забывает и моментально переходит на следующую.
Поправка: для того, чтобы программа прошла тесты, надо ее сначала отправить на проверку. А каждая неудачная отправка — это 20 минут штрафного времени. Поэтому программа не может пройти тесты «вопреки ожиданиям», тестируются только те программы, от которых ожидается прохождение всех тестов.

По стилю кода же — все верно. Более того, тренеры еще и специально учат использовать однобуквенные переменные, потому что их быстрее набирать.
Может быть, имелись в виду свои тесты?
Вспомнилось. Собеседовал как-то человека, который писал примерно вот так (код выдрал из первой попавшейся lib-ы, только переформатировал). Его код напоминал wall_of_text. Т.е. экран был заполнен от края до края кодом на каждой строке. Для того чтобы как-то в этом разбираться, он для разделения блоков кода использовал множественные табуляции (или просто груду пробелов). Долго хвалился тем, какой он опытный перец. Что у него собственный framework и большой пулл довольных клиентов. Робко задал ему вопрос, как у него дела обстоят с MVC, ООП и пр… Он заявил мне, что слышал про это, но в деле не использовал, ибо не особенно то и нужно. Видимо, ему и так удобно. Код он набирал в FAR-е. Мне кажется, я никогда это не забуду :)
Один мой коллега набирает код в FARе (при том 2-м, на 3-ку переходить не хочет принципиально, потому что НЕ НУЖНО). А то напридумывали тут ересь всякую, автоформатирование, подсветка синтаксиса, статические анализаторы какие-то…
Робко задал ему вопрос, как у него дела обстоят с MVC, ООП и пр… Он заявил мне, что слышал про это, но в деле не использовал

Мне как то кандидат сказал — я хорошо знаю PHP, я классы пишу!
Тут ещё вопрос что хуже — код, который пишет этот чувак, или тот, который пишу я — идеально отформатирован, все дела, но, например, я использую каррирование. :)
А что не так с каррированием?
Его не понимают. Ну то есть один из коллег считает, что оно увеличивает WTF/s.
25. сразу вспомнил газовый разряд в универе: есть формула, но без пояснения кто в каких единицах берётся она бесполезна, ибо она эмпирическая.
70 человек добавили статью в закладки :)
Бомба замедленного дествия получилась.
Это чтоб потом почитать.
36. Выбирайте для переменных названия, не связанные с соответствующими им надписями, которые появляются после запуска программы.

label1.text
Да меня как-то попросили подправить очень древнюю расчетную программу на паскале. Так там были
Label218 и Edit67 или около того) А, еще несколько StringGrid-ов, с фиксированными размерами из которых в куче мест брались параметры (типа константы глобальные) вот так
StrToDouble(StringGrid4.Cells[2,8]) * Pn + StrToDouble(StringGrid4.Cells[2,9]) * Qn
и тп
Похоже, в паскаль это из экселя переносили
вполне, кстати, вероятно.
Вы можете значительно упростить свою программу, если оставите во всем приложении (в методе main) единственный оператор try-catch, который вызывает метод System.exit().

Лучше оставить блок catch пустым. Вы же уверены, что в вашей программе никаких исключений произойти не может.
С появлением средств статистического анализа кода этот метод несколько утратил свою эффективность. Пустые catch блоки слишком просто обнаружить, гораздо лучше выбрасывать в catch новое исключение, никак не связанное с пойманным. Хотя лучше в некоторых случаях всё-таки выбрасывать именно то исключение, которое требует выбросить здравый смысл. Это позволит защититься от программистов, которые просто заменяют все catch блоки нормальными исключениями, не размышляя о том, что может быть на этот раз всё и так было хорошо.
И ловить на Throwable, а то мало ли
Лучше ловить и выбрасывать RuntimeException… через цепочку вызовов, чтобы и по стек-трейсу было не отследить — fatality!
Если в методе может выброситься исключение, просто добавьте throws Exception. <3
Есть еще более вредный вариант: throws Throwable.
Увы, но целый ряд советов уже неактуален в современных условиях. Например, совет «Никогда не пользуйтесь автоматическим выравниванием кода.» за одно нажатие исправляется автоформаттерами кода. В современных условиях видится необходимость в новых и креативных методах обфускации кода!
Ничего страшного. Достаточно немножко потанцевать с define'ами и все ваши автоформаттеры сделают отступы неправильными! Для этого нужно брать define'ы из какиго-нибудь хитрого места, так чтобы автоформаттер до него не добрался и чтобы в них были разбалансированы фигурные скобки — тогда точно «всё будет хорошо» (в смысле обсуждаемой статьи, я имею в виду). Одно нажатие кнопки — и код можно выкидывать :-)
Столько советов, а простой и надежный способ: «написать функцию с 1000-ей строк кода» — не упомянули :)
Потому что он, по большому счёту, совершенно ненадёжен.

Многое зависит от контекста, но лично мне проще разбираться в функции с 1000-е строк кода, чем с 1000й функций в каждой из которых по одной строке.
очень надёжен в применении с вложенными if и перемешанными переменными, используемыми там и тут. Сам недавно разбирал такой метод на кусочки — автор сего творения обыкался наверное
для микроконтроллеров есть такая замечательная либа как uip (миниатюрная реализация TCP)
случаем не с ней разбирались? Там как раз около 1000 строк кода сдобреное не только обильными If а ещё и лонгджампами, goto и под общим кейсом на почти всё тело в эту 1000 строк кода
нет. с Java-кодом — там функция была порядка нескольких сотен строк месива из линейно-древовидных if-else конструкций, сдобренные try-catch конструкциями. Вначале объявлена некая переменная X, которая в процессе наполняется, заменяется, сохраняется в БД, снова наполняется, заменяется, пополняется через вызов других функций и тыдыщ — держите на выходе её некое состояние.
а у меня была некоторая чудо-функция которая организует TCP сокет с самого низкого уровня — голого набора байт, вплодь до всех фишек обычного TCP, включая весь стек протоколов. Там например вся работа с 32 битными значениями делалась кодом
b0 += add0;
if (b0 < add0) b1++;
b1 += add1;
if (b1 < add1) b2++;
b2 += add2;
if (b2 < add2) b3++;
b3 += add3
и вообще все ПОБАЙТНО!
некоторые сравнения и операции в ПЛИС стиле: линейными логическими операциями сделаные ветвления и циклы.
ты видишь вложенное условие с проверками и арифметикой? нет? Там только логика? а всё это там есть!
Они даже умудрились напрочь скрыть настройку MTU и прочих фишек, ниодной константы и действия связанного с этим не нашол. Даже комментируя блоки — всё равно работает.
А для чего я это ковырял: отключить алгоритм нагля, сделать нормальное TCP окно и прочие твики чтоб можно было передавать данные быстрее 1кбайта в сек.
b0 += add0;
if (b0 < add0) b1++;
b1 += add1;
if (b1 < add1) b2++;

Здесь b0,b1,b2 — это unsigned char?
И что же она сделает, если b0=add0=0x80, b1=0xFF, add1=0x01?
После первого сложения будет b0=0;
Первая проверка покажет, что было переполнение;
b1 увеличится на 1 и станет равным 0x00;
После второго сложения он будет 0x01;
И вторая проверка покажет, что переполнения не было.
Это действительно то, что ожидалось?
извините, по памяти писал. накосячил малость

сейчас (исходник uip с гитхаба) код выглядит так:
uip_acc32[3] = op32[3] + (op16 & 0xff);
uip_acc32[2] = op32[2] + (op16 >> 8);
uip_acc32[1] = op32[1];
uip_acc32[0] = op32[0];

if(uip_acc32[2] < (op16 >> 8)) {
++uip_acc32[1];
if(uip_acc32[1] == 0) {
++uip_acc32[0];
}
}


if(uip_acc32[3] < (op16 & 0xff)) {
++uip_acc32[2];
if(uip_acc32[2] == 0) {
++uip_acc32[1];
if(uip_acc32[1] == 0) {
++uip_acc32[0];
}
}
}

раньше выглядел как то похоже на то что я написал ранее выше
Так нужно к 32-битному числу прибавить 16-битное?
Казалось бы,

a0+=b0;
c=(a0<b0 && ++a1==0);
a1+=b1;
if((c || a1<b1) && ++a2==0) ++a3;

К a3:a2:a1:a0 прибавляется b1:b0
там смысл в том чтоб увеличивать счётчик позиции окна вне зависимости от порядка байт архетектуры и особеностей и тараканов самых младших микроконтроллеров. Но как она сделана вгоняет в уныние.
Надо добавить: “как можно больше используйте рефлексию, желательно все вызовы других методов перевести на поиск метода по строковой константе имени объекта и метода и вызов посредством рефлексии. А все объекты — синглтоны"
Есть еще одна малоизвестная хитрость, использовать следующую конструкцию:

try{
    ...
    code here
     ...
}catch(Exception e){}
try{
    ...
    code here
     ...
}catch(Exception e){
    //If something went wrong
    //Let it go silently
}
Мне больше нравится такой вариант :)

try{
    ...
    code here
     ...
}catch(Exception e){
    //Who cares?
}
UFO just landed and posted this here
Ого! Григорий Остер подался в программирование!
Я из тех же соображений пообещал работодателю, что все новые разработки теперь буду вести исключительно на Erlang. Еще можно попробовать писать скрипты для каких-либо расчетов на Verilog.
давным давно, надо было сделать обусфакцию получения ключа с демодулятора на квадратурных детекторах и логике выделения бит и поиска синхронизации и начала байта, с последующей проверкой ключа и дешифрацией остальной прошивки модема.

Написал на Си но в ПЛИС стиле,.
Плис стиль в том что
1. Все логические и арифметические операции сведены к логическим базису И-НЕ. Остаются только пересылки и индексации, особенно монстроидальное умножение хоть и 8 бит но это нечто если глянуть асм листинг! особенно в сигнальной обработке.
2. Все функции в строгом инлайне
3. Ветвления делаются так:
было
if (cond)
a=b;
else
a=c;

стало
a = (cond and b) or (not cond and c)
4. Так же делаются автоматы (на счётчиках и сдвиговых регистрах)
5. Циклы разворачиваются
естественно с доработкой операций до и-не базиса
написал естественно нормальный алгоритм на си, сделал юнит тест, сделал в плис стиле с использованием инлайн функций и оформил нормально.

все это в функции которая вызывается для каждой выборки из АЦП
.
Даже написал мануал из каментов по каждой строке чтоб самому потом не напороться…
Напоролся…
коллега которому досталось сказал следующее:
<5 минут мата>,
смысл в юнит тестах, описания?, если я стаким стилем не знаком!
<ещё пара минут мата>
ты вообще асм листинг смотрел? я думал что знаю асм!
<мат>

неделю потом вместе правили — мне самому мануал не помог
пришлось проставляться, было стыдно.
И самое обидное — под конец простая мысль проскочила: «а ведь можно сбросив проц в режим загрузчика выудить напрямую уже дешифрованный ключ»

больше так не буду никогда!
не делайте так, пожалуйста!
1. Все логические и арифметические операции сведены к логическим базису И-НЕ.

Любопытно.
while(b){
c=a&b;
a=~c&(~(~a&~b)); // a^b
b=c<<1;
}
будет выполняться до 8 раз (для случая a=1, b=0xFF). Можно ли быстрее?
Старый текст на ту же тему: http://www.lightnet.obninsk.ru/Review/Relax/diag_prog_11.shtml
для c++ есть классный советик

#define if(x) if(x && (rand() % 10 == 0))
Может лучше != чтобы сложнее было поймать?
да! абсолютно верно, опечатался, спасибо, что заметили
56. Сделайте несколько псевдонимов для метода.
function open() {
...
}
function close() {
...
}

module.exports = {
    open: open,
    close: close,
    show: open, // <---- WTF?!
}

Объясняйте это тем, что иногда забываете конкретное имя метода. run(), start(), do()? или open(), show(), display().
Также, ни в коем случае не доверяйте системы управления версиями хранить историю изменений, смело создавайте MyClass2, MyClass3, MyClass2014, MyClass2015, MyClassAdvanced, MyClass3New, MyClass3Newest, MyClass2To3Compat и используйте их одновременно. При этом не надо использовать никаких фасадов, они только усложнят решение.
Еще лучше будет расположить эти классы в файлах с собственной и отличной от классов нумерацией.
Шедеврально, посмеялся какследует, спасибо :D
В дополнение к 19 совету: полю или глобальной переменной обязательно нужно дать имя вроде «temp», «aux», а ещё лучше «s1», «sss», «a». Но это не всё: нужно обязательно использовать значение этой переменной через несколько сотен строк после присвоения. А лучше в другом методе.
Можно даже пойти дальше: создать класс, который после создания с помощью конструктора перед полноценным использованием требует присвоить значения каким-то полям, вызова каких-то методов и т.д. Инициализируем это всё при старте программы и используем согласно совету выше.
Почему при старте? Инициализируем по мере появления соответствующих значений. Желательно, из разных модулей программы.
Такой способ имеет ограниченное применение: нужно быть уверенным, что все эти значения уже присвоились, иначе может выскочить ошибка в моём коде, и ругаться будут на меня. А так я соблазняю новичка, которому потребуется воспользоваться объектом того же класса не делать утомительную инициализацию нового объекта, а воспользоваться готовым проиницализированным, которому останется чуть-чуть изменить значение какого-то поля.
Да просто используйте глобальные переменные, этого достаточно =)
По комментариям: нужно писать их в таком формате:
...
code;

//Этот блок кода делает (это и это)


code;
...

Тем, кто будет спрашивать, о каком блоке кода речь идёт в комментариях — объяснять, что к тому, от которого отбит меньшим количеством строк. (неужели не понятно?). Самому это правило соблюдать не нужно.
UFO just landed and posted this here
UFO just landed and posted this here
Это ужастно. Не лучше ли так:

return a%4 && (!(a%100) || a%400);

Короче, но внимания требует не меньше.
Как это — «не меньше»? Я, к примеру, по этой формуле сразу же понял, что она делает. В отличии от формулы выше, которую не стал разбирать испугавшись количества скобочек :)
А я там скобочек даже не видел, смотрел только на буквы и цифры. Всё было понятно.
Хотя если бы формула делала не то, что кажется на первый взгляд, например, (((a%400)==0)?1:(a%100)==0) выступало бы условием для следующего оператора, было бы интереснее.
uint ETA = (a <= b ? (a <= c  && c < b) : (a <= c || c < b)) ? b-c : 0;
Так в том-то и дело, что просто так код не смотрят. Смотрят код, когда ищут ошибку. «Короткий» вариант я не только понял, но и быстро проверил. «Длинный» же сходу проверить не получится.
Вы нам работу застопорили :) Я сразу догадался, что это
посмотреть ответ
григориан
, и разослал коллегам… Результат был предсказуем.

Кстати, вот этот код весьма красив и на мой взгляд чуть ли не верх совершенства. Статья немного о другом.
Я бы ещё добавил сюда пункты «хранить данные в коде, системный код и код, выполняющий важные задачи, выносить в скрипты и сторонние библиотеки, а бизнес-логику писать на ассемблере. Вообще желательно писать всё шиворот-навыворот, нагромождая как можно больше парадигм и технологий, чтоб никто ничего не смог понять.»
Самое плохое что, подобный совет я на полном серьёзе иногда слышу от разных программистов, при этом добавляют: «иначе ты никогда не получишь прибавку к зп или тебя вообще заменят на студента». И не только из гос-контор и не только от наших программистов.
Тут уже больше от работодателя зависит. Хороший работодатель понимает, что студент набыдлокодит и уйдёт, а ответственный программист будет ценить, что ему дают возможность сделать красиво и правильно, и вряд ли где ещё дадут.
Сам через полгода с ума сойдёшь свой же код разбирать.
Sign up to leave a comment.