Software Creation Mystery - http://softwarecreation.org

The Elements of Pragmatic Programming Style. Composition.

A really great talent finds its happiness in execution.Johann Wolfgang von Goethe

source

Qualities of well composed code:

  1. Quick discovery and understanding of programming logic and components
  2. Clear organization (for human brains)
  3. Ease of reuse, modification and evolution
  4. Close connection between customer ideas and system implementation

Style Components:

  • Intention – understand your task and how to get it done
  • Approach – basic principles of writing code
  • Composition – organization of code
  • Expression – expressing ideas in code
  • Object Oriented Pragmatic Style

1. Design for customer problems
Make the customer problem space a core for the software system design. Make every line of code accountable for solving customer needs. It is true that any software system requires a lot of plumbing beyond pure implementation of customer needs. However, technology concerns shouldn’t prevail. High-level languages and modern programming platforms give us a power to concentrate on customer problems more than on solving technical problems.

Code could be divided into two categories:

  • customer oriented – solves customer problems directly, e.g. implementing domain logic, UI interactions and display of information;
  • system oriented – solves technical and application specific problems – data access, event handling, interfaces between subsystem, utilities and other code required for using libraries, systems and platforms.

Customer-oriented code brings most value and should be primary design concern. System-oriented code puts in place infrastructure and support for running customer oriented code.

Therefore, start with design of customer-oriented code and support it with minimal system-oriented code. This doesn’t mean that system-oriented code is not important. Complex system environments, sophisticated user interfaces and challenging non-functional requirements (performance, availability, reliability, etc.) could demand major system-oriented development effort. But still customers needs should pull and direct this effort and the whole implementation.
There are many ways to make technical problems dominant over customer problems:

  1. Making predetermined technical decisions (databases, programming platforms, messaging systems, expensive middle tier etc.) without considering simpler alternatives.
  2. Coming up with complex up-front design instead of evolving a simple design into optimal design.
  3. Starting programming around interesting technical problems forgetting what is important for a customer.
  4. Building frameworks and system-oriented code before writing code for immediate customer problems.

For a example, some teams could start with this picture in mind stretching customer requirements to fit it (even for relatively simple applications).

source
2. Organize and evolve code around domain concepts and customer ideas
Make system design reflecting customer domain and problem space. Keep customer ideas and system implementation connected and synchronized all the time. This is as important as refactoring practice for effective creation of the software system.
Domain-driven design is an excellent way to build software systems. It puts emphasis on ubiquitous language, distilled domain knowledge and shared domain models that drive software development.

3. Think as a customer
What we see depends mainly on what we look for – John Lubbock
A programmer often considers programming as an opportunity for solving interesting technical problems. Often a programmer even doesn’t understand what customers really want. There are 2 consequences. First, the programmer is not interested in simpler solutions focused on the customer problem. Second, the programmer is missing coherent view on the customer problems and purpose of the software system. Over-engineered, complex and misaligned code is one of the most serious problems in software development. An army of business analysts, stressed project managers and smart customer proxies will not force indifferent and ignorant programmers to write code in the best interest of customer.
The programmer should constantly ask himself: “Will this solution work for my customer?”. To give correct answer on this simple question the programmer should understand a lot – customer language, needs, problems, a big picture and business purpose.

Certainly, it is difficult to think as a customer if you are at the bottom of software creation chain:

  • separated from a customer by project and product managers, architects, designers, consultants, experts and panels of stakeholders
  • pressed by the heavy process, rigid organization structure and inability to make decisions
  • fed with over-processed, distorted and disjointed doses of information coming down through long chain

Beautiful software ideas can turn into monster applications – unusable and disconnected from real needs. Did you feel (as a user) frustrated by complex confusing and irrational program screens and logic? Yes, we, programmers, can easily produce bad stuff if we cannot think as people who need our systems.

4. Sketch programming ideas with unit tests.
Write unit tests first. Any programmer should understand what he is trying to solve with the new code and how it will be used – possible input, expected output and public interface. A programmer should think about behavior in isolation, under border conditions or within context of existing code. An unit test is an excellent place to sketch nonexistent code, play with it without compilation and see if it is convenient and makes sense. Once the programmer likes sketched ideas in unit tests, he just need to build simple implementation to satisfy these unit tests. The process of writing unit tests focuses a programmer on solving customer problems, understanding code intent and making optimal design decisions. As an additional side effect, the programmer receives comprehensive suite of automated regression tests and executable specifications.

For a example,

CustomerDB.Save(TEST_USER, TEST_PWD)
Customer customer = CustomerDB.Login(TEST_USER, TEST_PWD);
Assert.IsNotNull(customer, "customer should be logged in");
customer.Purchase(TEST_PRODUCT);
List<Order> orders = OrderDB.LoadFor(customer) or maybe... customer.Orders //what is better?
Assert.AreEqual(1, orders.Count, "count");

