Как писать много кода, оставляя его простым, как в начале?

Привет.
Я пишу код 5 лет, и меня всегда дико пугает, когда кода становится больше чем 1000 строк.
Как бы я его не разбивал по файлам, классам (структурам). Дошло даже того, что в одном проекте все сделал на микросервисах, от них вообще офигел, они сложность повысили раза в 3.

Речь не про надежность, тесты я пишу. А именно про: "оставить код простым, чтобы куда не сунься, хоп и все понятно, в голову влезло, легко добавить новое.". После 1000 строк у меня код в голову не влазит, начинаю его боятся, филонить...

Пишу на Go, JavaScript.

В энтерпразе не работал, только стартапы, поэтому опыта в этом нет.
Опытные подскажете?

UPD.
Может я конечно код неправильно пишу. Вот мои репозитории

Лучшие ответы:

  • Один класс решает одну конкретную задачу, не стоит городить комбайны. - @jamakasi666
  • Абстрагировать все максимально. - @jamakasi666
  • Число строк одного метода - не более 20. - @karminski
  • Сжатые доки (на русском), как писать чистый javascript код - @BUY33
  • Комменты описывают намерение (зачем?) а не реализацию (как?). - @dmz9
  • Называть переменные и функции так, чтобы и без комментариев было понятно зачем они?


Всем спасибо.
  • Вопрос задан
  • 10875 просмотров
Решения вопроса 3
jamakasi666
@jamakasi666
Просто IT'шник.
1) Документируй
2) Абстрагируйся всегда максимально
3) Пиши классы по принципу "черного ящика"
4) Один класс решает одну конкретную задачу, не стоит городить комбайны.
Ответ написан
@karminski
Разработчик CRM/ERP систем
Я стараю придерживаться простого совета: один метод - одно действие. Т.е. каждый метод класса должен выполнять строго одну функцию. Число строк одного метода - не более 20.
Ответ написан
Пригласить эксперта
Ответы на вопрос 15
Deerenaros
@Deerenaros
Программист, математик, задрот и даже чуть инженер
Ну в общем-то ответов много. Могу кое-что добавить от себя.

Во-первых, голова не бесконечна во всех смыслах. Запихнуть в неё больше чем 10 объектов одновременно вряд ли получится, у многих начинаются огромные проблемы уже с 5. Лично я испытываю ДИКУЮ больше если собственный код становится больше чем большой. Для меня это где-то 15~20 сущностей, причём чем сильнее они связаны, тем сложнее они даются, так как становится сложно абстрагироваться. Что примечательно, при работе с чужим кодом таких проблем практически нет, ну у меня по крайне менее. Всё дело в том, что чужой код изначально воспринимается чёрным ящиком, поэтому если он не представляет из себя дикую лапшу, аккуратная работа с ним с минимальным вмешательством в проект получается легко.

Во-вторых, не стоит делать код пушистым. Однообразность воспринимается лучше. Массивы некоторых объектов надо называть $названием_объекта + 's'. Классы с большой буквы, любой объект стоит называть так же, как класс, но с маленькой буквы. Конечно, если семантически прям просится по другому, то стоит правило нарушить, но в общем случае никаких выдумок не надо. Временная строка - str, временное число - tmp, временный объект - obj. И пробелов не жалей, внутри файла разделяй разные структуры двумя-тремя пробелами, стоит обособлять регионы, например, "глобальные" функции наверху, по середине структуры, потом классы. В C# есть #region.

В-третьих, объём тоже воспринимать сложнее. Делай плоско. В том смысле, что меньше наследований, больше включений. Очень тяжело воспринимать архитектурную лапшу. Суть микросервисов - единственная, максимум две точки связи. То есть контроллер де должен склеивать всё и вся, это задача "простейшего" диспетчера событий, который крутится в своём цикле и распихивает поступившие сообщения. Микросервису надо думать чем меньше, тем лучше. Вообще, микросервисы не всегда хорошо подходят, они очень удобны в сверхраспределённых командах с высокой степенью самодисциплины, когда привычка смотреть в доки сильнее привычки звонить тимлидеру на каждый чих. В случае с самостоятельной разработкой они скорее зло, хотя в web поначалу довольно приятно, заливать чрезвычайно низкую производительность ресурсами, забивая каналы связи под завязку, не лучшая идея. Порой намного лучше сделать монолитно, но аккуратно, особенно когда вся жизнь продукта под ответственностью одного человека.

