Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
10 views

09 Programming

The document discusses proper software development practices including programming style, code tuning, refactoring, and debugging. It provides guidelines for writing clean, well-structured code that is easy for humans to understand through use of descriptive names, comments, and consistent style. The document cautions that premature optimization can distract from the primary goals of producing correct, modular code and advises only optimizing based on measurable performance bottlenecks after the code is complete.

Uploaded by

Minh Huyen
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

09 Programming

The document discusses proper software development practices including programming style, code tuning, refactoring, and debugging. It provides guidelines for writing clean, well-structured code that is easy for humans to understand through use of descriptive names, comments, and consistent style. The document cautions that premature optimization can distract from the primary goals of producing correct, modular code and advises only optimizing based on measurable performance bottlenecks after the code is complete.

Uploaded by

Minh Huyen
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 25

2

For Your Amusement


• “Any fool can write code that a computer can understand.
Good programmers write code that humans can understand”
ITSS SOFTWARE DEVELOPMENT -- Martin Fowler
9. Programming • “Good code is its own best documentation. As you’re about
to add a comment, ask yourself, ‘How can I improve the
Nguyen Thi Thu Trang code so that this comment isn’t needed?’” -- Steve McConnell
trangntt@soict.hust.edu.vn • “Programs must be written for people to read, and only
incidentally for machines to execute.” -- Abelson / Sussman
• “Everything should be built top-down, except the first time.”
-- Alan Perlis

1 2

3 4

Ways to get your code right Outline


• Verification/quality assurance 1. Programming style
• Purpose is to uncover problems and increase confidence
• Combination of reasoning and test 2. Code tuning / optimization
• Debugging
3. Code refactoring
• Finding out why a program is not functioning as intended
• Defensive programming 4. Debugging
• Programming with validation and debugging in mind
• Testing ≠ debugging
• test: reveals existence of problem; test suite can also
increase overall confidence
• debug: pinpoint location + cause of problem

3 4

1
5 6
Programming style
Proper programming styles
• Coding • Use constants for all constant values (e.g. tax
convention/standard
rate).
• Use variables whenever possible
• Always declare constants before variables at
the beginning of a procedure
• Always comment code (especially ambiguous or
misleading code), but do not over comment.
• Use appropriate descriptive names (with/without
prefixes) for identifiers/objects (e.g. btnDone).

5 6

7 8

Proper programming styles (2) Proper programming styles (3)


• Use brackets for mathematical • Include a header at the top of your code:
expressions even if not required so that it • Programmer’s name
is clear what was intended. • Date
• Name of saved project
• E.g. (3*2) + (4*2)
• Teacher’s name
• Alwayswrite code as efficiently as • Class name
possible • Names of anyone who helped you
• Make user friendly input and output forms • Brief description of what the program does

7 8

2
9 10

Check style tool Outline


• Demo video 1. Programming style
2. Code tuning / optimization
3. Code refactoring
4. Debugging

9 10

11 12

Code tuning Code Tuning Myths


• Modifying correct code to make it run more • Reducing the lines of code in a high-level language
improves the speed or size of the resulting machine
efficiently
code – false!
• Notthe most effective/cheapest way to a[ 1 ] = 1
a[ 2 ] = 2
improve performance a[ 3 ] = 3
• 20%of a program’s methods consume for i = 1 to 10 a[ 4 ] = 4
a[ i ] = i vs a[ 5 ] = 5
80% of its execution time. end for a[ 6 ] = 6
a[ 7 ] = 7
a[ 8 ] = 8
a[ 9 ] = 9
a[ 10 ] = 10

11 12

3
13 14

Code Tuning Myths (2) Code Tuning Myths (3)


• A fast
program is just as important as a correct • Certainoperations are probably faster or
one – false! smaller than others – false!
• Always measure performance!

13 14

15 16

Code Tuning Myths (4) When to tune


