The Art Of Software Development (part 2): Designing For Simplicity

Keep it simple, stupid!

The Road Ahead

In the first part of this article, I introduced you to the five phases of a software development project, and spent some time on the first phase, requirements analysis. I explained the importance of clearly defining and understanding your customer's requirements before beginning work, and showed you how to put together a professional requirements document that would serve as the basis for future activity.

With that first phase out of the way, it's time to move on to what I personally find to be the most challenging phase of any software project: design. This is when you meditate on the requirements defined previously and design the architecture of your application so that it satisfies all of them, while simultaneously meeting the goals of stability, security, performance and maintainability. Needless to say, this isn't easy - but it will get your creative juices flowing. Keep reading!

The Best Laid Plans...

Once you've got your customer's agreement on the base requirements of the software to be developed (and, hopefully, an advance payment), it's time to put together a software development and management plan. This plan serves as the controlling document for the project, and it defines the processes, responsibilities and priorities for everyone associated with the project. The person in charge of the project uses this to assign appropriate roles and responsibilities to all stakeholders in the project, both customer and vendor.

Typically, this software development plan (also sometimes referred to as a project implementation plan) is split into several large sections, each of which is discussed below:

  1. Introduction: This section leads off the plan, providing an overview of the project, a list of the deliverables, a list of references and definitions for terms and acronyms used within the document. It may also include a cheat sheet of the important milestones, the project schedule and the resources or departments assigned to the project.

  2. Project organization: This section discusses the organizational structure of the project, providing a hierarchical overview of the relationships and interfaces between different departments. It also discusses the responsibilities of different teams, the reporting mechanisms between and within them, and the person or people responsible for each activity.

  3. Managerial processes: This section discusses the goals and priorities of the project management team, including information on the monitoring and notification mechanisms to be used for the project; these mechanisms serve as early alerts in the event of cost or time explosion. It also lists the assumptions of the plan, and lists dependencies between the various elements of the plan. A list of the project risks should also be included at this stage, together with ideas on how to reduce the impact of each; this risk list should be updated on a regular basis by the project manager.

  4. Technical processes: This section provides an overview of the tools and techniques used to implement the project. It includes information on development tools and languages, version control techniques, coding standards, and testing processes. It also includes a list of all the documentation that is to be developed during the project cycle, including information on the reviews and approvals needed at each stage.

  5. Quality assurance processes: This section provides information on the quality standard and processes followed to ensure that the final product is in conformance with the specifications agreed to by both parties. It also includes information on test procedures, problem reporting mechanisms and resolution processes.

  6. Schedules and resources: Detailed information on the project schedule and major milestones is described in this section, together with information on the various types of staff needed to execute the plan, and their allocation over the plan period. Schedules may be expressed in either absolute ("10 June 2002") or relative ("start date + 20") terms, depending on the amount of flexibility you need to give the customer (or yourself).

The end result is a clear outline of expectations at both sides of the table, and a concise snapshot of the project path and development activities needed to achieve the goal.

Building Blocks

Once the software development plan is complete, it's time to address the first item in your schedule - the software architecture. With the requirements document as your base, you can proceed to develop a structure for your application that meets each of the items within it.

One of the first things you need to do is design your data storage containers. These may be either database tables or flat files; you should select an appropriate container type depending on the performance and client access requirements. If you're using a database, take time out to normalize and optimize your design and eliminate redundancies; if you're going with flat files, remember to pay adequate attention to file locking issues.

Break your application up into discrete components or modules - this simplifies development by allowing you to focus on smaller pieces of the puzzle at any one time, and also makes the code easier to maintain. Consider using OOP techniques wherever possible. Remember to clearly define the input and output interfaces of each object, and the interfaces between objects. Create standard APIs so that you can implement "black box" techniques and thereby minimize the impact of a change in one module on other modules.

Make it a point to separate the application's business logic from its user interface - this makes it easy to update one without impacting the other. Consider using an interface abstraction layer, like a template engine, to perform this separation. If your application uses a database and you anticipate needing to change it as you scale up, make sure database calls are routed through a database abstraction layer so that RDBMS changes can be accomplished with minimal effort.

Exception handling is a critical part of application design. Ensure that your application code uses a flexible exception handler, and spend some time identifying and listing the various types of errors that could occur - this list will come in very handy when you sit down to code your exception handler. If your application needs to generate reports or logs, study the various standard log formats, as well as the requirements of your customer, and develop a report format that is both understandable and easily extensible, so that extra information can be easily included at a later stage.

