3.4 Requirements Prerequisite

If the requirements are explicit, the user can review them and agree to them. If they’re not, the programmer usually ends up making requirements decisions during programming. Explicit requirements keep you from guessing what the user wants.

The Myth of Stable Requirements

Specifying requirements adequately is a key to project success, perhaps even more important than effective construction techniques.

Handling Requirements Changes During Construction

Sure, it feels like you’re getting behind if you stop coding at this stage. But if you’re driving from Chicago to Los Angeles, is it a waste of time to stop and look at a road map when you see signs for New York? No. If you’re not heading in the right direction, stop and check your course.

Make sure everyone knows the cost of requirements changes:
"that sounds like a great idea. Since it’s not in the requirements document, I’ll work up a revised schedule and cost estimate so that you can decide whether you want to do it now or later.” The words “schedule” and “cost” are more sobering than coffee and a cold shower, and many “must haves” will quickly turn into “nice to haves.”

It’s all right for customers to change their minds and to realize that they need more capabilities. The problem is their suggesting changes so frequently that you can’t keep up. Having a built-in procedure for controlling changes makes everyone happy.

Evolutionary delivery is an approach that delivers the system in stages. You can build a little, get a little feedback from your users, adjust your design a little, make a few changes, and build a little more. The key is using short develop- ment cycles so that you can respond to your users quickly.

Checklist: Requirements

Non functional requirements

Requirements Quality

Requirements Completeness

3.5 Architecture Prerequisite

the quality of the architecture deter- mines the conceptual integrity of the system. That in turn determines the ultimate quality of the system.
It partitions the work so that multiple develop- ers or multiple development teams can work independently.

Good architecture makes construction easy. Bad architecture makes construction almost impossible.

The architecture doesn’t need to specify every class in the system. Aim for the 80/20 rule: specify the 20 percent of the classes that make up 80 percent of the system’s behavior.

The architecture should explain why a database is preferable to flat files.

User Interface Design

Careful architecture of the user interface makes the difference between a well-liked program and one that’s never used.

The architecture should be modularized so that a new user interface can be substi- tuted without affecting the business rules and output parts of the program.

Scalability

The architecture should describe how the system will address growth in number of users, number of servers, number of network nodes, number of database records, size of database records, transaction volume, and so on.

Internationalization/Localization

The architecture can decide to use strings in line in the code where they’re needed, keep the strings in a class and reference them through the class interface, or store the strings in a resource file. The architecture should explain which option was chosen and why.

Error Processing

Error processing is turning out to be one of the thorniest problems of modern computer science.
With so much code dedicated to handling errors, a strategy for handling them consistently should be spelled out in the architecture.
Error handling is often treated as a coding-convention-level issue, if it’s treated at all.

at what level are errors handled? You can handle them at the point of detection, pass them off to an error-handling class, or pass them up the call chain.

Is each class responsible for validating its own data, or is there a group of classes responsible for validating the system’s data? Can classes at any level assume that the data they’re receiving is clean?

Buy-vs.-Build Decisions

You can buy GUI controls, database managers, image processors, graphics and charting components, Internet communications components, security and encryption components, spreadsheet tools, text- processing tools—the list is nearly endless. One of the greatest advantages of program- ming in modern GUI environments is the amount of functionality you get automatically: graphics classes, dialog box managers, keyboard and mouse handlers, code that works automatically with any printer or monitor, and so on.

Change Strategy

The architecture should show that possible enhancements have been considered and that the enhancements most likely are also the easiest to implement.

General Architectural Quality

The architecture should be a polished conceptual whole with few ad hoc additions. The central thesis of the most popular software-engineering book ever, The Mythical Man-Month, is that the essential problem with large systems is maintaining their conceptual integrity.
When you look at the architecture, you should be pleased by how natural and easy the solution seems. It shouldn’t look as if the problem and the architecture have been forced together with duct tape.

Good software architecture is largely machine and language-independent.

The architecture should explicitly identify risky areas. It should explain why they’re risky and what steps have been taken to minimize the risk.

Checklist

3.6 Amount of Time to Spend on Upstream Prerequisites

Your clients wouldn’t want you to show up with wood, hammer, and nails and start spending their money before the architect had finished the blueprints.
People tend to understand software development less than they understand two-by-fours and sheetrock.
why you want to plan requirements development as a separate project. You might need to explain your reasoning to them.

Key points


Chapter 4: Key Construction Decisions