• You should optimize as you go – false! • Use a high-quality design
• It is hard to identify bottlenecks before a program is • Make the program right.
completely working • Make it modular and easily modifiable
• Focus on optimization detracts from other program • When it’s complete and correct, check the
objectives performance.
• Consider compiler optimizations
• Measure
• Write
clean code that’s easy to understand and
modify.

15 16

4
17 18

Measurement Optimize in iterations


• Measureto find bottlenecks • Measure improvement after each optimization
• Measurements need to be precise • If optimization does not improve performance –
• Measurements need to be repeatable revert it

17 18

19 20

Code Tuning Techniques Code Tuning Techniques


• Stop Testing When You Know the Answer • Order Tests by Frequency
Select char Select char
if ( 5 < x ) and ( y < 10 ) then ... Case "+", "=" Case "A" To "Z", "a" To "z“
ProcessMathSymbol(char) ProcessAlpha(char)
?
Case "0" To "9" Case " "
ProcessDigit(char) ProcessSpace(char)
Case ",", ".", "!", "?" Case ",", ".", "!", "?"
negativeInputFound = False; ProcessPunctuation(char) ProcessPunctuation(char)
for ( i = 0; i < iCount; i++ ) { Case " " Case "0" To "9"
if ( input[ i ] < 0 ) { ProcessSpace(char) ProcessDigit(char)
negativeInputFound = True; Case "A" To "Z", "a" To "z“ Case "+", "="
}
? ProcessAlpha(char) ProcessMathSymbol(char)
} Case Else Case Else
ProcessError(char) ProcessError(char)
End Select End Select

19 20

5
21 22

Code Tuning Techniques Code Tuning Techniques


• Unswitching loops • Minimizing the work inside loops
for ( i = 0; i < count; i++ ) {
for (i = 0; i < rateCount; i++) {
if ( sumType == SUMTYPE_NET ) { netRate[i] = baseRate[i] * rates->discounts->factors->net;
netSum = netSum + amount[ i ];
}
}
else { grossSum = grossSum + amount[ i ]; }
?
}
for (i = 0; i < rateCount; i++) {
? netRate[i] = baseRate[i] * ?
}

21 22

23 24

Code Tuning Techniques Code Tuning Techniques


• Initialize at Compile Time • Use Lazy Evaluation
const double Log2 = 0.69314718055994529; public int getSize() {
if(size == null) {
size = the_series.size();
}
return size;
}

23 24

6
25 26

When iterating through data, keep


Code Tuning Techniques
memory references sequential
var myClass = function() { var myClass = function() {
this.array_one = [1,2,3,4,5]; this.array_one = [1,2,3,4,5];
this.array_two = [1,2,3,4,5];
this.array_two = [1,2,3,4,5];
this.total = 0;
this.total = 0;
}
}
var my_instance = new myClass();
var my_instance = new myClass();
for (var i=0; i < 4; i++) {
my_instance.total +=
?
(my_instance.array_one[i] +
my_instance.array_two[i]);
}
25 26

27

Outline What is Refactoring?


1. Programming style Refactoring means "to
improve the design and
2. Code tuning / optimization quality of existing
3. Code refactoring source code without
changing its external
4. Debugging behavior".
Martin Fowler
• A step by step process that turns the bad code into good code
• Based on "refactoring patterns" à well-known recipes for improving the
code

2
8

27 28

7
Code Refactoring When to Refactor?
• What is refactoring of the source code? • Bad smells in the code indicate need of refactoring
• Improving the design and quality of existing source • Refactor:
code without changing its behavior • To make adding a new function easier

• Step by step process that turns the bad code into • As part of the process of fixing bugs
good code (if possible) • When reviewing someone else's code
• Have technical debt (or any problematic code)
• Why we need refactoring? • When doing test-driven development
• Code constantly changes and its quality constantly • Unit tests guarantee that refactoring does not change
degrades (unless refactored) the behavior
• Requirements often change and code needs to be • If there are no unit tests, write them
changed to follow them
2 3
9 0

29 30

Refactoring: Main Principles Refactoring: The Typical Process


