Вопрос: Как писать код без багов?

Автор Сергей Чистович задал интересный вопрос в категории Программирование. Он спрашивает: Как писать код без багов?. Среди полученных ответов он выбрал самый точный и полный ответ.

Самый полезный ответ для автора Сергей Чистович

Я пишу код уже больше 20 лет и, хотя в последнее время больше занимаюсь руководством, на пике формы был способен писать по 500+ строк хорошо работающего кода в день. Вот принципы, которые мне в этом помогали:Не переобобщайте. Если не получается малой кровью создать универсальное решение, то и неважно, решите конкретную текущую задачу и двигайтесь дальше. Обобщение, даже хорошее, в 70% случаев так и остается нигде больше не использованным.Не оптимизируйте код заранее. Идея усложнить код ради его ускорения почти всегда ошибочна. Исключение возможно только в том случае, когда именно этот участок код «тормозит» так, что это уже заметно на уровне продукта или бизнеса. «Пессимизировать» код тоже, конечно, не нужно, из двух версий, одинаковых по сложности и по объему кода, выбирайте более быструю. Из этого есть важное следствие: нельзя дублировать данные и нельзя кешировать результаты вычислений там, где этого не требует во весь голос производительность. Больше половины структурных багов возникает из-за того, что «разъехались» кэш и реальные данные, причем еще и отлаживать такое обычно адски сложно, потому что в момент собственно «разъезжания» никакого бага еще не видно, он проявится потом, когда ставить breakpoint-ы и проходить исполнение по шагам уже поздно.Называйте и группируйте всё происходящее правильно. Код, в котором нет алгоритмических или технологических сложностей, должен читаться как текст, написанный по-английски. Хорошо, когда код, в котором ниндзя куда-то крадётся, выглядит как-то вроде ninja.sneak(…), а не pDst2.trySetCoord(…) и ещё десять строчек после этой, ни одну из которых нельзя забыть. Если функция что-то меняет в состоянии объекта, она не может называться isSomething — если так сделать, следующий же код с её участием обречён на интересный дебаг. Если функция что-то трудно вычисляет, она не может называться getSomething — кто-нибудь наверняка начнёт вызывать её в цикле и удивляться, почему всё тормозит. Класс, который хранит состояние документа, может называться DocumentState или Document, но никак не SDManager. Кстати, про Manager-ов. Если единственное название, которое вы можете выбрать для класса или метода, получается очень расплывчатым, это верный признак того, что вы делаете что-то неправильно. Классы BaseObject и World или функции databaseOps и initService быстро приведут к самым разным проблемам и багам, связанным с нарушениями этого и предыдущего пунктов.Не смешивайте алгоритмы и другие технологически сложные участки кода с бизнес-логикой. Выразительности современных языков программирования вполне достаточно для того, чтобы, скажем, графический движок компьютерной игры ничего не знал о ниндзя и вертолётах, функции работы с БД в CRM-системе не знали слов «счёт» и «клиент», и т.д. и т.п. Для бизнес-логики типичны постоянные изменения, нечеткость и путаница. Как только сущности с разных уровней абстракции начинают упоминаться в соседних строчках кода, , всё это тут же начинает проникать и в технологически сложный код, и всё взрывается. Не используйте никакие advanced фичи никакого языка. В С++, например, не стоит пользоваться темплейтной магией, переопределением операторов, множественным наследованием и т.д. и т.п. Экзотические языки программирования (Haskell, диалекты Лиспа, хитрые декларативные язычки, работающие поверх JVM) вообще стоит использовать только как хобби, источник вдохновения. Не напрямую в той работе, за которую вам деньги платят. Эта точка зрения часто вызывает споры. К сожалению, обстоятельно аргументировать её в формате ответа на Знатоках не получится. Поэтому просто сошлюсь на свой почти 20-летний опыт индустриального программирования. Во всех областях и организациях, в которых я успел поработать, что в Яндексе, что в разработке игр, что в науке идея использовать в качестве рабочего инструмента «красивый полёт свободной мысли, недоступный простым умам» оказывалась разрушительной. Часто и для всего проекта, но всегда, без исключений, для автора идеи.Стоит выкинуть из головы все ООП. Единственное полезное, что в императивные языки пришло из этой идеологии — модификаторы private. Иерархии классов это зло, наследовать реализации нужно себе запретить. Наследовать можно интерфейсы, и то не слишком много уровней. Агрегация почти всегда лучше наследования. Большая часть классических «шаблонов проектирования» уже либо устарела, либо нашла поддержку на уровне языка.Используйте как можно больше assert’ов, логов и прочих способов поймать незапланированное состояние системы как можно раньше. Очень часто в момент, когда неверное поведение системы становится заметно пользователю, дебажить её уже сложно. Если же вы смогли поймать систему именно в тот момент, когда её внутреннее состояние впервые становится неконсистентным или она начинает вести себя не так, как вы задумывали, чаще всего разобраться в том, почему, становится тривиально. Каждая лишняя строчка кода это зло. Там, где это вообще возможно, не стоит пользоваться чужим кодом, который вы не прочитали и не поняли от и до. Это касается в том числе и широко известных библиотек и фреймворков общего назначения. Чем меньше кода (включая и тот, который пишешь сам, и тот, от которого зависишь) — тем лучше.Граничные случаи стоит проверять «в голове» прямо по ходу написания кода. Например: я пишу list.back(), а почему этот список не пуст? Как я «доказал» к этому моменту, что этого не может произойти? Что сделает эта функция, работающая со строчкой, если она пуста?Любой баг, если он все-таки вам встретился, старайтесь возводить до первопричины и до общего правила. Что я написал в коде такого, что этот баг вообще оказался возможен? Как я могу поменять свои практики так, чтобы больше никогда не допускать таких же? Например, баг состоял в том, что я написал такую-то строчку в функции save и забыл добавить симметричную в функции load. Может быть, пора, наконец, заменить эту пару на одну функцию serialize? Обложить их тестами? Или хотя бы поклясться вслух самому себе, что никогда не будете трогать их по одиночке? Или, например, причина бага была в том, что в указателе pNeighbor содержится null, а программа этого не ожидает и падает. Можно просто воткнуть if (pNeighbor != null) и закрыть баг как исправленный. А где ещё в коде разыменовывается pNeighbor? Везде ли есть такая же проверка? Насколько вообще эта ситуация легальна, может быть, настоящая ошибка там, где pNeighbor впервые оказался нулевым? Если значение pNeighbor это результат отображения NULL из БД на объектную модель, то как этот NULL попал в БД, кто его туда положил и не стоит ли воткнуть там constraint? И т.д и т.п. Никогда не считайте, что ваш код уже идеален! Наблюдайте за собой, совершенствуйтесь, старайтесь работать вместе с людьми, у которых есть, чему поучиться.Тема эта неисчерпаема, приёмов и приёмчиков можно вспомнить ещё много, но я, пожалуй, остановлюсь на этой десятке. Всем хорошего кода!

