Abstract

The principles, patterns, and practices chosen to assist with the delivery of software projects are essential to the long term success of the project. Design patterns, development principles, and clean code practices directly impact the quality and speed of delivery of software.

Lessons

Preface

Agile development is the ability to develop software quickly, in the face of rapidly changing requirements. To do so we must employ principles that keep the software flexible and maintainable along with using practices that support the necessary discipline and feedback.

1: Agile Practices & 2: Overview of Extreme Programming

Team and Process

Too much process is often worse than too little process since it often creates the problems that it is supposed to fix. Runaway-process inflation is often born out of the fiasco that comes from too little process. However, projects are not so simple as to be able to be handled error free just through constraints and artifacts.

Building the team is more important than building the environment which should be built and configured on the basis of need. Teams should be built around motivated individuals. Build projects around motivated individuals since people are the most important factor of success.

Responsibilities are handled by the team as a whole. A team is not allowed to work overtime.

A good process will not save a project from failure if the team doesn’t have strong payers, but a bad process can make even the strongest of players [or groups] ineffective.

A strong player is not necessarily an ace programmer. Working well with others, communicating and interacting, is more important than raw programming talent.

Planning

Projects cannot be accurately planned far into the future. A good planning strategy is to make detailed plans for the next iteration, very rough plans for the next three months, and extremely crude plans beyond that. You only need to invest in a detailed plan for the tasks that are immediate.

Remaining flexible and being able to respond to change is often what determines the success or failure of a project. The team views changes to the requirements as good things.

Pair Programming & Testing (TDD)

do you want to form an alliance

All production code should be written when pairing. The keyboard should move between the pair a few times an hour. Every person should switch partners twice a day. That way, everyone develops with all the other devs and everything that is being worked on at least once an iteration.

The best way to onboard new team members is to work with them even on tasks that may not align with their core talents. Pairing significantly increases the spread of knowledge and reduces or eliminates knowledge silos. It can reduce the defect rate while having a minimal effect on efficiency.

A test is written before any code is written and when the test passes that module is complete. Testing allows for deep and significant refactoring. Testing encourages decoupling since it must be decoupled in order to be testable.

Developers should refactor continually as part of their daily practices. It should be done every hour or half-hour to keep it as clean, simple, and expressive as possible.

Documentation

Produce no document unless its need is immediate and significant.

It is good for the team to maintain a rationale and structure document that is limited to at most a couple dozen pages for a large project (i.e. half million lines).

User stories are used so that we only need to know enough about a requirement to estimate it though you must know that they are details. The devs are free to chop the stories up into tasks.

The details of user stories are captured as acceptance tests.

Metaphors can be used to aid in the discussion and cognitive decomposition and understanding of a system.

Collaboration & Contracts

Software is not yet a commodity since it cannot be reasonably developed on a fixed schedule at a fixed price. Successful projects include customer feedback and significant interaction on a regular basis. The customer prioritizes the features. They work together to solve problems. Contracts are best negotiated so that they govern the way that the development team and the customer work together.

Agile team members prefer face-to-face conversations whenever possible and they do not attempt to capture all project information in writing. They are not required to write specs, plans or designs though they may be created if there is a significant need.

Delivery

Deliver early and often using iterations. Hopefully within the first two weeks of a project. In order to deliver often, the code is continously integrated and tested.

An iteration is a collection of user stories chosen by the customer according to a budget created by the developers based on their velocity. The developers decide how much a feature will cost to implement.

Agile projects are measured by the amount of functioning software delivered to the client. High quality is the key to high speed. The way to go fast is to keep the software as clean and robust as possible. Make the simplest implementation as possible that is consistent with the requirements and development goals.

In order to be efficient, choose the simplest approach that you practically can. When considering if you’re going to need something someday, assume that you aren’t going to need it (YAGNI).

Don’t tolerate code duplication that doesn’t change for different reasons. A good way to eliminate duplication is to create abstractions.

The less functional the initial delivery, the higher quality in the final delivery. The more frequent the deliveries, the higher the final quality.

The professional goal of every software developer and every development team is to deliver the highest possible value to their employers and customers.

3: Planning

The developers estimate user stories and assign points to them. Devs tend to underestimate large stories and over estimate small ones. You can use spikes, basically a few day sprints, to help determine velocity.

The implementation order of the stories is a technical decision and must be done in a manner that makes the most technical sense. The customer cannot change the stories in an iteration once the iteration has begun.

Devs break the stories down into development tasks. A task is usually something one developer can implement in 4 to 16 hours. Devs can sign up for any kind of task since this facillitates knowledge spread. Every developer has a personal points budget for the iteration. Every dev participates in picking tasks to work on.

4: Testing

The simpler it is to run a suite of tests, the more often those tests will be run. The more tests are run the faster any deviation will be found.

Unit Testing

Intentional programming is programming with a purpose.

Unit tests are white-box (knows and depends on the internal structure of the module) tests. Testing is more about design, documentation, and feedback than about verification of the method. Writing tests before code improves the design. A solid suite of tests allows for regressions to be caught.

Writing the test first forces your mind to view the code you are to write from the viewpoint of the caller of the program. Thus the code is designed to be conveniently callable. By testing first, the code is also testable and decoupled.

The act of writing tests first is an act of discerning between design decisions. The act of writing tests before production code often exposes areas in the software that ought to be decoupled

Use the Mock Object pattern when testing.

True unit tests test that a class is behaving as it should in isolation.

Decoupling of classes is good since it allows you to swap in different concretions for abstractions for the purpose of testing and extension of the application. Isolation of the module under test is good since it forces you to decouple in ways that are beneficial to the overall structure of the progrm.

Acceptance testing

Acceptance tests are black-box (does not know nor depend on the internal structure of the module) tests that verify customer requirements. They serve as runnable and verifiable documentation of the system.

In order for acceptance tests to be able to be written, it has to be decoupled at the highest architectural level. The UI has to be decoupled from the business rules.

You should invest time up front to ensure that runnable acceptance tests work automatically and not done manually since then decoupling is often sacrificed and thus the architectural trade-offs suffer.

Acceptance tests should be simple text.

5; Refactoring

The process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.

There’s a difference between getting something to work and getting something right.

A software module has three functions:

  1. it has a function that it performs
  2. it has to allow for change
  3. it has to communicate to its readers

Assume that the performance effect of extracting functions will be negligible.

Refactoring is cleaning up the kitchen after dinner. Skipping cleanup doesn’t really make dinner go faster since you will incur a penalty the next day. Clean your code every day.

You should be able to extend and modify your system with a minimum of effort and the enabler of that is refactoring.

6: A Programming Episode

Employ top-down, test-first design.

Diagrams can be inappropriate when you create them without code to validate them and then intend to follow them. Don’t assume that a diagram is the best design for a task. The best design often evolves as you take tiny steps writing tests first.

7: What is Agile Design?

Source code is the design.

Agile design is a process, not an event. It’s the continous application of principles, patterns, and practices to improve the structure and readability of the software.

Symptoms of Poor Design

These are code smells at a higher level. Apply principles to remove smells but don’t apply them when there are no smells. Overconformance to the principles leads to the design smell of Needless Complexity.

Software Rot

Professionalism dictates that we, software developers, cannot tolerate code rot.

Designs degrade in non-agile environments because the initial design did not anticipate the requirements change. DO NOT blame the drifting of the requirements for the degradation of the design. Requirements are the most volatile elements in the project. If the design fails due to constantly changing requirements then it is the team’s designs and practices that are at fault.

An agile team keeps a system flexible and easy to change through lots of unit tests, continuously improving the design as they go, and refactoring. This results in a design that is appropriate as it can be for the requirements in that iteration.

To add extra protection now would be to do work that served no current purpose.

  1. Detect problems by following agile practices
  2. Diagnose the problem by applying design principles
  3. Solve the problem by applying the appropriate design pattern

Never let the rot begin in the first place. Keep the design as appropriate and as clean as possible.

8: The Single-Responsibility Principle (SRP)

A class should have only one reason to change

The SRP is about cohesion.