• Keep it simple (KISS principle) 1. Save the code you start with
• Avoid duplication (DRY principle) • Check-in or backup the current code
• Make it expressive (self-documenting, comments, etc.) 2. Prepare tests to assure the behavior after the code is
• Reduce overall code (KISS principle) refactored
• Unit tests / characterization tests
• Separate concerns (decoupling)
3. Do refactoring one at a time
• Appropriate level of abstraction (work through
• Keep refactoring small
abstractions)
• Don't underestimate small changes
• Boy scout rule
4. Run the tests and they should pass / else revert
• Leave your code better than you found it
5. Check-in (into the source control system)

3 3
1 2

31 32

8
Refactoring Tips
• Keep refactoring small
• One at a time
• Make a checklist
• Make a "later" / TODO list
• Check-in / commit frequently
• Add tests cases
• Review the results
• Pair programming
CODE
• Use tools (Visual Studio + add-ins / Eclipse + plugins /
others)
REFACTORING
Live Demo
3
3

33 34

Code Smells Code Smells: The Bloaters


• Code smells == certain structures in the code • Long method
that suggest the possibility of refactoring • Small methods are always better (easy naming, understanding,
less duplicate code)
• Types of code smells: • Large class
• The bloaters • Too many instance variables or methods
• The obfuscators • Violating "Single Responsibility" principle

• Object-oriented abusers • Primitive obsession (overused primitives)


• Over-use of primitive values, instead of better abstraction
• Change preventers • Can be extracted in separate class with encapsulated validation
• Dispensables
• The couplers
3
5 36

35 36

9
Code Smells: The Bloaters (2) Code Smells: The Bloaters (3)
• Long parameter list (in / out / ref parameters) • Oddball solution
• May indicate procedural rather than OO style • A different way of solving a common problem
• May be the method is doing too much things • Not using consistency

• Data clumps • Solution: Substitute algorithm or use an Adapter

• A set of data are always used together, but not organized together • Class doesn't do much
• E.g. credit card fields in the Order class • Solution: Merge with another class or remove
• Combinatorial explosion • Required setup / teardown code
• Ex. ListCars(), ListByRegion(), ListByManufacturer(), • Requires several lines of code before its use
ListByManufacturerAndRegion(), etc. • Solution: use parameter object, factory method, IDisposable
• Solution may be the Interpreter pattern (LINQ)

3
37 8

37 38

Code Smells: The Obfuscators Code Smells: The Obfuscators (2)


• Regions • Poor / improper names
• The intent of the code is unclear and needs commenting (smell) • Should be proper, descriptive and consistent
• The code is too long to understand (smell)
• Vertical separation
• Solution: partial class, a new class, organize code
• You should define variables just before first use to avoid scrolling
• Comments
• In JS variables are defined at the function start à use small
• Should be used to tell WHY, not WHAT or HOW functions
• Good comments: provide additional information, link to issues, • Inconsistency
explain an algorithm, explain reasons, give context
• Link: Funny comments • Follow the POLA (Principle of Least Astonishment)
• Inconsistency is confusing and distracting
• Obscured intent
• Code should be as expressive as possible
3 4
9 0

39 40

10
41

Code Smells: OO Abusers Code Smells: Change Preventers


• Switch statement • Divergent change
• Can be replaced with polymorphism • A class is commonly changed in different ways / different reasons

• Temporary field • Violates SRP (single responsibility principle)

• When passing data between methods • Solution: extract class

• Class depends on subclass • Shotgun surgery


• The classes cannot be separated (circular dependency) • One change requires changes in many classes

• May break the Liskov substitution principle • Hard to find them, easy to miss some
• Solution: move methods, move fields, reorganize the code
• Inappropriate static field
• Strong coupling between static and callers
• Static things cannot be replaced or reused

4
2

41 42

Code Smells: Change Preventers (2) Code Smells: Dispensables