Кроме этого другие посетители написали Ещё 11 ответов

Я пишу код уже больше 20 лет и, хотя в последнее время больше занимаюсь руководством, на пике формы был способен писать по 500+ строк хорошо работающего кода в день. Вот принципы, которые мне в этом помогали:Не переобобщайте. Если не получается малой кровью создать универсальное решение, то и неважно, решите конкретную текущую задачу и двигайтесь дальше. Обобщение, даже хорошее, в 70% случаев так и остается нигде больше не использованным.Не оптимизируйте код заранее. Идея усложнить код ради его ускорения почти всегда ошибочна. Исключение возможно только в том случае, когда именно этот участок код «тормозит» так, что это уже заметно на уровне продукта или бизнеса. «Пессимизировать» код тоже, конечно, не нужно, из двух версий, одинаковых по сложности и по объему кода, выбирайте более быструю. Из этого есть важное следствие: нельзя дублировать данные и нельзя кешировать результаты вычислений там, где этого не требует во весь голос производительность. Больше половины структурных багов возникает из-за того, что «разъехались» кэш и реальные данные, причем еще и отлаживать такое обычно адски сложно, потому что в момент собственно «разъезжания» никакого бага еще не видно, он проявится потом, когда ставить breakpoint-ы и проходить исполнение по шагам уже поздно.Называйте и группируйте всё происходящее правильно. Код, в котором нет алгоритмических или технологических сложностей, должен читаться как текст, написанный по-английски. Хорошо, когда код, в котором ниндзя куда-то крадётся, выглядит как-то вроде ninja.sneak(…), а не pDst2.trySetCoord(…) и ещё десять строчек после этой, ни одну из которых нельзя забыть. Если функция что-то меняет в состоянии объекта, она не может называться isSomething — если так сделать, следующий же код с её участием обречён на интересный дебаг. Если функция что-то трудно вычисляет, она не может называться getSomething — кто-нибудь наверняка начнёт вызывать её в цикле и удивляться, почему всё тормозит. Класс, который хранит состояние документа, может называться DocumentState или Document, но никак не SDManager. Кстати, про Manager-ов. Если единственное название, которое вы можете выбрать для класса или метода, получается очень расплывчатым, это верный признак того, что вы делаете что-то неправильно. Классы BaseObject и World или функции databaseOps и initService быстро приведут к самым разным проблемам и багам, связанным с нарушениями этого и предыдущего пунктов.Не смешивайте алгоритмы и другие технологически сложные участки кода с бизнес-логикой. Выразительности современных языков программирования вполне достаточно для того, чтобы, скажем, графический движок компьютерной игры ничего не знал о ниндзя и вертолётах, функции работы с БД в CRM-системе не знали слов «счёт» и «клиент», и т.д. и т.п. Для бизнес-логики типичны постоянные изменения, нечеткость и путаница. Как только сущности с разных уровней абстракции начинают упоминаться в соседних строчках кода, , всё это тут же начинает проникать и в технологически сложный код, и всё взрывается. Не используйте никакие advanced фичи никакого языка. В С++, например, не стоит пользоваться темплейтной магией, переопределением операторов, множественным наследованием и т.д. и т.п. Экзотические языки программирования (Haskell, диалекты Лиспа, хитрые декларативные язычки, работающие поверх JVM) вообще стоит использовать только как хобби, источник вдохновения. Не напрямую в той работе, за которую вам деньги платят. Эта точка зрения часто вызывает споры. К сожалению, обстоятельно аргументировать её в формате ответа на Знатоках не получится. Поэтому просто сошлюсь на свой почти 20-летний опыт индустриального программирования. Во всех областях и организациях, в которых я успел поработать, что в Яндексе, что в разработке игр, что в науке идея использовать в качестве рабочего инструмента «красивый полёт свободной мысли, недоступный простым умам» оказывалась разрушительной. Часто и для всего проекта, но всегда, без исключений, для автора идеи.Стоит выкинуть из головы все ООП. Единственное полезное, что в императивные языки пришло из этой идеологии — модификаторы private. Иерархии классов это зло, наследовать реализации нужно себе запретить. Наследовать можно интерфейсы, и то не слишком много уровней. Агрегация почти всегда лучше наследования. Большая часть классических «шаблонов проектирования» уже либо устарела, либо нашла поддержку на уровне языка.Используйте как можно больше assert’ов, логов и прочих способов поймать незапланированное состояние системы как можно раньше. Очень часто в момент, когда неверное поведение системы становится заметно пользователю, дебажить её уже сложно. Если же вы смогли поймать систему именно в тот момент, когда её внутреннее состояние впервые становится неконсистентным или она начинает вести себя не так, как вы задумывали, чаще всего разобраться в том, почему, становится тривиально. Каждая лишняя строчка кода это зло. Там, где это вообще возможно, не стоит пользоваться чужим кодом, который вы не прочитали и не поняли от и до. Это касается в том числе и широко известных библиотек и фреймворков общего назначения. Чем меньше кода (включая и тот, который пишешь сам, и тот, от которого зависишь) — тем лучше.Граничные случаи стоит проверять «в голове» прямо по ходу написания кода. Например: я пишу list.back(), а почему этот список не пуст? Как я «доказал» к этому моменту, что этого не может произойти? Что сделает эта функция, работающая со строчкой, если она пуста?Любой баг, если он все-таки вам встретился, старайтесь возводить до первопричины и до общего правила. Что я написал в коде такого, что этот баг вообще оказался возможен? Как я могу поменять свои практики так, чтобы больше никогда не допускать таких же? Например, баг состоял в том, что я написал такую-то строчку в функции save и забыл добавить симметричную в функции load. Может быть, пора, наконец, заменить эту пару на одну функцию serialize? Обложить их тестами? Или хотя бы поклясться вслух самому себе, что никогда не будете трогать их по одиночке? Или, например, причина бага была в том, что в указателе pNeighbor содержится null, а программа этого не ожидает и падает. Можно просто воткнуть if (pNeighbor != null) и закрыть баг как исправленный. А где ещё в коде разыменовывается pNeighbor? Везде ли есть такая же проверка? Насколько вообще эта ситуация легальна, может быть, настоящая ошибка там, где pNeighbor впервые оказался нулевым? Если значение pNeighbor это результат отображения NULL из БД на объектную модель, то как этот NULL попал в БД, кто его туда положил и не стоит ли воткнуть там constraint? И т.д и т.п. Никогда не считайте, что ваш код уже идеален! Наблюдайте за собой, совершенствуйтесь, старайтесь работать вместе с людьми, у которых есть, чему поучиться.Тема эта неисчерпаема, приёмов и приёмчиков можно вспомнить ещё много, но я, пожалуй, остановлюсь на этой десятке. Всем хорошего кода!