В-четвёртых, внутри монолита также надо делать максимально растянуто, никаких супер-синглетонов бдящие за всем и вся. Равно как и с микросервисами, внутренние объекты должны иметь минимум точек входа. Они должны быть просты и понятны. Если какой-то класс выполняет слишком много задач, часть из них надо делегировать другим классам, возможно новым. Это по сути цикломатическая сложность, о которой упомянул Ivan Sokolov, но мне не нравится эта формальность. Да и некоторые вещи сложно формализовать ребром на графе.

В-пятых, иерархия должна быть логически правильной, наивной, без выпендрёжа. Тоже самое, что и в третьем, плоское лучше объёмного.
Плохо:
3dbadffb7d954b2d93a5dfec863289be.png
Лучше:
1238e5c4d83340239a250116d4d2fa3a.png
Пример немного утрирован, но суть, навроде, ясна. Не надо наследовать всё и вся от чего угодно, если есть возможность включить внутрь - включай. Всё остальное от лукавого и только в крайних случаях.

В-шестых, используй UML, mind maps, блокнот и таск менеджеры. Эти инструменты словно манна небесная спасают дедлайны от полного уничтожения. Хотя UML спорен, им очень удобно следить за структурой проекта, представлять картину с высока, рисовать его стоит самому, учитывая неявные связи и убирая рудиментарные. Mind maps помогут собрать мысли в кучу. Блокнот банально запишит то, что постоянно забывается. Таск менеджеры сформируют привычный график, отобразят прогресс, помогут фокусироваться на текущих задачах, не растекаясь по проекту.

В-седьмых, изучи декларативное программирование. Шикарная вещь, которая позволяет сконцентрироваться на задаче, а не на решении. Из коробки в функциональных (erlang) и логических (prolog) языках, однако многие элементы существуют и в монстрах вроде C#/Java, Python, особенно JavaScript. Насчёт Go не знаю, но на первый взгляд весьма декларативный.

И наконец, сложность проекта в любом случае будет расти. Единственное, что с этим можно сделать - это смирится. Да, все эти способы помогают, но они лишь оптимизируют имеющееся, позволяя больше впихнуть в голову. Но с ростом проекта сложность расти будет всё равно, энтропия замкнутой системы не уменьшается. Именно поэтому её локально уменьшают в разных кусках кода, однако натыкаются на другие грабли - один винтик может дёргать тысячи классов. Сам-то винтик прост, однако он всё равно берёт на себя слишком многое.

Ещё пара абзацев. Ну про вредные советы: методы надо делать ровно такими, какие они должны быть. 20 строк - это что-то вроде лакмусовой бумажки, однако это лишь один из параметров. Очень часто требуется сделать громадную функцию для работы со сложным API или подробить много разных чисел в циклах. Поэтому ориентироваться на это плохая идея. Равно как и папки-подпапки-подпапками погоняемы, максимум - два уровня в чрезвычайно сложных проектах. Иначе будет очень сложно ориентироваться. Ещё происходит странный возврат к комментариям. На мой взгляд, если это не продаваемая за большие деньги библиотека - нах*й надо. Документация в комментариях - рудимент кода, он нигде не используется, зато требует поддержки, на что приходится распылять внимание. Если нет условного рефлекса править комментарий после кода - выбрасывайте и не жалейте. Везде! Без исключений. Ещё есть много "архитектурных" паттернов. Снова вред, если вы не работаете в Google, где вашу зарплату надо оправдать умными словами. Самый эффективный способ - программировать наивно. Чем проще для вас сейчас, тем лучше для вас потом. Если думаете больше десяти минут - плохо думаете... Но об этом позже. Паттерны это некая систематизация неких знаний, причём совершенно не понимаю, зачем её вообще сделали. Да, архитектура в некотором роде нужна, но постоянно искать какой паттерн здесь надо использовать, если это не проект на 100500 лет вперёд - нельзя. Почти всегда будет дешевле в случае неуспеха отрефакторить всё и вся, чем переписывать с нуля или тратить месяцы на продумывание архитектуры... В которой также могут быть ошибки.