You should also spend some time identifying the complete set of variables to be used within your application, and making a distinct separation between configuration and user data. If you have transient data flowing through your application, make sure that you have a clear plan for how this data is to be created, handled and destroyed. Also make a note of which variables are global variables (accessible in all modules) and which ones are local (accessible only to one specific module).

Think about the coding standards to be followed when developing this application (block indentation, script header and footer blocks, script revision logs, comments for variable and function definitions and so on) and ensure that it is consistent and easy to understand and use. Ensure that your coding standard describes the format, style and length of function names, variable names, file names, and database and table names.

If your application needs to be installed to a customer system, spend time on the application packaging and delivery mechanism. Ensure that your packager contains all the functions you will need for a successful installation, and spend time understanding how the application will be installed, erased and upgraded with minimal difficulty to its users.

Last but not least, the nitty-gritty of application structure and storage. Spend some time developing a suitable directory structure within which your application code will reside, and clearly demarcate the locations in which different bits of data are to be stored. If your application files will be named according to a specific notation, consider all cases when defining this naming convention and ensure that it covers all possible situations.

I should stress at this point that it's important to spend as much time as possible in this design phase, and to ask yourself fundamental design questions while developing the system architecture. Sound design can go a long way towards reducing the size and number of bugs; experienced programmers know this, which is why they spend almost sixty to eighty percent of their time on this phase of the development cycle.

Make it a point to review and re-review your initial design to ensure that there are no flaws in it; it's almost certain that the first idea you come up with is flawed in some aspect, and you must be prepared to deal with this and improve on your first cut until you're satisfied that nothing is left to improve on. Simplify as much as possible - and when in doubt, remember the immortal words of Albert Einsten: "Things should be made as simple as possible, but not any simpler."

Again, all this thinking needs to be captured in a software design document, which serves as the base for the actual implementation of the code. Let's look at that next.

Drawing Class

Typically, a software design document contains the following sections, each of which is designed to address different components of the software architecture:

  1. Introduction: This section provides an introduction to the software being developed, together with background, scope, definitions and references.

  2. Application overview: This discusses the application to be developed in general, together with a broad description of software concepts and functions.

  3. Constraints, considerations and goals: This section describes the constraints and guidelines to be kept in mind when designing the application. It includes details about the hardware and software available, the development and execution environment, user profiles, performance and security considerations, data storage and format constraints, together with a list of important rules to be kept in mind while designing the software (and the basis of each).

  4. Design strategy: This section provides information on the programming paradigms used when designing the software. It includes information on the IDE or programming language used, the techniques used for data storage, interprocess communication, flow control, exception handling, resource management, garbage collection and traffic management

  5. System architecture: This contains high-level architectural diagrams of the system design, together with descriptions of the components used and their relationships via a modeling language. A description of the various modules that make up the system is also included.

  6. Component architecture: Each of the modules that makes up the system needs to be described in greater detail. This section may therefore be further split into sub-sections, each one containing a detailed description of the internals of the various modules that make up the overall system. These details include information on the purpose of each component, its functions, its data input and output formats, its input and output interfaces, its internal business logic, and its interaction with other components. You can include pseudo-code wherever needed to ensure that the details are clearly communicated.

This document serves as the basis for the developer or development team's efforts, and should be reviewed at least once to identify and correct design flaws, if any exist, before beginning work.

All Used Up

The software design document may also be accompanied with a user interface design document, which outlines the key features of the application user interface. This document usually contains information on the target users for the application, together with a list of usability goals and how they may be addressed.

Typically, a user interface design document contains the following sections, each of which is designed to address different components of the usability experience:

  1. Introduction: No prizes for guessing what goes here - a description of the application background and goals.

  2. User profile: This section includes a clear description of the user profile for this application. It must include as much detail as possible on the characteristics of the users who will be using the application including their level of familiarity with the Web-based model, and accessibility requirements for disabled users.

  3. User problems: This section lists the most common problems users of the application currently face, as a prelude to ensuring that the final interface successfully addresses these problems. It may also list the most common tasks users will be using the application for.

  4. Interface priorities: This section lists the priorities and objectives for the user interface, classifying each as essential or non-essential. This priority list is very important when it comes to making tough choices between different interfaces, and also helps to focus the interface designer's efforts.

  5. Interface constraints: In case there are specific constraints to be kept in mind while designing the interface - for example, ensuring the visibility of a corporate logo or specific branding colours - these constraints should be listed here.

  6. Sample screens: In case you've already got some options designed, you can add them here to give your customer a preview of what the finished product will look like, or to allow him or her to pick from the choices available.

Given the interactive nature of today's Web applications, a document by itself is often insufficient to communicate all the details of the user interface. Therefore, it's also usually a good idea to build a user interface prototype wherever possible, so that your customer can play with it and identify usability problems up front, before any actual implementation begins.

This prototype is usually a simple HTML mock-up, consisting of a series of screens that demonstrate the main sections of the application and the click-through paths between them. By vetting this prototype against the user problems and priorities noted in the interface design document, you can identify problems in your user interface at an early stage, and give your so-far theoretical application a more concrete feel.

Testing Times

Finally, you should also spend some time putting together an acceptance test plan for the project. This plan outlines the test cases that must be passed in order for the application to be accepted by the customer. All the test scenarios in the acceptance test plan must correlate to the requirements previously specified in the requirements document. Failure of any of these test cases will imply that the application is not compliant with the requirements specified in the initial phase, and may therefore be rejected by the customer.

Typically, an acceptance test plan consists of the following:

  1. Introduction: This section introduces the test plan, indicating the goals of the test, the schedule, the level of testing and the resolution procedure in the event of a test case failure.

  2. Test cases: This section consists of test cases to see if the software meets the the various requirements specified in the previously-agreed requirements document. Each test case must be accompanied with a description outlining the test procedure, required input(s), expected output(s) and rules for determining whether or not the test was successful.

  3. Traceability matrix: This matrix maps each test case to a requirement from the requirements document, in order to ensure that every requested feature is correctly implemented.

Like the requirements document, this acceptance test plan must be accepted and signed off on by the customer.

It should be noted that although I've put the development of this plan near the end of the pre-implementation phase, it should actually be closer to the beginning. Most of the time, this acceptance test plan is delivered to the customer at the time of requirements analysis, in order to give him or her confidence about the features that will be in the final software release. However, exigencies of time and cost may make it impractical to develop this plan during the early phases of pitching a project, especially if the project is a small- to medium-sized one. That said, you should make it a point to deliver the acceptance test plan before actual implementation begins.

Different Strokes, Different Folks

And that's about it. At this point, you, the developer, have enough information to actually get down to writing the code, secure in the knowledge that you've successfully reduced the scope for changes or deviations. This does not mean that changes or deviations will not occur; however, as you'll see in the subsequent parts of this tutorial, you can reduce their impact on the overall development plan by managing them carefully.

After reading the material above, you might be tempted to think that there's way too much paperwork involved in the pre-implementation phase of a project. You're right - there is - but much of it is aimed at protecting both you and your customer from creeping changes, and ensuring that the final product is delivered in an efficient and cost-effective manner.

It should be clearly understood, also, that the processes outlined above may not work for every Web project. For smaller projects or teams, it may not be practical to develop extensive documentation in the early stages of pitching a project. However, an effort should be made to ensure that requirements are formally captured and signed off on prior to development, and that there exists clear communication on the deliverables between customer and vendor, so as to avoid problems at a later stage. Time should also be taken to have a clear process of managing change requests, so that the overall development of the program is not unduly affected.

If you're interested in improving your design skills, here are a few articles you might find interesting:

The principles of good design, at http://www.w3.org/DesignIssues/Principles.html

Jakob Nielsen's usability Web site, at http://www.useit.com/

Principles of user interface design, at http://www.asktog.com/basics/firstPrinciples.html

An article on simplicity in software design, at http://construx.com/stevemcc/ieeesoftware/bp06.htm

Quotes on simplicity in software design, at http://www.vanderburg.org/soft-quotes.html

I hope you enjoyed this article, and that it gave you some insights and ideas on how to manage your software development projects more efficiently. In the next part of this article, I'll be spending time on the third part of the development cycle - actual code development - and offering some general tips and tricks on how you can simplify and streamline the process to account for changes at a later date. See you then!

Note: Examples are illustrative only, and are not meant for a production environment. Melonfire provides no warranties or support for the source code described in this article. YMMV!

This article was first published on30 Aug 2002.