There are many design decision are made in this unit test without writing any production code. And now programmer can concentrate on implemention without throes of creation.

5. Eliminate duplication
Simplification is ultimate sophistication – da Vinci
Elimination of duplication is the moving force of evolutionary design. This is straightforward and powerful method to grow an optimal system. Duplication in the code tells that you need better design. Many interesting design ideas are born from the simple need to eliminate duplication in the growing system. These ideas are based on deeper understanding and gained experience as oppose to speculative up-front design ideas.
The main goals of removing duplication are

  • reduce code
  • simplify solution
  • make the system more manageable for programmers’ minds.

Some sources of duplication:

  • copy and paste – acceptable only temporary
  • similar logic different in details – generalization is required
  • another implementation for the same problem – better team communication and reuse are required

Master the core skill of good evolutionary designers – how to detect and eliminate duplication.
The knowledge of design patterns is the most useful on this step. The initial simple solution rarely requires design patterns. But as a system grows, design patterns can effectively and elegantly solve new complex design problems.
Example of elimination of high level duplication with Template method design pattern
This code was used in many data access classes almost the same way, but with different inner steps

        using (SqlConnection conn = NewSqlConnection())

        {

            SqlCommand cmd = conn.CreateCommand();

            cmd.CommandType = CommandType.StoredProcedure;

            cmd.CommandText = "SearchCustomers";

            cmd.Parameters.Add(new SqlParameter("@city", city));

            ... //long list of parameters

            conn.Open();

            try

            {

                using (SqlDataReader reader = cmd.ExecuteReader())

                {

                    foreach (DbDataRecord record in reader)

                        customers.Add(new Customer(record["id"], record["name"]))

                }

            }

            catch (Exception e)

            {

                //error handling logic here

            }

        }

The common logic could be moved into one template method with placeholders for different steps:

List<Customer> customers =  LoadRecordsFromSP<Customer>("SearchCustomers",

    (SqlCommand cmd) => {

         cmd.Parameters.Add(new SqlParameter("@city", city));

         ...     //long list of parameters

    },

    (DbDataRecord record) => { new Customer(record["id"], record["name"]); }

);

6. Reduce code smells
Write code that doesn’t smell. Train your sense of smell for code and design problems. They will provide hints where code is probably bad.
These are common bad smells from a long list

  • Duplicated code
  • Large method
  • Large class
  • Long parameter list
  • Dead Code
  • Over-generalized code
  • Lazy class

7. Use design patterns and abstractions to make code simpler
Learn design patterns. At some level of complexity they will provide excellent solutions and simplify design.
However, wrong abstractions and over used design patterns will make code more complex and less manageable. So, apply them carefully.

8. Decouple and isolate components
Only talk to your immediate friends; Don’t talk to strangersLaw of Demeter.
Reduce connections between programming units – subsystems, modules and objects – as much as possible. More relations between components make software system more complex and rigid. Decoupled and isolated system components have significant benefits:

  • better understanding – easier to keep in memory and make sense for less states and relations
  • increased reuse – components with minimal well-defined public interface and relations less dependent on context and can be used in many scenarios
  • decreased breakdowns – caused by unpredictable run-time combination of relations
  • effective testing – highly connected system require much more testing beyond isolated testing of components

9. Keep related code together
Put related code to the same package. It will enhance

  • discovery – easier to find and reuse
  • design – allows isolation, minimal interactions and consistent structure for related components
  • maintenance and testing – reduce effect of new changes to fewer packages and system components.

There are two package approaches:

  • Feature – code related to the same problem placed together
  • Application layers – code for similar programming concepts

Benefits of packaging:

  • Feature: related code in one place, changes together and form self-sufficient granule for reuse and release
  • Application: separation of concerns, logical layering and reduced dependencies,  consistent global structure

Composing the program – Putting Things Together

  1. Understand what customer wants and form intent.
  2. Translate customer ideas into programming concepts (technology, platform and language, UI, libraries, components)
  3. Make them simple, minimal and clear with pragmatic approach.
  4. Organize code around domain concepts and customer needs; use domain-driven design.
  5. Describe UI ideas with paper sketches
  6. Specify program ideas with automated unit tests.
  7. Merge UI and program ideas
  8. Package related ideas together, isolate subsystems and components
  9. Express ideas in code and UI screens
  10. Remove duplication and bad code smells; improve design; consider using design patterns
  11. Evolve and refactor common code into framework; emerge layers and abstractions for similar concepts
AddThis Social Bookmark Button AddThis Feed Button


Comments

[…] Software Creation Mystery » The Elements of Pragmatic Programming … […]

Pingback by How To Draw Cars | Watch Free Channel | January 20, 2009 11:15 pm

This blog have little value without you and your comments, thoughts and discussions. Please, leave your comments. You are welcome to debate and criticize any idea, but, please, don't attack other people. Thanks for your contribution!

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Subscribe without commenting

Software Creation Mystery - http://softwarecreation.org
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License .