Smells
Материал из AgileWiki
Содержание |
Smells catalog
Автор практики: Денис Миллер
Команда формирует список Smells. Процесс формирования аналогичен формированию Coding and naming convention и является его частью.
Каталог
Класс
Внутри класса
Между классами
База данных
Архитектурные
Тестовый код
Медленные тесты
Один из самых очевидных признаков загнивания проекта. Кратко описать идею можно так - требуется слишком много времени для выполнения теста. Когда вы разрабатываете приложение, то должны иметь возможность выполнять весь набор тестов (TestSuite) неограниченное количество раз и не должны ждать тесты слишком долго. Весь набор модульных тестов приложения (конечно, если приложение не слишком большое) должен исполняться не дольше 1-2 минут. А тесты на тот модуль, который вы разрабатываете в данный момент должны срабатывать вообще мгновенно.
Почему это так важно:
- Медленные тесты запускаются реже, из-за этого снижается отдача от тестов, ошибки находятся позднее, их исправление занимает много времени. Вы должны иметь возможность проверить все приложение при малейшем подозрении, что текущие изменения могут оказать влияние на другие части приложения.
- Медленные тесты - это признак плохой изоляции тестируемого класса от других классов. Здесь может быть 2 причины: 1) или разработчик не умеет пользоваться продвинутыми методиками тестирования, такими как моки и стабы, 2) или же архитектура приложения не позволяет этими техниками пользоваться, что говорит о больших зависимостях между компонентами архитектуры. Во втором случае - это признак загнивающего проекта.
Хрупкие тесты
Этот запах часто является отговоркой новичков, почему они не пишут тесты. Якобы тесты слишком часто ломаются при изменении кода, поэтому они значительно увеличивают нагрузку на разработчика. Существует несколько причин появления этого запаха:
- Чаще всего причиной этому становятся медленные тесты. Если полный набор выполнять слишком долго, то разработчики скорее всего начнут выбирать, когда и как часто запускать полный набор, а все остальное время будут запускать тесты только для того кода, над которым они работают в текущий момент. В зависимости от того, как хорошо до этого были разработаны тесты и прочий код, этот подход может быть успешным и не очень. Если разработчик изменил достаточно много кода, а только потом выполнил общих набор тестов и обнаружил, что тесты не выполняются, он уже не может знать точно, такие сделанные им изменения привели к ошибкам. А это и есть причина того, что разбираться становится сложно и утомительно. Запускайте ваши тесты чаще и они ломаться будут реже!
- Другая причина - неправильное тестирование. Тест «знает» слишком много деталей тестируемого кода, из-за этого при малейших изменениях в тестируемом коде тесты ломаются. Приходится править и тесты, и тестируемый код, что увеличивает нагрузку на разработчиков. Относиться к этому можно двояко - 1) если вы меняете поведение класса, то изменение в тестах прогнозируемы, 2) а вот если вы проводите рефакторинг кода без изменения функциональности, а тесты постоянно ломаются - значит есть повод подумать, как переделать тесты.
- Еще одна причина - это дизайн системы, который не позволяет писать изолированные тесты. В этом случае винить тесты не стоит - они явно дают понять, что код требует рефакторинга.
Сложный тест или тест-"спагетти"
Данная проблема часто возникает у разработчиков, которым не терпится протестировать некоторую функциональность досконально(и это не обвинение!), исчерпывающе проверяя всевозможные внутренние состояния. Самый лучший способ распознать такой код - посмотреть на тесты через некоторое время, скажем через неделю. Если тест на самом деле показателен, у вас не возникнет трудности с интерпретацией того, что же именно тестируется. Если же от количества частичных моков и странных проверок рябит в глазах, можно уверенно заявить, что перед вами тестовое «спагетти».
Есть несколько причин появления этого запахи:
- Тесты должны как можно меньше опираться на внутреннее состояние тестируемой сущности и скорее относится к ней, как к некоторому черному ящику. Тем самым разработчик гарантированно огораживает себя от трудности дальнейшей поддержки и модификации тестов при рефакторинге кода.
- Тесты должны тестировать модули именно так, как это будет делать остальной код в реальном приложении. Нет необходимости тестировать все мыслимые комбинации, если класс никогда не будет таким образом использован. Помните принцип YAGNI (You Aren't Gonna Need It) и цените свое время.
- Эта проблема часто возникает из-за неправильного внутреннего дизайна разрабатываемой функциональности и является первым звонком для переработки(рефакторинга) как разрабатываемого, так и тестового кода.
"Эхо в горах"
Если вы тестируете что-либо в одном месте и только в этом месте, тогда одна внесенная логическая ошибка в тестируемый код должна приводить к поломке только в одной части набора тестов. Но когда вы обнаруживаете, что исправление этой ошибки отражается на многих тестах, как говорится «словно эхо в горах», тогда ваш набор тестов содержит большой набор дублирующих друг друга проверок. При разработке тестов нужно четко представлять, что же именно вы тестируете, и избегать подобных дублирующих проверок, чтобы не создавать себе дополнительной работы. Помните основной принцип прагматичного программиста Don't repeat yourself - DRY. Если дублирующих проверок избежать не удается, тогда стоим задуматься о рефакторинге дизайна системы, которую вы тестируете.
Набор тестов не срабатывает без подключения к сети
Большинство приложений уровня предприятия требуют подключения к внешним приложениях для различных целей. Это могут быть платежные системы, серверы аутентификации и т.д. Если тесты требуют реальных взаимодействий с такими приложениями, вы очевидно вышли за границы модульного тестирования и приблизились к системному тестированию. Такие тесты должны быть перенесены в приемочные тесты и исключены из набора модульных тестов. Это подтолкнет вас к выделению классов, которые взаимодействуют с внешними ресурсами, снижению зависимостей от этих классов, использованию заглушек и моков.
В идеале модульные тесты должны работать с консольном режиме, без web-сервера и базы данных (не обязательно), без дополнительных библиотек и проч. Там, где это можно и целесообразно, нужно использовать моки и заглушки. Такой пуританский подход приведет к тому, что тесты будут исполняться с очень быстрой скоростью, однако есть шанс нарваться на другую проблему - чрезмерное упование на моки.
Чрезмерное доверие мокам
Если вы окружили свой тестируемый код плотным кольцом моков и заглушек, то есть риск забыть про реальное применение класса и снова попасть в ловушку. Лежащие в основе использования моков предпосылки заключаются в том, что они действуют во многом именно так, как действовали бы реальные объекты. Если это не так (например, мы не полностью протестировали возможные комбинации взаимодействия, или же при настройке моков были допущены ошибки), а тестов с реальными классами недостаточно, тогда реальность напомнит о себе в самых неудобных и неожиданных местах и обстоятельствах.
Эта проблема стоит тем более остро, что в PHP4 нет никакой встроенной в язык возможности проверить типы возвращаемых объектов, кроме явной. Очень часто мы сталкиваемся с ситуацией, когда весь набор модульных тестов низкого уровня срабатывает, а приемочные тесты - нет. Причина ошибок часто заключается в том, что тесты были изолированны моками, поведение которых уже более не соответствует реальному поведению классов, которые они заменяют.
Какие пути решения этих проблем:
- Аккуратно использовать моки, только там, где это уместно.
- Обязательно иметь в наличии функциональные тесты или модульные тесты высших уровней, которые реализованы без использования моков и заглушек. Путь эти тесты будут не такими подробными и быстрыми, зато они помогут отловить те ошибки, которые остались незамеченными при модульном тестировании.
Недостаточное доверие мокам
Очевидно, что это противоположная сторона предыдущего запаха. Если вы чаще предпочитаете использовать реальные классы в тестах, где тестируемый класс зависит от других классов, то это прямой путь в тестовый ад с раздутыми, медленными и хрупкими тестами, которые часто ломаются, медленно работают и которые очень трудно писать и понимать. Явный признак этого запаха - большой и сложный метод setUp().
В конце концов, если один тест проверяет, что база может вернуть набор A по запросу B, разве мы не может предположить, что она сделает то же самое в десяти других тестах вашего тестового набора, которые зависят все от того же запроса B?
Правильное использование моков зависит от опыта разработчика и сложившейся ситуации. Необходимо слушать свой код, как тестируемый, так и тестовый, для того, чтобы не попадать ни в одну их этих ловушек.
SCRUM Smells
Toward a Catalog of Scrum Smells By Mike Cohn
Even with Scrum things can go wrong. As diligent ScrumMasters it is our responsibility to constantly keep an eye on our projects and look for small problems before they can become big problems. In his book Refactoring, Martin Fowler introduced the term smell to refer to something that may not be right. Just because something smells doesn’t mean there’s a problem; it does mean, though, that further investigation is warranted.
This article is a first step toward collecting a catalog of Scrum smells; that is, signs that something may be amiss on a Scrum project.
Loss of Rhythm
Symptom: Sprints are not always the same length.
Discussion: On a well-executed Scrum project the team establishes a natural rhythm. Each sprint is the same length. Each sprint starts with a Sprint Planning Meeting where requirements are discussed and the sprint planned. Each sprint concludes with a Sprint Review where the team demonstrates a potentially shippable product increment. In-between the project rhythm is supported by daily scrums.
If sprints are sometimes two weeks and sometimes four weeks then this natural rhythm is never established. Sprints then begin to feel like arbitrary units of time with endpoints selected more by outside forces (perhaps political or competitive) rather than designed to enhance the overall productivity of the team. When sprint duration is allowed to vary teams have a harder time selecting the right amount of work for the sprint backlog, which may result is less commitment to completing all of the items in the sprint.
Talking Chickens
Symptom: Chickens attending the daily Scrum are allowed to ask questions or make observations. Discussion: During the daily Scrum the only participants allowed to speak are the pigs (those committed to the project); chickens (those involved but not committed) may attend and observe but are not allowed to speak. Allowing chickens to talk can be a slippery slope. If a chicken is allowed to make a comment one time (when the comment is useful), how do we later prevent a chicken from commenting (when the comment may not be useful)?
A few months ago I took my young daughters to a local fair. The line for one ride led up to a short flight of stairs at the base of the ride. No one was allowed on those stairs, but many of the young kids wanted to sit on the stairs while waiting. The ride operator, though, held firm to her rule of no one on the stairs. She told them, “If I let you sit on the first step soon you’ll be on the second step and then the third step.” Clearly sitting on the first step would not have endangered any riders but the stairs were an obvious delineation and the ride operator used this as her simple rule. Not allowing chickens to talk during the daily meeting is one of Scrum’s simple rules. Of course one comment from a chicken may not hurt—but it will lead to others and then there will be no easy place to draw the line.
Missing Pigs
Symptom: Not all pigs attend the daily Scrum meeting.
Discussion: When I started working there was no such thing as “flex time.” Every company had a starting time and everyone was expected to be at work by that time. Flex time, combined with the nocturnal habits of many software developers, makes it common to have some members of a team arrive at 11:00 am, or even later. I worked with one team who had nicknamed one programmer “Captain Midnight” because he quite literally came to work around midnight each day.
All of this can make it very difficult for a team to get together for a daily Scrum every day. However, having a daily meeting and having it at the same time and in the same place each day help a project establish and maintain its rhythm. If too many pigs are missing daily Scrums too often then perhaps the meeting is taking too long or is deviating from the three standard questions. When run well the daily Scrum should almost never exceed fifteen minutes and will almost always be of value to each pig. Yes, sometimes we’ll have specific deadlines that are so urgent and so imminent that we may be tempted to skip a daily Scrum. And that’s not all that bad. It’s when the situation becomes chronic or when too many pigs miss meetings that the situation begins to smell.
Persistent Signatures
Symptom: The wild fluctuations shown on a team’s initial sprint burndown charts continue to be seen in much later sprints.
Discussion: In Agile Software Development with Scrum, Ken Schwaber and Mike Beedle write about each team having a unique signature—some teams bring in too much work at the start of a sprint, others bring in too little, and so on. However, it is important that a team learn from its past sprints. For example, suppose a team of five started the last sprint with an estimated 600 hours of work (the first point on their sprint burndown chart). Midway through that sprint they realized they were attempting too much and worked with the Product Owner to remove 100 hours of work. Now, in planning the next sprint they again want to bring in 600 hours of work. They need to look at that and answer for themselves why they think they can achieve 600 hours of estimated work this sprint when it was too much last sprint.
Over time, as teams learn about themselves and their project their sprint burndown charts should lose the wild variations that may have existed in initial sprints and begin to somewhat approximate the idealized straight line. If this trend is not apparent for a team then they are missing opportunities to learn from their own past performance.
ScrumMaster Assigns Work
Symptom: Work is assigned by the ScrumMaster rather than signed up for by developers.
Discussion: Self-organization is one of the underlying principles of Scrum. When a ScrumMaster assigns work it undermines the responsibility developers assume when they are allowed to self-organize around the achievement of a goal. The real danger here is that even an occasional assignment from a ScrumMaster can do a lot of damage. Teams need to feel completely in control of their own work.
The Daily Scrum is For the ScrumMaster
Symptom: The Daily Scrum feels like it is a status update from the team members to the ScrumMaster.
Discussion: Sometimes the daily Scrum begins to feel as though it exist solely for the ScrumMaster. You’ll see the ScrumMaster furiously scrawling notes about who committed to what work and why some other task wasn’t completed. Daily meetings like this take on the feeling of the status meetings of other development processes.
There are two main purposes of the daily Scrum and neither is to provide status information to the ScrumMaster. The first purpose of the daily Scrum is to provide a coordination mechanism for everyone on the project. Once a day it can be very useful for everyone to hear where everyone else is. Ideally there are many hallway or random conversations throughout the day but those don’t usually include the full team. During the daily Scrum everyone gets a sense of where everyone else is.
The second purpose of the daily Scrum is for each team member to make commitments in front of his or her peers. When a developer answers the “What are you going to do today?” question she is making a commitment to her peers, not to the ScrumMaster. At the next meeting if that commitment has not been fulfilled it is not the ScrumMaster’s role to say, “Tsk, tsk, Lucy, yesterday you told us you’d be done.” If Lucy said she’d be done and isn’t she probably feels bad enough. She knows what she told her peers and she knows if she didn’t finish because of random bad luck, a lack of effort, misunderstanding the size of complexity of the task, or any of a myriad other reasons.
Specialized Job Roles
Symptom: A project team has highly specialized job roles or descriptions such as Architect, Designer, DBA, or Tester.
Discussion: Scrum teams need to have a “we’re all in this together attitude.” This can sometimes be undermined if a team has specialized job descriptions or roles. For example, if a team includes a dedicated “tester” then this may give the rest of the team an opportunity to shirk the responsibility to test. With the highly complex technologies of the world today it is too simplistic to think that everyone can be a DBA and everyone can write server-side J2EE or .Net code. A successful Scrum team does not need to be comprised entirely of generalists. However, for a team of specialists to be successful each specialist must accept general responsibility for the system as a whole. I may not know how to solve our project’s most intricate Oracle problems but I’m going to do whatever I can to help, which may simply include taking on some of our database specialist’s other responsibilities to free her to solve the complex problems.
Ссылки
- http://c2.com/cgi/wiki?CodeSmell
- http://en.wikipedia.org/wiki/Code_smell
- http://industriallogic.com/papers/smellstorefactorings.pdf
- Toward a Catalog of Scrum Smells by Mike Cohn. October 2003. Scrum Alliance
Книги
- Refactoring: Improving the Design of Existing Code, Martin Fowler
- Refactoring To Patterns By Joshua Kerievsky
- Refactoring Databases: Evolutionary Database Design by Scott W. Ambler and Pramod J. Sadalage
- Refactoring in Large Software Projects Performing Complex Restructurings Successfully by Stefan Roock
- Practical Analysis for Refactoring by Donald Bradley Roberts, 1999
- Refactoring object-oriented frameworks by William F. Opdyke, University of Illinois at Urbana-Champaign, 1992
