Pragmatic Program Design

1 05 2009

You are asked to create the architecture or design of a program/subsystem/component. What is the most practical, timely, value-adding approach to do it? While this article doesn’t definitively answer this question, it attempts to get as close as possible to doing so.

Any program design effort is primarily concerned with organizing program logic (in the form of code) into what can be called “program units” such as functions, methods, or classes; and on a higher level, components and subsystems. But not only that. Design is also concerned with making implementing and changing program logic an easy job that can be done efficiently.  That means with minimum effort and in minimum time by the developer.

The beauty of defining program design as the act of organizing program logic is its simplicity and fundamentalism. Think about it. When you decide, for example, to put a specific piece of code in a class method, you’ve made a decision about organizing a piece of program logic. You’ve placed it somewhere and given it a name.

Now, with that notion in mind, how can you differentiate a good design from a bad one?

The Code is the Ultimate Judge

For me, the Code is what has the final say on the quality of a design. Design exists as an activity to make code readable… understandable… reusable… and most importantly, to make code easily accomodate foreseen change. If the code exhibits these traits, then it can only be the result of good deliberate design.

Writing code that has these good traits depends on many things such as the support of the programming language itself for code organization, the complexity of the problem that is being solved by the program, the foreseen changes that are possible or likely to happen in the program, and the developer’s lust for neatly written code.

As an example on programming language support, Object Oriented Design was a remarkable step towards enabling good program design. OOD came to the rescue after Procedure Oriented Design failed epically with the increase in code size and complexity. OOD provided ideas for better organization of program logic. Instead of functions, procedures, there are classes, methods, and packages.

All OOD’s foundational pillars: Encapsulation, Inheritance, Polymorphism, were geared toward helping making code more understandable, reusable, and elegantly capable of accomodating change… in other words, OOD enabled better designs by promoting better code organization.

Pragmatic Program Design (PPD)

Since program logic is materialized in the form of code, PPD is a bottom-up approach that essentially starts with writing code, not drawing diagrams. I claim that to be able to elegantly design a program, you have to implement it first. This might seem like a silly riddle, but let me show you why this approach has far more common sense into it than the usual “model first” approach.

Traditional approaches encourage addressing all aspects of the program in the initial design effort, perhaps even from different views (usually a variation on the 4+1 Architectural Views). This is evident in a number of software processes such as the Rational Unified Process and others.

You start by component modelling: creating a set of components and distributing program responsibilities among them. You first “somehow” understand what the responsibilities are, and then create components to carry them out. You end up with a document or a diagram that says Component A does X and Component B does Y.

In order to model components, 99% of the time designers would just use their imagination and prior experience to gain an initial understanding of the different program responsibilities. They imagine the steps that will be taken by the program to fulfill a specific required Use Case. Designers start by the important Use Cases and imagine the happiest possible scenarios: the ones where everything goes right.

Armed with fresh understanding, designers rush to their modeling tool to diagram components and their relationships and interactions.

Big mistake.

The biggest fault done here is abstracting too early. Abstractions as high-level as architectural components are supposed to be long-lasting and stable. They must not be based on early immature understanding of program responsibilities. The resulting component model in such case is always naive, and is highly likely to change very shortly afterward as a result.

Instead, the best way to better comprehend and appreciate a program’s responsibilities is to implement the program. Implement it as quickly and simply as possible. The focus here should not be on writing clean organized code. It should be to gain insight into program responsibilities and how they interact. You can always refactor and improve the organization of your code later when your understanding allows you to do so with ease.

After you write some code, and get some sample output, and feel that you’ve covered several happy scenarios and reasonable special cases, only then you start thinking about how you organize code into components. At that point, you are armed not just by theoretical understanding, but also by insight given to you by the quick implementation you have coded.

Actual design starts in PPD by re-distributing responsibilities on classes, creating the classes on the way. Immediately when you look at the code you’ve written, you’ll be able to spot parts of the code that are good candidates for abstraction. The best thing about starting your design bottom-up from detailed code is that you are more immune to over-engineering your program. You will only abstract and generalize when it makes sense to do so.

Now, which sounds to you like a better design approach?

To conclude this section, here is Pragmatic Program Design in three simple steps:

Write focused straightforward code FIRST,

Make it work,

Then organize and refactor code into a suitable design.

PPD is nothing but pure common sense. In fact, I’m sure that many people out there are doing it already. I’ve chosen to write about it here, however, in order to help rising designers accelerate their learning curve and avoid common beginner pitfalls.

However, there are cases when doing upfront design modeling can actually make sense. When? It is when you are so familiar with the kind of program at hand and have written it many times before to the extent that you know by heart the responsibilities involved and the best way to organize and distribute them. Its when you can strongly assert that the design will not change substantially later on.

PPD is Great for Validating Designs

The Pragmatic Program Design approach can be applied selectively to devise good program designs on all abstraction levels from architecture and down to detailed class design. PPD provides high value especially when you’re designing at a high abstraction level, because that’s where the detailed information needed for producing practical stable designs is missing. PPD helps you fill-in the missing pieces by promoting writing focused code prior to elaborate design.

For example, let’s say you’re thinking about using the “Layering” architectural pattern in your program. You want to validate whether the layers you’ve come up with are practical or not. A good way to get your answer quickly is to do PPD. Write code that contains the logic from all the layers, and see whether you can divide that code somehow into your supposed layer structure.

After all, layering may turn out to be a bad idea in the case of your program’s architecture, but you’ll never know that for sure until you write code.

Concluding Remark

I hope I have detered you from being the perfectionist “Designer” who gets carried away spending days and nights “designing” programs… only to end up with many neat and fancy Visio diagrams that represent your best theoretical thought without adding much value.

In the next article, I’ll discuss how to best incorporate Design Patterns in your designs when applying PPD.


Actions

Information

3 responses

1 05 2009
rkeurel

You make some really good points. I used to be a somewhat strong proponent of big upfront design because so many of the apps I’ve seen seemed to suffer from a lack of understanding of the domain and I thought that with more analysis and design, this could be prevented. But now that you mention it, I do notice that a lot of the problems can be attributed to abstracting too early, thus leading to an incorrect representation of the domain, or a lack of refactoring, leading to the eventual erosion of the design. I’m gonna keep this in mind from now on.

3 10 2009
amr

good work and rich blog Moataz as usual..

I’ve been always pro the bottom-up school of design where architecture evolve incrementally in an iterative process, but sometime managers are reluctant where they don’t give the development the green light till they get a design document in their hands

3 10 2009
manany

Thanks amr, appreciate your positive comment :)

Yeah tell me about management :) One of the primary “tricks” that you develop over time is the ability to give management what they want, and then actually do the right thing. Rarely is management interested in doing something “right” unless it impacts the schedule/budget positively.

However, this trick works only when your manager is confident enough (in you and the team) not to micro-manage, otherwise, it wouldn’t work.

Leave a comment