И последнее. Отдыхайте. Если чувствуете, что задыхайтесь - пройдитесь, подышите. Если чувствуете, что мозги плывут - отвлекитесь, подумайте о другом. 8 часов в день для программиста - это дикость, но это реальность. Для разных людей наибольшая эффективность поддерживается где-то 3~6 часов, причём некоторым требуются перерывы каждые пол часа, другому хватит обеденного перерыва, а третьему вообще нельзя сегодня никаких отвлекающих факторов, ибо "волна". На самом деле, человек существо динамичное, так что будут разные дни. Но если не получается сейчас - не бесите самого себя. Отдохните, перекусите, пробежитесь, покурите, проверьте почту, послушайте музыку, почитайте статью. Не тратьте время бездарно и, что интересно, проблемы будут решаться, усилия будут прикладываться аналогичные, но вот ощущения от работы станут совершенно другими.
Ответ написан
@dmz9
не знаю зачем тут пацаны советуют чистый код Р. Мартина
https://www.ozon.ru/context/detail/id/5011068/
вот что нужно на замену
https://www.ozon.ru/context/detail/id/138437220/
есть обе у меня, но красненькую купил раньше. в ней больше информации как внешне так и по сути.

в чистом коде как то всё "поверхностно" и после "совершенного кода" ощущается какая то "казуальность", а еще там дурацкие картинки, не имеющие отношения к программированию.
-----------------------------
боязнь большого кода, как правильно заметил, идет от того что ты не можешь запомнить всё что происходит "вокруг" кода, т.е. не хватает твоей собственной "оперативной памяти". и это вполне нормальное явление, среди нас пока мало киборгов (хотя они и есть уже), а уж с суперкомпьютером в голове так и вовсе нет.

во многих конвенциях о code-style указываются очень грамотные вещи, и они не просто так там находятся. в общем смысл такой - код должен быть самодокументируемым/самоописательным, а комментарии к коду не должны быть в стиле "моё почтение, капитан!".
  • код должен напоминать хорошую прозу.
  • комменты описывают намерение (зачем?) а не реализацию (как?).
  • прими во внимание время жизни переменной. чем ближе переменная к "месту военных действий" тем лучше. перекликается со временем жизни переменной - чем быстрей "умирает" тем лучше, часто можно даже без переменной обойтись не в убыток читаемости.
  • люди советуют тут ограничиваться конкретным числом строк. имхо - вредный совет. метод не должен ограничиваться. метод должен быть такой длины, которая требуется. иногда без "супер-методов" не обойтись (это не о функциях на 100500 входящих параметров), невозможно просто разбить тяжелую функцию на более мелкие куски, не умножив число запоминаемых переменных/других методов. метод может быть в 3 строки, может быть даже в одну, а может быть в сто-двести строк и более. если из метода ничего нельзя выбросить и нечего добавить - значит он в точности занимает столько сколько нужно.
  • многословие приветствуется но без фанатизма. машине почти всё-равно какой длины у тебя функция (если вы понимаете о чем я), а для тех кому нет - есть минификаторы (для js например)
  • название метода должно однозначно говорить о его назначении. спрашивай себя - зачем этот метод? зачем это свойство? если ты не можешь ответить значит и запомнить/вспомнить не сможешь, значит у метода/свойства нет конкретного предназначения и потом (через какое то время) будет сложно разобраться для чего он вообще нужен.
  • визуально отделяй приватные/публичные методы. это тоже некоторая подсказка которая помогает разбираться в коде.
  • следуй одному стилю как минимум в отдельно-взятом файле. (кемелкейс отдельно, лодаш отдельно).
  • следуй принципу наименьшего удивления (программиста). т.е. поменьше роялей в кустах
  • соблюдай абстракции. если класс это не просто набор статичных методов - значит он описывает какие то действия вполне определенного объекта. не раздувай и не разбивай на несколько классов одну цельную и четкую абстракцию. это поможет сосредоточиться на отдельном куске кода в один момент.
  • старайся не писать рядом в одном классе методы, которые "проникают" во все аспекты приложения (антипаттерн - суперкласс/божественный объект).


вообще стоит почитать про паттерны и антипатеррны, это конечно не библия но знать хотя бы основные стоит.
Ответ написан
@Free_ze
Пишу комментарии в комментарии, а не в ответы
Необходимо развивать абстракцию в коде. Чтобы думать о том "что эта штука умеет?", а не "как это работает?". Кроме того, это избавляет эффекта бабочки (от изменения в одной строчке перестает работать все остальное), разделяя сферы ответственности.

