Через месяц после выхода на новое место руководство, предвидя очередную волну короновируса, разогнало нас по домам, обязав появляться в офисе по графику — один дома, второй в офисе и меняемся. Поэтому как выпал жребий я засел дома, снабженный корпоративной симкой и VPN.
Дома работать неудобно — монитор один, стол маленький, по комнате бродят жена и собака, выпрашивая ласку, внимание и еду, и если собака на место уходит послушно, то жена скалит зубы и рычит. Превозмогая трудности подключился к рабочему месту и стал разбирать почту.
Пришло письмо от второго сотрудника, находящегося в офисе, с текстом «Тут менеджер жалуется на deadlock, разберись». По тексту письма сразу понятно — чтобы я дома не скучал на меня сгрузили проблему, которую решать надо, но не сильно хочется. Скрин ошибки в письме был, но как это порой бывает из него можно было узнать название продукта, версию фреймворка, имя разработчика, дату рождения, его сексуальные предпочтения, имена родителей и положение солнца, когда собирался релиз. Ничего относящегося к делу там не было и мне пришлось пытать менеджера на предмет входных данных — кто, как и главное — зачем. На удивление менеджер очень бодро рассказал, где лежит тестовая база с нужной таблицей и процедурой. Вооружившись студией, я начал изучать процедуру с таблицей, оказалось всё просто — из 50 входных параметров брался один, если такой был в базе — остальными 49 обновлялись имеющиеся данные, а если не был — просто дописывались. Просто и понятно.
Из переписки выяснилось, что эти данные раз в n минут обновляются процедурой, но т.к. между обновлениями интервал невелик часто накладываются друг на друга, т.к. других желающих лезть в эту таблицу нет. Откладываю процедуру в сторону и лезу в таблицу. При изучении метаданных выпадает левый глаз от крайнего удивления — в базе нет ни одного индекса и порядка 20 тысяч записей, что гарантирует перебор всех данных в ней каждый запуск. Возвращаю глаз на место и согласовываю с менеджером создание уникального индекса на поле, которое проверяется в процедуре. Пытаюсь создать индекс и получаю ошибку создания из‑за нарушения уникальности по полю, где должен быть порядковый номер сотрудника и по словам менеджера дубликатов там быть не должно. Ищу запросом совпадения номеров и выпадает правый глаз — совпадений до 10 штук! Сразу вспомнился Гоголь Николай Васильевич, и мать его, «Мёртвые души». Против самого автора я ничего против не имел, читал всё с удовольствием, только «Мёртвые души», навязанные уроками литературы не пришлись ко двору, т.к. была непонятна первопричина, заставившая Чичикова мотаться по России. Это уже потом мне объяснили, что причина была в грядущем освобождении крестьян от крепостного права и похожа на схему с возвратом НДС через левую контору, но учительница литературы историю знала плохо, а про отмывание денег — тем более и на мои вопросы невнятно мычала.
Ну да бог с ним с Гоголем. Откуда взялись в таблице повторные записи о крестьянах сотрудниках? Пытаюсь выяснить у админов кто вообще это чудо писал, оказывается, что это было давно и неправда, описания таблиц нет, можно только догадываться, какое поле и за что отвечает. Ладно, поля name, surname, phone и подобные я пойму по названию и содержимому, а как быть с полями ID,PID,GID,SID? Где в varchar хранится целочисленные значения? Вообще все поля в таблице, кроме дат, имеют тип varchar(50) и varchar(100), удивительная согласованность.
Перехожу в общий чат и пытаюсь призвать коллективный разум на помощь. Самые старые сотрудники рассказывают, что это была интеграция сначала одной системы с другой, потом с третьей, причем переписывать это особо никто и не пытался — работает же. Также никто не парился с индексами, ключами и прочим. Приплыли. Пытаюсь объяснить, что дубли выкосить можно, но сложно, индекс я создам, но наличие дублей попортит картину, ну и обновлять данные, размазанные по 10 строками не совсем правильно. Пишу письмо менеджерам и пытаясь не использовать слова на «Х», «Б» и «Ж», дабы не сорваться и не написать лишнего объясняя ситуацию, что индекс я создам, но надо проверить поможет или нет. Если не поможет — буду пытаться получить добро на чистку таблицы от «мертвых душ».
Через 15 минут, потраченных на выпрашивание у жены капучино и его последующее употребление получаю письмо, в котором менеджер радуется, что проблема сдвинулась с мертвой (опять это слово!) точки, рапортует, что на тестовом контуре всё «ок» и интересуется, когда я это перенесу на продуктив. Осторожно объясняю, что в таблице есть несостыковки с данными, нужно их приводить в порядок, и вообще может оказаться так, что то что работало на тестовом контуре на продуктиве покажет себя не так. И тут в голову приходит запоздалая мысль — а что в продуктиве? При изучении рабочей базы я понимаю — вот тут то он и пришел, пушной зверь... 75 тысяч строк, из которых 15 тысяч дублей, от 2 до 10 копий одной строки... Что будет индекс, что не будет, ворочаться это будет очень долго. Ещё раз призываю коллективный разум, который высказывает те слова на «Х», «Б» и «Ж», которых я избегал и предлагает писать письмо руководителю — на благословление разгребать авгиевы конюшни.
Менеджер присылает очередное письмо, в котором излагает желание пообщаться со мной по телефону. По телефону объясняю ситуацию, что в продуктиве данные сильно отличаются от тестовой базы и создание индекса на поле ID нам не сильно поможет, но можно попробовать, на что получаю изумительный ответ: конечно не поможет, я посмотрел процедуру и оказывается там используется поле PID!...Немая сцена. Оказывается продуктив отличался от тестовой площадки, причем сервисная шина передавала все данные в XML, а что ты будешь выбирать как первичный признак ты уже сам в процедуре базы прописывал. В какой‑то момент процедуру поменяли, но кто, когда, зачем никто уже не знал. Наученый горьким опытом я проверил совпадения по этому полю, они нашлись и, о чудо, их было всего три. Причем, строки не отличались ничем, что естественно вызывало отдельные проблемы при их удалении, кто знает, что такое 2НФ и кто пытался удалить или изменить строки с полностью одинаковыми значениями во всех полях, поймут как это весело.
В итоге данные были исправлены, индекс и первичный ключ созданы, менеджер отправлен контролировать процесс обмена данными и раньше следующей недели не писать мне, а я остался дальше думать о странной архитектуре системы обмена, которая работала годами, кривая и косая, без сбоев.
Комментарии (83)
ErshoffPeter
04.07.2023 16:02+8Слишком быстрый happy-end! ????Не хватает части как искали собственников данных, как согласовывали изменения, как ИБ не давало доступ и так далее... ????
Gallemar Автор
04.07.2023 16:02+1С доступами было наоборот хорошо :) как и с ИБ, что потом в итоге организации вышло боком
ErshoffPeter
04.07.2023 16:02+8Доступы и ИБ обычно в противофазе - если с доступами хорошо, то значит с ИБ плохо и наоборот. ????
Tishka17
04.07.2023 16:02И ещё подробностей, как в процессе исправления нашли критический баг приводящий к некорректной логике, а потом выяснилось, что на этот баг уже сделали костылей и его исправление ломает всё
ErshoffPeter
04.07.2023 16:02...а ещё нашли целого подрядчика, который сидит на пребывании данных из одной кривизны в другую... ????
klis
04.07.2023 16:02кто знает, что такое ACID и кто пытался удалить или изменить строки с полностью одинаковыми значениями во всех полях, поймут как это весело.
А при чем тут ACID?
Gallemar Автор
04.07.2023 16:02+1Извините, acid за уши притянут, тут скорее нарушение 2нф и атомарности
Aquahawk
04.07.2023 16:02+4Надо сказать что я в жизни ни разу не видел в продукте которому больше года в продакшне базы приведённой к любой нормальной форме.
Gallemar Автор
04.07.2023 16:02+1Даже 1нф и 2нф?
Aquahawk
04.07.2023 16:02согласен, с первой нф погорячился, её нарушения редко видно. А вторую только так нарушают, особенно в big data.
klis
04.07.2023 16:02+2Так НФ - это вобщем-то не требование/правило, чтобы ее нарушать. Это стремление минимизировать таблицы с точки зрения оптимизации хранения. На практике довольно часто приходится этим принебрегать в угоду скорости выборки, в этом нет проблемы.
mentin
04.07.2023 16:021нф отсутствует как класс во всевозможных document db. В бигдате чаще всего отсутствует если база данных поддерживает repeated field.
Wesha
04.07.2023 16:02была непонятна первопричина, заставившая Чичикова мотаться по России. Это уже потом мне объяснили, что причина была в грядущем освобождении крестьян от крепостного права и похожа на схему с возвратом НДС через левую контору,
Эммммм... помнится, первопричина вообще-то была стара как мир: гребля (на байдарках и каноэ) — Чичиков хотел жениться, но родители невесты хотели не какого-то там мелкого чиновника, а солидного
рабодушевладельца, поэтому Чичиков идемпинговалпокупал себе ну хоть какеие-нибудь души "на бумаге", которые помещики отдавали ему за сущие копейки, потому как всё равно ведь померли.gazkom
04.07.2023 16:02+13Что за эротические фантазии. Хотел взять кредит под залог крестьян и пропасть. Инспектору, которые приедет проверять наличие душ, дать на лапу.
Wesha
04.07.2023 16:02-1Не знаю, не знаю. Лично мне помнится так. Может быть, потому, что в советское время было бы довольно сложно объяснить (да ещё школоло), что такое "кредит", да ещё под залог.
SlFed
04.07.2023 16:02+3Это при встрече с Ноздрёвым Чичиков историю историю про свадьбу придумал:
— Ну, так я ж тебе скажу прямее, — сказал он, поправившись, — только, пожалуйста, не проговорись никому. Я задумал жениться; но нужно тебе знать, что отец и мать невесты преамбиционные люди. Такая, право, комиссия: не рад, что связался, хотят непременно, чтобы у жениха было никак не меньше трехсот душ, а так как у меня целых почти полутораста крестьян недостает...
— Ну врешь! врешь! — закричал опять Ноздрев.А поскольку это самое первое объяснение (хоть и придуманное на ходу) которое встречается в книге, вот оно и осталось в памяти. А настоящая причина скупки мертвых душ раскрывалась в последней главе :
"Эх я Аким-простота, — сказал он сам в себе, — ищу рукавиц, а обе за поясом! Да накупи я всех этих, которые вымерли, пока еще не подавали новых ревизских сказок, приобрети их, положим, тысячу, да, положим, опекунский совет даст по двести рублей на душу: вот уж двести тысяч капиталу!...."
rinac
04.07.2023 16:02+1Ну а зачем ему деньги-то? Да, в том числе, чтобы женится на хорошей невесте. Почему по вашему во времена Пушкина мужчины женились в основном в 30+ лет, а женщины могли вообще в 15 лет выйти замуж? Именно поэтому, от мужчины ожидали, что он будет полностью содержать семью, а женщина просто будет рожать детей.
mayorovp
04.07.2023 16:02+5Деньги ему для того чтобы жить хорошо. Хорошая невеста сюда, может, и входит (а может и нет) — но вот никакие родители никакого условия на количество "душ" ему совершенно точно не ставили.
oragraf
04.07.2023 16:02+10Ниже комментарий верен. Мне в свое время учитель объяснял так. Перепись крестьян проводилась не чаще 1 раза в 10 лет. В этот промежуток они считались живыми по "ревизским сказкам". Чичиков хотел заложить несуществующих крестьян и получить под это дело кругленькую сумму. Когда все это выплыло бы - крестьяне оказались умершими и формально к Чичикову претензий бы не было.
FreeNickname
04.07.2023 16:02+2Помню, как-то работал с системой, в которой было 3-5 (уже точно не помню) способов представить булевый тип данных в базе, и ни один из них не был нормальным. Запомнился только мой фаворит: varchar(1), где символ
'1'
– это true, аnull
– это false.Ещё там в качестве "языка разметки" при формировании документов использовался Lisp, интерпретатор которого был зашит в Java (система была на Java).
DikSoft
04.07.2023 16:02+1Помню, как-то работал с системой, в которой было 3-5 (уже точно не помню) способов
В конфигах линуксовых оно и сейчас так.
Хорошо ещё, если не THIS_CRAZY_VALUE_IS_TRUE как "истина", любое другое - "ложь".
Latrommy
04.07.2023 16:02Иванов *
Иванов *
Иванов *
Иванов Пётр *
Иванов Пётр *
Иванов Пётр Сидорович
Иванов Пётр Геннадьевич
Такое... пишет в поле имён гостей одна достаточно известная ERP. Почему и зачем - загадка.
MVS366
04.07.2023 16:02+12Автор такой молодец и пусечка, возьми с полки пирожок, ибо походит на хвастовство в стиле "они все дураки, а я умный".
Возьмём в пример это негодование:
Ладно, поля name, surname, phone и подобные я пойму по названию и содержимому, а как быть с полями ID,PID,GID,SID ? Где в varchar хранится целочисленные значения? Вообще все поля в таблице, кроме дат, имеют тип varchar(50) и varchar(100), удивительная согласованность
Потому, что данные приходят из других источников и придти может что угодно и когда угодно. Например, строка вместо числового идентификатора. И разработчики просто абстрагировали себя от каких-либо проблем с этим, потому что четкого понимания "что нам пришлют" не было ещё на этапе проектирования этой таблицы. А не потому, что разработчики были дураки.
Огромное количество ИС построены в таком духе и это поведение никто не меняет, потому что сделать "правильно", на первый взгляд, может оказаться фатальными последствиями и вечными проблемами. Если вы видите, что в программе/таблицах творится, на первый взгляд дичь, не надо спешить все переделывать, ибо есть очень высокие шансы все сломать. Т.е. тут фигурирует принцип "работает - не трогай".
То, что у автора все сразу завелось, это конечно, замечательно. Но вот бывают в жизни куда печальные истории, когда самоуверенный в своих силах юноша делает вроде бы всё правильно, а в итоге все рушится. Лично видел лет 10 назад ситуацию, когда найденная и убранная в коде ошибка привела к невозможности сделать заказ товара в коде интернет-магазина.
AnthonyMikh
04.07.2023 16:02+2Лично видел лет 10 назад ситуацию, когда найденная и убранная в коде ошибка привела к невозможности сделать заказ товара в коде интернет-магазина.
Я жажду подробностей. Какого рода ошибка? Какие ещё ошибки были (ибо один багфикс не мог бы сломать работоспособность всей системы)? Как оно вообще работало?
MVS366
04.07.2023 16:02+3Это была старая версия магазина Мвидео, написанная на PHP в ужасном стиле. Ошибка была как я помню такая:
if ($var = true) {
Как только это пофиксили (сделали $var === true), все сломалось. Логика сломалась.
NickyX3
04.07.2023 16:02ой, да я целые куски кода иногда внедряю через какой-то то if и проверку feature flag, а потом перед окончательным внедрением некоторое время остается
if(true){...}
Danem
04.07.2023 16:02+3Очень странный фикс, конечно сломалось, т..к. логика поменялась. Вместо присваивания, поставили проверку. Разбираться конечно надо, что имелось ввиду.
Мне встречались такие записи. И в моем случае их надо было фиксить типа так:
$var = true;
if ($var) {
Под true - там может быть и переменная и функция.
joeyes
04.07.2023 16:02Вас не смущает, что if ($var = true) это как бы и не проверка вовсе? Тело этого if'a будет выполняться всегда. Если вы уберете if, то в логике, собственно, ничего не изменится.
ogost
04.07.2023 16:02-2Потому, что данные приходят из других источников и придти может что угодно и когда угодно. Например, строка вместо числового идентификатора.
Это в стиле хуяк-хуяк и впрод?
isadora-6th
04.07.2023 16:02+5В базу могут писать из чужой системы свои id. Если завязаться на то, что на handler вам всегда будут прилетать uint32, и в базе хранить также, будет очень больно -- когда поставщик добавит нолик в начало или дефисы "021-34-124". А уж когда добавляют префикс... ммм, закачаешься)
Поэтому при интеграции с внешними системами id и хранят строкой, просто по той дурацкой причине, что входные данные могут поменяться в любой момент.
Gallemar Автор
04.07.2023 16:02В моем случае системы не были внешними, так что вполне можно было узнать какой тип поля использовать. Другой момент в том, что база использовалась как промежуточная для шины данных, возможно передача данных строками единственное что можно быть, но тут уже вопросы к разработчика biztalk.
iig
04.07.2023 16:02Потому что ID не является числом. Это идентификатор. Арифметические операции с ним бессмыссленны.
barbaris76
04.07.2023 16:02Очень напоминает логику одной системы для хранения, скажем так, сущностей клиентов в одном очень большом зеленоватом банке, название которой начинается на "Е" и состоит из трёх букв )
Прилететь действительно может что угодно и откуда угодно, от ФНС до планшета операциониста, поэтому абсолютно все поля - string, и только внутренние идентификаторы самой системы long int.
1CUnlimited
04.07.2023 16:02Просто одно дело когда такое делают в программном коде на языках программирования для быстрой разработки без строгой типизации, а другое дело когда закладывают бомбу в структуре СУБД (в которой целостность данных как минимум должна быть, констрейнты и много другое) . Разные уровни последствий (исправить код против исправить структуру СУБД) . Да автор умный - разгреб , а они все ...
Wesha
04.07.2023 16:02+1я остался дальше думать о странной архитектуре системы обмена, которая работала годами, кривая и косая, без сбоев.
Daddy_Cool
04.07.2023 16:02+7Ну нет.
Есть такие авторы которые начинают писать текст без каких-то вводных частей, сплошным потоком мыслей персонажа. Читатель в такой момент выдает автору некий кредит доверия, надеясь что через не слишком продолжительное время все ниточки завяжутся в узелки и будет понятно ради чего это всё. И в конце читатель скажет: "Эвон как! Ого! А я-то и не думал..." . Что имеем здесь. Автор решил типовую проблему для БД - наличие дублей. Автор молодец. Всё. И об этом статья?
Dimaasik
04.07.2023 16:02+15Меня недавно спросили, почему программисты ненавидят работать с чужим кодом. Долго думал, как донести до обычного пользователя всю суть пиздеца.Решил привести небольшую аналогию:
Вот представь, что тебе доверили достроить за другим прорабом лабораторию на острове. Ты приходишь на объект, а там кроме недостроенного здания: огромный вентилятор (размером со здание), большой воздушный шар и комната набитая швабрами. Почесав голову, ты разбираешь этот хлам и доделываешь лабораторию. Сдаешь объект ученым, но через 5 минут они выбегают с криком: "УТЕЧКА ЯДОВИТОГО ГАЗА!!!".
— Как так–то, блять! Должно же работать! — в отчаянии кричишь ты и звонишь прошлому прорабу:
— Вася, у нас ядовитый газ потёк! В чем проблема?
— Не знаю, должно было все работать. Что–то в проекте менял?
— Немного, швабры вынес...
— Швабры потолок держали!
— Что??? Что, блять, извините???
— Говорю, швабры потолок держали. Над ними цистерны с газом были. Очень тяжелые, пришлось в комнату снизу швабры напихать.
— Ты хотя бы записку на двери повесил бы, что швабры для держания потолка! У нас тут ядовитый газ течет! Что нам делать?
— Включай вентилятор. Он сдует газ с острова.
— Я его, блять, демонтировал сразу же!
— Зачем?
— Зачем ты построил 120 тонный вентилятор? Ты не мог положить ящик блядских ПРОТИВОГАЗОВ?
— Ящик противогазов искать нужно, а вентилятор у меня с прошлого заказа оставался.
— Вася, я убрал твой вентилятор! Мы тут задыхаемся!
— Херли вы тогда там делаете? Садитесь на воздушный шар и уебывайте!DmitryKuzmenko
04.07.2023 16:02+3в начале 90х работал в конторе. Язык ДИАМС (mumps). И в отделе было 3 основных программиста. Поскольку я переквалифицировался из электронщика в программисты, приходилось иногда работать с чужим кодом.
Один вроде писал нормально. Но остальных двух запомнил навсегда:
1 - именовал переменные только как A2, B5, C31 и так далее. Переменных было много. После страницы текста было уже невозможно вспомнить, зачем нужна переменная D4.
2 - использовал динамический код. Хранил в базе куски кода, которые подгружались по условиям, после чего формировался какой-то код в переменных, переменные выполнялись по execute. По коду было невозможно понять, что происходит в программе, приходилось понимать только через отладчик.
Slonosvin
04.07.2023 16:02+5Не помню, где я это спёр, но по духу очень подходит ))
Hidden text
randomsimplenumber
04.07.2023 16:02Кул стори, бро ;) Выглядит как тайм-бомба. А такие вещи делают не от неумелости, а для подстраховки от кидка. Судя по всему, кидок произошел
Gallemar Автор
04.07.2023 16:02Не думаю, просто неумелость разработчиков
iig
04.07.2023 16:02Неумелость? Не думаю! (ц)
Заложить переполнение в правильное место, чтобы оно свалилось, но не не сразу. это надо умеючи. И чтобы DBA, даже просматривая содержимое таблиц, ничего не заподозрил. Да, и в результате срабатывания всё остановилось, и заказчик потерял денег. Напоминает поделки выетконговских партизан - выглядят коряво, но результат ровно как планировался.
Joysi
04.07.2023 16:02+2"Случаи они разные бывают" (c) анекдот. Начинающие финтех разрабы делают счета как decimal, а потом "внезапно" появляются счета металлов.
nivorbud
04.07.2023 16:02а как быть с полями ID,PID,GID,SID? Где в varchar хранится целочисленные значения? Вообще все поля в таблице, кроме дат, имеют тип varchar(50) и varchar(100), удивительная согласованность.
А что вас смущает? Предполагаю, ваше ретроспективное мышление, когда вы видите конечный результат и его текущее окружение.
Давайте переместимся назад во времени - в точку одной из многочисленных переинтеграций. Откуда то из-вне приходят xml-файлы. Айдишники там в то время возможно были символьными и только в одной из следующих интеграций стали целочисленными. Или в то время не было четко понятно или не было гарантий, что айди будут абсолютно всегда целочисленными. Но даже если при очередной интеграции было строго определено (с гарантией), что айдишники далее будут только целочисленными, то... возникает вопрос: "А стоит ли трогать работающую систему?"
В общем не удивляйтесь подобным странностям, когда видите старый продукт. Зачастую перед руководством встает возможных решения: 1) Модернизировать старую систему с компромиссами и костылями, либо 2) переписать всё с нуля. Далеко не всегда второй вариант возможен и целесообразен.
OlegZH
04.07.2023 16:02Давайте переместимся назад во времени ...
В реляционных базах данных (наверное, в базах данных, вообще) с давних времён существует понятие домена — некоторого семантического диапазона допустимых значений. По идее, схема БД должна опираться на домены.
... Далеко не всегда второй вариант возможен и целесообразен.
К сожалению, да. Хотя, с точки зрения целесообразности, нужно именно переписывать, а сами данные прогонять через конвертер, используя, при этом, некий внешний открытый формат для промежуточного хранения.
mayorovp
04.07.2023 16:02+3Домен помогает когда задача обеспечения формата данных важнее задачи их сохранения. Если задача системы — хранить все поступающие в неё данные независимо от изменений в их формате, то тут не то что varchar, тут BLOB бывает хранить приходится.
Ну а если некоторые данные являются суррогатным ключом из внешней системы и не имеют смысла за её пределами — то попытки их конвертировать или как-то валидировать — и вовсе вредительство.
czz
04.07.2023 16:02+7Не очень понятна проблема. Если нужно просто ускорить поиск, индекс не обязательно должен быть уникальным.
Дальше упоминаются объемы данных — 20 тысяч, 75 тысяч записей — это по современным меркам вообще ничто, оно бы целиком лежало в дисковом кэше, и даже полный скан выполнялся бы за миллисекунды. Неочевидно, почему с таким объемом оно бы еле ворочалось.
Или это какая-то совсем специфичная БД, которую пора закапывать, но не дают.
Gallemar Автор
04.07.2023 16:0275 тысяч строк, из которых 15 тысяч дублей, от 2 до 10 копий одной строки... Что будет индекс, что не будет, ворочаться это будет очень долго.
Тут индекс нужен для ограничения добавления дубликатов записи, по скорости проблема возникла из-за частого обновления данных и ненужного перебора одинаковых строк. 75к строк в одной таблице, а таких таблиц было достаточно, что запуск обмена данными накладывался друг на друга.
czz
04.07.2023 16:02То есть, они хотели, чтобы не добавлялись дубликаты, но при этом в данных уже была куча дубликатов. Тогда понятно :)
nikalone555
04.07.2023 16:02>Откуда взялись в таблице повторные записи о
крестьянахсотрудниках?Можно было и не зачеркивать
Dominux
04.07.2023 16:02-1В ВК, месте встречи "лучших", на всех проектах, что я работал, а они были буквально в несколько месяцев отроду, были точно такие же проблемы
В душе не ипу, откуда берутся такие слепые олухи, что не включают мозг от слова совсем
martyncev
Простите, но оформление статьи лютая жесть...
Gallemar Автор
Полностью согласен! Крутил исходник и так, и эдак - всё равно кроме двух абзацев и картинки в голову ничего не пришло, поэтому кто осилит до конца сею пиСсанину имеет право на шоколадную медальку!
Ilusha
Выделение абзацев Вам бы помогло. Куплю себе медальку, обязательно шоколадную
Gallemar Автор
выделил абзацы
igrek11
А я считаю, что оформлено как надо - упор на юмор. Здесь не надо ничего структурировать - это так, "лёгкое чтение". Мне понравилось - сразу вспоминаются аналогичные
sepetov
Не-не-не, к юмору определённо претензий нет и быть не может. В этом смысле статья на 5+.
Коллега, вероятно, имеет в виду абзацы, пунктуацию. Но с этим лучше бороться через ctrl+Enter, конечно же.
Gallemar Автор
сделал легкий тюнинг текста
TsarS
Мы же на Хабре — рефакторинг!