Programs consist of definitions and expressions. Large programs consist of hundreds and thousands of definitions and expressions. Programmers design functions, use other programmer's functions, leave, start on the project. Without a strong discipline we cannot hope to produce software of high quality. The key to programming discipline is to understand the design of programs as a means to describe computations, which, in turn, means to manipulate data through combinations of basic operations. For that reason, the design of every program---whether it is small and for personal use or large and for business use--- must start with an analysis of the surrounding world of information and a description of the classes of data that represent the relevant information. If the classes are unusual or new, we make up examples so we understand the structure of the class description. After we understand the world of information surrounding our project and its data representation, we make a plan. A project plan identifies what data we wish to produce from the data that the program will be given. In many cases, though, a program doesn't just process data in one way but in many ways. For example, a program for managing bank accounts must handle deposits, withdrawals, interest calculations, tax form generation, and many other tasks. In other cases, a program may have to compute complex relationships. For example, a program for simulating a ping-pong game must compute the movement of the ball, bounces on the table, bounces from the paddle, paddle movements, etc. In either case, we need to describe what the various ways of processing data are and how they relate to each other. Then we rank them and start with the most important one. We develop a working product, make sure that it meets our specifications, and refine the product by adding more functions or taking care of more cases or both. Designing a function requires a rigorous understanding of what it computes. Unless we can describe its purpose and its effect with concise statements, we can't produce the function. In almost all cases, it helps to make up examples and work through the function's computation by hand. For complicated functions or for functions that use generative recursion, we should include some examples with the purpose statements. The examples illustrate the purpose and effect statements for others who may have to read or modify the program. Studying examples tends to suggest the basic design recipe. In most cases, the design of a function is structural, even if it uses an accumulator or structure mutation. In a few others, we must use generative recursion. For these cases, it is important to explain the method for generating new problems and to sketch why the computation terminates. When the definition is complete, we must test the function. Testing discovers mistakes, which we are bound to make due to all kinds of reasons. The best testing process turns independently developed examples into test suites, that is, a bunch of expressions that apply the function to select input examples and compare its results and effects with expected results and effects (mostly) automatically. If a mismatch is discovered, the test suite reports a problem. The test suite should never be discarded, only commented out. Every time we modify the function, we must use the test suite to check that we didn't introduce mistakes. If we changed the underlying process, we may have to adapt the test suite <#60272#>mutatis mutandis<#60272#>. No matter how hard we work, a function (or program) isn't done the first time it works for our test suite. We must consider whether the development of the function revealed new interesting examples and turn such examples into additional tests. And we must edit the program. In particular, we must use abstraction properly to eliminate all related patterns wherever possible. If we respect these guidelines, we will produce decent software. It will work because we understand why and how it works. Others who must modify or enhance this software will understand it, because we include sufficient information on its development process. Still, to produce great software, we must practice following these guidelines and learn a lot more about computing and programming than a first book can teach.