4.1 Choice of Programming Language

By relieving the brain of all unnecessary work, a good notation sets it free to concentrate on more advanced problems, and in effect increases the mental power of the race. Before the introduction of the Arabic notation, multiplication was difficult, and the division even of integers called into play the highest mathematical faculties. Probably nothing in the modern world would have more astonished a Greek mathematician than to learn that … a huge proportion of the population of Western Europe could perform the operation of division for the largest num- bers. This fact would have seemed to him a sheer impossibility…. Our modern power of easy reckoning with decimal fractions is the almost miraculous result of the gradual discovery of a perfect notation.
Alfred North Whitehead

An earlier study at IBM found that programmers who had extensive experience with a programming language were more than three times as productive as those with minimal experience.

Programmers working with high-level languages achieve better productivity and quality than those working with lower-level languages.
You save time when you don’t need to have an awards ceremony every time a C statement does what it’s supposed to.
More- over, higher-level languages are more expressive than lower-level languages. Each line of code says more.

The Sapir - Whorf hypothesis says that your ability to think a thought depends on knowing words capable of expressing the thought. If you don’t know the words, you can’t express the thought and you might not even be able to formulate it.
Programmers may be similarly influenced by their languages.

A typical story goes like this: “We were writing a new system in C++, but most of our programmers didn’t have much experience in C++. They came from Fortran back- grounds. They wrote code that compiled in C++, but they were really writing dis- guised Fortran. They stretched C++ to emulate Fortran’s bad features (such as gotos and global data) and ignored C++’s rich set of object-oriented capabilities.”

4.2 Programming Conventions

construction guidelines for variable names, class names, routine names, for- matting conventions, and commenting conventions.

One key to successful programming is avoiding arbitrary variations so that your brain can be free to focus on the variations that are really needed.

Coding-convention details are at such a level of precision that they’re nearly impossible to retrofit into software after it’s written.

4.3 Your Location on the Technology Wave

In early-wave environments—Web programming in the mid-1990s, for example, Programmers spend significant amounts of time simply trying to figure out how the language works instead of writing new code.

Programmers who program “in” a language limit their thoughts to constructs that the language directly supports. If the language tools are primitive, the programmer’s thoughts will also be primitive.
Programmers who program “into” a language first decide what thoughts they want to express, and then they determine how to express those thoughts using the tools provided by their specific language.

Example of Programming into a Language

In the early days of Visual Basic, I was frustrated because I wanted to keep the business logic, the UI, and the database separate, but there wasn’t any built-in way to do that in the language.

Consequently, I adopted a design convention that the .frm file (the form file) was allowed only to retrieve data from the database and store data back into the database.
Each form supported an IsFormCompleted() routine, which was used by the calling routine to determine whether the form that had been activated had saved its data.

If your language lacks constructs that you want to use or is prone to other kinds of prob- lems, try to compensate for them. Invent your own coding conventions, standards, class libraries, and other augmentations.

4.4 Selection of Major Construction Practices

Checklist: Major Construction Practices

Key Points


Part II - Creating High-Quality Code


Chapter 5 Design in Construction

the design might be intended to be detailed enough for coding to be fairly mechanical, but design is rarely that complete—the programmer usually designs part of the program, officially or otherwise.

On small, informal projects, a lot of design is done while the programmer sits at the keyboard. “Design” might be just writing a class interface in pseudocode before writ- ing the details. It might be drawing diagrams of a few class relationships before coding them. It might be asking another programmer which design pattern seems like a bet- ter choice.

Regardless of how it’s done, small projects benefit from careful design just as larger projects do, and recognizing design as an explicit activity maximizes the ben- efit you will receive from it.

5.1 Design Challenges

Design Is a Wicked Problem

Horst Rittel and Melvin Webber defined a “wicked” problem as one that could be clearly defined only by solving it, or by solving part of it. This paradox implies, essentially, that you have to “solve” the problem once in order to clearly define it and then solve it again to create a solution that works. This process has been motherhood and apple pie in software development for decades.

a dramatic example of such a wicked problem was the design of the original Tacoma Narrows bridge.
wind created an unexpected, side-to- side harmonic ripple. One blustery day in 1940, the ripple grew uncontrollably until the bridge collapsed.
until the bridge collapsed, its engineers didn’t know that aerodynamics needed to be considered to such an extent. Only by building the bridge (solving the problem) could they learn about the additional consideration in the problem that allowed them to build another bridge that still stands.

