One of the main hindrances to teams being able to respond rapidly to new features are technical problems resulting from bad coding practices, also known as technical debt. Melissa and Brett will cover Agile tools and practices that help development teams write better code and increase maintainability. Topics that will be covered include:
Pair programming
Automated Unit Testing
Refactoring
Test-Driven Development
Agile Architecture
5. ● Continuous Code Review
● Better Design w/ Less Code
● Faster Problem Solving
● More People Familiar w/ System
● Team Grows Closer
Pair Programming - Benefits
9. What is Automated Unit Testing?
Use of special software to control the
execution of unit tests and the comparison of
actual outcomes with predicted outcomes.
To get full benefit run as part of the build
process and schedule to run automatically
once or twice a day.
10. Automated Unit Test Suite Benefits
● Unit tests find problems early in the development cycle
● Developers will be less afraid to change code
● Development process becomes more flexible
● Easier for a developer to take over code they are
unfamiliar with (improves “Truck Factor”)
● Reduces need for manual testing
● Software development becomes more testable and
repeatable
11. What is a Unit Test Case?
● A test for a single unit of functionality
● Each method tests a specific bit of
functionality
● Automated by the test-runner framework
12. AAA Test Structure
● Structure:
○ Arrange
○ Act
○ Assert
● Benefits:
○ Readable
○ Consistent
public class SalesTaxTest
{
private SalesTax unit;
@Test
public void TestCalculate()
{
unit = new SalesTax(); // ARRANGE
double tax = unit.Calculate(10); // ACT
Assert.assertEquals(tax, 0.9); // ASSERT
}
}
13. Unit Testing Best Practices
● Test simple stuff first
● Get it working - then test boundaries/exceptions
● Use assert
● Keep tests small & understandable
● Make test method names descriptive
● Keep tests independent of each other
● Avoid System.out messages
● Don’t repeat tests! Test once and trust it.
14. Test Doubles
● Stand in for a collaborating object e.g. database,
webservice
● Create test independence
● Make tests run faster
● Types of Doubles
○ Fakes: working implementation e.g.fake web service
○ Mocks: preprogrammed with expectations
○ Stubs: provide canned answers to the test’s calls
17. What is Refactoring?
“A change to the system that leaves its behavior
unchanged, but enhances some non-functional quality -
simplicity, flexibility, understandability, performance”
-- Kent Beck, Extreme Programing Explained (p. 179)
“A change made to the internal structure of software to
make it easier to understand and cheaper to modify without
changing its observable behavior.”
-- Martin Fowler, Refactoring (p. 53)
18. Why Refactor the Code?
● Prevent “design decay”
● Clean up messes
● Simplify
● Increase readability / understandability
● Find bugs
● Reduce debugging time
● Build in what we learn about the application
● It’s part of the creative process
19. When to Refactor
● Code reviews / TDD cycle
● Rule of three
○ cut & paste. Third time copying? Now is the time to
generalize and move this to a new procedure.
● When you:
○ add functionality
○ learn something about the code
○ identify a code smell
20. What is a Code Smell?
● any symptom in the code that possibly
indicates a deeper problem.
● not bugs - but indicate weaknesses in design
that may be slowing down development or
increasing the risk of bugs or failures in the
future.
21. Code Smells (1/2)
● Duplicated code: near-identical code in multiple places
● Long method: a too large method/function/procedure
● Large class: a class that has grown too large.
● Too many parameters: decreases readability/quality.
● Identifier names are too short or excessively long
● Complex conditionals: branches that check lots of
unrelated conditions and edge cases that don't seem to
capture the meaning of a block of code.
22. Code Smells (2/2)
● Comments - refactor so these are no longer needed!
● Feature envy: excessive use of another class’ methods
● Inappropriate intimacy: a class that has dependencies
on implementation details of another class.
● Lazy class / Freeloader: a class that does too little.
● Contrived complexity: simpler design would suffice.
● Excessive use of literals (use named constants)
23. Why Developers Resist Refactoring
● Lack of understanding
● Short-term focus
● Not paid for overhead tasks like refactoring
● Fear of breaking current program
Use TDD to overcome this fear!
25. TDD as a Design Approach
● Tests define up front what “works” means
● Programing by intention - write tests for what
code intends to do not what the code you just wrote
does.
● Focus on minimum set of required features
● Regular integration of small changes
● Evolutionary Design: Tests provide a safety
net for refactoring.
27. How to Get to Green? Implement it!
Obvious
Implementation
code the real implementation, if
it's obvious and can quickly make
test pass, otherwise...
public int Sum(int x, int y)
{
return x + y;
}
28. How to Get to Green? Fake it
Fake it
hardcode constants to make test
pass, gradually generalize code
using variables
public int Fibonacci(int n)
{
if (n >= 1) return 1;
return 0;
}
29. How to Get to Green? Triangulate
Triangulate
figure out the behavior of an
algorithm by examining a couple
of test cases instead of just one.
public void TestSum()
{
Assert.AreEqual(4, Plus(3, 1));
Assert.AreEqual(7, Plus(4, 3));
}
30. Use TDD to Earn a Gift Card!
TDD is a great tool to use in solving the code challenges. E.g. Function takes a
Roman Numeral String and returns the Decimal value.
● Start with simple valid numbers I, III... up to more complex tests
● Incorporate tests for boundary conditions and invalid numbers
● With a set of tests you can easily refactor your solution.
VALID NUMBERS INVALID NUMBERS
I Boundary: Empty, 0, MMMM
III iii
IV IVIV, IIII, IIX, MXXXX
VI, XLIX, MMMDCCCLXXXVIII ABCDEF
31. A Great TDD Example!
● Bowling Kata - how to score a bowling
game.
● Eclipse and junit step-by-step presentation
on red - green - refactor to solve (butunclebob.
com/files/downloads/Bowling%20Game%20Kata.ppt)
36. Standards and Principles
● Agree as a team
○ coding and naming conventions
○ toolset
■ Static Code Analysis, Test mocks, etc
○ best practices
● Avoid Technical Debt
● Follow well established principles and
patterns
37. Object Oriented Design Principles
S ingle Responsibilty
O pen/Closed
L iskov Substitution
I nterface Segregation
D ependency Inversion
38.
39. The Single Responsibility Principle
● Every class should have a single responsibility, and that responsibility
should be entirely encapsulated by the class. All its services should be
narrowly aligned with that responsibility.
● Single Reason to Change
40. class Book {
function getTitle() {
return "A Great Book";
}
function getAuthor() {
return "John Doe";
}
function turnPage() {
// pointer to next page
}
function printCurrentPage() {
echo "current page content";
}
}
41.
42. The Open/Closed Principle
● You should be able to extend a classes
behavior without modifying it
● Open for extension, but closed for
modification
43. public double Area(object[] shapes)
{
double area = 0;
foreach (var shape in shapes) {
if (shape is Rectangle) {
Rectangle rectangle = (Rectangle) shape;
area += rectangle.Width*rectangle.Height;
} else {
Circle circle = (Circle)shape;
area += circle.Radius * circle.Radius * Math.PI;
}
}
return area;
}
44.
45. The Liskov Substitution Principle
● Derived classes must be substitutable for
their base classes
46. public class Rectangle {
private int width;
private int height;
// setters and getters
}
public class Square extends Rectangle {
public Square(int height, int width) {
setWidth(width);
setHeight(height);
}
}
47.
48. The Interface Segregation Principle
● No client should be forced to depend on
methods it does not use
49. interface Worker {
void work();
void eat();
}
class HumanWorker implements Worker {
Public void work { /* do work stuff */ }
Public void eat { /* eat stuff */ }
}
class RobotWorker implements Worker {
Public void work { /* do work stuff */ }
Public void eat { /* ACK! I don't eat! */ }
}
interface Worker {
void work();
}
interface Eater {
void eat();
}
class HumanWorker implements Worker, Eater {
Public void work { /* do work stuff */ }
Public void eat { /* eat stuff */ }
}
class RobotWorker implements Worker {
Public void work { /* do work stuff */ }
}
50.
51. The Dependency Inversion Principle
● High-level modules should not depend on
low-level modules. Both should depend on
abstractions.
● Abstractions should not depend on details.
Details should depend on abstractions.
52. class Worker {
private FileWriter fileWriter;
public Worker() {
fileWriter = new FileWriter();
}
}
class Worker {
private FileWriter fileWriter;
public Worker(FIleWriter writer) {
fileWriter = writer;
}
}
interface FileWriter {
// writer files or something
}