Up Front Design
In our current understanding of software development we believe that we design and then we code. A good design comes first, and then the code comes second. Over time the code will be modified, and the integrity of the system, its structure according to that design, gradually fades. The code slowly sinks from engineering to hacking.
How about Agile development, Evolutionary Design?
Agile suggests evolutionary design that allows evolution to become a viable strategy. It also provides new challenges and skills as designers need to learn how to do a simple design, how to use refactoring to keep design clean, and how to use patterns in an evolutionary style. - Martin Fowler.
Why is Refactoring important in Agile?
With refactoring you find the balance of work changes. You find that design, rather than occurring all up font, occurs continuously during development. You learn from building the system how to improve the design. The resulting interaction leads to a program with a design that stays good as development continues. Refactoring is part of evolutionary design.
Does Refactoring violate YAGNI?
The point of YAGNI is that you don't add complexity that isn't needed for the current stories. That is part of the practice of simple design. Refactoring is needed to keep the design as simple as you can, so you should refactor whenever you realize you can make things simpler.
So Is Design Dead?
Not by any means, but the nature of design has changed. In Agile design looks for the following skills
- A constant desire to keep code as clear and simple as possible.
- Refactoring skills so you can confidently make improvements whenever you see the need.
- A good knowledge of patterns: not just the solutions but also appreciating when to use then and how to evolve into them.
- Designing with an eye to future changes., knowing that decisions takes now will have to be changed in the future.
- Knowing how to communicate the design to the people who need to understand it, using cod, diagrams and above all: conversation.
That's a fearsome selection of skills, but then being a good designer has always been tough.
When should we refactor the code?
Should we allocate two weeks every couple of months to refactoring? Refactoring is something you do all the time in little bursts. You don't decide to refactor, you refactor because you want to do something else and improve the design.
The rule of three
- Refactor when you add function
- Refactor when you need to fix a bug
- Refactor as you do a code review.
Bad Smells In Code
if it stinks, change it.
- Grandma Beck, discussing child-rearing philosophy
Deciding when to start refactoring, and when to stop, is just an important to refactoring as knowing how to operate the mechanics of a refactoring.
Code smells can be separated into two categories.
- Code Smells Within Classes
- Code Smells Between Classes.
Code Smells Within Classes
There's a fine line between comments that illuminate and comments that obscure. Are the comments necessary? Do they explain "why" and not "what"? Can you refactor the code so the comments aren't required? And remember, you're writing comments for people, not machines.
All other things being equal, a shorter method is easier to read, easier to understand, and easier to troubleshoot. Refactor long methods into smaller methods if you can.
Long Parameter List
The more parameters a method has, the more complex it is. Limit the number of parameters you need in a given method, or use an object to combine the parameters.
Duplicated code is the bane of software development. Stamp out duplication whenever possible. You should always be on the lookout for more subtle cases of near-duplication, too.Don't Repeat Yourself!
Watch out for large conditional logic blocks, particularly blocks that tend to grow larger or change significantly over time. Consider alternative object-oriented approaches such as decorator, strategy, or state.
You have lots of code that does almost the same thing.. but with tiny variations in data or behavior. This can be difficult to refactor-- perhaps using generics or an interpreter?
Large classes, like long methods, are difficult to read, understand, and troubleshoot. Does the class contain too many responsibilities? Can the large class be restructured or broken into smaller classes?
Type Embedded in Name
Avoid placing types in method names; it's not only redundant, but it forces you to change the name if the type changes.
Does the name of the method succinctly describe what that method does? Could you read the method's name to another developer and have them explain to you what it does? If not, rename it or rewrite it.
Pick a set of standard terminology and stick to it throughout your methods. For example, if you have Open(), you should probably have Close().
Ruthlessly delete code that isn't being used. That's why we have source control systems!
Write code to solve today's problems, and worry about tomorrow's problems when they actually materialize. Everyone loses in the "what if.." school of design. You (Probably) Aren't Gonna Need It.
There should only be one way of solving the same problem in your code. If you find an oddball solution, it could be a case of poorly duplicated code-- or it could be an argument for the adapter model, if you really need multiple solutions to the same problem.
Watch out for objects that contain a lot of optional or unnecessary fields. If you're passing an object as a parameter to a method, make sure that you're using all of it and not cherry-picking single fields.
Code Smells Between Classes
Alternative Classes with Different Interfaces
If two classes are similar on the inside, but different on the outside, perhaps they can be modified to share a common interface.
Don't use a gaggle of primitive data type variables as a poor man's substitute for a class. If your data type is sufficiently complex, write a class to represent it.
Avoid classes that passively store data. Classes should contain data and methods to operate on that data, too.
If you always see the same data hanging around together, maybe it belongs together. Consider rolling the related data up into a larger class.
If you inherit from a class, but never use any of the inherited functionality, should you really be using inheritance?
Watch out for classes that spend too much time together, or classes that interface in inappropriate ways. Classes should know as little as possible about each other.
Beware of classes that unnecessarily expose their internals. Aggressively refactor classes to minimize their public surface. You should have a compelling reason for every item you make public. If you don't, hide it.
Methods that make extensive use of another class may belong in another class. Consider moving this method to the class it is so envious of.
Classes should pull their weight. Every additional class increases the complexity of a project. If you have a class that isn't doing enough to pay for itself, can it be collapsed or combined into another class?
Watch out for long sequences of method calls or temporary variables to get routine data. Intermediaries are dependencies in disguise.
If a class is delegating all its work, why does it exist? Cut out the middleman. Beware classes that are merely wrappers over other classes or existing functionality in the framework.
If, over time, you make changes to a class that touch completely different parts of the class, it may contain too much unrelated functionality. Consider isolating the parts that changed in another class.
If a change in one class requires cascading changes in several related classes, consider refactoring so that the changes are limited to a single class.
Parallel Inheritance Hierarchies
Every time you make a subclass of one class, you must also make a subclass of another. Consider folding the hierarchy into a single class.
Incomplete Library Class
We need a method that's missing from the library, but we're unwilling or unable to change the library to include the method. The method ends up tacked on to some other class. If you can't modify the library, consider isolating the method.
If it takes five classes to do anything useful, you might have solution sprawl. Consider simplifying and consolidating your design.