Design Is a Sloppy Process (Even If it Produces a Tidy Result)

Design is sloppy because you take many false steps and go down many blind alleys— you make a lot of mistakes. Indeed, making mistakes is the point of design—it’s cheaper to make mistakes and correct designs than it would be to make the same mis- takes, recognize them after coding, and have to correct full-blown code.

design is “emergent.” Designs don’t spring fully formed directly from someone’s brain. They evolve and improve through design reviews, informal discussions, experience writing the code itself, and experience revising the code.

5.2 Key Design Concepts

Accidental and Essential Difficulties

In philosophy, the essential properties are the properties that a thing must have in order to be that thing. A car must have an engine, wheels, and doors to be a car. If it doesn’t have any of those essential properties, it isn’t really a car.
Accidental properties are optional.

Even if we could invent a programming language that used the same terminology as the real-world problem we’re trying to solve, programming would still be difficult because of the challenge in determining precisely how the real world works

Importance of Managing Complexity

Projects fail most often because of poor requirements, poor planning, or poor management. But when projects do fail for reasons that are primarily technical, the reason is often uncontrolled complexity. The software is allowed to grow so complex that no one really knows what it does.
When a project reaches the point at which no one completely understands the impact that code changes in one area will have on other areas, progress grinds to a halt.

Managing complexity is the most important technical topic in software development. In my view, it’s so important that Software’s Primary Technical Imperative has to be managing complexity.

Dijkstra pointed out that no one’s skull is really big enough to contain a modern computer program (Dijkstra 1972), which means that we as software developers shouldn’t try to cram whole programs into our skulls at once; we should try to organize our programs in such a way that we can safely focus on one part of it at a time.

The goal is to minimize the amount of a program you have to think about at any one time. You might think of this as mental juggling—the more mental balls the program requires you to keep in the air at once, the more likely you’ll drop one of the balls, leading to a design or coding error.

Humans have an easier time comprehending several simple pieces of information than one complicated piece. The goal of all software-design techniques is to break a complicated problem into simple pieces.
The more independent the subsystems are, the more you make it safe to focus on one bit of complexity at a time.
Packages provide the same benefit at a higher level of aggregation.

Keeping routines short helps reduce your mental workload.
working at the highest level of abstraction reduce the load on your brain.

Desirable Characteristics of a Design

A high-quality design has several general characteristics. If you could achieve all these goals, your design would be very good indeed. Some goals contradict other goals, but that’s the challenge of design—creating a good set of tradeoffs from competing objectives.

Minimal complexity

Avoid making “clever” designs. Clever designs are usually hard to understand. Instead make “simple” and “easy-to-understand” designs.

Ease of maintenance

Continually imagine the questions a maintenance programmer would ask about the code you’re writing. Think of the maintenance programmer as your audience, and then design the system to be self-explanatory.

Loose coupling

you should hold connections among different parts of a program to a minimum.
Use the principles of good abstractions in class interfaces, encapsulation, and information hiding to design classes with as few interconnections as possible.
Minimal connectedness minimizes work during integration, testing, and maintenance.

Extensibility

To enhance a system without causing violence to the underlying structure.
You can change a piece of a system without affecting other pieces.

Reusability

you can reuse pieces of it in other systems.

High fan-in

High fan-in refers to having a high number of classes that use a given class. High fan-in implies that a system has been designed to make good use of utility classes at the lower levels in the system.

Low-to-medium fan-out

means having a given class use a low-to-medium number of other classes.
High fan-out (more than about seven) indicates that a class uses a large number of other classes and may therefore be overly complex. Researchers have found that the principle of low fan-out is beneficial whether you’re considering the number of routines called from within a routine or the number of classes used within a class.

Portability

you can easily move it to another environment.

Leanness

Leanness means designing the system so that it has no extra parts.
Voltaire said that a book is finished not when nothing more can be added but when nothing more can be taken away.
In software, this is especially true because extra code has to be developed, reviewed, tested, and considered when the other code is modified.

Stratification

you can view the system at any single level and get a consistent view.
Design the system so that you can view it at one level without dipping into other levels.

The beneficial effects of stratified design in such a case are (1) it compartmentalizes the messiness of the bad code and (2) if you’re ever allowed to jettison the old code or refactor it, you won’t need to modify any new code except the interface layer.

Standard techniques

Try to give the whole system a familiar feeling by using standardized, common approaches.