К сожалению идеального ничего не бывает (в первую очередь это касается людей, так как им свойственно ошибаться, а потом учиться на ошибках и самосовершенствоваться)… Баги это побочное явление любого продукта в том числе и продуктов коддинга, полностью избежать багов вряд ли удасться (многое зависит от навыков и опыта), самое главное уметь их грамотно находить и исправлять, еще их можно представить в голове до написания самого кода… Напишу от себя лично несколько советов, следуя которым вряд ли получится исключить все баги, но они помогут их минимизировать:Заранее продумайте логику своего проекта или его части, попробуйте представить потенциальные баги и недочеты, которые могут возникнуть…Пишите код только на свежую голову и ясный разум, если вы чувствуете усталость, тягость или недомогание, лучше отказаться от этой затеи и оставить на потом, так как в таком состоянии можно допустить много ошибок и сильно поплатиться нервами за «скорость»!Старайтесь не строчить код «как угорелый», вдумывайтесь в каждую строчку кода и не бойтесь «возвращаться назад».Соблюдайте табуляцию, делайте больше подробных комментариев и следите за оптимальностью кода.Никогда не доверяйте (даже себе), всегда проверяйте и тестируйте, чем чаще, тем лучше.Если вам кажется что ваш код недостаточно хорош и вы можете сделать лучше — выбор за вами переделывать/не переделывать, главное не навредить (коду и себе).Всегда записывайте свои идеи (хоть маркером на столе), лучше следовать своим идеям, чем пытаться «высосать из пальца»… Идеи — наше всё!Надеюсь эти советы принесут пользу и вы отметите что-то новое для себя, спасибо за внимание!

