Chapter 4
Design Knowledge
The previous chapters have spelt out some of the challenges associated with design in general, as well as with designing software, and from this point we focus upon the latter task. Clearly, there are people who can very ably and successfully juggle the many factors involved in designing software, and in this chapter we look at what it is that characterises expert designers, the designs that they produce, and the nature of their expert knowledge about designing.
4.1What do expert software designers do?
When learning any skill, it is useful to analyse what the experts do (and why they do things as they do). Studying the experts might (will) not instantly turn anyone into a tournament-winning tennis player, or an award-winning artist, but it will almost certainly be a good way to improve one's skills.
In their witty little book Software Design Decoded: 66 Ways Experts Think, Marian Petre and André van der Hoek (2016) provide just such a study of software designers. Their book briefly illustrates and explains some of the practices that expert software designers are observed to employ. Of course this is only half of the story. It isn't just what an expert does that matters, it is what they don't do that may be just as important. (And particularly, the sort of things that novices will probably do, and that experts will avoid!)
So it is worth considering here something of what is known about expert software designers, not least because it reinforces some of the issues described in the preceding chapters. It also recognises that designing software is a social process as well as a technical one (Shapiro 1997). What follows is by no means everything that is known about expert designers, but we identify some significant studies (many of which have been conducted by psychologists rather than software engineers) and extract some of the key concepts and terms that emerged from these.
One of the earliest studies of how people set about the task of designing software was undertaken by Adelson & Soloway (1985). They observed how both experienced and inexperienced designers set about the task of designing solutions when the problem set before them was:
familiar to the participant; or
unfamiliar in detail, but set in a context/domain that was familiar to the participant; or
unfamiliar in both senses.
They had a small number of participants, and in many ways what they termed ‘experiments’ might be better categorised as ‘controlled observational studies’. However, some key observations that were produced from this work are ones that have also been noted in later studies of the ways that designers work, and those related to expert behaviour are summarised in Table 4.1.
Observed Behaviour |
Purpose |
|
---|---|---|
The designer forms an abstract mental model for their design. |
‘Mental execution’ of the ‘design model’ assists the designer with simulating the dynamic behaviour of the eventual system. |
|
The model is systematically expanded, keeping all elements of the design at the same level of detail. |
Assists with the task of simulation, which itself becomes more detailed as the design model evolves. |
|
Constraints on the problem/solution are made as explicit as possible. |
Bounds the ‘decision space’ when addressing an unfamiliar problem. |
|
Reuse of previous design plans. (‘Labels for plans’) |
Occurs when the designer recognises that part of the design task can make use of a previous solution or part-solution. This is then ‘labelled’ (as a plan for how this will be resolved in the future) and the designer moves on to focus on less familiar aspects. |
|
Making of notes about future intentions. |
Aiding systematic expansion of the design model. |
Simulating the ‘execution’ of a design model in one's head is a concept familiar to programmers, who often mentally trace through the execution of code as a check on the formulation of such constructs as conditional expressions. Arguably this is something that is particular to designing software—whereas such activities as note-making and reuse are design activities widely observed in other domains too. As we will see later, the idea of ‘labelling’ previously used plans is one that also resonates with the idea of the design pattern, described in Chapter 15.
An observational study by Bill Curtis and his co-workers examined the characteristics of a set of designers who had been identified by their organisations as being particularly successful (Curtis, Krasner & Iscoe 1988). They observed that the number of exceptional designers identified in this way was small—and hence they were usually treated as an important resource within an organisation. Interestingly though, the outstanding designers were often not considered to possess particularly good programming skills, suggesting that programming and design may call upon different abilities in an individual. Table 4.2 summarises three key findings from this study.
Observed Behaviour |
Purpose |
---|---|
Familiarity with the application domain. |
Making it possible to map between problem characteristics and solution structures with ease. |
Skill in communicating technical vision to other project members. |
Much of the design work was often accomplished while interacting with others. |
Identification with project performance. |
Achievement of technical progress—the designer might even take on significant management responsibilities to ensure this. |
In studying the ways that designers make decisions, Visser & Hoc (1990) used the term opportunistic to describe the way that an expert designer would revise their strategy for ‘solving’ a design problem. When working opportunistically, although a designer may have chosen a strategy for how they are going to develop their design ideas such as ‘top-down’ (systematically decomposing the solution elements into smaller actions), they may still deviate from the strategy to either:
postpone a decision, perhaps because the information needed to make this has not yet been resolved; or
process information that is readily at hand, and can be used to define design elements in anticipation of the way that the design will evolve.
The term ‘opportunistic’ was originally introduced by Hayes-Roth & Hayes-Roth (1979) in the context of planning (an activity which can be considered as a form of design, as we noted earlier). It stems from the ill-structured aspects of design, in that, as a solution to a problem (i.e. a design model) emerges, and creates a correspondingly better understanding of the problem, this improved knowledge may well lead the designer to modify their strategy for solving the problem. So this can also be considered as a form of feed-back mechanism that occurs during the design process.

Skill in communicating technical vision
What else drives such opportunistic behaviour? Well, Akin (1990) has suggested that there are three ‘classic’ actions that occur during creative activities such as design. These are:
The recognition step, sometimes termed as the ‘Aha!’ response. This arises when the designer recognises a solution that has been there all along. As a creative act, it typically occurs when a designer realises that there is a simpler way of doing something than the model that they have been employing.
The problem restructuring step, in which a change in the way that a problem or design solution is being described or modelled leads to a major advance towards finding a design solution. In many ways this is similar to the idea of refactoring that we will discuss later in the context of agile design. As a simple example, a designer might be planning to store some of the key information for an application in a relational database, but then realises that managing the information within a set of objects will greatly simplify all of the associated operations.
The development of procedural knowledge about how a particular type of problem is best addressed, allowing the designer to perform many creative acts within a domain. One way that this is manifested in software engineering is through the use of patterns to provide reusable solution models.
All three actions involve recognising that a different (and better) way to address the current problem can be achieved by changing the way that it is viewed or modelled. All are related to the concept of ‘design as adaptation’, drawing upon the experiences of the designer as well as of others.
An observational mode of study was also employed for the project ‘Studying Professional Software Designers’, which is summarised in the book Software Designers in Action (van der Hoek & Petre 2014). Three video recordings of pairs of software designers who all addressed the same design task provided the basis for a number of different analyses, using a range of perspectives. This study also provided some of the underpinnings for the later book by Petre & van der Hoek (2016) that was mentioned at the beginning of this section.
It is useful here to look at three of the 66 characteristics of ‘expertness’ identified in the latter, where these identify some characteristics that have only partly emerged so far, and that are very important to the later material covered in this book. They are also ones where expert behaviour may particularly be likely to differ from the behaviour of inexperienced designers. Each characteristic has been highlighted by placing it in a box, a format that we will also use in later chapters. The numbers identify their position in the 66 characteristics. (The discussions provided here are formulated in a rather different way to those provided in their book, although they contain the same essential messages.)

Experts prefer simple solutions
Given a choice between doing something in a simple way and doing it in a more complicated way, the expert will almost always choose the former. In contrast, an inexperienced designer is more likely to choose a solution that involves bells & whistles and incorporates features that ‘may be useful in the future’, even though (or perhaps because) their vision of what that future may be is less clear than that of the expert.
What this really means is that experts sketch—on paper, whiteboards or any other readily available surface (paper napkins, backs of envelopes etc.). While the sketches may well be simple ‘box and line’ diagrams, sketching can also involve making lists, drawing tables, or a mix of the three. Externalising their ideas in this way helps the expert think them through (including use of ‘mental execution’ at a relatively abstract level), and adapt them. Inexperienced designers appear to be more reluctant to explore different ideas once they have a design model that they think looks feasible.

Experts externalise their thoughts
This partly relates to the previous item. Essentially, what this says is that experts are not constrained to using particular notations. Part of this book is about the variety of notations used to describe software models, and about their syntax and semantics. However, while these may be (and are) useful for documenting and sharing design models, they are not always so useful for developing them, and over-attention to syntax may constrain thinking. Design sketches can take all sorts of forms (and do so) and the less experienced designer should not feel bound to keep too closely to the syntax of established notations, even when following a plan-based design strategy.
In the rest of this chapter we briefly look at the sort of experiences and mechanisms that help build up design expertise. One thing that this book definitely can't do is teach anyone how to be a great designer. That only comes from experience together with understanding. And so one thing that this book can do is to help the reader to acquire a deeper understanding of the processes involved in software design. First though, we consider a bit more fully just what the factors might be that determine how ‘good’ we consider a particular design model to be.
4.2Some software design principles
When considering the design choices involved in developing a design model for a software application, what are the desirable characteristics that might make us consider a particular design model as being ‘good’?
Most of us will readily accept that the software used for controlling an aircraft needs to be carefully designed and rigorously tested—particularly if we think that we might be a passenger on that aircraft one day. Yet good design matters for smaller and less obviously safety-critical things too, not least because the end-users expect efficiency (whatever that means) and reliability. We expect our software tools to behave reliably and consistently—if our bank's software systems lose an on-line transaction involving a payment into our account, it may not be life-threatening, but it can still significantly disrupt our lives. The associated non-functional properties are often referred to as the ‘ilities’, and form yet another perspective upon the complex process of addressing ISPs. And they can be much harder to assess from the design model than functionality itself (since we can assess this by use of mental execution of the model).
One way of trying to ensure that our design model will be reliable, consistent, robust, and maintainable, is to be guided by some of the basic principles that have emerged over the years. Although conformance to these cannot ensure that we have a sound design model, non-conformance will almost certainly mean that we don't have one! We outline some important ones below and will return to them at various points in the following chapters. All of them relate to the characteristics of the ‘design model’ itself.
4.2.1Fitness for purpose
This should be considered as the ‘ultimate’ requirement for any design model—and as a principle, it is one that comes close to providing the ultimate test for an ISP. However elegant or efficient a design model may be, the two basic needs are that the eventual system should work reliably, and that it should perform the required task. That is not to say that other factors are not important, but simply that they are subordinate to the need to create a software application that does the job required of it.
Needless to say, this is an easier goal to specify than to achieve. The nature of ISPs makes it difficult to obtain a definitive specification of purpose or a test that demonstrates that it is achieved. However, what this principle really means is that the designer should not get diverted into providing unnecessary ‘bells and whistles’ and should focus upon the essence of what a piece of software should do. (Remember too that “experts prefer simple solutions”.) The wonderful pliability of software makes it easy to add features that are not really necessary, and the temptation to do so needs to be avoided. So, what this principle gives us is a ‘litmus test’ for assessing a design model, in that we can look at each of its features and structures and ask if it is really essential and what it contributes to this principle.
If we ask why this should be so, the answer is very simple. Anything additional to what is needed for the core purpose of the software adds potential technical debt by incurring unnecessary complexity.
4.2.2Separation of concerns
Most of us are familiar with the concept of the Swiss Army Knife as a tool that can be used to do many things (almost unlimited things if we have a really grand model). However, although a single knife can be used to do many things, each component of the knife has been designed to do one thing, whether it be cutting, filing, sawing etc. And this principle of having each element “do one thing and do it well” also applies to the design of software.
What this means for software is that a design model should clearly separate different elements. Input, output, data storage, handling user interaction etc. are all quite distinct tasks. And this is where the flexibility and pliability of software (Brooks 1987) can tempt the designer to cut corners and combine different things in the elements of the model. When writing software in this way, we refer to the end result as “spaghetti code”, implying complexity (rather than edibility) and hence longer-term problems of technical debt, to say nothing of shorter-term problems with testing. The same issues are true for design—clear separation of concerns can greatly assist with later changes and updates to an application, as well as making for more modular testing.

Separation of concerns
4.2.3Minimum coupling
When constructing anything but the smallest software applications, we need to adopt some form of modularity for its organisation, whether the modules concerned are methods (sub-programs), classes, processes or some other form. Indeed, the choice of these may well relate to the previous principle regarding the separation of concerns. And since these modules are part of the whole, they need to interact and share information in some manner. This inter-module dependency is usually termed coupling, a concept that was first recognised in the early 1970s when the modules concerned were most likely to be sub-programs (Stevens, Myers & Constantine 1974). And as new forms of modular structure for software have emerged, it is one that has continued to be relevant.
Coupling can take a range of forms. For example, module A may make use of operations, data or data types that are provided by module B. And it can occur in different ways too. Module A may invoke methods in Module B, or obtain values of variables it contains, or even inherit properties from it. The presence of any of these forms will create a dependency of A upon B, and what it particularly means is that if any changes are made to module B, they may potentially affect module A. Some coupling is of course necessary for the construction of any software system—the point is that this should be as much as is needed and no more—since unnecessary coupling can add to technical debt, complicating future evolution of an application.
Excessive or unnecessary coupling may well indicate a poor choice of modular structure. If the concerns are well separated, the dependency of a module upon other modules should be minimal, and in particular, should not involve requiring knowledge about how the other modules perform their tasks.
4.2.4Maximum cohesion
The concept of cohesion is concerned with intra-module relationships. The issue here is that the elements grouped within a module should all share the characteristic of being related to the purpose of the module. For example, if we have a class that consists of a data structure representing some part of an application, all of the other elements of the class should be concerned with updating and reporting on that data structure. Cohesion is conceptually fairly straightforward, but can be quite difficult to assess.
4.2.5Information hiding
This principle is also related to the way that modularity is achieved within an application, but incorporates additional notions about how information should be organised and stored within an application. It is particularly relevant to thinking about objects and in this context it is often referred to as encapsulation.
The basic concept is concerned with knowledge about the detailed form in which data is represented and stored. The aim is to keep this knowledge local to a module, so that it is not visible outside of a particular module (Parnas 1972, Parnas 1979). Like cohesion it is concerned with intra-module properties, and its use requires that a module should provide a number of methods that can be used to access the data, where these are used to ‘hide’ the way that the data is organised internally. Again, the motivation for this is to enable and simplify subsequent changes to an application.
4.3The evolution of design ideas
Given that we have some ideas about the characteristics of an effective and even great designer, together with some ideas about what they are trying to achieve, this then raises the question of what knowledge does a software designer need to acquire?
Long before software was thought of, people probably began learning their design skills primarily through some form of ‘apprenticeship’, and many would then continue to learn from sharing their experiences with their peers. There is still quite a strong element of this in many creative disciplines, and indeed, the concept of the ‘design studio’ has been successfully adapted for teaching about software development (Tomayko 1996).
We can reasonably assume that the craftsmen who designed the pyramids of Egypt and those who created Europe's medieval cathedrals, will have exchanged ideas with, and learned from, their peers. However, each resulting building was a single unique creation, and while new ideas did emerge, such as the use of ribbed vaulting or flying buttresses, these were more likely to relate to the design product rather than to the process of learning about designing. It was only with the emergence of what we would now consider as engineering practices that new contributions to the way that people learn about designing really began to occur. Two of these are worth noting here as being particularly relevant to software design.
Knowledge gained from scientific research. As the properties of materials became better understood, and the means of predicting their behaviour became more refined and dependable, so designers could utilise such knowledge to help create new structures. It is interesting to note that in a period when the distinctions between the professions were less rigid (and probably not even recognised) Sir Christopher Wren, the designer of some of London's most iconic churches, including St Paul's Cathedral, began his career as an astronomer and was regarded as an outstanding mathematician and geometer.
The concept of reuse. While ideas were certainly reused before the industrial revolution, the idea of fabricating structures by using ‘standardised’ components was not. The availability of reusable components both extends and constrains a designer's repertoire. Their use may reduce the design task as well as offering speedier development and time to market and reducing costs—while at the same time introducing new trade-offs into the design process. In particular, the opportunity to reuse things raises the question as to when it would be better to ‘buy-in’ existing elements and adapt one's design around these, which introduces a dependency upon a supplier, or to adopt a policy of ‘build-your-own’? There is also the related question of how to document and catalogue components. Two of the greatest designers of the past maintained records in the form of ‘sketchbooks’ (Leonardo da Vinci) and the engineer's ‘commonplace book’ (Isambard Kingdom Brunel). These both formed an aid to their own thinking and also provided a ‘pattern book’ that could help convey their ideas to others.
Both scientific knowledge and reuse have had an influence upon ideas about software design as will emerge in later chapters. In the 1970s and 1980s, it was expected that using the power of mathematics could enhance the process of software design, largely by providing rigorous specification through the use of ‘formal methods’ (Shapiro 1997). We will examine this further later, but apart from fairly specialised areas such as safety-critical systems, mathematical forms of modelling have probably had much less impact than expected. (However, as we will see in the next chapter, scientific knowledge of a different form is playing an increasing role in software development as a whole. The development of rigorous empirical studies is increasingly providing insight into design activities.)
The concept of reuse has been much more influential and has provided software developers with an increasingly large palette of options as well as a range of forms of reusable components that have influenced design ideas and also design products. Concepts related to different forms of reuse such as patterns, software product lines and components have all enriched the design process itself, playing an important role in ‘design as adaptation’ alongside a designer's own experiences.
There are probably many reasons why these two practices have had rather different degrees of influence on thinking about software design. One possible reason is that they embody (or at least support) different philosophies. Mathematical forms and scientific models (as we have already observed) are essentially associated with reductionist thinking, commonly associated with WSPs. While that is not wholly incompatible with designing software applications, it is likely to be more successful where the requirements for such applications are very well understood. They also require an element of formal mathematical understanding for their application. On the other hand, reuse lends itself to a more compositional way of thinking, in which we build up a solution from parts, and hence is intrinsically more suited for use with ISPs. It certainly fits better with the idea of an opportunistic design strategy, as used by agile approaches to software development, and as such, is probably much more widely applicable to software development.
Finally, a useful concept first posited by Herb Simon is that of satisficing. This can be summarised as “seeking a satisfactory solution to a problem rather than the optimal one” (van Vliet & Tang 2016). Satisficing is often observed as occurring during design activities and forms an important decision-making strategy. It may be employed for a variety of reasons: a designer may have only limited time to work on an issue; there is a lack of information needed to make an optimal decision; or the designer is trying to simplify a cognitively complex issue. In essence, the use of satisficing often involves taking the first solution that ‘fits’ a need and can be considered ‘good enough’. It may involve the use of analogy with other software applications to help make a decision, although as van Vliet and Tang observe, the risk is that “the context is different from what one is used to”.
4.4The nature of expert design knowledge
From the material of the preceding sections, we can see that a designer clearly needs to acquire quite a lot of rather complex knowledge, both about how to proceed with developing a design, and also about how to model their ideas. Since much of the content of Parts II and III is concerned with describing ways of sharing design knowledge and ideas, it is important to understand something of the challenges such sharing poses, and the associated limitations in what can be done.
Indeed, while the idea of being able to transfer design knowledge from experts to less experienced designers is an attractive one, when we look at the nature of design knowledge, the reality involved in doing it is rather more complex. To begin with, an expert designer may well be unable to explain why they know how to do something. As an example, someone who is an expert in graphic design may know which typefaces can be most effectively used for a particular type of document, but be unable to provide a rationalised argument as to why this combination works well.
Such design heuristics or ‘rules of thumb’ are no less effective or less valid for the lack of an explanation. Indeed the tacit knowledge that underpins them may be based upon such deep and complex cognitive issues that any explanation will be of limited general usefulness. When studying how people design software, Vessey & Conger (1994) observed that “examining expert problem solving can be quite difficult, since experts automate their problem-solving processes to the point at which they are no longer able to articulate what they are doing”. Unfortunately, without some understanding of why particular solution strategies work—one of the characteristics that distinguishes an engineering discipline from a craft—a heuristic may be limited in usefulness. In a domain where the problems are relatively repetitive (something that characterises many crafts), this may not be too critical an issue. However, in software development, although there may be common elements between different applications, the requirements that they address are usually quite distinctive.
An obvious, and related, question is how do designers ‘store’ their expertise in their brains? While this is really a question for which any serious answer must fall well beyond the scope of this book, there has been research that examines how software designers organise their design knowledge. Clearly this is not something that can readily be observed, but in Détienne (2002), the author uses the term schema to describe an important and rather abstract form of design knowledge.
There are different forms of schema, and in this model, programming expertise is partly based upon the possession of a set of constructional schemas, which may be further reinforced by a set of domain-specific schemas that help map and adapt the programming schemas on to certain types of problems. So implicitly, in addressing a design problem, an expert may well make use of a number of such schemas, where these address different facets of the problem.

Experts develop knowledge schemas
Not only does the transfer of knowledge pose a challenge, the quality of much of our knowledge about how to design software may also be rather mixed. The concept of shells of knowledge proposed by Brooks (1988), provides a useful way of categorising the confidence that we might be able to place in specific pieces of design knowledge, using the following three classifications.
Findings, or well-established scientific truths, with their value being measured in forms that correspond to the rigour with which they have been established. In terms of empirical software engineering concepts, these would most likely correspond to findings from a systematic review (Kitchenham, Budgen & Brereton 2015).
Observations, that report on actual phenomena, with their value being determined by the degree of ‘interestedness’ that they possess for us. Again, in terms of empirical studies, these would be the outcomes from a ‘good’ primary study, whether experimental or observational in nature.
Rules of Thumb, which consist of generalisations that are ‘signed’ by the author, but not necessarily supported by data, with their value being determined by their usefulness. These might well be found in ‘experience reports’—lacking empirical rigour, but possibly providing useful, if informal, analysis of experiences.
Advances in empirical software engineering have improved our knowledge about many aspects of software engineering in the period since Brooks proposed this model, and these are reviewed in the next chapter. In a paper by le Goues, Jaspan, Ozkaya, Shaw & Stolee (2018) the authors offer a more detailed set of categories for the quality of evidence and discuss how these map on to this smaller set proposed by Brooks. However, regardless of how we categorise software engineering knowledge, the quality of much of our knowledge about designing software largely falls into the categories of observations and rules of thumb.
So, given that design knowledge is difficult to codify, and its quality is mixed, this emphasises why expert knowledge about how to design software systems can be difficult to share with others. From the 1970s onwards, various mechanisms have been developed to assist with this, starting with plan-driven design methods, and going on through such ideas as software architecture, agile methods and design patterns. In essence, these provide ways of translating expert design schemas into some ‘rules’ that can be used when designing software intended to meet specific purposes. While they may be relatively crude, relative to the subtlety of the cognitive knowledge involved, they have nonetheless shown themselves to be useful, and we will be looking more closely at them in Parts II and III of this book.
Key take-home points about design knowledge
This chapter has examined some of the characteristics of expert software designers and of the ways that they design software systems.
Experienced designers use abstract mental models. An important role for these models is to allow the designer to mentally ‘simulate’ the way that a system will behave in particular conditions. This characteristic is one that is largely peculiar to software design, since the ‘product+process’ nature of software forms one of its distinctive features.
Designers often work opportunistically. Rather than following some fixed set of procedures to develop their design model (as advocated by plan-driven design approaches), experienced designers will employ a general strategy but adapt it as their understanding of the problem (and solution) evolves.
Designers sketch their ideas. Design ideas are fluid and experts like to concentrate on abstractions which can easily be revised to allow ready exploration of their models for a solution. Using informal notations to aid the development of a design model helps with this by avoiding the constraints of a fixed syntax and semantics.
Design principles embody accumulated design experience and provide a set of criteria for assessing the quality of a design model.
Designers reuse. This may occur at the abstract level, where designers reuse part-solutions that they previously found to be successful, and also at the level of reusing actual software components that are known to work and be trusted, restructuring and adapting their design plans to accommodate these where appropriate.
Design decisions may rely upon satisficing—whereby the designer opts to use a ‘good enough’ solution to a need, rather than expending time and effort to try and find an optimum solution.
It may be difficult to articulate and codify design knowledge. So far, everything described in this book emphasises that design cannot be performed by ‘following a procedure’. Indeed, successful designers may find it difficult to explain why they make particular decisions, since their expertise is encapsulated in the form of a set of schemas rather than procedural rules.