2. WHAT IS THIS TALK ABOUT?
Large apps are a completely different beast than small apps
• Domain complexity
• Tight code coupling across domain concepts
•…
This talk is about working with large apps.
… it’s about fixing code.
3. WHAT IS THIS TALK ABOUT?
…it’s about responsibility
fixing bad and improving
code.
4. WHAT IS GOOD CODE?
There is no one
?
right answer
8. WHAT WE WANT?
• Robustness
• Simplicity
• Softness
“Software must be soft: it has to be easy to change because
it will change despite our misguided efforts otherwise.”
The Pragmatic Programmers
9. WHAT WE FREQUENTLY SEE?
• Fragility
• Complexity
• Rigidity
As a result we have code that hard to
understand and modify.
10. WHAT IS REFACTORING???
“…process of changing a software system in such
a way that it does not after the external behavior of
the code yet implemented its internal structure.”
Martin Fowler
11. WHY REFACTOR??
• Improve code structure and design
• More maintainable
• Easier to understand
• Easier to modify
• Easier to add new feature
• Understand code better and sometimes it helps to
find bugs.
• Helps you develop better code, faster
To get better code!!!
12. WHEN REFACTOR
• You add new features and the code is hard to
understand
• You fix bug
• During code review
[!!!] Do not add new features during refactoring
… and sometimes you should throw things out
and start again.
13. WHAT DO WE NEED?
• If you want to refactor, the essential
precondition is having solid tests
• Knowledge of full context
14. THE REFACTORING CYCLE
• Chose the worst smell
• Select a refactoring
• Apply the refactoring
• Run all the tests
!!! Do it using a little steps…
15. TYPICAL CODE SMELLS
• Duplicated code
• Large classes
• Long methods
• Long parameters list
• Feature envy
• Improper naming
• Comments
• Magic numbers
• etc…
16. CLASSES
• Long classes
• Code that isn’t executed
• Too many responsibilities
• Different abstraction level
• Variables and function should be defined close
to where they are used
• Using deprecated methods
• Useless comments
17. METHODS
• Long methods
• Deeply nested method
• Different abstraction level
• Method doesn’t perform
• one identifiable task
• Long parameters list
• Many conditional statements
• Local variables should be declared just above
their first usage
18. VARIABLES
• Instance variables are an implementation
not an interface
• Bad variables name
• Magic numbers
• Several instances
19. COMMENTS
• Obsolete comments (Old comments that have lost their
meaning)
• Redundant comments
• Version History
• Commented out code
• Repeating the variable name or condition in the comment
• Repeating the called method name in a comment after the call
• Comments related to structure
20. TYPICAL REFACTORINGS
Class Method Variable
add (sub)class to add method to class add variable to class
hierarchy
rename class rename method rename variable
remove class remove method remove variable
push method down/push push variable down
method up pull variable up
add parameter to create assessors
method
move method to abstract variable
component
extract code in new
method
RearchitectingAs you increase your knowledge of the code, making it more comprehensible and better factored, you'll inevitably find some bits you don't like. When you started this project, you weren't really capable of changing much, but now you've played with the code, and it's not as damaged as it once was. Along the way, you have seen some bad things and had some ideas on how to fix them. It's time to make some serious changes. Rewrite code you don't understandIf you know what the code should do, and it seems to do it, but you can't figure out quite how, it's bad code. If the original author did something tricky, some comments should have been provided. More likely though, the author was clueless and the code really is a disaster. Rewrite it. By rewriting the code, you have the opportunity to do it the easy way, and to include useful comments. Start by writing down in Javadoc comments what the new code does (just to make sure that you know). Then you can take advantage of the byproducts of your refactoring: call this new method that does that, use that new data structure to store this. The new code will make more sense to everybody and will probably run faster as well. Your unit tests will tell you whether your new code works. Go into your bug tracking system and close all the bugs you have fixed. Move to a layered architectureLet's hope that your inherited, formerly bad code now looks better. It's time to look at the bigger picture: how the packages relate to each other. Using some sort of tool that tells you what classes invoke what methods (that information can be extracted from the class files), find out the interclass dependencies. From those dependencies, infer the interpackage dependencies. If you can't draw a hierarchy (without any loops!) of what packages depend on what other packages, you have some architectural work to do. Using your instincts, a code browser, and a great deal of guesswork, figure out what such an architecture might look like one fine day. Then identify the particular problem spots. A particular class that frequently refers to other packages or a particular constant referred to from other packages hints that classes or parts of classes reside in the wrong package. Ask yourself whether it would make sense to move that code to another package. After a lot of consideration, you can derive a package hierarchy that looks sensible. Although it may require a lot of work to achieve the clean architecture, it gives you a goal to work toward. Your unit tests have clarified what the code should do, and now the architecture allocates the responsibility to do it to packages and classes. All you have to do is fill in the code.
Break up big methodsJust as big classes prove difficult to understand, so do big methods, whose usual causes include:Too many options, causing the method to do too many thingsNot enough support from other methods, causing the method to do tasks at a lower level than it shouldOverly complicated exception handlingMy rule of thumb for methods: If I can't fit the entire thing on one screen, it's too long. It's an entirely practical rule with no theoretical basis, but it seems right and works for me. Remember though, when you read a method of 100 statements for the first time, it looks like 100 unrelated statements, and it is only after some study that you can see the internal structure. Life would be easier for you and for others if you made that internal structure explicit.
Fix the Javadoc commentsYou never realize a comment's importance until you need to read it. Even if nobody else reads it (and usually, they don't), Javadoc comments are important for the code authors to help them remember what the code/class/parameter should do. Pieces of information particularly important to include are: Whether an object parameter may be null, and what it means if it isWhether a parameter needs to be mutable or notWhether a return value is mutableWhether a return value may be nullWhether changes to the return value affect the returner's internal stateFixing the Javadoc comments doesn't mean just adding them where there were none before. You should also delete misleading and trivial comments (such as documenting setters and getters) as they engender a false sense of security. Important information exists for inclusion in Javadoc comments, so there's no point wasting the time of potential readers. When you write new methods and classes, try to include some Javadoc comments (just a few) to explain what they do. Even if it's obvious to you, the next person to get the code might thank you for the effort. If you don't know what to write in the Javadoc comments, you have no business writing the code. Javadoc comments can also serve as a record of what you learned about the code. When you figure out what a particularly tricky, clever, or ugly method does (especially a private or protected method with no implied contract), write a comment to record it for posterity. This expands the amount of the code under your control and helps you later when you're trying to decipher something related. Of course, Javadoc comments should only explain the effect of a method, never the implementation (because you might change that). If code within a method needs explanation, use standard code comments. A school of thought exists that says with good enough identifiers you don't need code comments at all. I'm not that extreme, but I take the point. Indeed, many code comments don't tell you anything new. I urge you to leave those uninformative comments out, as they only serve to make simple code verbose and intimidating.