/ clean_code_recommendations.md
clean_code_recommendations.md
1 # Рекомендации по созданию читаемого и качественного кода 2 3 Этот документ обобщает ключевые принципы и практики для написания "чистого" (clean code), читаемого и поддерживаемого кода, основанные на анализе научных статей с ресурса arXiv. 4 5 ## 1. Что такое "идеальный" код? 6 7 Идеальный, или "чистый", код — это не просто код, который работает. Это код, который **легко понять, модифицировать и поддерживать** другим людям (и вам самим в будущем). Ключевые характеристики: 8 9 - **Читаемость (Readability):** Код должен читаться как хорошо написанный текст. Его логика и намерения должны быть ясны без необходимости запуска и отладки. 10 - **Предсказуемость:** Поведение кода соответствует ожиданиям. 11 - **Простота:** Решает задачу самым прямым и эффективным способом, без лишних усложнений. 12 - **Поддерживаемость (Maintainability):** Внесение изменений не требует титанических усилий и не ломает другие части системы. 13 14 --- 15 16 ## 2. Ключевые практики для чистого кода 17 18 ### 2.1. Осмысленные имена (Meaningful Names) 19 20 **Проблема:** "Загадочные имена" и "непоследовательные соглашения" являются одними из главных барьеров для понимания кода. (Из статьи `2501.10037v1`) 21 22 **Рекомендации:** 23 24 - **Используйте описательные имена:** Имя переменной, функции или класса должно четко отражать его суть и предназначение. 25 26 *Плохо (из-за кратких и неочевидных имен):* 27 ```python 28 # data - это список температур, crit - порог 29 def proc(data, crit): 30 res = [] 31 for val in data: 32 if val > crit: 33 res.append(val) 34 return res 35 ``` 36 37 *Хорошо (имена отражают суть):* 38 ```python 39 def filter_high_temperatures(temperatures, threshold): 40 """Отбирает температуры, превышающие заданный порог.""" 41 high_temperatures = [] 42 for temp in temperatures: 43 if temp > threshold: 44 high_temperatures.append(temp) 45 return high_temperatures 46 ``` 47 48 - **Придерживайтесь единого стиля:** Выберите и используйте один стиль именования во всем проекте (например, `camelCase` для переменных и `PascalCase` для классов, или `snake_case` для всего). Это делает код визуально опрятным и предсказуемым. 49 50 - **Избегайте сокращений:** Не используйте сокращения, которые могут быть непонятны другим (например, `calc_int` вместо `calculate_interest`). 51 52 ### 2.2. Комментарии и документация 53 54 **Проблема:** Отсутствие или неадекватность документации — основная причина, по которой код становится сложным для повторного использования и воспроизводимости научных результатов. (Из статьи `2501.10037v1`) 55 56 **Рекомендации:** 57 58 - **Комментируйте "почему", а не "что":** Код сам по себе должен объяснять, *что* он делает. Комментарии нужны, чтобы объяснить, *почему* было принято то или иное неочевидное решение. 59 60 *Плохо (комментарий дублирует код):* 61 ```python 62 # Проверяем, больше ли баланс нуля 63 if balance > 0: 64 ... 65 ``` 66 67 *Хорошо (комментарий объясняет бизнес-логику):* 68 ```python 69 # Клиенты с положительным балансом получают бонус, 70 # это требование из маркетинговой кампании Q3. 71 if balance > 0: 72 ... 73 ``` 74 75 - **Используйте Docstrings и аннотации типов:** Для функций и классов пишите документацию (docstrings), описывающую их назначение, параметры и возвращаемое значение. В современных языках используйте аннотации типов. 76 77 *Плохо (непонятно, что принимает и возвращает функция):* 78 ```python 79 def get_users(data, min_log): 80 # ... 81 ``` 82 83 *Хорошо (понятные типы и документация):* 84 ```python 85 from typing import List 86 87 class User: 88 # ... 89 90 def get_active_users(users: List[User], min_logins: int) -> List[User]: 91 """ 92 Фильтрует список пользователей, оставляя только активных. 93 94 Args: 95 users: Список объектов пользователей. 96 min_logins: Минимальное количество логинов для статуса "активный". 97 98 Returns: 99 Список активных пользователей. 100 """ 101 # ... 102 ``` 103 104 ### 2.3. Структура кода и паттерны 105 106 **Проблема:** Качество кода можно измерять через "использование обобщений, наследования, переиспользования и других объектно-ориентированных концепций". (Из статьи `1106.6159v1`) 107 108 **Рекомендации:** 109 110 - **Принцип единственной ответственности (Single Responsibility Principle):** Каждая функция или класс должны делать что-то одно, но делать это хорошо. Не создавайте "швейцарские ножи". 111 112 - **Не повторяйтесь (Don't Repeat Yourself - DRY):** Если вы копируете и вставляете код, вероятно, его можно вынести в отдельную функцию или класс. 113 114 - **Используйте паттерны проектирования:** Для решения типовых архитектурных задач используйте общепринятые паттерны (например, "Фабрика", "Стратегия", "Декоратор"). Это делает ваш код более понятным для тех, кто знаком с этими паттернами. 115 116 ### 2.4. Автоматизация и инструменты 117 118 **Проблема:** Многие разработчики не используют инструменты для проверки качества кода, хотя такие инструменты могут автоматически находить проблемы. (Из статьи `2501.10037v1`) 119 120 **Рекомендации:** 121 122 - **Интегрируйте статические анализаторы и линтеры:** Инструменты, такие как ESLint (для JavaScript), Pylint (для Python), или SonarLint, могут находить потенциальные ошибки, "запахи" в коде и нарушения стиля еще до его запуска. 123 124 - **Автоматизируйте форматирование:** Используйте инструменты вроде Prettier или `black`, чтобы автоматически форматировать код по единому стандарту. Это избавляет от споров о стиле и делает код единообразным. 125 126 --- 127 128 ## 3. Психология восприятия кода: Не доверяйте слепо метрикам 129 130 **Проблема:** Статические анализаторы и IDE часто показывают метрики качества кода (например, "читаемость", "сложность"). Однако, как показывает исследование `2012.09590v2`, эти цифры могут вводить в заблуждение. 131 132 **Ключевая идея: Эффект якоря (Anchoring Effect)** 133 134 - **Суть эксперимента:** Разработчикам показывали фрагменты кода и рядом выводили случайное число, якобы представляющее "метрику читаемости". 135 - **Результат:** Оценка читаемости, которую давали разработчики, сильно зависела от показанного им "якоря". Если им показывали высокий балл, они оценивали код как более понятный, и наоборот, даже если сам код был одинаковым. 136 - **Вывод:** Наше восприятие качества кода очень субъективно и легко поддается влиянию внешних, даже бессмысленных, факторов. 137 138 **Рекомендации:** 139 140 - **Относитесь к метрикам скептически:** Используйте числовые показатели (цикломатическая сложность, индекс поддерживаемости) как отправную точку для анализа, а не как окончательный вердикт. 141 - **Доверяйте своей команде:** Лучший способ оценить читаемость кода — это code review. Если ваши коллеги не могут понять код, он нечитаемый, какой бы высокий балл ему ни ставил анализатор. 142 143 ## 4. Академический взгляд на измерение качества 144 145 **Проблема:** Многие общепринятые метрики качества кода не имеют под собой строгой научной валидации. Мы *верим*, что они работают, но это не всегда так. (Из статьи `2012.09590v2`) 146 147 **Рекомендации:** 148 149 - **Фокусируйтесь на конкретных характеристиках:** Вместо абстрактной "читаемости" оценивайте более конкретные вещи: 150 - **Время на понимание:** Сколько времени у нового человека в команде займет понимание этого модуля? 151 - **Легкость внесения изменений:** Насколько сложно добавить новую функциональность в этот класс? 152 - **Количество потенциальных ошибок:** Насколько легко допустить ошибку при работе с этим кодом? 153 - **Вклад в научное сообщество:** Если вы разрабатываете инструменты анализа, основывайте свои метрики на эмпирических исследованиях, а не только на интуиции. 154 155 --- 156 157 ## 5. Структура кода и паттерны 158 159 Качество кода определяется не только именами переменных, но и его общей структурой. Хорошо структурированный код интуитивно понятен и легко расширяем. 160 161 **Рекомендации:** 162 163 ### 5.1. Принцип единственной ответственности (Single Responsibility Principle - SRP) 164 165 Каждый класс или функция должны отвечать за что-то одно. Это делает их проще для понимания, тестирования и повторного использования. 166 167 *Плохо (класс отвечает и за данные, и за их сохранение):* 168 ```python 169 class Report: 170 def __init__(self, title, content): 171 self.title = title 172 self.content = content 173 174 def get_content(self): 175 return f"Отчет: {self.title}\n{self.content}" 176 177 def save_to_file(self, path): 178 """Этот метод нарушает SRP, т.к. класс отчета не должен заниматься сохранением.""" 179 with open(path, 'w') as f: 180 f.write(self.get_content()) 181 182 # Использование: 183 # report = Report("Итоги месяца", "Продажи выросли.") 184 # report.save_to_file("report.txt") # Класс сам себя сохраняет 185 ``` 186 187 *Хорошо (ответственность разделена):* 188 ```python 189 class Report: 190 """Класс отвечает только за структуру и форматирование отчета.""" 191 def __init__(self, title, content): 192 self.title = title 193 self.content = content 194 195 def get_content(self): 196 return f"Отчет: {self.title}\n{self.content}" 197 198 class ReportSaver: 199 """Этот класс отвечает только за сохранение отчета.""" 200 def save(self, report, path): 201 with open(path, 'w') as f: 202 f.write(report.get_content()) 203 204 # Использование: 205 # report = Report("Итоги месяца", "Продажи выросли.") 206 # saver = ReportSaver() 207 # saver.save(report, "report.txt") # Разные объекты для разных задач 208 ``` 209 210 ### 5.2. Не повторяйтесь (Don't Repeat Yourself - DRY) 211 212 Если вы видите повторяющиеся блоки кода, вынесите их в отдельную функцию. Это уменьшает количество кода, упрощает внесение изменений (менять нужно в одном месте) и снижает риск ошибок. 213 214 *Плохо (логика расчета скидки дублируется):* 215 ```python 216 def process_order_for_vip_client(order_total): 217 # Расчет 10% скидки 218 discount = order_total * 0.10 219 final_price = order_total - discount 220 print(f"Цена для VIP-клиента: {final_price}") 221 # ... какая-то еще логика 222 223 def process_order_for_promo_code(order_total): 224 # Расчет 10% скидки 225 discount = order_total * 0.10 226 final_price = order_total - discount 227 print(f"Цена по промокоду: {final_price}") 228 # ... другая логика 229 ``` 230 231 *Хорошо (логика вынесена в функцию):* 232 ```python 233 def calculate_discounted_price(order_total, discount_percentage): 234 """Рассчитывает и возвращает цену после применения скидки.""" 235 discount = order_total * (discount_percentage / 100) 236 return order_total - discount 237 238 def process_order_for_vip_client(order_total): 239 final_price = calculate_discounted_price(order_total, 10) 240 print(f"Цена для VIP-клиента: {final_price}") 241 # ... какая-то еще логика 242 243 def process_order_for_promo_code(order_total): 244 final_price = calculate_discounted_price(order_total, 10) 245 print(f"Цена по промокоду: {final_price}") 246 # ... другая логика 247 ``` 248 249 --- 250 251 ## 6. Инструменты и автоматизация 252 253 Человеческий глаз несовершенен. Автоматизированные инструменты помогают поддерживать качество кода на высоком уровне, обнаруживая проблемы, которые легко пропустить при ручной проверке. 254 255 **Рекомендации:** 256 257 Используйте следующие инструменты в своих Python-проектах: 258 259 - **Линтеры (Linters):** 260 - **`Pylint`** или **`Flake8`**: Проверяют код на соответствие стандартам стиля (PEP 8), находят потенциальные ошибки (например, неиспользуемые переменные) и оценивают сложность кода. 261 262 - **Форматтеры (Formatters):** 263 - **`black`**: "Бескомпромиссный" форматтер кода. Автоматически переформатирует ваш код в едином стиле. Устраняет все споры о форматировании в команде. 264 - **`isort`**: Автоматически сортирует импорты в алфавитном порядке и группирует их. 265 266 - **Проверка типов (Type Checking):** 267 - **`mypy`**: Статический анализатор типов. Проверяет, что вы передаете в функции и возвращаете из них данные правильных типов, что помогает отловить множество ошибок еще до запуска программы. 268 269 **Пример рабочего процесса (workflow):** 270 271 1. Вы пишете код, используя аннотации типов. 272 2. Перед коммитом вы запускаете `black` и `isort`, чтобы автоматически отформатировать код. 273 3. Затем запускаете `mypy` для проверки типов. 274 4. Наконец, запускаете `Flake8` или `Pylint` для поиска оставшихся проблем. 275 5. Только после этого отправляете код на code review. 276 277 Этот подход позволяет передавать на проверку коллегам уже очищенный и стандартизированный код, чтобы они могли сосредоточиться на логике, а не на стиле. 278 279 --- 280 281 ## 7. Источники и использованные материалы 282 283 При подготовке этого документа были использованы следующие научные работы: 284 285 1. **A Guideline for Writing Clean, Understandable, and Reproducible Scientific Code** (arXiv:2501.10037v1) 286 * *Ключевые идеи:* Важность документации, осмысленных имен и использования инструментов для воспроизводимости кода. 287 * *Ссылка:* [https://arxiv.org/abs/2501.10037v1](https://arxiv.org/abs/2501.10037v1) 288 289 2. **The Anchoring Effect in Code Readability Assessments** (arXiv:2012.09590v2) 290 * *Ключевые идеи:* Психологический "эффект якоря", который показывает, что числовые метрики читаемости могут вводить разработчиков в заблуждение. 291 * *Ссылка:* [https://arxiv.org/abs/2012.09590v2](https://arxiv.org/abs/2012.09590v2) 292 293 3. **A Metrics-suite for Code Readability** (arXiv:1106.6159v1) 294 * *Ключевые идеи:* Анализ того, как объектно-ориентированные концепции (наследование, полиморфизм) влияют на метрики качества кода. 295 * *Ссылка:* [https://arxiv.org/abs/1106.6159v1](https://arxiv.org/abs/1106.6159v1) 296 297 --- 298 299 ## 8. Продвинутые темы и современные исследования (2023-2025) 300 301 Сообщество разработчиков и исследователей постоянно ищет новые способы улучшения качества кода. Последние работы (2023-2025 гг.) фокусируются на производительности идиоматичного кода, управлении техническим долгом и использовании больших языковых моделей (LLM) для автоматизации рефакторинга. 302 303 ### 8.1. "Пайтонический" код: не только красиво, но и быстро 304 305 **Проблема:** Часто считается, что "пайтонический" (идиоматичный) код, следующий заветам "The Zen of Python", в первую очередь нацелен на читаемость, а не на скорость. Однако исследования показывают, что это не всегда так. 306 307 **Рекомендации (из статьи 2203.14484v1):** Использование встроенных идиом Python может значительно повысить производительность и уменьшить потребление памяти. 308 309 * *Плохо (использование традиционного цикла для фильтрации):* 310 ```python 311 # Отфильтровать список, оставив только четные числа 312 numbers = list(range(1000000)) 313 even_numbers = [] 314 for num in numbers: 315 if num % 2 == 0: 316 even_numbers.append(num) 317 ``` 318 319 * *Хорошо (использование list comprehension):* 320 ```python 321 # List comprehension работает быстрее, так как выполняется на более низком уровне (в CPython), 322 # и код становится более декларативным и читаемым. 323 numbers = list(range(1000000)) 324 even_numbers = [num for num in numbers if num % 2 == 0] 325 ``` 326 **Вывод:** Идиоматичный код часто является не компромиссом, а выигрышем как в читаемости, так и в производительности. 327 328 ### 8.2. Технический долг в эпоху AI: от `TODO` до `PromptDebt` 329 330 **Проблема:** Технический долг — это не просто плохой код. Это осознанный компромисс, когда разработчики оставляют неоптимальное решение, чтобы сэкономить время сейчас, но с намерением исправить это позже. Часто это проявляется как "самопризнанный технический долг" (Self-Admitted Technical Debt - SATD) в комментариях. 331 332 **Рекомендации (из статей 2501.09888v1, 2409.11826v1):** 333 334 * **Фиксируйте SATD явно:** Комментарии `TODO` или `FIXME` — это первый шаг. Они делают проблему видимой. 335 * **Автоматизируйте возврат долга:** Современные исследования показывают, что LLM можно обучить находить и даже автоматически исправлять такие участки кода. 336 337 * *Плохо (долг оставлен и может быть забыт):* 338 ```python 339 def calculate_tax(amount, region): 340 # TODO: реализовать правильный расчет налогов для каждого региона. 341 # Пока что используем заглушку 10% для всех. 342 if region == "EU": 343 return amount * 0.20 # Временное решение для EU 344 return amount * 0.10 345 ``` 346 347 * *Хорошо (долг устранен с использованием паттерна "Стратегия"):* 348 ```python 349 from abc import ABC, abstractmethod 350 351 # Определяем интерфейс для стратегий 352 class TaxStrategy(ABC): 353 @abstractmethod 354 def calculate(self, amount: float) -> float: 355 pass 356 357 # Реализуем конкретные стратегии для каждого региона 358 class EUTaxStrategy(TaxStrategy): 359 def calculate(self, amount: float) -> float: 360 # ... сложная логика для EU 361 return amount * 0.20 362 363 class USTaxStrategy(TaxStrategy): 364 def calculate(self, amount: float) -> float: 365 # ... сложная логика для US 366 return amount * 0.07 367 368 # Контекст, который использует стратегию 369 class TaxCalculator: 370 def __init__(self, strategy: TaxStrategy): 371 self._strategy = strategy 372 373 def calculate(self, amount: float) -> float: 374 return self._strategy.calculate(amount) 375 376 # Использование: 377 # calculator = TaxCalculator(EUTaxStrategy()) 378 # tax = calculator.calculate(100) 379 ``` 380 381 **Новый вид долга: `PromptDebt` (из статьи 2509.20497v1)** 382 383 С появлением LLM возник новый тип технического долга — **PromptDebt**. Он возникает, когда разработчики используют неоптимальные, слишком общие или плохо протестированные промпты для взаимодействия с AI. 384 385 * *Пример PromptDebt:* 386 ```python 387 # Промпт слишком общий и может давать нестабильные результаты 388 prompt = f"Summarize this text: {user_text}" 389 # summary = openai.Completion.create(prompt=prompt) # Пример вызова 390 ``` 391 Такой промпт может работать по-разному для разных текстов. "Хороший" промпт был бы более детальным, с примерами и четкими инструкциями (техника few-shot prompting), что снижает риск непредсказуемого поведения модели. 392 393 **Вывод:** Управление техническим долгом становится более комплексной задачей, требующей внимания не только к коду, но и к компонентам на базе AI. 394 395 ### 8.3. LLM как ассистент по рефакторингу: автоматическое улучшение качества 396 397 **Проблема:** Ручной рефакторинг — трудоемкий процесс. Разработчики часто откладывают его из-за нехватки времени, что ведет к накоплению технического долга. 398 399 **Рекомендации (из статей 2502.07399v1, 2309.12938v1):** Используйте LLM-ассистенты (такие как GitHub Copilot или специализированные инструменты) для автоматического анализа и рефакторинга кода. Эти инструменты могут находить "запахи" в коде и предлагать конкретные исправления. 400 401 Исследовательские фреймворки, такие как **CodeQUEST**, показывают, что LLM могут итеративно улучшать код, повышая его читаемость, поддерживаемость и даже безопасность. 402 403 * *Плохо (функция делает слишком много: загружает, обрабатывает и сохраняет данные):* 404 ```python 405 import json 406 import pandas as pd 407 408 def process_data_from_json(file_path): 409 """Загружает данные, фильтрует пользователей старше 30 и сохраняет результат.""" 410 # 1. Загрузка данных 411 with open(file_path, 'r') as f: 412 data = json.load(f) 413 414 df = pd.DataFrame(data) 415 416 # 2. Обработка данных 417 df_filtered = df[df['age'] > 30] 418 419 # 3. Сохранение результата 420 output_path = file_path.replace('.json', '_processed.csv') 421 df_filtered.to_csv(output_path, index=False) 422 print(f"Processed data saved to {output_path}") 423 ``` 424 425 * *Хорошо (рефакторинг, предложенный LLM-ассистентом, разделяет логику):* 426 ```python 427 import json 428 import pandas as pd 429 from typing import List, Dict 430 431 def load_users_from_json(file_path: str) -> pd.DataFrame: 432 """Отвечает только за загрузку данных из JSON в DataFrame.""" 433 with open(file_path, 'r') as f: 434 data = json.load(f) 435 return pd.DataFrame(data) 436 437 def filter_users_by_age(df: pd.DataFrame, min_age: int) -> pd.DataFrame: 438 """Отвечает только за фильтрацию пользователей по возрасту.""" 439 return df[df['age'] > min_age] 440 441 def save_df_to_csv(df: pd.DataFrame, output_path: str): 442 """Отвечает только за сохранение DataFrame в CSV.""" 443 df.to_csv(output_path, index=False) 444 print(f"Data saved to {output_path}") 445 446 # Использование: 447 # source_file = 'users.json' 448 # users_df = load_users_from_json(source_file) 449 # adult_users_df = filter_users_by_age(users_df, 30) 450 # save_df_to_csv(adult_users_df, 'adult_users.csv') 451 ``` 452 **Вывод:** LLM-инструменты становятся мощными партнерами в поддержке чистоты кода, беря на себя рутинные задачи рефакторинга и позволяя разработчикам сосредоточиться на более сложных проблемах. 453 454 --- 455 456 ## 9. Обновленный список источников 457 458 В этот раздел добавлены работы, рассмотренные в секции 8. 459 460 4. **Does Coding in Pythonic Zen Peak Performance?** (arXiv:2203.14484v1) 461 * *Ключевые идеи:* Исследование производительности идиоматичного Python-кода. Показывает, что "пайтонические" конструкции часто не только более читаемы, но и более эффективны. 462 * *Ссылка:* [https://arxiv.org/abs/2203.14484v1](https://arxiv.org/abs/2203.14484v1) 463 464 5. **Understanding the Effectiveness of LLMs in Automated Self-Admitted Technical Debt Repayment** (arXiv:2501.09888v1) 465 * *Ключевые идеи:* Анализ способности LLM автоматически находить и исправлять "самопризнанный технический долг" (комментарии `TODO`). 466 * *Ссылка:* [https://arxiv.org/abs/2501.09888v1](https://arxiv.org/abs/2501.09888v1) 467 468 6. **PromptDebt: A Comprehensive Study of Technical Debt Across LLM Projects** (arXiv:2509.20497v1) 469 * *Ключевые идеи:* Введение и анализ концепции "PromptDebt" — нового вида технического долга, возникающего из-за некачественных промптов к LLM. 470 * *Ссылка:* [https://arxiv.org/abs/2509.20497v1](https://arxiv.org/abs/2509.20497v1) 471 472 7. **On Iterative Evaluation and Enhancement of Code Quality Using GPT-4o** (arXiv:2502.07399v1) 473 * *Ключевые идеи:* Представление фреймворка `CodeQUEST`, использующего LLM для итеративной оценки и улучшения качества кода по множеству параметров. 474 * *Ссылка:* [https://arxiv.org/abs/2502.07399v1](https://arxiv.org/abs/2502.07399v1)