Да, инфраструктурного кода станет еще больше, но чтобы понимать работу системы в целом будет не обязательно держать в голове механизмы тысячи черных ящиков, достаточно знать, как выглядят интерфейсы основных сервисов. Если же нужно углубиться - изучаем отдельный, независимый уровень сервиса.
Ответ написан
saboteur_kiev
@saboteur_kiev
build engineer
Код не получится простым.
Из-за сложности кода, возникла парадигма ООП, возникла модульность и так далее.

Для упрощения используют следующие вещи:
Комментарии. В java для этого есть целый стандарт с автоматически генерящимися javadoc. Комментарии имеют стандарты, например только английский, в едином стиле.
Стандарты именования переменных, классов, методов, что упрощает чтение.
Собственно ООП - при грамотном подходе, это позволяет создавать максимально независимые инкапсулированные классы.

А иначе - никак.
Ответ написан
sim3x
@sim3x
TDD - если строго следовать и не мудрить при рефакторинге, то сложным сделать не получится
Ответ написан
LightAlloy
@LightAlloy
Ruby developer
Читайте книги (и другую информацию) по архитектуре приложений, старайтесь использовать техники/следовать правилам из них (таким, как single responsibility principle, слабая связанность и т.п.) Я сейчас читаю Confident Ruby, обещают научить, как сделать так, чтобы приложение со временем не превращалось в адски-сложное нечто, думаю, и по "вашим" языкам есть подобные книги.
Ответ написан
Santacruz
@Santacruz
Cryptocurrencies + Trading
Ты же на Go пишешь... выноси все интерфейсы в types.go - посмотрел и все ясно.
+ SOLID для Go https://dave.cheney.net/2016/08/20/solid-go-design
Ответ написан
index0h
@index0h
PHP, Golang. https://github.com/index0h
Делайте маленькие интерфейсы. Например сервис регистрации пользователя на вход принимает dto и реализует внутри кучу логики, но у этого сервиса интерфейс из 1 метода. Безусловно, внутри может юзаться и 5 и 10 и щещ больше не экспортируемых, это ок, но работать с таком сервисом будет очень просто, да и тестировать другие штуки, использующие его будет проще.

Не делайте сервисы с изменяемым состоянием, на сколько это возможно конечно.

Старайтесь не использовать экспортируемые переменные пакета, это аналог глобальных переменных, а про их вред уже 100500 обзоров написано.

Грамотно разделяйте алгоритм по методам. Если ваш метод делает 2 крупных разных вещи - вероятно стоит разделить. Если одну крупную возможно разделять не стоит. По примеру с регистрацией: весь процесс от валидации, сохранении пользователя до подергивания сервисов отправки почты спокойно можно забить в один метод. К эмпирическим правилам в стиле метод не больше 20 строк - подходите с осторожностью, далеко не факт что разделение будет хорошей идеей.
Ответ написан
@lega
Я делаю как бы "микросервисы внутри" (черные ящики) - модули с несколькими публичными методами, которые должны давать гарантированный результат. А уж внутри черного ящика может быть любое месево, главное оно не влияет на проект. Так же любой черный ящик может быть переписан с нуля не затронув остальные части проекта.
Любой черный ящик можно будет превратить в реальный микросервис, так же не затронув остальной проект.
Тут я для себя сделал небольшой свод правил, хотя он относится к синхронному коду (стилю), а у вас js, тут со старта сложность повышается. Бизнес логику писать в асинхроне не желательно.
Ответ написан
@private_tm
JAVA dev
Вызов многих методов заворачивайте в один метод то есть повышайте абстракцию и все будет ок=) используйте с умом наработки других(библиотеки, фраимворки, апи).

Ну и дробите на все на мелкие файлы/папки/подпапки/методы/класы с четкой структурой.
Ответ написан
@mpavlov
Стив Макконелл – «Совершенный код» вам прописан
Ответ написан
@zoh
Всё приходит с опытом.

Предлагаю почитать "Совершенный код" - очень помогает при любых скилах. Хорошо разъяснено в ней про "управление сложностью" разработки.
По js - Design Pattern.

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

Вы пишите что разбили на микросервисы - и у вас новая проблема - интергация всех этих сервисов!
вопрос - зачем?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы