34
Sustainable systems How to avoid rotten software by Sergei Anikin

How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Sustainable systemsHow to avoid rotten software

by Sergei Anikin

Page 2: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Agenda

Sustainability qualities Continuous Care vs. Initial Design Code smell Design principles Integration Scaling

Page 3: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Sustainability qualities

Easy to change Easy to scale Easy to integrate Easy to maintain

Page 4: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Continuous Care vs. Initial Design

Initially good design degrade because requirements change in not anticipated way

Many believe, that only bad design degrade, and good design is the one that can tolerate change

Massive efforts are spent on initial design, but once initial design in place, it is considered to be ready and the system maintenance left to someone else

In fact an on-going care will steadily improve initially poor design, and thus much more important than the latter

Constant migration of the design requires intimate knowledge of the system

Page 5: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Code smell

Rigidity - System is hard to change. Fragility - Changes causes system to break easily and

require other changes. Immobility - Difficult to entangle components that can be

reused in other systems. Viscosity - Doing things right is harder than doing things

wrong. Needless Complexity - System contains infrastructure

that has no direct benefit. Needless Repetition - Repeated structures that should

have a single abstraction. Opacity - Code is hard to understand

Page 6: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Design principles

The Open-Closed Principle - software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

The Liskov Substitution Principle – functions that use references to base classes must be able to use objects or derived classes without knowing it.

The Dependency-Inversion Principle - don't call us, we call you.

The Interface-Segregation Principle - clients should not be forced to depend on methods that they do not use.

The Single-Responsibility Principle - a class should have only one reason to change.

Page 7: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

The Open-Closed Principle

Says that modules must be: “Open for extension” - means that behavior can be

extended to meet new requirements “Closed for modification” - No one is allowed to

make source code changes to it. How can these two opposing attributes be resolved?

Abstraction is the Key No significant program can be 100% closed Plan for strategical changes by abstracting usage

Page 8: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

The Liskov Substitution Principle

Functions that use references to base classes must be able to use objects or derived classes without knowing it.

All derivatives must conform to the behavior that clients expect of the base classes that they use. Square is not a rectangle. At least doesn't behave

alike. When using an object through base class interface user

knows only precondition and postcondition of the base class, and thus derived object must: Accept everything base class accepts Conform to all postconditions Not confuse users by output

Page 9: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

The Dependency Inversion Principle

High level modules should not depend upon low level modules. Both should depend upon abstractions.

Abstractions should not depend upon details. Details should depend upon abstractions.

IoC containers and Spring Framework in particular are best examples

Page 10: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

The Interface-Segregation Principle

Clients should not be forced to depend upon interfaces that they do not use

“Fat” interfaces can be segregated into abstract base classes through: Delegation – Adapter pattern Multiple inheritance

Page 11: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

The Single-Responsibility Principle

Responsibility is “a reason to change”. There should never be more than one reason for a class

to change. One the simplest principles, but very hard to get it right.

Page 12: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Integration challenges

Influences and influenced by organization structure and politics.

Integrators do not have any influence on the systems, they integrate.

Integration standards do not exist. Technical:

network is not reliable – depends on so many different components,

network is slow – much slower, than interprocess communication,

applications are different – platform, language, data structures, etc.,

changes are inevitable – applications must be able to evolve separately

Page 13: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Integration types

Over time those different types of integration evolved: File exchange – one application writes to a file and

other reads it. Shared database – common data stored in one

database. RPC(remote procedure invocation) – application

exposes part of its functionality, so, that other application may use it over network.

Messaging – one application sends a message to a common channel and other reads it out.

Page 14: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Why messaging?

Remote connection – messaging by design supports that. Platform independence – messaging can be used on different

platforms. Asynchronous communication – supports “send and forget” model. Time difference – sender and receiver can do their job at different

times. Throttling – receiver can regulate itself, how fast he is receiving

messages Reliable communication – using “store and forward” approach. Disconnected work – some applications work without network and

only time to time exchange messages with other systems. Mediation – messaging system can be the only system all other

systems are connected to. Multithreading – messages can be processed in parallel.

Page 15: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Scaling – clustering databases

Separation of data into several partitions based on certain criteria: Data for different dates into different tables Data of different users into different databases

How to find correct partition? Can be done in 2 ways:

Hashing – partition of the cluster is chosen on the basis of common agreed algorithm: hash function of the user name.

Partition lookup – partition is chosen by lookup in the separate database.

PL/Proxy – database partitioning system for Postgres database. Open source Developed by Skype http://pgfoundry.org/projects/plproxy/

Page 16: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Scaling – clustering servers and applications

The Linux Virtual Server – is a highly scalable and highly available server build on a cluster of real servers.

Fully transparent to end users. Applications must be stateless, in order to be able to run on LVS. For stateful Java applications there is a solution: JGroups JGroups – A toolkit for reliable multicast communication, very flexible and

configurable: Transport: UDP, TCP, JMS Fragmentation of large messages Reliable unicast and multicast Failure detection, Ordering protocols, Encryption

Page 17: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

Thank you!

Page 18: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

1

Sustainable systemsHow to avoid rotten software

by Sergei Anikin

Page 19: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

2

Agenda

Sustainability qualities Continuous Care vs. Initial Design Code smell Design principles Integration Scaling

Page 20: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

3

Sustainability qualities

Easy to change Easy to scale Easy to integrate Easy to maintain

Page 21: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

4

Continuous Care vs. Initial Design

Initially good design degrade because requirements change in not anticipated way

Many believe, that only bad design degrade, and good design is the one that can tolerate change

Massive efforts are spent on initial design, but once initial design in place, it is considered to be ready and the system maintenance left to someone else

In fact an on-going care will steadily improve initially poor design, and thus much more important than the latter

Constant migration of the design requires intimate knowledge of the system

Initial design, if planed well, is kept till the first release

Then hacks get introduced, system starts to smell, until them begin to dominate the design of the system

Everyone starts to cry for redesign

Such redesign rarely succeed, as it have to keep up the pace with all changes introduced into the old system, which makes new design full of smells even before the release

The forces that act to degrade a design are ubiquitous, and therefore the developers of a

system must apply continuous effort to keep the design simple and flexible. They must

aggressively attack any accumulation of dependencies, duplicate code, or unnecessary

infrastructure. They must constantly be on the lookout for localized areas of rigidity,

fragility, and immobility.

Though documentation can help, initimate knowledge of a system is gained by working

with that system for long periods of time. This does not mean that individuals cannot transfer into or out of the team, but this must be done gradually

Page 22: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

5

Code smell

Rigidity - System is hard to change. Fragility - Changes causes system to break easily and

require other changes. Immobility - Difficult to entangle components that can be

reused in other systems. Viscosity - Doing things right is harder than doing things

wrong. Needless Complexity - System contains infrastructure

that has no direct benefit. Needless Repetition - Repeated structures that should

have a single abstraction. Opacity - Code is hard to understand

Rigidity is due to the fact that a single change to heavily interdependent software begins a cascade of changes in dependent modules. When the extent of that cascade of change cannot be predicted by the designers or maintainers, the impact of the change cannot be estimated.

Fragility is the tendency of a program to break in many places when a single change is made. Often the new problems are in areas that have no conceptual relationship with the area that was changed.

A design is immobile when the desirable parts of the design are highly dependent upon other details that are not desired.

Page 23: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

6

Design principles

The Open-Closed Principle - software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

The Liskov Substitution Principle – functions that use references to base classes must be able to use objects or derived classes without knowing it.

The Dependency-Inversion Principle - don't call us, we call you.

The Interface-Segregation Principle - clients should not be forced to depend on methods that they do not use.

The Single-Responsibility Principle - a class should have only one reason to change.

Page 24: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

7

The Open-Closed Principle

Says that modules must be: “Open for extension” - means that behavior can be

extended to meet new requirements “Closed for modification” - No one is allowed to

make source code changes to it. How can these two opposing attributes be resolved?

Abstraction is the Key No significant program can be 100% closed Plan for strategical changes by abstracting usage

Says that you should design modules, that never change. When requirements change, you extend behavior by adding new code, not changing old code that already works.

It is possible for a module to manipulate an abstraction. Such a module can be closed for modification since it depends upon an abstraction that is fixed. Yet the behavior of that module can be extended by creating new derivatives of the abstraction.

It should be clear that no significant program can be 100% closed. In general, no matter how “closed” a module is, there will always be some kind of change against which it is not closed. Since closure cannot be complete, it must be strategic. That is, the designer must choose the kinds of changes against which to close his design. This takes a certain amount of prescience derived from experience. The experienced designer knows the users and the industry well enough to judge the probability of different kinds of changes. He then makes sure that the open-closed principle is invoked for the most probable changes.

There is much more that could be said about the open-closed principle. In many ways this principle is at the heart of object oriented design. Conformance to this principle is what yields the greatest benefits claimed for object oriented technology; i.e. reusability and maintainability. Yet conformance to this principle is not achieved simply by using an object oriented programming language. Rather, it requires a dedication on the part of the designer to apply abstraction to those parts of the program that the designer feels are going to be subject to change.

Page 25: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

8

The Liskov Substitution Principle

Functions that use references to base classes must be able to use objects or derived classes without knowing it.

All derivatives must conform to the behavior that clients expect of the base classes that they use. Square is not a rectangle. At least doesn't behave

alike. When using an object through base class interface user

knows only precondition and postcondition of the base class, and thus derived object must: Accept everything base class accepts Conform to all postconditions Not confuse users by output

Design by Contract declares that there are

Preconditions, which must be true in order for the method to execute

Upon competition method guarantees that the postconditions will be true

Now the rule for the preconditions and postconditions for derivatives is:

...when redefining a routine [in a derivative], you may only replace its

precondition by a weaker one, and its postcondition by a stronger one.

Page 26: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

9

The Dependency Inversion Principle

High level modules should not depend upon low level modules. Both should depend upon abstractions.

Abstractions should not depend upon details. Details should depend upon abstractions.

IoC containers and Spring Framework in particular are best examples

It is the high level modules that contain the important policy decisions and business models of an application. It is these models that contain the identity of the application. Yet, when these modules depend upon the lower level modules, then changes to the lower level modules can have direct effects upon them; and can force them to change.

It is high level modules that we want to be able to reuse. We are already quite good at reusing low level modules in the form of subroutine libraries. When the high level modules are independent of the low level modules, then the high level modules can be reused quite simply. This is the principle that is at the very heart of framework design.

Copy programm example, where Copy should depend on AbstractWriter and AbstractReader, not KeybordReader and FileWriter.

Page 27: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

10

The Interface-Segregation Principle

Clients should not be forced to depend upon interfaces that they do not use

“Fat” interfaces can be segregated into abstract base classes through: Delegation – Adapter pattern Multiple inheritance

When a client depends upon a class that contains interfaces that the client does not use, but that other clients do use, then that client will be affected by the changes that those other clients force upon the class. We would like to avoid such couplings where possible, and so we want to separate the interfaces where possible.

Example of delegation:

TimedDoor implements AbstractDoor

DoorTimerAdapter implements AbstractTimerClient

DoorTimerAdapter has reference to TimedDoor

TimedDoor does not have to have the exact same interface as TimerClient. The DoorTimerAdapter can translate the TimerClient interface into the TimedDoor interface.

In case of multiple inheritance TimedDoor would implement both AbstractDoor and AbstractTimerClient

Page 28: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

11

The Single-Responsibility Principle

Responsibility is “a reason to change”. There should never be more than one reason for a class

to change. One the simplest principles, but very hard to get it right.

If you can think of more than one motive for changing a class, then that class has more than one responsibility. This is sometimes hard to see. We are accustomed to thinking of responsibility in groups. For example, consider the Modem interface

dial(String pno);

hangup();

send(char c);

recv();

The first responsibility is connection management. The second is data communication. The two sets of functions have almost nothing in common. They’ll certainly change for different reasons. Moreover, they will be called from completely different parts of the applications that use them. Those different parts will change for different reasons as well.

If a class has more then one responsibility, then the responsibilities become coupled. Changes to one responsibility may impair or inhibit the class’ ability to meet the others. This kind of coupling leads to fragile designs that break in unexpected ways when changed.

Page 29: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

12

Integration challenges

Influences and influenced by organization structure and politics.

Integrators do not have any influence on the systems, they integrate.

Integration standards do not exist. Technical:

network is not reliable – depends on so many different components,

network is slow – much slower, than interprocess communication,

applications are different – platform, language, data structures, etc.,

changes are inevitable – applications must be able to evolve separately

Page 30: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

13

Integration types

Over time those different types of integration evolved: File exchange – one application writes to a file and

other reads it. Shared database – common data stored in one

database. RPC(remote procedure invocation) – application

exposes part of its functionality, so, that other application may use it over network.

Messaging – one application sends a message to a common channel and other reads it out.

Page 31: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

14

Why messaging?

Remote connection – messaging by design supports that. Platform independence – messaging can be used on different

platforms. Asynchronous communication – supports “send and forget” model. Time difference – sender and receiver can do their job at different

times. Throttling – receiver can regulate itself, how fast he is receiving

messages Reliable communication – using “store and forward” approach. Disconnected work – some applications work without network and

only time to time exchange messages with other systems. Mediation – messaging system can be the only system all other

systems are connected to. Multithreading – messages can be processed in parallel.

Page 32: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

15

Scaling – clustering databases

Separation of data into several partitions based on certain criteria: Data for different dates into different tables Data of different users into different databases

How to find correct partition? Can be done in 2 ways:

Hashing – partition of the cluster is chosen on the basis of common agreed algorithm: hash function of the user name.

Partition lookup – partition is chosen by lookup in the separate database.

PL/Proxy – database partitioning system for Postgres database. Open source Developed by Skype http://pgfoundry.org/projects/plproxy/

Page 33: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

16

Scaling – clustering servers and applications

The Linux Virtual Server – is a highly scalable and highly available server build on a cluster of real servers.

Fully transparent to end users. Applications must be stateless, in order to be able to run on LVS. For stateful Java applications there is a solution: JGroups JGroups – A toolkit for reliable multicast communication, very flexible and

configurable: Transport: UDP, TCP, JMS Fragmentation of large messages Reliable unicast and multicast Failure detection, Ordering protocols, Encryption

Page 34: How to avoid rotten software by Sergei Anikin · Continuous Care vs. Initial Design Initially good design degrade because requirements change in no anticipated way Many believe, that

17

Thank you!

Click to add text