я не программист, но могу предполагать, что поможет1 делать всё максимально просто,2 отчётливо3 делать только уникальные имена4 писать комментарии5 прежде всего писать псевдокод6 не спешить7 всегда проверять программу в деле8 найтинаписать наиболее полный справочник исключений9 работать лишь при ясной голове, не ночью, высыпаться10 отлично знать язык11 смотреть на программу глазами пользователя12 стремиться локализовать ошибку13 делать сущностные тесты, не считать что просто прошедшая тест программа пригодна

никак

Ответ см. в книге «Совершенный код» С. Макконнелла

спасибо. много полезного нашел

надо учиться, учиться и еще раз учиться! как говорил дедушка Ленин..

Сам программист, неповерите, такие же проблемы)

Баги будут всегда, главное уметь их находить и устранять

Программ без ошибок не бывает. В принципе.Задача программиста — минимизировать их число. Это достигается только углубленным тестированием программы.

Вдумчиво и осознанно.

Практика, практика и еще раз практика в 10-й степени!А если честно, то это практически невозможно. Любой код, написанный не тобой, будет одним сплошным багом)) Можно конечно попытаться сосредоточиться и не спешить с написанием кода, обдумывая каждую строку, но это малоэффективно. Написать все, запустить отладчик, исправить и радоваться! имхо