• Conditional complexity • Lazy class
• Cyclomatic complexity (number of unique paths that the code can • Classes that don't do enough to justify their existence should be
be evaluated) removed
• Symptoms: deep nesting (arrow code) and buggy if-s • Every class costs something to be understood and maintained
• Solutions: extract method, "Strategy" pattern, "State" pattern, • Data class
"Decorator"
• Some classes with only fields and properties
• Poorly written tests • Missing validation? Class logic split into other classes?
• Badly written tests can prevent change • Solution: move related logic into the class
• Tight coupling

4 4
3 4

43 44

11
Code Smells: Dispensables (2) Code Smells: The Couplers
• Duplicated code • Feature envy
• Violates the DRY principle • Method that seems more interested in a class other than the one it

• Result of copy-pasted code actually is in


• Solutions: extract method, extract class, pull-up method, • Keep together things that change together

Template Method pattern • Inappropriate intimacy


• Dead code (code that is never used) • Classes that know too much about one another

• Usually detected by static analysis tools • Smells: inheritance, bidirectional relationships


• Solutions: move method / field, extract class, change bidirectional
• Speculative generality
to unidirectional association, replace inheritance with delegation
• "Some day we might need this …"
• The "YAGNI" principle

4 4
5 6

45 46

Code Smells: The Couplers (2) Code Smells: The Couplers (3)
• The Law of Demeter (LoD) • Message chains
• A given object should assume as little as possible about the • Something.Another.SomeOther.Other.YetAnother
structure or properties of anything else • Tight coupling between client and
• Bad e.g.: customer.Wallet.RemoveMoney() the structure of the navigation
• Indecent exposure • Middle man
• Some classes or members are public but shouldn't be • Sometimes delegation goes too far
• Violates encapsulation • Sometimes we can remove it or inline it
• Can lead to inappropriate intimacy
• Tramp data
• Pass data only because something else needs it
• Solutions: Remove middle-man data, extract class

4 4
7 8

47 48

12
49

Code Smells: The Couplers (4)


• Artificial coupling
• Things that don't depend upon each other should not be artificially
coupled
• Hidden temporal coupling
• Consecutively performed operations should not be guessed
• E.g. pizza class should not know the steps of making pizza ->
Template Method pattern
• Hidden dependencies REFACTORING
• Classes should declare their dependencies in their constructor
• new is glue / Dependency Inversion principle PATTERNS
Well-Known Recipes
for Improving the Code Quality

49 50

Rafactoring Patterns Refactoring Patterns (2)


• When should we perform refactoring of the code? • Class or method has weak cohesion à split into
• Bad smells in the code indicate need of refactoring several classes / methods
• Unit tests guarantee that refactoring preserves the • Single change carry out changes in several classes
behavior à classes have tight coupling à consider redesign
• Rafactoring patterns
• Related data are always used together but are not
• Large repeating code fragments à extract duplicated code in
part of a single class à group them in a class
separate method
• Large methods à split them logically • A method has too many parameters à create a
• Large loop body or deep nesting à extract method class to groups parameters together
• A method calls more methods from another class
than from its own class à move it
5 5
1 2

51 52

13
Rafactoring Patterns (3) Rafactoring Patterns (4)
• Two classes are tightly coupled à merge them or redesign • Complex expression à split it into few simple parts
them to separate their responsibilities • A set of constants is used as enumeration à convert it to
• Public non-constant fields à make them private and define enumeration
accessing properties • Too complex method logic à extract several more simple
• Magic numbers in the code à consider extracting methods or even create a new class
constants • Unused classes, methods, parameters, variables à
• Bad named class / method / variable à rename it remove them
• Complex boolean condition à split it to several • Large data is passed by value without a good reason à
expressions or method calls pass it by reference

5 5
3 4

53 54

55 56

Rafactoring Patterns (5) Refactoring Levels


• Few classes share repeating functionality à
extract base class and reuse the common code
• Different classes need to be instantiated depending
on configuration setting à use factory
• Code is not well formatted à reformat it
• Too many classes in a single namespace à split
classes logically into more namespaces
• Unused using definitions à remove them
• Non-descriptive error messages à improve them
• Absence of defensive programming à add it

