Tool Support for Planning the Restructuring of Data ... - CiteSeerX

23 downloads 136 Views 462KB Size Report
Qualcomm Incorporated. 6455 Lusk Boulevard. San Diego, CA 92121 cabaniss@qualcomm.com. } Nuera Communications, Inc. 10445 Pacific Center Court.
Tool Support for Planning the Restructuring of Data Abstractions in Large Systems William G. Griswold+ Morison I. Cheny Robert W. Bowdidgez Jenny L. Cabanissx Van B. Nguyen} J. David Morgenthaler+ +

Department of Computer Science and Engineering University of California, San Diego La Jolla, CA 92093-0114 fwgg,[email protected]

y

GDE Systems, Inc. 16250 Technology Dr. San Diego, CA 92127-1806

z

IBM T. J. Watson Research Center P.O. Box 704, Yorktown Heights NY 10598 [email protected]

[email protected] x

}

Qualcomm Incorporated 6455 Lusk Boulevard San Diego, CA 92121

Nuera Communications, Inc. 10445 Pacific Center Court San Diego, CA 92121

[email protected]

[email protected]

Abstract Restructuring software to improve its design can lower software maintenance costs. One problem in restructuring is planning out the redesign. The star diagram manipulable visualization can help a programmer redesign a program based on abstract data types. However, the underlying meaningpreserving transformational support for restructuring is costly to provide. In particular, an efficient data flow analysis implementation is needed, and multi-language analysis and transformation is needed for any program written in multiple languages (a common practice in large projects). We also found that a star diagram view can be too large for a programmer to effectively assimilate and inflexible for a complicated restructuring, such as one involving complex types. To solve this problem we moved away from meaning-preserving transformational support and focused on helping programmers plan and carry out their restructurings. For example, we created a way of manipulating a star diagram—called trimming—that mimics the way that basic restructuring transformations affect the star diagram display, allowing a programmer to plan a restructuring without depending upon restructuring transformations. With the ability to annotate trimmed star diagram components, plans can be recorded and later recalled. Also, programmer-controlled elision can remove clutter from the star diagram view. We built a star diagram planning tool for C programs, measured its elision capabilities, and performed a programmer study for the encapsulation of a widely-used data structure in a 28,000 line program. We found that elision is effective in controlling star diagram size. In the study we found that each programming team successfully planned its restructuring in rather different, unanticipated ways. These experiments indirectly influenced important improvements in the tool’s software design and user interface.  This work supported in part by NSF Grants CCR-9211002 and CCR-9508745, a Hellman Faculty Fellowship, and UC MICRO

grants 95-065 and 96-5449 with Hughes Aircraft.

1

1 Introduction Lientz and Swanson found that software maintenance can account for 70% of a software system’s lifetime cost [Lientz & Swanson 80]. Boehm documented an Air Force project in which the development cost was $30 per line, but the maintenance cost was $4,000 per line [Boehm 75]. Much of these costs can be attributed to degraded software structure [Belady & Lehman 71]. One way to lower software maintenance costs is to restructure the system into a more modular form while preserving the original functionality [Griswold 91, Opdyke 92, Griswold & Notkin 93, Johnson & Opdyke 93]. By isolating the code pertaining to a changing design decision within a module, enhancements can be applied locally, and hence at lower cost [Parnas 72]. Restructuring in order to achieve data encapsulation [Parnas 72, Liskov & Zilles 74] is an important special case. Performing a restructuring to encapsulate a data structure can be difficult when working with only the source code. Most of the source is unrelated to the data structure, yet the computations on the data structure are usually widely dispersed in the system, thus complicating the identification of all information required to design an appropriate interface. Often, a text searching tool such as grep is used by programmers to locate direct references to a data structure or global variable. However, its lexical and line-based orientation often yields false matches, includes irrelevant information, misses surrounding context, and misses cases such as indirect data structure references created through assignment. Even with such support, a programmer must keep careful records in order to correctly perform the data encapsulation task. To address these problems we invented the star diagram, a graphical view of a data structure that can be used as a user interface to a meaning-preserving restructuring tool [Bowdidge & Griswold 94, Bowdidge 95]. This paper describes how a variety of scalability issues and the programmers’ need for help in keeping track of their progress in a restructuring led to the design of a tool for planning restructurings of C programs that is based on the star diagram visualization. Experiments with this first prototype are described, as well as how they guided the tool’s evolution to a second prototype with improved performance and usability. New experiments to assess the improvements are also described.

1.1 The Star Diagram Figure 1 shows an example of the original star diagram for Scheme programs. The root, at the far left (in this case labeled *line-storage*), represents the variable to be encapsulated. The children of the root represent language constructs in the program that directly reference the variable. The children of the next level represent language constructs that reference the previous level’s computations, and so on. A node having a stacked appearance indicates that two or more pieces of code correspond to (perhaps) the same computation. The last level of children, shown as parallelograms, represent the functions that contain these computations. The resulting tree contains all of the computations that refer directly or indirectly to the data structure, and omits computations not related to the data structure. Because of the stacking of similar computations, each unique computation is represented just once. Hence, each node in the tree possibly represents the implementation of an abstract operation on the data structure. A node near the left of the tree is an expression or statement that might implement a “low level” operation, encapsulating little more than the data structure representation itself. A node farther to the right might implement a “higher level” operation. Each node in a star diagram is linked to a text view of the source so that elided details are readily accessible. Double-clicking on a node displays and highlights its corresponding source code in the program text view. In the case of a “stacked” node—where there are multiple statements associated with a node—the programmer can navigate to each occurrence through a menu selection. To apply a restructuring transformation to the program, the programmer clicks on a node, then clicks on the transformation’s button to apply it to the selected code. The tool first checks the chosen transformation 2

Figure 1: Identifying the abstract operation “words on line”, the first step in the function extraction sequence for the original Scheme star diagram restructuring tool. against the program to ensure that it will leave the program’s functionality unchanged. If the check is successful, the tool performs the transformation on the source representation and updates the star diagram to reflect the changes. Transformations appropriate for performing data encapsulation in the star diagram include Extract Function, Inline Function, Extract Parameter, Inline Parameter, Rename Function, Move into Interface, and Remove from Interface. Transformation Extract Function is a central transformation. It creates a new function from existing code and replaces the existing code with calls to that function. It is invoked from the star diagram by clicking on a node stack and then clicking the Extract Function button (Figure 1). A dialog appears with a summary of each computation associated with the node stack, and the programmer selects which ones are to be replaced by a call to the new function. The tool then presents a dialog for choosing the function’s name and what parts of the computation will be its arguments. Finally, the tool verifies that the chosen computations are in fact behaviorally identical and that the resulting transformation will preserve the functionality of the program. If so, the tool performs the transformation and updates the star diagram to reflect the changes to the program (Figure 2). The programmer can then click on the new function definition in the diagram, and click the Move into Interface button, removing the definition and its body from the star diagram and placing it in the interface panel to the left (Figure 3). Repeating this basic sequence—interleaved with other transformations that prepare the code for function extraction—results in an empty star diagram, signaling that every computation on the data structure has been abstracted as a module operation.

3

Figure 2: After the “words on line” function has been extracted by the Extract Function transformation. The new function is visible at the top of the text view and in the bottom half of the star diagram view.

Figure 3: After moving the new function from the star diagram into the module interface with the Move into Interface operation.

4

1.2 Problems with the Approach Difficulties with providing meaning-preserving restructuring. Implementing scalable data flow and alias analysis for semantic checking of meaning-preserving transformations is complicated, although possible [Atkinson & Griswold 96, Morgenthaler 97]. An even more severe challenge is that a large software system is likely implemented in multiple languages, requiring a comprehensive restructuring tool to provide support for multi-language data flow analysis and transformation. A large project can have specialized languages for user interface description, database access, and domain-specific functions. Performance-critical parts of the system may be written in assembler. Also, popular languages like C and C++ support a macro language providing conditional compilation and in-line macros that are not syntactically compatible with the programming language, and are intrinsically difficult to understand or manipulate semantically using traditional methods. As a non-commercial example, the planning tool described in this paper is implemented in 6 languages: C++, C, flex, bison, Tcl/Tk, and the C preprocessor. Popular macro-free languages like Java give hope that the macro problem may diminish over time, but program generators (e.g., bison) are becoming ever more popular. Moreover, many projects use in-house languages, complicating the production of off-the-shelf restructuring tools. Although no one issue is a matter for serious concern, and not all systems manifest the above problems, taken together these issues caused us to consider a less technological solution to tool-assisted restructuring. Shortcomings of the star diagram. The star diagram has proven to be useful in planning and carrying out small restructurings, and a detailed study of its usage showed that it can help programmers keep track of crucial information during the restructuring task [Bowdidge 95]. In particular, its management and presentation of program information helped programmers perform their ch anges completely and consistently [Bowdidge & Griswold 97]. However, a few problems limit its usefulness on large systems. First, our examination of star diagrams for large C and MUMPS programs revealed that a star diagram for a widely used data structure can be unacceptably large. For example, a star diagram for all variables of type struct buffer in GNU Emacs 19.22 [Stallman 93] contained 3741 nodes and was 246 inches high when printed on paper. Such a star diagram is time-consuming for a programmer to assimilate while comprehensively planning a restructuring. Second, in our study of star diagram usage we found that programmers wanted to record design ideas prior to restructuring, even for a small system. In particular, we observed that programmers sometimes created a function and moved it into the module’s interface in order to record a tentative design idea. The programmers were aware that the transformation sequence might have to be undone later, but it had the effect of changing the star diagram view (i.e., removing a path and giving it a name) to help recall the design idea. This approach to design recall is time-consuming because the programmer must specify the function’s interface and the tool must perform data flow analysis to verify that the transformation is meaning-preserving. Moreover, not every design idea is best represented as a function. These issues become magnified in larger programs due to the larger number of abstractions and the greater cost of data flow analysis. Third, the original star diagram implementation allows viewing computations on only a single variable. However, an abstract data type might consist of many components. For instance, a linked list might be represented by two variables, a pointer to its first element and a pointer to its last element. To properly encapsulate this data structure requires that both pointers be viewed in the star diagram. Also, if there are many linked lists in a program, all need to be viewed in order to abstract them all into a single module.

5

Figure 4: Initial star diagram for the rooms array in saadventure as it appeared in the first C star diagram planning tool prototype.

1.3 Adapting the Star Diagram Concept to Large-Scale Restructuring These latter observations led us to the realization that since a large restructuring activity may require dozens of interrelated code changes to produce a coherent redesign, it can benefit from the formulation and recording of a comprehensive plan to avoid overlooking or forgetting an essential detail. Moreover, formulating such a plan requires that all variables of interest be included in the star diagram, and that the programmer be able to assimilate the (perhaps large) star diagram in a cost-effective fashion. Since we had also found that scalable transformational support was costly, we considered how to provide support for planning a restructuring independent of transformational support. This led us to the idea of emulating the effects of restructuring transformations on the star diagram as a way of recording plans for a data encapsulation. We believed that the other problems observed could be addressed by making small changes to the star diagram concept that exploited its tree and node-stacking properties. Such a tool could prove useful in planning complex restructurings regardless of whether the programmer performs the restructuring with an editor or with a restruct uring tool. However, such support also could be used for activities other than restructuring. For example, a phased inspection [Knight & Myers 93] of data structure invariants could be facilitated by a star diagram view. If problems were detected in such an inspection, changes would be necessary, but they probably would not be restructuring changes. To provide the flexibility required for planning large-scale data encapsulations, we found we could exploit basic tree and node-stacking properties of the star diagram, as described below. Elision of uninteresting nodes. In our study on the star diagram restructuring tool, we had found that programmers tend to view a star diagram from left to right, starting at the root. However, they sometimes use the function definition nodes at the far right to identify the context of usage. Aside from the function nodes, then, the right side of a diagram is not required much of the time and can be hidden until needed. Not only can such “depth” elision remove a significant number of nodes in a diagram, but it also allows for a more compact layout of the remaining nodes when using some off-the-shelf tree drawing algorithms. For example, the standard Tcl/Tk tree widget [Brighton 93] uses an algorithm that places the children of a 6

node as close together as possible [Moen 90], meaning that two siblings with many children will be placed far apart from each other. Eliding the children allows the siblings to be placed close together, making for a more compact layout that permits easier comparison of the siblings. As we discovered when testing our first star diagram planning tool prototype, depth elision alone is not adequate for many situations. Sometimes there are too many function nodes for a compact view, or the uninteresting nodes may be more properly identified by a node property other than its depth in the tree. For example, a star diagram can often be crowded by nodes denoting heavily nested if-then-else statements. Consequently, providing a variety of elision methods can be beneficial. Emulation of basic restructuring transformations. To allow the programmer to record a design decision without restructuring, we introduced support for mimicking the extract-function-then-move-into-interface restructuring sequence that programmers tended to use in the study. Recall that in the Scheme tool, Move into Interface removes the path from a selected function node back to the root and places a textual annotation of it in the interface panel. In the planning tool, this operation is extended to be performable on any node, with the implication that the code associated with the removed node corresponds to a key part of the data abstraction being planned. Since the removed node (and associated path) has not actually been restructured into an operation and also has no name that is suggestive of its role in the abstract data type, the trimmed node can be annotated with a textual entry provided by the programmer. This can include, for example, a description of the abstraction the node represents or how the associated code is to be restructured. Additionally, the trimmed path can be viewed as a star diagram and used to navigate to its associated text. It is easy, then, for a programmer to later review an annotation and then visit the associated text to make the planned changes. Besides recording plans, the trimming feature can be used as an additional method of permanently or temporarily removing uninteresting portions of a star diagram. Extending the star diagram’s root concept. To support the visualization of data structures incorporating many data elements, we allow specifying multiple variables and expressions as the root of the star diagram. The composite root can be specified using a combination of methods. For example, multiple variables can be selected from the textual view of the source. Alternatively, any number of existing star diagrams can be unioned together, effectively taking all of their roots as the root of a new star diagram. As we discovered when observing users of our first planning tool prototype, it is also useful to allow a selected variable’s type to be used as the specification of a star diagram for all variables of that type. Enhancements to navigation and the star diagram itself. A key task of a programmer who is investigating a star diagram is to decide what nodes constitute abstract operations of the data type and how their associated code should be restructured. For stacked nodes, this requires comparing the multiple associated code fragments from throughout the program to decide what parts of the code fragments are common and which need to be abstracted away as arguments. To better facilitate comparing all the code associated with a stacked node, we introduced a grep-like view of stacked nodes that is called up by double-clicking on a node. Also, by clicking on any single code fragment in the grep view, its full text view is opened to allow seeing the fragment in context. Finally, because larger programs consist of many files—and a file’s name often suggests its role in the overall system—we added file nodes to the right side of the diagram.

7

Figure 5: Initial star diagram for the rooms array in saadventure as it appeared in the redesigned C star diagram planning tool prototype.

1.4 First Prototype, Experiments, and Redesign To gain insight on the ability of a tool employing these ideas to assist a programmer in understanding and changing a complex system, we implemented a star diagram planning tool for C programs and then performed a programmer study and a quantitative scalability study [Griswold et al. 96b]. This first prototype contained all of the above ideas, with the exception that elision was limited to depth elision and it was not possible to use the type of a variable to specify multiple roots. Figure 4 shows the principal window of this prototype.1 In the programmer study we found that programmers were largely successful in planning their restructurings, that the tool was amenable to a number of styles of usage, and in fact programmers discovered new techniques for managing scale and for reasoning about the correctness of their changes. However, limitations such as the lack of a type-based star diagram feature were identified, leading to several improvements that were incorporated into a second version of the tool. This study is described in Section 3. From the scalability measurements we learned that depth elision alone is inadequate for managing complicated diagrams, leading to the more general concept of property-based elision. A generalized mechanism was incorporated into the revised tool and the experiment was reperformed to determine their impact on scalability. The experiment is described in Section 4. The new results suggest that the improved elision mechanism is effective as long as programmers are willing to aggressively elide their star diagrams and then progressively unelide them as they need more information. Somewhat unexpectedly, these experiments taught us that providing raw functionality is not enough for such a tool. Because a large amount of information is being managed by both the tool and the programmer in the process of planning a restructuring, care is required in the tool’s software design and its user interface. In Sections 5 and 6 we discuss these issues and how they influenced the redesign of our prototype. The effect of the user interface redesign and the addition of property-based elision is shown in Figure 5. 1 This star diagram and others appearing later in this paper are shown in a small pane, both to improve readability and to approximate the limits that screen size imposes on viewing larger, more realistic star diagrams.

8

Figure 6: The project window after the files are loaded. In Section 7, we briefly discuss issues in restructuring programs that are constructed from multiple languages, a feature we have not yet explored in this tool. Before closing we discuss the ability of other tools to record plans.

2 A Planning Scenario To provide a more detailed, dynamic perspective of the ideas introduced in the previous section, we present part of a session for planning the restructuring of a dungeon exploration game program written by Scott Adams called saadventure [Adams 80]. This version of saadventure is a 1,700 line C program (excluding comments and blank lines), although it was originally written in BASIC. The maintenance task. A major enhancement is planned for the rooms data structure, a global array of room descriptions. In particular, the geometry of the rooms is going to be changed and the number of rooms may vary during the game due to the actions of the player. However, rooms is directly accessed throughout the program, so the enhancement is expected to be costly. Our goal, then, is to encapsulate the rooms array in a module to isolate the enhancements, hence making them easier to perform. We start the tool, which immediately displays a root window that provides all the tool’s project-wide operations. We select the appropriate directory and files for the project, and the files are loaded (Figure 6). If we wished, we could save out this configuration with the Save button so that future work on this project could be resumed by using the Load option. The bottom of the project window contains an entry for each created star diagram, which includes a list of its roots and an optional annotation.

9

Figure 7: Grep-like view of the ArrayRef star diagram node.

Early exploration. To begin planning the encapsulation, we build a star diagram based on the type of rooms variable (Figure 5). This is most easily accomplished by using the Search for a String in All Files option in the project window, which returns a grep-like result, and then selecting a match that corresponds to a rooms reference to navigate to the match’s place in the source code. From here we request that the reference’s type be included in the root set for a new star diagram, and then display the star diagram. Having chosen the type of rooms as the root means that all expressions of the same type as rooms are included in the star diagram. However, we notice that only references to rooms are included, indicating that rooms is unique and that it is never passed as a parameter to a function call or assigned to another variable. Consequently, it will not be necessary to create an interface for rooms that passes it in as a parameter; it can be completely hidden in the new module. We also observe that the rooms variable is an array since 30 out of 36 accesses occur inside an ArrayRef node. We click on the ArrayRef node to investigate these references, and a grep-like list of lines containing the array references appears (Figure 7). We can see that an array of rooms is being indexed by integers. There is a lot of information to assimilate, and nothing comes to mind in terms of an encapsulation except an overly simplistic “get room” operation. We dismiss the view. Because only a couple of the root’s children—our current subjects of interest—can be seen on the screen at once, we decide to apply depth elision to adjust the diagram’s width to 3 levels of nodes (Figure 8). The diagram now fits on the screen, although we may now have cut out too much of the diagram for parts of the task. However, at the top of the diagram we see a stack of five declaration nodes (PtrDec followed by Declaration). Curious, we click on the node stack, and the grep-like view of the declarations appears (Figure 9). One entry is the actual declaration of the rooms array, and the others are extern declarations that import the array into the other files of the program. It is interesting that the rooms array is actually a pointer to a Room structure, because it indicates that the rooms array is probably dynamically allocated. We are also concerned that the rooms pointer might be used in a more dynamic fashion than merely pointing at the beginning of an array. Since we are encapsulating rooms behind a set of functions, we realize that the extern declarations will soon be obsolete. Consequently, we decide to remove this node (and the paths passing through it) from the diagram. This is performed with the Trim Arm button appearing at the top of the star diagram window. The declaration node and associated paths are removed from the diagram and put into the trimmed-node panel on the left. We then click the Edit Entry Annotation button above the panel in order to record our

10

Figure 8: The elided rooms star diagram.

Figure 9: Grep-like view of the rooms declarations.

Figure 10: Diagram with declarations trimmed and annotated. 11

Figure 11: Taking a closer look at the initialization of the array. plans for the code associated with the node. We type “Declaration of rooms array and external declarations. Double-check that rooms is not changed after initialization. If OK, move declaration to new module file, and delete external declarations” (Figure 10). As a consequence of this action, we have not only begun a concrete plan for encapsulating the rooms array, but we have also further shrunk the diagram so that the remaining components are easier to view. Identifying part of the initialization of rooms. Besides the 30 array references, there is only one other child of the root left in the diagram, labeled LHS of assn, which means that rooms appears on the left-hand side of an assignment somewhere in the source code. Since we are concerned about how rooms might change during program execution, and the array portion of the diagram is daunting, we decide to look into this assignment. The node itself indicates that there is only one assignment to rooms, which seems promising, but if it were in a loop or a frequently called function, then more than one array of rooms could be created. The function definition node appearing to its right—which is on-screen because of the elision—tells us that the assignment appears in the function ReadDataFile. It is probably an initialization routine. Clicking on the LHS of assn node, we are presented with the code itself (Figure 11), which confirms our belief that this is initialization code. If we were not so sure, we could build a star diagram for ReadDataFile to find where it is called. Now that we are sure the assignment is an initialization, we know that rooms really “is” the rooms array, which will allow a more straightforward interpretation of the rest of the star diagram. Since we have been investigating the initialization, we could try to figure out what module interface function we should have for initialization. In particular, do we want just the malloc call, do we want the whole ReadDataFile routine, or do we want something in between? However, right now we are more concerned with the geometry of rooms, and it seems that the array references must be directly involved. Before leaving the initialization, however, we trim it from the diagram to simplify the view and also record our (unfinished) plans for the assignment with the annotation, “Allocation; part of initialization. Find code that initializes the contents of array and decide if should be separate or combined.”

12

Abstractions for room operations. Now only the ArrayRef child node remains, and we can see that it is used in two record field accesses, as indicated by the Field:desc and Field:dir labels. We first examine the dir field accesses. We unelide the star diagram one level to get a look beyond this reference, but this shows little, and the Field:desc node is interfering with its layout. So instead we view this star path by trimming it into the trimmed star arm panel and viewing it separately (Figure 12a). Since the child node of the Field:dir star node is an ArrayRef node, the dir field must be an array. A quick look at one of the print statements under the array reference (Figure 12b) indicates that each integer entry represents a possible exit from the room. Among the 21 ArrayRef exit references, we see that (1) their addresses are taken, (2) they are printed, (3) they are assigned to another variable (i.e., RHS of assn), and (4) they are compared for equality and inequality. We notice that the consumer of the addressOf star node is an fscanf function call node in the function ReadDataFile. Examining the source code reveals that the fscanf call initializes the rooms array from a file. Thus, we can trim the fscanf node with the annotation, “To be grouped with the allocation operation for initialization” (Figure 13). Moving on, two comparison nodes about half way down the diagram stand out, a != node and a == node. The source code for the != node is: if (rooms[currentRoom].dir[i] != 0) ...

This code helps us determine that the expression is a test for a valid direction. We proceed to trim this node from the star diagram view and annotate it as the operation “IsValidDirection”. Interestingly, the equality comparison is also with 0, and we determine that this is a test for an invalid direction. We trim this node with the annotation, “IsInvalidDirection, consider combining with IsValidDirection.” It is notable that the star diagram’s global view facilitated finding the similarity between these two operations. The next child we consider is a RHS of assn star node. The corresponding source code is: currentRoom = rooms[currentRoom].dir[noun - 1];

This computation sets the currentRoom variable to a new room after the player makes a move in a valid direction. Our first inclination is to move this star path to the trimmed-node panel as the “MoveTo” operation. However, this operation updates currentRoom, which is not part of our planned module. To explore whether currentRoom should be part of our module or part of its own module, we build a star diagram for currentRoom. Along the way, we find a similar variable startingRoom, and union it into the currentRoom diagram using the Combine Diagrams feature in the project view to create a star diagram with both of them as roots. However, realizing that there could be many such variables, we build a type-based star diagram for currentRoom. This diagram turns out to be huge because currentRoom is an integer, yet few integers appear to be room indices. So acting on the assumption that the programmer who wrote this code used the Room suffix to denote integer variables that are room indices, we return to the project view and use Search for a String in All Files to search for all Room substrings. We then build a variable-based diagram for the variables that match, which is much more manageable yet still large (Figure 14). Trying a few elisions, we find that excluding all statement-level nodes as well as function and file nodes provides a concise view (Figure 15). After perusal of this diagram, we conclude that currentRoom represents the player’s current relationship to the rooms, and so is not intrinsic to the rooms abstraction. For instance, if this game were extended to have many players, the rooms data structure might not change, but there would be a current room for each player. Consequently, the “MoveTo” operation is part of the player abstraction. However, we still need to encapsulate this rooms reference, which—without the assignment—represents the “adjoining room” operation. We trim the ArrayRef node (not the assignment node), annotating it as “AdjoiningRoom(int room, int direction)”. 13

(a) Viewing Field:dir from the trimmed node view. fprintf(f, "

North:%d South:%d East:%d West:%d Up:%d Down:%d\n", rooms[roomNumber].dir[0], rooms[roomNumber].dir[1], rooms[roomNumber].dir[2], rooms[roomNumber].dir[3], rooms[roomNumber].dir[4], rooms[roomNumber].dir[5]);

(b) Code view of the fprintf node. Figure 12: Viewing computations relating to the Field:dir node.

Figure 13: After trimming additional initialization code. 14

Figure 14: Initial diagram for currentRoom and related variables.

Figure 15: Elided diagram for currentRoom and related variables.

15

If we had decided that currentRoom or one of the other room indices should be part of the rooms abstraction, we would have built a star diagram combining the rooms and the index variables of interest. Such a big change to the rooms abstraction would have led us to reconsider the prior decisions. The low cost of identifying and recording design decisions in the planning tool—relative to actually restructuring— makes this backtracking palatable. On the other hand, the lightweight planning support has delayed the behavioral checking of the planned operations until the actual restructuring is performed. Discussion. The planning of the encapsulation proceeds in this fashion until the star diagram is empty, at which point there is a complete plan to encapsulate every computation on the rooms variable as a function of the planned module. The final plan is for a module with seven operations, although there are additional trims for items such as the declarations. The above example demonstrates the key features of the star diagram planning tool. Both trimming and elision were used to create a succinct but still meaningful view of all uses of the rooms array. We were able to peruse the diagram to inspect related groups of code on a case-by-case basis. The grep-view displayed a summary of each member of a node stack, exposing similarities and differences, as in the case of the external declarations of rooms. The grep-view also supported navigating to a textual view of the code, permitting the examination of nearby statements. By trimming a node and its associated paths and then giving it an annotation, we constructed a plan of the encapsulation. The trimmed node and its associated paths can be reexamined as either a star diagram, grep-view, or source code, thus providing all the information needed to later make the changes. As new information is discovered in the process of the encapsulation, such as the special status of the currentRoom variable, we could construct new star diagrams on the fly and merge them together as needed to plan a multi-variable encapsulation. The process of removing “handled” paths from the diagram allows focusing on the computations that have not yet been handled. The shrinking size of the diagram also provides a visual cue of the task’s progress. In a more complex example, more elision and node trimming would likely be required to produce a comfortably viewable star diagram, probably resulting in a view that is missing crucial information. However, the quick reversibility of these operations allows for flexible viewing strategies.

3 User Study Our earlier experience with programming tools taught us the importance of designing tools using knowledge of how programmers work [Bowdidge & Griswold 97]. Consequently, we undertook an exploratory systematic observational study [Weick 68] on the first star diagram planning tool prototype in order to guide the its continued design [Griswold et al. 96b]. The study was designed to investigate the impact of the tool’s planning functionality on programmer behavior when performing a restructuring to encapsulate a key data type. Three programming teams were observed.

3.1 Methodology Our study is modeled after Bowdidge’s programmer study [Bowdidge 95], which is based on Flor’s laboratory studies of organization within programmer teams [Flor & Hutchins 91, Flor 94]. Their approach of observing a pair of programmers working together, known as constructive interaction [Miyake 86, Wildman 95], is used in our study because it provides a more natural way to induce programmers to verbalize their thoughts than single-person think-aloud methods. Working dialogue also reveals how the programmers perform problem solving. Unlike Bowdidge’s study, we did not perform a formal translation of the video into transcripts, but instead viewed the videos looking for particular episodes of interest. The results were shared with the study subjects, who corroborated our conclusions. 16

We used three teams of two programmers each for the study. 2 Team A consisted of two programmers who helped design the star diagram concept, so they were both highly motivated and skilled with the star diagram concept. One of the programmers had also run a version of the program being manipulated for this study. Consequently, this team constitutes an ideal restructuring scenario, not unlike a team of expert system designers who had used a star diagram tool on previous projects. Team B and team C consisted of graduate students who have industrial programming experience. All programmers understood the concepts of modularization and data encapsulation, and had prior exposure to the star diagram concept. Participation was voluntary and a subject could leave the study at any time. We conducted the study session in a laboratory setting to limit interruptions and ease information capture. Only the subjects and a graduate student observer were present in the laboratory during the session. The observer avoided interaction with the subjects. The two subjects (of a team) worked together at a computer monitor. We used a video camera with clip-on microphones to record the computer screen, gestures and discussions. We also saved the notes written by the programmers. We did not provide source listings in order to force the teams to inspect the code on the computer screen in view of the video camera. We gave the programmers the source code to a complex, rogue-like dungeon exploration game called omega [Brothers 89]. It consists of roughly 28,000 lines of C. To complicate the task slightly, we had modified the program to introduce some parameter passing of the Objects array. We asked the programmers to perform a data encapsulation of Objects in preparation for an enhancement that would allow storing the data in the global array Objects on disk. The encapsulation requires examining all the functions in which the Objects variable appears and performing several global changes. We asked the programmers not to change the program’s running behavior. We also asked the programmers to plan the restructuring task using the star diagram interface, although the programmers could use standard UNIX tools in a separate window.

3.2 High-Level Observations Progress in planning and restructuring. All three teams completed the planning phase of their restructuring in two and one-half hours. Only team B carried out (part of) their restructuring. It is notable, however, that comprehensive plans for the encapsulation of a widely used data structure in a 28,000 line program could be developed in this time frame. None of the teams spent significant time searching the code, preferring instead to trust that the star diagram presented direct access to all relevant information. Chosen encapsulation. Each team chose a different encapsulation of the Objects array. Team A, the expert team, recognized after considerable effort that the Objects array was actually an array of kinds of things that is used to clone new instances of objects. Consequently, they planned a module with several operations relating to how cloning is performed. Their final module contained 13 operations. Team B recognized that there was some complex behavior in Objects, but decided to encapsulate the low-level behavior first, and then worry about the complex behavior in a second pass (for which they did not have time). Consequently, they planned simple access operations that hid the array representation, but did not address the proper maintenance of clone state. Team C took a similar approach to team B, but planned “put” and “get” operations that perform a complete object copy and update. This choice addressed the possibility of the array being stored on disk, since it did not allow pointing to components of the original object. Team C also identified a third “initialize” operation for creating the array and initializing its contents. 2

The term team is used here to indicate that the pair of programmers were working together towards a common goal, not that they work together regularly. However, each pair was well acquainted and seemed comfortable working together.

17

Planning method. Each team planned its encapsulation differently, and interleaved planning with restructuring differently. Team A methodically visited direct and indirect references to the Objects array, top to bottom, left to right, making a point of understanding the code before making a design decision. They also made heavy use of annotations to keep track of their decisions—a good choice for this team since they planned 13 functions. Team A built additional star diagrams to investigate issues related to Objects. For instance, they built a star diagram for the function inititem to determine where and when object initialization occurs (i.e., where and when inititem is called). They formulated their plan completely before considering any code changes. They primarily used trimming to scale down the star diagram view, experimenting with depth elision only later in the experiment. Team B tired of scrolling in the Objects star diagram, so they trimmed key parts of it into the trimmed-node panel to view them in isolation. This tactic surprised the experimenters, who had not thought of it.3 After planning much of the interface, team B created one of their key operations, objectgetnth, and modified the source code to call it. They then generated a new star diagram that not only had the Objects root, but also the new function so they could investigate all calls on it. Team B preferred to not use annotations. Team C’s approach was similar to A’s, but they tended to defer issues that initially confused them, resulting in multiple passes over the Objects star diagram. For instance, Objects is passed as an argument to functions disarm and inititem. As their plan developed, team C revisited these functions to ascertain, for example, if they might be part of the planned module or if the uses of the pointers in these functions violated the newest aspect of their plan. Toward the end, to help answer the latter question, they built a star diagram of the parameter in disarm, and quickly determined that it was in fact used only as a pointer to the entire Objects array, and not an individual object in the array. As soon as team C converged on their basic plan—but before making final decisions about the treatment of disarm and inititem—they began sketching out the new code in new source and header files. Conceptualization. All teams treated nodes in the diagram as candidates for abstracting into a function. However, team B’s novel use of multiple roots to analyze the correctness of their changes suggests that they conceptualize the star diagram root as being “everything of interest”, and not just “all references to the data structure being encapsulated.” In hindsight, this team’s conception is not surprising, because once they had created a new function, the elements of interest naturally grew to include the new function and its uses.

3.3 Observed Problems Lack of a searching capability. All teams had problems locating the declaration of rooms, since the first planning tool prototype lacks a program searching functionality other than the star diagram itself. Thus, the teams relied on UNIX grep to find the declaration and to perform other searches over the Omega source code. Lack of automatic restructuring. Team B complained that it was tedious and error-prone to perform the actual restructuring by hand. They used UNIX’s sed utility to apply their global changes, and took a few tries to fashion a script that made the change correctly. Time ran out for the other teams before they could attempt the changes. 3

The example of this technique described in the previous section and shown in Figure 12a was introduced after the studies were completed.

18

Manually specifying type-oriented star diagrams. The teams complained about the inability to quickly generate a star diagram for all variables of a particular type. Teams wanted this feature to observe how an object copied out of the Objects array is used. Although the star diagram planning tool allows manually specifying all the variables for the root, the teams only explored this option briefly because it was (reasonably) perceived to be too much work to be worthwhile. Selecting a subset of a “stacked” node. Team A complained that the star diagram planning tool lacks a feature to select a subset of the members of a stacked star node for trimming. We observed the same problem in the saadventure example when trimming the external declarations, although the flexibility of annotations mitigate this problem. Interestingly, the function extraction operation of the original Scheme star diagram tool supports this selectivity, but we overlooked it in our design of the planning tool.

3.4 Observed Successes Support for planning. All teams successfully planned the creation of a set of functions to encapsulate the data structure in a limited period of time. (By successful, we mean that the plan, if carried out, would have succeeded in localizing the code involved in the proposed change.) No team’s planning was inhibited by the lack of restructuring capability, although the task does not capture the potential interaction of planning two modules prior to restructuring. Moreover, each team chose a different abstraction, suggesting that the star diagram is a mechanism for data encapsulation, not forcing a particular style of interface on a module. Flexible mechanisms for coping with scale. Not only did team A use depth elision and all the teams use trimming to scale their diagrams, but team B invented new method on their own. Team B’s divide-andconquer approach of moving a node into a subdiagram is effective because it provides a better layout and allows “stacking” several diagrams on top of each other by using standard window system features. It also may be a more effective way of achieving elision than removing “uninteresting” nodes, since there may be more interesting subdiagrams than uninteresting ones. Use in correctness analysis. One of the intrinsic shortcomings of the tool is its lack of meaning-preserving transformations to help the programmer carry out the planned restructuring. Only navigational support is directly provided. However, team B’s tactic of building a star diagram to investigate their restructuring changes suggests that programmers can use the tool’s features to assist in checking the correctness of their changes, somewhat compensating for this lack.

4 Effects of Elision On Scalability Since scale issues are not readily exposed by the techniques and subject matter used in the programmer study, we performed a separate quantitative study on the impact of elision and trimming on star diagram size. For this experiment, we used the second prototype, which includes the more general property-based elision mechanism. For these measurements we used ubiquitous data structures in omega (See the previous Section) and temacs, the terminal-based version of GNU Emacs version 19.34 [Stallman 93], which contains 78,000 lines of actual C code. The primary concern about the size of star diagrams is the effort required to assimilate a star diagram. Two influential factors are the volume of information in a star diagram and how much scrolling is required to view this information. Both are important to measure because a star diagram may paradoxically contain a large amount of information in a compact layout or a few nodes in an inefficient layout. To capture the

19

Impact of Elision on Node Count 350

depth only decls trimmed functions and files trimmed

70

300

60

250

50

Height (Inches)

Number of Nodes

Impact of Elision on Height

depth only decls trimmed functions and files trimmed

200

150

40

30

100

20

50

10

0

0 2

4

6 Levels of Elision

8

10

12

2

4

6 Levels of Elision

8

10

12

Figure 16: Impact of elision and trimming on omega’s Objects. Impact of Elision on Node Count

Impact of Elision on Height 700

depth only decls trimmed functions and files trimmed

5000

depth only decls trimmed functions and files trimmed 600

500

Height (Inches)

Number of Nodes

4000

3000

400

300

2000 200

1000 100

0

0 5

10

15 Levels of Elision

20

25

5

10

15 Levels of Elision

20

25

Figure 17: Impact of elision and trimming on temacs’s current buffer. volume of information, we count the number of nodes remaining in the star diagram. (Another measure, the number of paths, is also interesting, but harder to interpret.) To capture the complication of scrolling, we measured the height of the star diagram. Although left-right scrolling also occurs in a star diagram, our experience is that it never extends farther than a couple of pages. Since height can vary significantly according to the layout algorithm and font chosen for node labels, only the relative heights of different elisions and trimmings should be considered. The font used for these measurements are the same as shown in Figure 5, which is reduced in the linear dimension about 45% for publication. We measured the star diagrams at four levels of depth elision, each with and without the function and file nodes elided. The first diagram has no depth elision at all. The second is pruned to a depth that we deemed appropriate for the data structure being encapsulated. The last two are pruned at the two most extreme depths that provide useful information. We chose function and file node elision because a quick look at these star diagrams revealed that the chosen data structures contain a large number of these nodes, causing their parents in the star diagram to be drawn far apart. Several styles of elision are similar to depth elision, so their measurement here would not provide much 20

additional information. Both unstacked and statement nodes occur on the right side of a diagram, so eliding them is tantamount to depth elision, although they have the advantageous property that the depth will vary on each arm according to the particulars of each computation on the root. On the other hand, some elisions are more valuable for their unusual perspective, rather than simply reducing the number of nodes. For example, the elision of stacked nodes provides a concise view of all the unique nodes in the star diagram. Such elisions are difficult to evaluate quantitatively. As a measure of the potential benefits of trimming a path, we chose to trim the declarations in each star diagram at each of the elision depths. Trimming the declarations conservatively measures the effects of trimming, since presumably at least a few paths can be trimmed during encapsulation as a diagram reduction measure. Although the declaration subtree is often quite small, these variables are broadly exported, so their elision significantly simplifies the layout of file nodes. Big diagrams like these probably provide more trimming opportunities, but identifying appropriate candidates could be more difficult, too. The results, shown in Figures 16 and 17, suggest that both elision and trimming can be helpful in providing adequate reductions. Trimming is directly effective at reducing height, especially when the elision is modest. Depth elision is most effective at reducing the number of nodes, particularly in larger diagrams, where it also can produce substantial height reductions. Function and file node elision are extremely effective at reducing the height of a star diagram. The effectiveness of these elisions is of course dependent on the programming style for the application; if these systems were coded with large functions and just a few files, function and file node elision would not have been as effective—nor as necessary. However, because of the wide range of elision options—including string pattern matching on node labels—programmers are able to fashion an elision effective for their application. Modest depth elisions on their own do not produce enough reduction to minimize scrolling and keep relevant data clustered together. Function and file nodes must be elided and aggressive depth elision of some kind is required. Such elisions will necessarily hide valuable information, requiring the programmer to frequently change the elision options to include the information required at the time. Despite the large reductions for temacs’s current buffer data structure, the second-smallest diagram is awkward to view and the smallest does not provide much information. The problem is that the diagram’s greatest branching occurs at the first level of children, largely due to the fact that the data structure being visualized is a record containing in excess of 40 fields. We believe that viewing such a star diagram can be eased by laying out the first-level nodes according to their class of access, such as placing all structure field accesses together, placing all function calls together, and so forth [Bowdidge 95]. Additionally, programmer-controlled node-stacking criteria could increase node stacking—such as stacking all printing operations together—to produce more compact diagrams [Bowdidge 95, Chen 96].

5 Software Design and Implementation Because the C star diagram planning tool is intended for use on large programs, scalability of performance is a major concern. Although exhausting main memory is an issue for some tools, this has not been a concern of this tool to date. However, the time for computing and drawing star diagrams was a bottleneck for the first prototype. For example, drawing a star diagram for current buffer in Emacs required 20 minutes or more on a 160MB Sparc 10/61, regardless of the amount of elision. Adding type star diagrams and more elision choices could only make performance worse, since star diagrams could be bigger and the tweaking of elisions would increase. Consequently, we undertook a redesign of the software in order to improve performance. The first version of the tool [Chen 96, Griswold et al. 96b] was prototyped in stages, yielding a pipeline architecture (See Figure 18). First the AST construction was implemented in C++, then a star diagram

21

control flow data flow mapping

Control

C source

AST

AST

Mapping 1

Internal C++ tree Visualized Star Diagram Star Diagram

Tk tree

Elided Star Diagram

Mapping 2 Mapping 2

Figure 18: The original, pipeline-oriented architecture, which implemented elision as deletion. Boxes are modules, dark edges are calls to invoke the stages in order, light edges are data flow, and dashed edges are pointers for mappings. constructor was written in C++. To allow displaying the diagrams, a Tcl/Tk interface was added [Ousterhout 94], and the Brighton tree widget [Brighton 93] was adopted for representation and drawing of the star diagram.4 At this point, there were two stages of star diagram construction—one in C++ called the internal star diagram and one in Tcl/Tk called the visualized star diagram—introducing some redundancy. This redundancy is difficult to avoid, since Tcl/Tk and its interface to C++ are not efficient enough to walk the relatively massive AST implemented in C++ and construct a visualized star diagram from scratch. On the other hand, writing the user interface in Tcl/Tk is very attractive because it is high-level and flexible. The next stage of implementation was to incorporate the depth elision and trimming features into the visualized star diagram. As before, these were added in a pipeline fashion. However, this stage was destructive to the visualized star diagram it consumed. In most instances this stage is relatively efficient, but it has the side-effect of requiring a new visualized star diagram to be built from the internal star diagram if further elisions or trims were made—a potentially costly recomputation. Finally, navigational capabilities were added to the tool, such as popping up text windows and grep-like windows from star node selections. These operations do not fit in a pipeline style. Consequently, mappings were added to keep track of the association between a visualized star diagram node and its internal star diagram node, and also from the internal star diagram node to its underlying AST nodes. To pop up a text window, then, the tool merely follows the pointers back to the AST, unparses the AST for the containing compilation unit, and adds highlighting to the text corresponding to the selected AST nodes. Although this design grew naturally out of the incremental prototyping process, it has serious shortcomings in both performance and extensibility. First, we were dissatisfied at an intuitive level that the design was a mixture of translation-oriented pipeline stages and navigation-oriented mappings. Second, adding more elision options through additional pipeline stages would be cumbersome and slow. The existing depth elision component could not be extended because it took advantage of the unique property that all nodes to the right of a depth-elided node are also elided. Third, because two star diagrams have to be built before any elision takes place, drawing a highly elided star diagram can take much more time than expected (although drawing time would be reduced to offset some of the loss). We want programmers to be playing around with elisions to get the precisely desired view. Because this implementation of elision is destructive to the visualized star diagram, this would cause the tool to reconstruct a full visualized star diagram from the internal star diagram before computing a new elision. 4

Although the star diagram as displayed is not a pure tree, the Brighton tree widget can represent some graph structures if used carefully.

22

Main 1

4 Visualized layer

Visualized Program

Visualized Tree

Visualized node To Internal node

2

5

7

ElisionTest

8 Internal layer Program

Internal Tree

3 Internal node To AST node

6 AST layer

AST

AST

Star diagram

Figure 19: The revised, layered architecture, which implemented elision as omission. Boxes are modules, edges are calls, and dashed edges are calls for mappings. The numbers on edges show calling order for the construction of a star diagram. Our intuition for resolving these problems was to restructure the system to a fully layered design. Layering itself, however, does not solve the problem of constructing two full star diagrams before elision could be undertaken. It is possible to optimize some elisions by recognizing when a new elision will only remove more nodes than the current one (i.e., eliding at a higher depth), but this is not always be the case. However, we realized that there is no reason to implement elision of the visualized star diagram as an activity of deleting nodes. Instead, the elided, visualized star diagram can be directly constructed from the internal star diagram, simply omitting nodes that are to be elided during the construction phase. With this approach reeliding does not require reconstructing a complete visualized star diagram, but only the parts the programmer wants. The programmer still has to wait for the initial complete construction of the internal star diagram, but a significant performance improvement is still possible by eliminating one full stage of star diagram construction. Moreover, because of the abstraction provided by the layered design, the internal diagram implementation could be changed to incrementally construct a star diagram without the knowledge of Tcl/Tk. The revised architecture is shown in Figure 19. We expected that the new design might run more slowly in some instances, since its more general elision mechanism can incur additional overhead whenever elision is being performed. However, we expected the new design to run more quickly on larger star diagrams, since the old design makes one additional pass to delete the star diagram nodes to be elided and memory hierarchy issues such as caching and paging might come into play. On the other hand, we were concerned that the internal star diagram construction time might still dominate, since it required a full traversal of the AST, which is much larger than a typical star diagram. To assess the performance differences between the two designs, we timed a variety of elisions on Omega’s Objects variable (Figure 20) and Emacs 19.28’s current buffer variable (Figure 21) on

23

7

Time (seconds)

6

5

4

Timing of old design Timing of new design

3

2 1 (160)

3 (196)

5 (239)

7 (269)

9 (283)

Depth (number of nodes)

Figure 20: Timings of elisions of Objects star diagram in Omega from depths 1 to 10. 30 28

Timing of old design Timing of new design

26 24

Time (minutes)

22 20 18 16 14 12 10 8 6 4 2 0 1 (367)

2 (564)

3 (836)

4 (1185)

5 (1591)

6 (1983)

7 (2325)

8 (2605)

Depth (number of nodes)

Figure 21: Timings of elisions of current buffer star diagram in Emacs from depths 1 to 8. the Sparc 10/61. On internal star diagrams that contain a few hundred nodes at their largest, the new design does typically run more slowly than the old one. However, on the diagrams that really matter—ones whose size runs into the thousands of nodes—the new design is much faster. The new design is faster on more aggressive elisions regardless of the size of the internal star diagram. Importantly, the new design’s running time decreases in relation to the number of nodes to be displayed, so that programmers are not forced to wait unexpectedly for the drawing of a small diagram.

6 User Interface Design Although our experiments on the first prototype yielded positive results in terms of how programmers exploited the basic functionality, we also observed a number of user interface problems that promoted frustration and other undesirable results [Cabaniss 97]. Most notably, as programmers progressed in their work, the number of windows on the screen grew, leading to confusion when trying to find an old window. Also, inconsistent placement and access to functionality in different windows caused delays. For example, depending on the window, it might be closed through a pull-down menu on the left or a button on the right. Some functionality and useful data—like the elision menu and the trimmed arm panel—were hidden in 24

pop-up panels that were sometimes forgotten or lost in the proliferation of windows (See Figure 4). Some functions were misinterpreted because of where they were placed in the tool or the way the functionality was named. On top of all this, we found programmers repeating simple actions such as selections in order to get real work done. Being aware that these problems would only get worse as larger tasks were undertaken and functionality was added to the tool [Nguyen 97], we decided to redesign the original user interface using standard HCI principles. We identified four basic principles seemed most relevant to our application.

6.1 Basic Principles and Their Initial Application The visibility principle: Relevant data and functionality ought to be as visible as possible to a tool user. This principle is predicated on the assumption that the human capacity for recognition is more powerful than recall [Preece 94, pp. 118–121]. Clearly, the programmers in our studies were suffering from severe visibility problems due to the proliferation of windows and hidden functionality. Unfortunately, much of the visibility problem is inherent in the task: the star diagram planning tool is intended to help programmers to discover relationships amongst syntactically separated pieces of code. Because there is no restructuring engine, the programmer must also revisit all these dispersed program locations in order to carry out the actual restructuring changes. Inevitably, visibility would have to play a big role in our redesign, although we felt we could only achieve limited success due to the global understanding required to restructure complicated programs. The consistency principle: Things that are similar should look similar, and things that are different should look different [Brown 88, pp. 9, 21–24, 32, 101][MacLennan 87]. When successfully applied, a user interface is easier to understand because the user can reliably respond to visual cues of similarity and difference in the interface, aiding recall and also the ability to guess how alien parts of the tool might behave when used. The challenge in applying the consistency principle lies in making decisions about what “typical” tool users will judge to be similar, and what they will judge to be different. We expected that our programmer studies and subsequent interactions with users would play a role in helping us make these decisions. The clear and concise language principle: Names or images given to functions and displayed data should be easy to understand without cluttering the interface [Brown 88, pp. 21–24]. Our first interface was cluttered with obscure language such as “elision” and “AST”, and many programmers found these terms unfamiliar and intimidating. The streamlined scenarios principle: Common patterns of work should be recognized and then streamlined so as to avoid awkward interactions such as repetitive clicking or keyboard entry [Carroll 91, pp. 293– 5].5 Repetition leads to fatigue, mistakes, wasted time, and forgetting of key information. However, streamlining should not lead to an increase in the number of ways to carry out a task, which can confuse the user. We first applied these principles by addressing the most egregious problems in the interface, such as adding a general source code searching capability and moving all the window-closing functionality into a button in the upper right-hand corner of every window and giving it the standard label Dismiss. However, we found few obvious changes to be made and the interface had not been substantially improved. Also, we 5 A scenario is an account of a sequence of events involving people and artifacts that perhaps has been generalized or simplified to expose important issues [Carroll 91, p. 81].

25

realized that even the simple change of fixing the window-closing functionality involved the complementary interaction of all four of the principles: the functionality was now always visible in a button, in a consistent location, with a clear, concise, and consistent name (for instance, “dismiss” does not have the fearful connotation of program termination or costly data loss). Moreover, it streamlined scenarios involving the closing of windows because programmers did not have to think about where the functionality was, nor did they have to open a menu to access it. Such complementary interactions are not prescribed by the principles themselves. These observations made us aware that we had to have a unifying concept of how to apply the principles.

6.2 Unifying Design Rules At about this time, we came upon the concepts of the engineering model and the user model [Gentner & Grudin 96]. An engineering model represents the way that a system engineer conceptualizes and realizes a tool. Consequently, a tool interface based on this model provides direct access to the functionality inside a tool, which makes it ideal for testing and other tool construction activities. However, it is ill-suited to those who do not understand the detailed workings of the tool, and its flexibility can allow uses of the tool that are not efficacious. In contrast, a user model represents the way that a user conceptualizes that tool, typically through the problem to be solved by the tool. An interface based on a user model provides a paradigm for accessing functionality and information that is natural to the user and the problem being solved. Moreover, mechanisms are typically put in place to prevent erroneous or damaging actions. These distinctions led us to realize that our initial design of the star diagram planning tool was based largely on the engineering model. Many interface decisions that we had taken as reasonable were in fact made because they aided debugging (e.g., machine addresses were displayed in the user interface) or they were easy to implement (e.g., Tcl/Tk’s mechanism for managing on-screen selections automatically deselects the item after it is used). This interface was appropriate during initial development, but now all interface design decisions needed to be reconsidered. We realized, however, that we lacked an operational definition for a user model that could be applied in our redesign effort. Consequently, we reexamined the basic HCI principles and the successful aspects in the redesign so far and derived a set of interface redesign principles for the tool. The contextual visibility rule: The tool’s functionality must be directly visible and placed with the data on which it operates. There are several immediate implications of this rule that can be applied as auxiliary rules. One, all functionality must be accessible directly through buttons or item selections, meaning that there should be no pop-up menus or dialogs. Two, pop-up dialogs must be tiled into the window on which the dialog operates or otherwise incorporated into its parent window. This rule addresses several visibility problems and also makes functionality easier to understand because operations can be defined concisely in terms of the surrounding context. At the same time, the number of windows that a programmer must manage are reduced. However, the buttons and tiling can take up extra screen space, which is a valuable resource in this application. To address this problem, we eliminated functionality that could be adequately provided by other tools. Also, we found that by locating functionality with its data, button names got shorter and some redundant functionality was eliminated. The scenario organization rule: The tool’s windows and subwindows must be laid out and linked according to common user scenarios. In particular, subwindows of a large window should be tiled left-toright, top-to-bottom in likely order of access. Moreover, selected items are to be retained across operations, and if an operation creates a new data item, that item becomes a current selection. Since functions are located with their data, each subwindow containing data can have its own current selection. Lastly, buttons 26

are also placed in scenario order, and button invocations that would cause an error given the current state of the tool are disabled and the button is grayed out.6 Highlighting available functionality can help guide the programmer through viable scenarios without the need for extra error windows popping up to signal failures. The window-stealing rule: Apply “window stealing” in a scenario-oriented fashion. Because windows easily proliferate in the star diagram planning tool compromising visibility by showing too much information, we felt it would be helpful if the tool automatically took back windows that are no longer needed—if the tool could reliably detect such windows without also taking away windows that the programmer wanted. Our decision was to preserve all windows that are created in a depth-first traversal of windows, such as navigating from a star diagram window to a node’s grep window and then from the grep window to a text window for the first item in the grep list. Thus, a “leaf” window can be easily viewed in the context in which it was derived—visibility is preserved. However, if the parent of a leaf window opens a new window, then the current leaf window is closed and the new one is put in its place. For example, when a grep window’s item selection is advanced to the next item, it opens a new text window to show the selection in context and closes the window for the previous item. This rule does not apply to star diagram windows, since typically few such windows exist and moving between them is not that common. Example: the star diagram window. As an example of the first two rules, consider the star diagram window. All functions except zooming are directly available through buttons,7 and each subwindow has a set of buttons that apply only to it. Exploiting the buttons’ context, many of their names were shortened without losing clarity. The subwindows are tiled in a scenario presentation: In reading from left-to-right, top-to-bottom, the programmer first encounters the elision window to easily observe what nodes are excluded from the diagram and can make changes as deemed necessary. (In fact, a star diagram initially displays only its root—that is, with full elision—in order to shorten initial drawing time. In an earlier design the star diagram started out empty but the elision window was still a hidden pop-up [Nguyen 97], which confused programmers.) Following the left-to-right, top-to-bottom ordering, the star diagram itself is next examined, and even the star diagram itself tends to be read left-to-right. Next is the trimmed nodes panel, which the programmer may now begin using as the star diagram becomes better understood. Finally, a status display is placed on the bottom of the window, where it is available if needed without intruding upon the programmer’s concentration.

6.3 Evaluation After completing our redesign, we were aware of some problems in the design—some minor, others of greater concern. We also were aware that programmers less familiar with the star diagram planning tool could have a different reaction to the redesigned tool than the its developers. Consequently, we repeated our earlier experiment with one additional programming team, team D. The experimental set-up was identical to the preceding experiment’s, except that type-based star diagrams and more flexible elision had been added to the tool (neither are really required for the study) and that the interface had been redesigned. Without engaging a large number of subjects, precisely correlating differences in behavior to any one of these changes is difficult. Also, the programmers’ task is somewhat narrow, limiting the conclusions we can draw about the effectiveness of our scenario streamlining. The conclusions reached here are the best 6

In the examples in Section 2, the buttons are typically not grayed to improve readability. It did not concern us that the Zoom button invokes a pop-up, because it is currently a rarely used feature and the amount of zooming is apparent from the star diagram itself. Zooming could be handled directly with increment/decrement buttons if redrawing were fast enough, but directly specifying the amount of desired zooming currently saves considerable time. 7

27

judgments of the authors based on the video and audio data, as well as the post-mortem interview, which was used to clarify issues not fully captured by direct means. Team D study chose a design approach based on retrieving and setting fields of Objects elements. This is roughly the design discussed by team B before they opted for the simpler whole-object operations. Although they did not explicitly acknowledge that this design would require some kind of refinement to permit moving Objects onto disk, they were aware that this was a naive object-oriented design somewhat necessitated by the time constraints of the study. It is unlikely that the difference in approach can be attributed to the revisions between the first and second prototype, since it falls within the spectrum of designs found amongst teams A, B, and C. Since this new team’s high-level behavior was not significantly different than those observed in the previous study, their lower level behavior can be fairly compared to the other teams. Problems encountered. Before the study began, we were concerned about having two Trim Arm buttons, as this partial redundancy is also something of an inconsistency if the context of the subwindows is not taken into account. According to context, the Trim Arm button in the star diagram subwindow would trim a selected arm, whereas the Trim Arm button in the trimmed star arms subwindow would trim the arm associated with an entry. In fact, this was a problem with team D only because of several bugs in the trimming interface led them to false conclusions about how to use it. Fortunately, they found a workaround to achieve their desired end. At this time, the only way we know to avoid this problem is to retitle the Trim Arm button in the trimmed star arms subwindow Trim Arm Associated with Entry, which violates the concise language principle. Another problem encountered was that team D became confused about how to construct a star diagram. After selecting a use of the Objects variable and then clicking Use Type of Var to select it as a root star diagram root, they expected some feedback from the tool, but got none because the project window was covered, where the selection is logged. The forwarding of data between windows violates our rule of locating functionality with the data, but we have not yet found a reasonable way of resolving this problem. Interestingly, this selection-forwarding feature is retained from the earlier version of the tool. However, it was also possible to create a star diagram from the text window in the previous tool. Finally, team D complained that the trimmed arms panel was too small (short), another problem we were aware of. This was a necessary consequence of tiling in the elision panel, which proved useful to the programmers. Other problems in the redesign that we had encountered informally, such as how to start up a project (programmers are confused by having to select both directories and files; a files-only interface would probably be better), were avoided in the study, and so cannot be assessed here. Successes. In general, we found that team D followed scenarios similar to those in the previous study, and consequently their work was streamlined (reduced number of clicks and window management) by retaining selections and the tiling layout. Only the star diagram creation scenario was impeded, as already described. Another success that we observed was that team D instinctively visited the project window whenever a question or problem arose. This first occurred when they wondered what Use Type of Var did in the text window, and they found that a star diagram entry had been created. The second occurred when they wanted to save out their results, which is indeed accomplished in the project window. In the earlier version of the tool, there was no project window, but rather four independent pop-ups that were hidden by default. The success of the project window can be attributed to the contextual visibility rule, which brought all the project operations together. The visibility of functionality facilitated experimentation and more sure problem-solving. For instance, team D tried a variety of elisions to get the kind of view they wanted. The other teams largely ignored 28

elision—which was hidden in a menu selection—even though the star diagram for Objects is large. Trouble-shooting by team D can be characterized as trying out various selections and buttons or checking the project window. The other teams’ trouble-shooting can be characterized as searching through menus. The programmers did not comment about the tool’s automatic reclamation of windows, so we believe that the window stealing rule is not detrimental, at least for the activity defined in the study. Finally, during the post-mortem interview, the programmers said that button-graying is preferable to error dialogs, even though the graying does not provide an explanation of why the functionality is not available. Conclusion. The design rules and their application had a significant effect on the C star diagram planning tool’s user interface, both visually and in low-level behavior. Most notably, the follow-up study revealed smoother work patterns than observed in our previous study. However, there remain a few places in the tool where we have not yet found a way to apply the rules. Due to the nature of the problem domain and some design decisions from the first prototype that proved difficult to change, these problems may be difficult to eradicate in the future. Another problem is that button-oriented and tiled interfaces do not always scale well as functionality is added. Adding a second row of buttons in some windows may not be a serious problem, but additional subwindows would cause problems. Indeed, there is already a need to shrink down or move the elision panel to make more room for the trimmed arms panel. Finally, we approached this redesign with an evolutionary attitude, so there are no doubt suboptimal aspects of the design that went unnoticed because we did not undertake a (costly) redesign from scratch.

7 Coping with a System Written in Multiple Languages Although one of the motivations for this work was the infeasibility of providing multi-language transformational support, we have not yet explored this issue. The current tool only supports planning on C code that has been processed by the C preprocessor, cpp. Based on our experience, we see two major possibilities for coping with the multi-language problem in the context of a star diagram planning tool. The solution preferred by programmers—but also the most expensive—is to provide a tool that can process and display code from a system written in multiple languages. Such a tool would contain multiple lexers and parsers, and would construct AST’s in multiple languages. The star diagram constructor would then be responsible for walking these differing AST’s to construct a multi-language star diagram. Presuming that the AST’s are all derived from the same basic AST infrastructure, the methods for traversal and querying would be consistent across all the languages. However, the star diagram tool would have to resolve issues regarding what is similar and what is different for the purposes of stacking. Even issues such as the naming of variables and procedures shared amongst the languages would need to be resolved by the AST mechanism or the star diagram constructor. At the other end of the spectrum, only the “main” language is supported, say C after processing by cpp. However, there are some mitigating features of this approach besides simplicity. First, the name of a source file (e.g., saadvn.i) provides a strong hint of the true source of the source code (saadvn.c). Even without tool support a programmer can open the original source with an editor and search for the related code using a number of informal but reliable cues. However, a tool can typically provide significant help in this regard. The processed file normally contains file and line directives (which are used, for example, by the compiler to report static semantic errors) that a star diagram tool can use to automatically navigate to the correct line in the original source code. For example, the following text on line 15 of file saadvn.c: #include "saadvn.h" 29

gets expanded to: # 14 "saadvn.c" 2 # 1 "saadvn.h" 1

extern int CondCommand(int); extern int CondValue(int); . . . Unfortunately, mapping to a particular expression on a line is not supported, although typical preprocessors maintain enough information that they could pass such information along if appropriately modified. In any event, a programmer typically has little problem in mapping between original and processed code on a line-by-line basis. Fortunately, tools such as lex, flex, yacc, and bison insert such file and line number information into their C output (again, for debugging purposes), meaning that these languages can be accommodated as well. User interface and database interface generators, as well as many homegrown language translators, also work on the principle of generating “native” code, and are amenable to such treatment. Unfortunately, extension interpreters such as Tcl/Tk and object code from other low level languages such as FORTRAN and assembly cannot be handled this way. In such cases, the programmer must use grep or related tools to search the “non-native” source for code related to a change being planned through the native code interface. Because of its simplicity, we have begun experimenting with the use of line directives in processed source to help programmers more easily navigate to the original source. At the time same, to avoid the preprocessor problem as much as possible, we will be applying simple parser techniques to permit the representation of most macros in an AST [Griswold et al. 96a].

8 Related Work Like many program visualization techniques, the star diagram attempts to graphically highlight relationships of interest for some task while hiding irrelevant details. In particular, to help a programmer design data abstractions in an existing program, the star diagram graphically displays both direct and indirect computations on a data structure and stacks similar computations, thereby eliding computations not related to the task. The star diagram differs from other structural visualization tools in the purpose of the visualization, the granularity at which the program is analyzed and presented, and the planning tasks the visualization can facilitate. For example, the Rigi reverse engineering environment provides semi-automatic construction of graphical views of a system to assist broad architectural understanding [Muller et al. 92]. A view based on entities such as functions, files, classes, or modules is initially constructed by the environment and then augmented by the programmer. The additions allow the programmer to describe the system in terms of abstractions not visible in the implementation. Such additions can be used to redevelop documentation or to plan changes that are related to managing these invisible abstractions, but there is no specific support for constructing change plans. In contrast to the star diagram’s visualization of low-level details relevant to a 30

particular task, Rigi’s general architectural support obligates it to provide a high-level “component” view of every computation in the system. However, hierarchical structure in the visualization can be used to collapse several nodes into a single node to provide more efficient use of space. The SeeSoft system supports visualizing a property (such as age) of each line of a program as a colored graphical line, thus allowing a programmer to identify patterns between lines in multiple files by comparing colors [Eick et al. 92]. Each file of source code in a program is represented as a long box, and each line of code is represented by a horizontal line of pixels in the box. The color of the pixels represents a value of a metric measured for that line, such as how recently the line was modified, whether a line is associated with a specific modification, or whether a given line is a member of a program slice [Weiser 84, Ball & Eick 94]. (Later enhancements to SeeSoft condense the view further by displaying one or more measurements per file, rather than per line.) Such visualizations can facilitate the planning of program modifications by highlighting historical or intrinsic structure that would be invisible otherwise. SeeSoft supports selective highlighting of lines falling within a specified range of values, which can be used to represent simple maintenance plans or structure identified by the programmer. SeeSoft’s use of graphics to represent file size, position to represent source-line location, and color to highlight source-line relationships obligates it to providing a (low-resolution) representation of every line in the system. However, representing lines of only certain colors perhaps could provide useful compressed views of a program.

9 Conclusion One way to lower the high cost of software maintenance is to restructure an existing system into a more maintainable form. Restructuring through a star diagram visualization can help a programmer understand and manipulate the overall structure of a program for the purpose of creating new data abstractions, thus localizing future changes and lowering maintenance costs. However, it is costly to provide scalable meaning-preserving transformation support. Moreover, a large system is likely developed in multiple languages, complicating the development of a comprehensive transformational tool. This led us to consider using the star diagram as a tool for assisting programmers in planning and carrying out their restructurings without transformational help. Providing such assistance required coping with shortcomings in our original approach to constructing and displaying star diagrams. A star diagram is difficult to assimilate when quite large. Moreover, we had observed programmers using restructuring transformation s merely to change the star diagram view for the recording of a design idea. We have demonstrated that three techniques can accommodate complexity and better facilitate planning when using the star diagram: Separation of planning from restructuring. The star diagram planning tool permits a programmer to plan a restructuring without actually modifying the source code. In particular, when a programmer identifies an abstraction with the star diagram, trimming and annotations can be used to emulate the Extract Function and Move into Interface transformations from the original star diagram tool. When the star diagram becomes empty, all uses of the data structure have been accounted for and the planning process is complete. The star diagram tool can then be used to execute actual restructuring commands or to guide a manual restructuring. Our programmer study indicates that the star diagram can assist planning, and that it is a versatile mechanism for planning, managing scale issues, and analyzing the correctness of changes. Moreover, this planning functionality can be used not only for assisting manual restructurings, but also for non-restructuring activities or as the interface for a transformation-based restructuring tool. Use of elision and trimming to reduce star diagram size. Our tool provides node elision and path trimming to manage a star diagram’s size. First, a programmer can indicate which nodes are not of interest 31

by choosing a set of identifying properties, thus reducing the length and height of the star diagram and helping the programmer to focus on the remaining nodes of interest. Second, a programmer can trim a node (and its associated paths) to remove uninteresting computations from the view, cutting down the height of the star diagram. Our programmer study also revealed that node trimming can be employed to look at an interesting subdiagram in isolation. Our measurements of elision and trimming in large C programs showed that a star diagram can be scaled down to an adequate level. Such scaling removes too much information, however, requiring the elision parameters to be frequently changed to keep the most relevant information visible. Support for multiple star diagram roots. To accommodate the needs of complex data structures, we customized the generic star diagram algorithm to support three different methods for creating a star diagram with a root containing multiple variables and expressions: selection of one or more variables, selection of all variables and expressions of a selected type, and the union of existing star diagrams. This feature allows all data elements of interest to be included in a star diagram for the purposes of planning. Moreover, new elements can be added to the root as they are discovered by unioning star diagrams together. The programmer study and scalability experiments on our first prototype not only informed us about the basic functionality of the star diagram planning tool concept, but also led us to redesign the software to improve performance and to redesign the interface to ease the programmer’s interaction with the tool. Architectural analysis on our first prototype revealed that a pipeline architecture was the cause of poor performance in the building of large star diagrams. Evolving the system to a layered design allowed exploiting the presence of a star diagram’s C++ representation to provide insertion-based elision, eliminating a construction stage whose performance is related to the number of elided nodes. Using classical HCI principles and concepts, we analyzed the work of programmers using our first prototype to better understand the problems they encountered. The concepts of the user and engineering models crystallized our thinking and guided us in the application-specific use of general HCI principles such as consistency and visibility, leading us to formulate a comprehensive set of redesign rules for the interface. A few observations from this work might serve as lessons to those developing other kinds of programming tools. First, providing general-purpose, highly automated source-code analysis or manipulation of an entire system is complicated by the need for scalable performance and multi-language support. Second, with or without the heavy automation, the ability of a tool to highlight program relationships for the programmer and to assist in recall of design information can be valuable. Third, the design of such a tool will likely have its own scale issues, especially in its interface to the programmer, since the programmer is bearing more responsibility in carrying out the work. Acknowledgments. We are especially grateful to the anonymous study subjects for their invaluable contributions to this paper. Thanks to Walter Korman for his detailed comments on an earlier draft of this paper.

References [Adams 80] S. Adams. Pirate’s adventure. Byte, 5(12):192–212, December 1980. [Atkinson & Griswold 96] D. C. Atkinson and W. G. Griswold. The design of whole-program analysis tools. In Proceedings of the 18th International Conference on Software Engineering, March 1996.

32

[Ball & Eick 94] T. Ball and S. G. Eick. Visualizing program slices. In Proceedings of the IEEE Symposium on Visual Languages, pages 288–295, October 1994. [Belady & Lehman 71] L. A. Belady and M. M. Lehman. Programming system dynamics or the metadynamics of systems in maintenance and growth. Research Report RC3546, IBM, 1971. Reprinted in M. M. Lehman, L. A. Belady, editors, Program Evolution: Processes of Software Change, Ch. 5, APIC Studies in Data Processing No. 27. Academic Press, London, 1985.

[Boehm 75] B. W. Boehm. The high cost of software. In E. Horowitz, editor, Practical Strategies for Developing Large Software Systems, pages 3–15. Addison-Wesley, Reading, MA, 1975. [Bowdidge & Griswold 94] R. W. Bowdidge and W. G. Griswold. Automated support for encapsulating abstract data types. In ACM SIGSOFT ’94 Symposium on the Foundations of Software Engineering, pages 97–110, December 1994. [Bowdidge & Griswold 97] R. W. Bowdidge and W. G. Griswold. How software tools organize programmer behavior during the task of data encapsulation. Empirical Software Engineering, ?(?), To Appear 1997. [Bowdidge 95] R. W. Bowdidge. Supporting the Restructuring of Data Abstractions through Manipulation of a Program Visualization. PhD dissertation, University of California, San Diego, Department of Computer Science & Engineering, November 1995. Technical Report CS95-457. [Brighton 93] A. Brighton. Tktree widget, Copyright 1993. Available by anonymous ftp at ftp.aud.alcatel.com/tcl/extensions/tree. [Brothers 89] L. Brothers. omega [A complex, rogue-like game of dungeon exploration], Copyright 1989. Available from: [email protected]. [Brown 88] C. M. Brown. Human-Computer Interface Design Guidelines. Ablex Publishing Corporation, Norwood, New Jersey, 1988. [Cabaniss 97] J. C. Cabaniss. Lessons learned from applying HCI techniques to the redesign of a user interface. Masters Thesis, University of California, San Diego, Department of Computer Science and Engineering, June 1997. Technical Report CS97-548. [Carroll 91] J. Carroll. Design Interaction: Psychology at the Human-Computer Interface. Ambridge University Press, New York, New York, 1991. [Chen 96] M. I. Chen. A tool for planning the restructuring of data abstractions in large systems. Masters Thesis, University of California, San Diego, Department of Computer Science and Engineering, February 1996. Technical Report CS96-472. [Eick et al. 92] S. C. Eick, J. L. Steffen, and J. E. E. Sumner. Seesoft—a tool for visualizing line-oriented software statistics. IEEE Transactions on Software Engineering, 18(11):957–68, November 1992. [Flor & Hutchins 91] N. V. Flor and E. L. Hutchins. Analyzing distributed cognition in software teams: A case study of team programming during perfective software maintenance. In J. KoenemannBelliveau, T. G. Moher, and S. P. Robertson, editors, Empirical Studies of Programmers: Fourth Workshop, pages 36–64. Ablex, Norwood, NJ, 1991. [Flor 94] N. Flor. Dynamic Organization in Multi-Agent Distributed Cognitive Systems. PhD dissertation, Cognitive Science Department, University of California, San Diego, 1994. 33

[Gentner & Grudin 96] D. R. Gentner and J. Grudin. Design models for computer-human interaction. IEEE Computer, 29(6):28–35, 1996. [Griswold & Notkin 93] W. G. Griswold and D. Notkin. Automated assistance for program restructuring. ACM Transactions on Software Engineering and Methodology, 2(3):228–269, July 1993. [Griswold 91] W. G. Griswold. Program Restructuring as an Aid to Software Maintenance. PhD dissertation, University of Washington, Dept. of Computer Science & Engineering, August 1991. Technical Report No. 91-08-04. [Griswold et al. 96a] W. G. Griswold, D. C. Atkinson, and C. McCurdy. Fast, flexible syntactic pattern matching and processing. In Proceeedings of the IEEE 1996 Workshop on Program Comprehension, March 1996. [Griswold et al. 96b] W. G. Griswold, M. I. Chen, R. W. Bowdidge, and J. D. Morgenthaler. Tool support for planning the restructuring of data abstractions in large systems. In ACM SIGSOFT ’96 Symposium on the Foundations of Software Engineering, October 1996. [Johnson & Opdyke 93] R. E. Johnson and W. F. Opdyke. Refactoring and Aggregation. In Object Technologies for Advanced Software, volume 742 of Lecture Notes in Computer Science, pages 264–278. First JSSST International Symposium, November 1993. [Knight & Myers 93] J. C. Knight and E. A. Myers. An improved inspection technique. Communications of the ACM, 36(11):51–61, November 1993. [Lientz & Swanson 80] B. Lientz and E. Swanson. Software Maintenance Management: A Study of the Maintenance of Computer Application Software in 487 Data Processing Organizations. AddisonWesley, Reading, MA, 1980. [Liskov & Zilles 74] B. Liskov and S. Zilles. Programming with abstract data types. In ACM SIGPLAN Symposium on Very High Level Languages, pages 50–59, March 1974. SIGPLAN Notices 9(4). [MacLennan 87] B. J. MacLennan. Principles of Programming Languages: Design, Evaluation, and Implementation. Holt, Rinehart, and Winston, New York, 2nd edition, 1987. [Miyake 86] N. Miyake. Constructive interaction and the iterative process of understanding. Cognitive Science, 10(2):151–177, 1986. [Moen 90] S. Moen. Drawing dynamic trees. IEEE Software, 7(4):21–8, July 1990. [Morgenthaler 97] J. D. Morgenthaler. Static Analysis for a Software Transformation Tool. PhD dissertation, University of California, San Diego, Department of Computer Science & Engineering, August 1997. [Muller et al. 92] H. A. Muller, S. R. Tilley, M. A. Orgun, B. D. Corrie, and N. H. Madhavji. A reverse engineering environment based on spatial and visual software interconnection models. In Proceedings of the SIGSOFT ’92 Fifth Symposium on Software Development Environments, December 1992. [Nguyen 97] V. B. Nguyen. Impact of adding customizability on software architecture: A case study. Masters Thesis, University of California, San Diego, Department of Computer Science and Engineering, March 1997. Technical Report CS97-523.

34

[Opdyke 92] W. F. Opdyke. Refactoring: A Program Restructuring Aid in Designing Object-Oriented Applications Frameworks. PhD dissertation, University of Illinois at Urbana-Champaign, Dept. of Computer Science, 1992. Technical Report No. 1759. [Ousterhout 94] J. K. Ousterhout, editor. Tcl and the Tk Toolkit. Addison-Wesley, Reading, MA, 1994. [Parnas 72] D. L. Parnas. On the criteria to be used in decomposing systems into modules. Communications of the ACM, 15(12):1053–1058, December 1972. [Preece 94] J. Preece. Human Computer Interaction. Addison-Wesley Publishing Company, Menlo Park, California, 1994. [Stallman 93] R. Stallman. GNU Emacs (The extensible self-documenting text editor). Free Software Foundation, Cambridge, MA., November 1993. Available by anonymous ftp at prep.ai.mit.edu/pub/gnu. [Weick 68] K. E. Weick. Systematic observational methods. In G. Lindzey and E. Aronson, editors, The Handbook of Social Psychology, pages 357–451. Addison-Wesley, Reading, MA, 1968. [Weiser 84] M. Weiser. Program slicing. IEEE Transactions on Software Engineering, SE-10(4):352–357, July 1984. [Wildman 95] D. Wildman. Getting the most from paired-user testing. ACM Interactions, 2(3):21–27, 1995.

35

Suggest Documents