|Chapter 9 - Writing Code|
There are so many words for writing code that are confused with other activities. I have called it writing code. I shall not argue the point, call it what you will.
We are too hung up on the clever (and confusing) names we give to things in software. I even saw a list of comparisons between the terminology of different software processes. It was frightening. Process A calls this an artifact, process B calls it an object, process C ...
As Bruce Lee said:
Learning Jeet Kune Do is not a matter of seeking knowledge or accumulating stylised pattern, but is discovering the cause of ignorance.
If people say Jeet Kune Do is different from íthisí or from Ďthatí, then let the name of Jeet Kune Do be wiped out, for that is what it is, just a name. Please donít fuss over it.
The software process this book is based on is the Unified Process for Software Development. It is a fairly detailed process, and may not be particularly suitable for your environment. Even so, if you can understand the detailed processes, you can decide how much of them you need to include in your world, and how much you can leave out.
In this chapter, we shall look at some of the processes where writing code far outweighs the other practices. I shall refer to them as agile processes.
You know all about coding, right? Youíre a developer. Youíve read Code Complete and do your best to implement all of those good coding ideas. Itís a little tight on time sometimes thoughÖ
Maybe this is true, but to become a software architect you must approach coding as part of a process. It should be done in a structured, predefined way and follow an architecture and then a design. Writing code should be as effortless as a mechanic knowing where just the right tool for the job is. That is, the coder knows the correct method or algorithm to use, and uses it effectively. Design leads code. Code must not lead design.
As a software architect, you will probably not be writing any code other than test cases or prototypes, but you will be involved in leading how the code is developed, ensuring it follows the vision that requires it. You will also be reviewing code, ensuring standards are followed and correct algorithms and methods have been used.
You will also be involved in defining the environment for code creation. Whether you use a detailed or lightweight process is for you to decide. The lightweights I shall discuss are Rapid Application Development (RAD), Extreme Programming(XP) and Agile Modelling(AM). RAD has its flavours, known as Joint Application Development (JAD), Rapid System Development (RSD) and the Dynamic System Design Method (DSDM). DSDM has itís own organisation. XP has its own books and websites, and a growing following.
This discussion will be only a light breeze over the surface of each. Whole books exist on each subject, and in XP, whole books exist on a single practice.
Not all projects are suitable for RAD, but all system and application development can benefit from its ideas.
Briefly, they are:
The project should be visible to its intended users as soon as possible, and regularly updated. The stakeholders will then be able to have more input to the system, to contribute to it, and see that its growth is continuing in the right direction. When the final release is made, there will be no surprises. They will be able to see their contributions, and also receive something that is immediately recognisable. User input is vital for project success and the users are considered a part of the development team.
Some software houses have a daily build of their product. If the build fails, or fails to work properly, then the developer fixes what has gone wrong, and learns a painful lesson in the process. Daily builds, backed up with configuration management, ensure that problems are never larger than the effort one developer can put into a day.
Daily builds are also an excellent way of controlling flights of fancy. When coupled with frequent reviews, they ensure the software system is a cohesive whole, rather than a collection of unrelated applications.
Get business people involved throughout the development cycle, from your side and your customerís side. If you actively involve them, they will be part of your team. The less you involve them, the more work you will have to do to keep them happy later. Development decisions are made more easily with input from both sides, and as the two sides work together, they begin to understand each otherís ways of working and their personalities. People factors are critical to project success.
Have the developers sit with the end users during development. If there is a problem or misunderstanding, it can be resolved with immediate access to the users. It will build a good relationship, smoothing the difficult transition to live. Often going live forces two contrasting sets of people together, i.e. the developers and the users, and that is only the beginning of many problems.
Sitting together removes the communication overhead associated with telephone, email, time zones etc. People communicate far better face to face. Millions of years of evolution has taught us the meaning of the slightest facial or bodily nuance, which is lost immediately when technology comes between us. Even videoconferencing is third rate compared with face to face meetings. Sitting together also enforces overhearing. Overhearing binds teams together. You cannot overhear an email unless youíre snooping.
Testing and release should be integrated into the development cycle rather than performed as a separate activity at the end of development. If testing is coupled with a daily build which is then released to a live preview system, further strengthened by face to face communication, then the project will run far more smoothly.
Many of the RAD practices are reflected in eXtreme Programming(XP), which has taken RAD through its next evolutionary step. XP has also produced the agile movements.
During project planning, the customer defines the business value of each requirement. This is matched with the cost estimate for development, and the two measures together decide what is to be included or deferred in each small release. The planning process is horse trading. Imagine the sponsor wants your application to have its own wallpaper. Itís not likely to help the project succeed, and along with the extra time it would take to write and test the code, it is not something you want to include in the project until the very end (when your customer may have forgotten he asked for it). So you tradeÖ
Customer: I really need the project to have itís own distinctive wallpaper based on our company logo.
You: OK. Thatís quite a bit of development effort, so for the first release, I wonít be able to include it if you also want me to include the email facility and deliver it by the end of the year.
Customer: Oh no, I must have the email link. The first phase must be in before year end so we can transfer all our accounts. If we don't transfer the accounts, we will have to keep the old system running until year end the following year and we want to phase it out next year because it will not be supported after that.
The customer is then led down the path of not saying what goes in a release and what stays out, but rather makes the choice between options.
It helps both you and your client, as the client is involved (making the choice) and you get to do one instead of both. Project success or failure is often based on the leanness of the offering.
Allowing your customer to choose between items is a sales technique. The trick is to wait until you have some interest from your customer, and then instead of blurting out: ďSo you want it then?Ē, you offer them a choice.
Would you like twenty or fifty in the first batch?
Do you prefer the green or the red?
Answering no to this type of question is very difficult, and once you have a stated preference, youíre on the Cresta Run to a sale.
Would you like delivery by year end, or the wallpaper?
This is much easier with an external customer. When developing software for internal use, the customer is often your superior. You do not have the luxury of saying: ĎYes you can have A and B. I shall have to raise my original estimate by £100,000. Sign here.í Instead, your boss/customer says I shall have A and B and you will do it. No interface, no separation, no way out.
Or rather, the way out is your relationship with your internal customer. If it is one built on mutual trust and respect, then you have something to work with. Building that respect is like climbing a snow slope. Steady progress is better than a lunge. A lunge is the beginning of a long slide back down to the bottom.
Teams put a simple system into production early, and update it frequently on a very short cycle. Again, if your project is visible to all, they will grow with it and contribute to it. It will be seen as their project rather than yours. Another side benefit of this is that all developers must continually update their own set of code, keeping everybody in synch. Having different developers on different versions can be irritating and wastes time as you try to get them up to base. Updating everyoneís code daily improves team interaction. And donít do the builds at home time!
The teams use a common system of names and a common system description that guides development and communication. This may seem a rather obvious thing to do, to start a project by defining the business or system objects, and terms that your customer uses. It could be as simple as this:
|Employee||A person employed by the company. Applies to permanent and contract staff.|
|Personnel Number||A unique identifier for each employee.
Assigned to all permanent and contract staff on joining.
Its format is AAAANNNNA.
|Assignment||A unit of work undertaken by one or more employees|
|Assignment Code||A unique and unchanging identifier|
Itís such an obvious idea that it is frightening how often it doesnít get done. Letís do one for this book:
|Reader||You (yes, I know its silly, but letís not forget.)|
|Developer||Someone who writes code|
|Requirement||Something the product must do|
|Scenario||A brief play to help explain a requirement|
|Use Case||Something that something does, usually at a high level|
|Implementation||Whatever you want it to mean|
Constructing and maintaining a data dictionary, or system of names, needs a champion to manage it, and buy in from the various interested parties.
A system or application should be the simplest program that meets the current requirements. There is not much building for the future. Instead, the focus is on providing business value, and delivering it as soon as possible to bring quick benefits to the business. In these times of rapidly changing business processes, the biggest profits are often made by those embracing the change most rapidly.
This does not mean the programmers should thrash out code. Good design is an absolute necessity for quick delivery of reliable business systems. What it does mean is that adding weight to a system by designing for expansion or adding alternatives to cope with changing requirements, is not done. The code is as lightweight as possible. If the business changes in a year, then write the code again.
Changing platforms, technologies and programming languages have often made our old software obsolete, where no amount of rework will save the ageing product. From this point of view it is better to write thin, then rewrite thin when required. In many cases, the design is also obsolete, so an argument to design for the future and code for the present would be folly.
Teams focus on validation of the software at all times. Programmers develop software by writing tests first, then software that fulfils the requirements reflected in the tests. Customers provide acceptance tests that enable them to be certain that the features they need are provided.
Writing tests before writing code is not a bad idea. What it does mean is that there must be a design upon which the tests can be written before coding starts.
On the negative side, developers writing tests before coding begins leads to procrastination. It is better to have testers writing tests in conjunction with the developers. Both development and test must be based on a clearly understood design, and clearly stated requirements.
Teams improve the design of the system throughout the entire development. This is done by keeping the software clean: without duplication, and with high communication. It should be simple, yet complete.
Refactoring is an excellent technique which can be used to continuously improve existing code, to rewrite parts of a system, and to save projects in trouble.
Take a scenario of adding a single function to an existing piece of code. When the function is added, you realise there is another function which does almost the same thing. You refactor by isolating the common functionality into a new function, then call it from both the new and the existing location. A small amount of code will call the new parameterized function from two places, but most of the work will be handled by the new function. The non-refactored code would have two similar functions, leading to test, debug and update confusion in the future.
There are four steps to refactoring on a large scale, they are isolation, re-creation, test and replacement. The first step is to identify all interfaces to other systems or objects and ensure that there is clean isolation by rewriting the interfaces where necessary. The second step is to rewrite part that is obsolete, is no longer performing adequately or requires a more modern algorithm. When the new part is adequately tested, it replaces the old one.
Refactoring is a fairly large subject and discussed in Refactoring: Saving projects in trouble.
There are many methods of refactoring, and even refactoring tools which plug into development environments. Refactors have also been classified at the code level into method, field and class refactors. Such classification can detract from larger refactors where entire chunks of systems are refactored as one. Here are some of the more obvious methods of refactoring.
Replace a magic number with a symbolic constant
Rename a method to something more logical, more up to date, or more in keeping with related methods
Pull up field (move to a class higher in a hierarchy)
Push down field (move to a class lower in a hierarchy)
Encapsulate field (make a field private, and wrap it up in a public function or property)
Extract a superclass
Extract an interface
In terms of our framework and toolset discussed during design, when a piece of functionality is extracted from an application, then made more general in the framework or toolset, then it is a refactor.
If during design, pieces are extracted from applications and merged into a shared class†or common tool, then the design and code are both being refactored. Extracting common themes from requirements is refactoring requirements. Merging test cases is refactoring tests.
Developers pair up and select a particular piece of a system they will tackle together. The pair should choose each other rather than being assigned.
Pair programming has been shown by many experiments to produce better software at similar or lower cost than with programmers working alone. There can be problems with a dominant programmer, but the technique works very well with a programming expert and a domain expert.
Of course, there may be occasion when you might suggest some pairs. For example: you have designed an FTP application. You have a user interface designer, an FTP specialist, and Hotin Ruuf, a new programmer. Having the UI man working with the FTP specialist will get the look and feel on the go pretty soon. Then the UI person can work with Hotin to get the underlying functionality of the user interface going. Finally, Hotin can work with the FTP specialist, being brought up to speed on FTP, the politics of the organisation, the best local sandwich shop etc, while the FTP specialist learns the cool new language C2 litespeed, at which Hotin is a whiz.
The idea of the pairs being self assigned means people select those they are comfortable working with, rather than being made to work with those they are not.
All of the code belongs to all of the programmers. This lets the team go at full speed, because when something needs changing, it can be changed by anyone without the delay of waiting for a response from its author.
Working this way requires a full buy in to the change mechanism, and historic recording of change. It does not mean you have a heap of shared code and everyone hacks away together at it. A strong, well understood process is needed, and it must not be restrictive.
Teams integrate and build the software system often. Depending on the nature of the project, this could mean many times a day.
Integration problems can plague development, especially when it is least needed, i.e. Friday evening, or the end of the project.
Continuous integration keeps all the programmers on the same page, and enables rapid progress. Perhaps surprisingly, integrating more frequently tends to eliminate integration problems that plague teams who integrate less often.
Tired workers make more mistakes. XP teams do not work excessive overtime. They keep themselves fresh, healthy, and effective. Whether you, as a software architect, will have the luxury of 40 hour weeks depends very much on you and your environment. Even if you do work 40 hours a week at software architecture, you must still find the time to keep your technical knowledge up to date.
A 40 hour week requires some personal strength. Some developers seem to relish the family feeling of an all-nighter, or the collection in the morning of the early risers. We should accept that some people perform better in the morning, some late in the day. People working long hours under heavy pressure will end up taking back their hours from the business one way or another. They may leave, die, or grow disillusioned, spending all day surfing the net and avoiding the boss. They might continue to do sixty hours of good work each week for the rest of their working life, just as we might use delicately carved chocolate as a fireguard.
The project team has a dedicated individual who is empowered to determine requirements, set priorities, and answer questions as they are discovered. This empowered user is a real, connected and present member of the team. The direct contact the developers have with empowered users means that they do not create their own conclusions about business process or best methods for presenting their ideas. There are many knock on effects, such as there is always a second person to bounce ideas off, so poor ideas are soon quashed. The paper trail, or email trail is also much reduced.
This does require significant business buy in.
For a team to work effectively in pairs, and to share ownership of all the code, all the programmers need to write the code in the same way, with rules that make sure the code communicates clearly.
As all code is shared, reviews are continuous although ad-hoc. As architect, you must ensure the coding standards and code produced from them reflect your desires and contribute to the overall architecture.
Peer code review ensures that developers conform to common standards, as those not doing so are creating more work for others. Coding techniques, algorithms and the use of hardware and software technologies comes under intense scrutiny, particularly when those experienced in certain technologies can review where others are going.
If there are no coding standards, the development team must be made responsible for creating them. They may address the following:
Coding standards may also contain the software development process.
These twelve points are the foundation Extreme Programming. For more information, look at eXtreme Programming Explained.
Those having most success with XP tend to be small, tight teams who work closely together all the time. Even then, they pick and mix with the XP practices and build their own version of a software development process around it.
How you, as a software architect, use these practices is a matter for your own judgement. Even if you decide against them, you should define your point of view, and be able to articulate it.
The agile movement has grown out of XP, not necessarily as a replacement, but to support it and make it more complete from a process point of view.
The principles and values of the Agile Alliance sum up their approach to delivering software excellently. I quote them here without embellishment.
Our highest priority is to satisfy the customer through early
and continuous delivery of valuable software.
Welcome changing requirements, even late in development. Agile processes harness change for the customer's competitive advantage.
Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale.
Business people and developers must work together daily throughout the project.
Build projects around motivated individuals. Give them the environment and support they need, and trust them to get the job done.
The most efficient and effective method of conveying information to and within a development team is face-to-face conversation.
Working software is the primary measure of progress.
Agile processes promote sustainable development. The sponsors, developers, and users should be able to maintain a constant pace indefinitely.
Continuous attention to technical excellence and good design enhances agility.
Simplicity--the art of maximizing the amount of work not done--is essential.
The best architectures, requirements, and designs emerge from self-organizing teams.
At regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly.
Individuals and interactions over processes and tools.
Working software over comprehensive documentation.
Customer collaboration over contract negotiation.
Responding to change over following a plan.
The arguments the agile alliance put forward are indisputable, especially their values. Yes, I use a process, but that process is tailored for each project. Many software houses work this way. If you can absorb any of the agile ideas into your process, you will certainly do better. Improving the quality and vision of software is paramount. If you need a process to do it, use one. If you can be agile, be it.
I shall repeat one of their principles, which I believe should be the principal goal of any software development process or agile team:
Maximising the amount of work not done is essential.
If you only learn one lesson about developing software from this book, let that be the one. It is certainly the most important.