55 56

14
Data-Level Refactoring Data-Level Refactoring (2)
• Replace a magic number with a named constant • Create a local variable for local purposes rather than a
• Rename a variable with more informative name parameter
• Replace an expression with a method • Convert a data primitive to a class
• Additional behavior / validation logic (money)
• To simplify it or avoid code duplication
• Convert a set of type codes (constants) to enum
• Move an expression inline
• Introduce an intermediate variable • Convert a set of type codes to a class with subclasses with
different behavior
• Introduce explaining variable
• Change an array to an object
• Convert a multi-use variable to a multiple single-use
• When you use an array with different types in it
variables
• Encapsulate a collection
• Create separate variable for each usage

5 5
7 8

57 58

59 60

Statement-Level Refactoring Method-Level Refactoring


• Decompose a boolean expression • Extract method / inline method
• Move a complex boolean expression into a well- • Rename a method
named boolean function
• Convert a long routine to a class
• Use break or return instead of a loop control
variable • Add / remove parameter
• Return as soon as you know the answer instead of • Combine similar methods by parameterizing
assigning a return value them
• Consolidate duplicated code in conditionals • Substitute a complex algorithm with simpler
• Replace conditionals with polymorphism
• Separate methods whose behavior depends on
• Use null-object design pattern instead of checking parameters passed in (create new ones)
for null
• Pass a whole object rather than specific fields
• Encapsulate downcast / return interface types
59 60

15
Class-Level Refactoring Class Interface Refactorings
• Change a structure to class and vice versa • Extract interface(s) / keep interface segregation
• Pull members up / push members down the hierarchy • Move a method to another class
• Extract specialized code into a subclass • Split a class / merge classes / delete a class
• Combine similar code into a superclass
• Hide a delegating class
• Collapse hierarchy
• A calls B and C when A should call B and B call C
• Replace inheritance with delegation
• Remove the man in the middle
• Replace delegation with inheritance
• Introduce (use) an extension class
• When you have no access to the original class

6
• Alternatively use the Decorator pattern 6
1 2

61 62

Class Interface Refactoring (2) System-Level Refactoring


• Encapsulate an exposed member variable • Move class (set of classes) to another
• Always use properties
namespace / assembly
• Define proper access to getters and setters
• Remove setters to read-only data • Provide a factory method instead of a simple
• Hide data and routines that are not intended to be used constructor / use fluent API
outside of the class / hierarchy • Replace error codes with exceptions
• private -> protected -> internal -> public
• Use strategy to avoid big class hierarchies
• Extract strings to resource files
• Apply other design patterns to solve common class and • Use dependency injection
class hierarchy problems (Façade, Adapter, etc.)
• Apply architecture patterns

6 6
3 4

63 64

16
65 66

Outline 4.1. Overview


1. Programming style • Error
• Bug
2. Code tuning / optimization
• Fault
3. Code refactoring • Defect
4. Debugging • Failure

65 66

67 68

Whence “bug” 4.2. Defense in depth


• Alan Perlis i • Make errors impossible
• Java makes memory overwrite errors impossible
• Don’t introduce defects
• Correctness: get things right the first time
• Make errors immediately visible
• Local visibility of errors: best to fail immediately
• Example: assertions
• Last resort is debugging
Grace Hopper • Needed when failure (effect) is distant from cause (defect)
i • Scientific method: Design experiments to gain information about the
defect
• Fairly easy in a program with good modularity, representation hiding, specs,
unit tests etc.
• Much harder and more painstaking with a poor design, e.g., with rampant rep
exposure

67 68

17
69 70

4.2.1. First defense: Impossible by design 4.2.2. Second defense: Correctness


• In the language • Get things right the first time
• Think before you code. Don’t code before you think!
• Java makes memory overwrite errors impossible • If you're making lots of easy-to-find defects, you're also making hard-to-
• In the protocols/libraries/modules find defects – don't use the compiler as crutch
• Especially true, when debugging is going to be hard
• TCP/IP guarantees that data is not reordered
• Concurrency, real-time environment, no access to customer environment, etc.
• BigInteger guarantees that there is no overflow
• Simplicity is key
• In self-imposed conventions • Modularity
• Divide program into chunks that are easy to understand
• Banning recursion prevents infinite recursion/insufficient stack – • Use abstract data types with well-defined interfaces
although it may push the problem elsewhere • Use defensive programming; avoid rep exposure
• Immutable data structure guarantees behavioral equality • Specification
• Write specs for all modules, so that an explicit, well-defined contract exists between
• Caution: You must maintain the discipline each module and its clients

69 70

71 72

Strive for simplicity 4.2.3. Third defense: Immediate visibility


“There are two ways of constructing a software ¨ If we can't prevent errors, we can try to localize them to a
design: small part of the program
•One way is to make it so simple that there are ¤ Assertions: catch errors early, before they contaminate and are
obviously no deficiencies, and Sir Anthony Hoare perhaps masked by further computation
•the other way is to make it so complicated that i ¤ Unit testing: when you test a module in isolation, you can be
there are no obvious deficiencies. confident that any error you find is due to a defect in that unit
The first method is far more difficult.” (unless it's in the test driver)
¤ Regression testing: run tests as often as possible when changing
code. If there is a failure, chances are there's a mistake in the
“Debugging is twice as hard as writing the code
code you just changed
in the first place. Therefore, if you write the code
as cleverly as possible, you are, by definition, ¨ When localized to a single method or small module,
not smart enough to debug it.” defects can usually be found simply by studying the
Brian Kernighan program text
i

71 72

18
73 74

Benefits of immediate visibility Don't hide errors


• The key difficulty of debugging is to find the defect: the // k is guaranteed to be present in a
int i = 0;
code fragment responsible for an observed problem while (true) {
• A method may return an erroneous result, but be itself error- if (a[i]==k) break;
free, if there is prior corruption of representation i++;
}
• The earlier a problem is observed, the easier it is to fix
• Frequently checking the rep invariant helps
• General approach: fail-fast •This code fragment searches an array a for a value k.
• Check invariants, don't just assume them • Value is guaranteed to be in the array
• Don't (usually) try to recover from errors – it may just mask them • What if that guarantee is broken (by a defect)?
•Temptation: make code more “robust” by not failing

73 74

75 76

Don't hide errors


Don't hide errors
// k is guaranteed to be present in a // k is guaranteed to be present in a
int i = 0;
int i = 0; while (i<a.length) {
while (i<a.length) { if (a[i]==k) break;
i++;
if (a[i]==k) break; }
i++; assert (i=a.length) : "key not found";
}
•Assertions let us document and check invariants
¨ Now at least the loop will always terminate • Abort/debug program as soon as problem is detected: turn an error into a failure
¤ But it is no longer guaranteed that a[i]==k •But the assertion is not checked until we use the data, which might be a long
time after the original error
¤ If other code relies on this, then problems arise later • “why isn’t the key in the array?”
¤ This makes it harder to see the link between the defect and
the failure

75 76

19
77 78

Checks In Production Code 4.2.3. Debugging: Last resort


• Should you include assertions and checks in production • Defects happen – people are imperfect
code? • Industry average: 10 defects per 1000 lines of code (“kloc”)
• Yes: stop program if check fails - don’t want to take chance • Defects that are not immediately localizable happen
program will do something wrong Found during integration testing

Or reported by user

• No: may need program to keep going, maybe defect does not
• The cost of finding and fixing an error usually goes up by an
have such bad consequences (the failure is acceptable) order of magnitude for each lifecycle phase it passes through
• Correct answer depends on context!
• step 1 – Clarify symptom (simplify input), create test
Ariane 5 – program halted because of • step 2 – Find and understand cause, create better test
• step 3 – Fix
overflow in unused value, exception thrown but
not handled until top level, rocket crashes… • step 4 – Rerun all tests
[although the full story is more complicated]

77 78

79 80

4.3. Debugging Debugging and the scientific method


• Step 1 – find a small, repeatable test case that produces the • Debugging should be systematic
failure (may take effort, but helps clarify the defect, and also
• Carefully decide what to do – flailing can be an instance of an epic fail
gives you something for regression)
• Keep a record of everything that you do
• Don't move on to next step until you have a repeatable test
• Don’t get sucked into fruitless avenues
• Step 2 – narrow down location and proximate cause
• Study the data / hypothesize / experiment / repeat • Formulate a hypothesis
• May change the code to get more information • Design an experiment
• Don't move on to next step until you understand the cause
• Perform the experiment
• Step 3 – fix the defect
• Adjust your hypothesis and continue
• Is it a simple typo, or design flaw? Does it occur elsewhere?
• Step 4 – add test case to regression suite
• Is this failure fixed? Are any other new failures introduced?

79 80

20
81
82

Reducing input size example Reducing absolute input size


// returns true iff sub is a substring of full
// (i.e. iff there exists A,B s.t.full=A+sub+B)
• Finda simple test case by divide-and-conquer
boolean contains(String full, String sub); • Pare test down – can't find "very happy" within
•User bug report
• It can't find the string "very
happy" within:
• "Fáilte, you are very welcome! Hi Seán! I am very
"Fáilte, you are very welcome! Hi Seán! I am very very very happy to see you all."
happy to see you all."
•Less than ideal responses • "I am very very happy to see you all."
• See accented characters, about not having thought about • "very very happy"
unicode, and go diving for your Java texts to see how that is
handled • Can find "very happy" within
• Try to trace the execution of this example
• "very happy"
•Better response: simplify/clarify the symptom
• Can't find "ab" within "aab"

81 82

83 84

Reducing relative input size General strategy: simplify


• Sometimes it is helpful to find two almost • In general: find simplest input that will provoke failure
identical test cases where one gives the correct • Usually not the input that revealed existence of the defect

answer and the other does not • Start with data that revealed defect
• Keep paring it down (binary search “by you” can help)
• Can't find "very happy" within
• Often leads directly to an understanding of the cause
• "I am very very happy to see you all."
• When not dealing with simple method calls
• Can find "very happy" within
• The “test input” is the set of steps that reliably trigger the
• "I am very happy to see you all." failure
• Same basic idea

83 84

21
85 86

Localizing a defect binary search on buggy code


public class MotionDetector {
private boolean first = true;
¨ Take advantage of modularity private Matrix prev = new Matrix(); no problem yet
¤ Start with everything, take away pieces until failure goes
public Point apply(Matrix current) {
¤ Start with nothing, add pieces back in until failure appears
if (first) {
¨ Take advantage of modular reasoning prev = current;
} Check
¤ Trace through program, viewing intermediate results Matrix motion = new Matrix(); intermediate
getDifference(prev,current,motion); result
¨ Binary search speeds up the process applyThreshold(motion,motion,10); at half-way point
¤ Error happens somewhere between first and last statement labelImage(motion,motion);
Hist hist = getHistogram(motion);
¤ Do binary search on that ordered set of statements
int top = hist.getMostFrequent();
applyThreshold(motion,motion,top,top);
Point result = getCentroid(motion);
prev.copy(current); problem exists
return result;
}
}

85 86

87
88

binary search on buggy code Detecting Bugs in the Real World


public class MotionDetector {
private boolean first = true; • Real Systems
private Matrix prev = new Matrix();
no problem yet • Large and complex (duh!)
public Point apply(Matrix current) { • Collection of modules, written by multiple people
if (first) { Check
intermediate • Complex input
prev = current;
result
} at half-way point • Many external interactions
Matrix motion = new Matrix();
getDifference(prev,current,motion); • Non-deterministic
applyThreshold(motion,motion,10); problem exists • Replication can be an issue
labelImage(motion,motion);
Hist hist = getHistogram(motion); • Infrequent failure
Quickly home in
int top = hist.getMostFrequent();
on defect in O(log n) time
• Instrumentation eliminates the failure
applyThreshold(motion,motion,top,top);
Point result = getCentroid(motion);
by repeated subdivision • Defects cross abstraction barriers
prev.copy(current);
return result;
• Large time lag from corruption (defect) to detection
} (failure)
}

87 88

22
89 90

Heisenbugs Logging Events


• Sequential, deterministic program – failure is • Build an event log (circular buffer) and log
repeatable events during execution of program as it runs at
• But the real world is not that nice… speed
• Continuous input/environment changes
• When detect error, stop program and examine
• Timing dependencies
• Concurrency and parallelism
logs to help you reconstruct the past
• Failure occurs randomly • The log may be all you know about a
• Hard to reproduce customer’s environment – helps you to
• Use of debugger or assertions à failure goes away reproduce the failure
• Only happens when under heavy load
• Only happens once in a while

89 90

91 92

Tricks for Hard Bugs Where is the bug?


• Rebuild system from scratch, or restart/reboot ¨ If
the bugis not where you think it is, ask yourself
• Find the bug in your build system or persistent data structures where it cannot be; explain why
• Explain the problem to a friend ¨ Look for stupid mistakes first, e.g.,
• Make sure it is a bug – program may be working ¤ Reversed order of arguments: Collections.copy(src,
dest)
correctly and you don’t realize it!
¤ Spelling of identifiers: int hashcode()
• Minimize input required to exercise bug (exhibit failure)
n @Override can help catch method name typos
• Add checks to the program ¤ Same object vs. equal: a == b versus a.equals(b)
• Minimize distance between error and detection/failure
¤ Failure to reinitialize a variable
• Use binary search to narrow down possible locations
¤ Deep vs. shallow copy
• Use logs to record events in history ¨ Make sure that you have correct source code
¤ Recompile everything

91 92

23
93 94

When the going gets tough Key Concepts in Review


• Reconsider assumptions • Testing and debugging are different
• E.g., has the OS changed? Is there room on the hard drive?
• Testing reveals existence of failures
• Debug the code, not the comments – ensure the comments and
specs describe the code • Debugging pinpoints location of defects
• Start documenting your system • Goalis to get program right
• Gives a fresh angle, and highlights area of confusion
• Debugging should be a systematic process
• Get help
• We all develop blind spots
• Use the scientific method
• Explaining the problem often helps • Understand the source of defects
• Walk away • To find similar ones and prevent them in the future
• Trade latency for efficiency – sleep!
• One good reason to start early

93 94

95 96

Supporting Tools: Eclipse plug-in Findbugs features


• Checkstyle:Help programmers write Java • Not concerned by formatting or coding standards
code that adheres to a coding standard • Detecting potential bugs and performance issues
• Can detect many types of common, hard-to-find bugs
• FindBugs:Uses static analysis to look for • Use “bug patterns”
bugs in Java code Address address = client.getAddress();
if ((address != null) || (address.getPostCode() != null)) {
• Standalone Swing application NullPointerException
...
}
• Eclipse plug-in
• Integrated into the build process (Ant or public class ShoppingCart {
private List items;
Maven) Uninitialized field public addItem(Item item) {
items.add(item);
}
}

95 96

24
97 98

How Findbug works? What Finbugs can do?


• FindBugs comes with over 200 rules divided
into different categories:
INPUT PROCESSING OUTPUT
• Correctness
• Set of “.class” • Bug Pattern Code • E.g. infinite recursive loop, reads a field that is
files containing
byte-code • Source Line never written
Number • Bad practice
• Configurations
• Descriptive • E.g. code that drops exceptions or fails to close file
Message
• Performance
Detectors
• Multithreaded correctness
• Dodgy
• E.g. unused local variables or unchecked casts

97 98

99

99

25

You might also like