Культовый разработчик Кент Бек сформулировал принципы написания ПО, которые в вольном изложении звучат приблизительно так:
1. Заставь код работать.
2. Сделай его понятным.
3. Оптимизируй для лучшей производительности.
Этим основам не один десяток лет, однако джуны (да и старшие порой, будем честны:) спотыкаются уже на втором шаге. В результате получается рабочий, но нечитаемый код, который не просто трудно понять — его нельзя дополнить или исправить, не поломав программу.
Причина проста: на проектной работе важно как можно быстрее выйти на рынок и получить деньги. Джуны спешат разобраться с задачей, не тратя времени на рефакторинг и комментарии.
Обычно до оптимизации дело так и не доходит: важные участки кода скорее «заморозят» и запретят вносить в них изменения, чем перепишут. Беда, если именно в них будет крыться экономическое преимущество системы.
Чистый код существует?
Лирическое отступление
Вопрос на экзамене по программированию: «Что такое Clean Code?»
Студент мучается, пыхтит: «Ну-у-у, там, методы без... э... имена не… Простите, профессор, знал, но забыл!»
Профессор встает и торжественно объявляет аудитории: «Друзья, трагедия! Единственный человек знал про Clean Code — и тот забыл!»
В четком виде теории чистого кода не существует: авторы тематических книг опираются на субъективный опыт и порой противоречат друг другу. В конце концов, всё зависит от задач, которые выполняет код. Однако мы можем выделить несколько эффективных принципов, которые применимы в большинстве случаев.
Принципы написания чистого кода
1. Используйте значимые имена. Назначение переменных, функций, классов и других идентификаторов должно быть понятно из их названия.
Пример (Java):
public void assign(Author author, Book book)
{
book.setAuthor(author);
book.save();
}
По названию метода нельзя определить, что в нём происходит. Это плохо. Конкретизируем имена, и код объяснит сам себя:
public void assignAuthorForBook(Author author, Book book)
{
book.setAuthor(author);
book.save();
}
2. Оставляйте комментарии в сложных участках. Пусть они будут нечастыми, но содержательными. Идеальный код можно понять без дополнительной документации.
Пример (Dart):
Комментарии могут связать код с реальностью и дать объяснение, почему мы решили закодить именно так.
///! category && product открывают одинаковый экран
/// Это нужно для обработки диплинков
/// Так как на вебе в url может быть либо products, либо category
GoRoute(
path: CatalogueRoutes.productsItem.path,
name: CatalogueRoutes.productsItem.path,
builder: (context, state) => CategoryPage(
key: state.pageKey,
slug: state.pathParameters['slug']!,
args: state.extra as CategoryArgsModel?,
),
),
GoRoute(
path: CatalogueRoutes.categoryItem.path,
name: CatalogueRoutes.categoryItem.path,
builder: (context, state) => CategoryPage(
key: state.pageKey,
slug: state.pathParameters['slug']!,
args: state.extra as CategoryArgsModel?,
),
),
3. Наводите красоту пустотами. Пробелы, табы и отступы помогут разделить код на логические секции и сделать его удобным для чтения.
Пример (Javascript):
function filterPermissions(permissions, searchQuery) {
const q = searchQuery.trim().toLowerCase()
if (!q) return permissions
const searchDeep = (permission) => filterTree(permission, ({title}) => title.toLowerCase().includes(q))
return permissions.map(searchDeep).filter((v) => v)
}
Такими кирпичами только стены строить. Добавим отступы, выстроим лесенку, и наш увесистый блок превращается в прекрасный читабельный код:
function filterPermissions(permissions, searchQuery) {
const q = searchQuery.trim().toLowerCase()
if (!q) {
return permissions
}
const searchDeep = (permission) => filterTree(
permission,
({title}) => title.toLowerCase().includes(q)
)
return permissions
.map(searchDeep)
.filter((v) => v)
}
4. Отбрасывайте ненужное. После рефакторинга сразу удаляйте участки, которые больше не несут практической ценности. Не оставляйте на случай «вдруг пригодится» — удалённый кусок всегда можно отыскать в истории Git-а.
Пример (go):
//test_banner := model.Banner{ID: 1, FeatureId: 45, TagIds: []int32{5, 12, 244}}
if !banner.IsActive {
log.Warn("attempt to get inactive banner")
//return model.Banner{ID: -1, FeatureId: -1, TagIds: []int32{}, content {"forbidden! - empty"}}
return nil, fmt.Errorf("banner {id:%d, featureId:%d, tagIds:%v} is inactive",
banner.ID, banner.FeatureId, banner.TagIds)
}
Тестовые строчки в комментариях засоряют код и мешают нормальному чтению. Их нужно сносить беспощадно.
Подведем итоги
Чистый код не только «легко читается». В первую очередь мы говорим о грамотном проектировании системы, которое дает нам куда большие преимущества:
— Код легко расширить без технических затрат. Сущности не связаны между собой слишком тесно.
— Код стабилен и предсказуем. Если появляется проблема, мы уже знаем, куда смотреть.
P.S.
На практике к чистке кода стоит подходить с умом. Помните, что в каждом правиле есть свои исключения. Принципы могут стать хорошим подспорьем, но не основой успеха вашего продукта.
Наши источники
Для статьи мы отбирали советы из книг Роберта Сесила Мартина, Стива Макконнелла и Кента Бека, советовались с практикующими программистами и черпали вдохновение на канале ExtremeCode. Для закрепления информации читайте краткое изложение трудов Дяди Боба и похожий материал о принципах чистого кода, где можно подглядеть ещё больше примеров.
Кстати, как вам материал? Если хотите больше таких – пишите в комментах, нам будет приятно :)