Sunday, November 12, 2006

Peer Review and Problem Solving

Peer review, open communication and collaboration are probably the hardest nuts to crack and the foundation of successful team. Some years ago, a colleague of mine suggested that the increased communication encouraged by OO design and interfaces is more important than the OO itself as a determinant of success.

Curiously, she has a background in communications and media before completing a postgrad computing degree. Some of the most successful people I know in this field have three year degrees or a background outside of computing. Not to mention the fathers of the field like Dijkstra, Barry Boehm and David Parnas, whom I had the pleasure of meeting at ASWEC 2005 in Brisbane two years ago.

My current reading extends to Karl Popper (falsability and scientific progress), Kuhn-Popper "debate" on established order in science versus recurrent critique, where the philosophy is stretching my own thinking beyond its usual bounds (Bertrand Russell, Piaget in the stack of books).

I encourage peers to read and try to understand books in problem solving and Hofstadter's GEB, for instance, in order to extend themselves intellectually and to learn new approaches to problem solving (eg. induction, depth first, work backwards) to add to their own personal toolkit of problem solving techniques.

Gerald Weinberg introduced the concept of egoless programming in The Psychology of Computer Programming, originally published in 1971 and re-released in 1998. Weinberg recognized that people tie much of their perceived self-worth to their work.

Lessons and Licensing

I had the great pleasure of meeting and having several extended conversations with the renowned Prof. David Parnas who I met at the Australian Software Engineering Conference where he was guest speaker in Brisbane, 2005.

The chats I had with Prof. Parnas reinforced the unchanging nature and lessons of software development. He talks about loose coupling between modules, software engineering as a profession and attention to fundamentals. We both have strong, personal interests in the quality of undergraduate education, postgraduate and continuing professional education and licensing.

In Australia, there is ongoing discussion between the Australian Computer Society and the ITEE College of Engineers Australia about licensing arrangements. The likely outcome is hopefully a joint board that offers national accreditation, similar to those that operate in Canada and Ireland where Prof. Parnas is himself involved in these activities.

He must have influenced more than I had realised because I have been hammering on these points ever since!

Note that while I am a competent engineer my professional standing does not compare to David Parnas who is widely regarded as a pioneer in software engineering.

What is Software Engineering?

Half the battle in any discussion is nailing down what is the same and what is different. It is easy to disagree about pretty much everything while having lots of common ground, or conversely to agree in words while having significant underlying differences.

The Guide to the Software Engineering Body of Knowledge serves as a foundation for the accreditation of university curricula, the licensing and certification of practicing software engineers, and raising software engineering toward a professional status.

Lessons learned and sometimes forgotten include Coplien's Commonality Variability Analysis (*). David Parnas warns about forgetting the past and being doomed to repeat the same mistakes. Tom Demarco, Barry Boehm, Fred Brooks, Edward Yourdon, in addition to Grady Booch, Martin Fowler, Robert Martin and others, have much to teach us through their writing.

It is important that software engineers recognise the context in which they work:
  • Management. In particular, risk management, mitigation and minimisation.
  • Metrics. Formal and informal. How to measure success at each step of the way.
  • Planning, estimation and reporting. Keeping stakeholders informed, onboard and happy.
(*) My good friend and colleague Gian introduced me to Commonality-Variablity and taught me a lot about interface and class design, among other lessons in life.

Saturday, November 11, 2006

Refactoring

Some people may disagree however I believe that if you read Is Design Dead? and Refactoring with Martin Fowler the fair reader should get the idea that Martin Fowler supports some upfront design.

The absence of some design thought up front and lack of visible design effort before refactoring is that the programming effort may be just adhoc coding and rework rather than directed refactoring. The mitigating practice in XP is pair programming. But what about those not doing XP?

Any in-depth discussion about interface contracts, abstraction and encapsulation, Liskov and open-closed principles, state and invariants with regard to elements of design can be more than a little surreal. These are fundamental object-oriented design principles that some post-modern developers choose to ignore based on their understanding of Agile development.

Martin Fowler and Kent Beck allow for top-down, architectural refactorings in addition to smaller, finer-grained bottom-up refactorings. Most of the smaller refactorings can go either way while the bigger refactorings have bigger cost-benefit tradeoffs.

Baby steps do not always cut it.

Refactoring, Chapter 12: Big Refactorings (from Amazon summary):
Kent Beck co-wrote this chapter with Mr. Fowler. They discuss what they call the 4 Big Refactorings: Tease Apart Inheritance, Convert Procedural Design to Objects, Separate Domain from Presentation, and Extract Hierarchy. These refactorings are of a more all-encompassing type than the smaller individual refactorings from the preceding chapters. The co-authors do a great job at putting in a nutshell what would normally take very long explanations.
The problem with taking only baby steps during refactoring is getting stuck in a local minimum within the phase space of possible designs and having no way of getting out. The question in reality is to ask when the Big Refactorings turn into simply a code rewrite.

Simple Agile

Continuing the previous exchange in the form of a Socratic dialogue

Why are you talking about Karate and JKD?


The ability to draw analogies sets sentient beings apart. I was using Karate and JKD (if it exists) as similar creative and technical analogies for software engineering. Life in general. Primary school; secondary school; tertiary education; post-education independence is another which fits this context. Fundamentals followed by application, plus understanding, then extension.

I conclude that you now completely agree with my points, which is very fine since I believe I have spoken absolutely sensibly. Please confirm this.

You indeed have spoken sensibly. I neither expect to agree with all of your points, nor you with mine. Nor could it be so if we are thinking and developing independently without following a common dogma.

We agree with Fowler and Kent's opinions as I have quoted them. We both agree that a simple agile approach to software engineering is the most likely approach.

Not clear to the first assertion. We certainly disagree on interpretation of Fowler's words, among others. The "simple agile approach" needs a little more elaboration and definition if you want to pick-and-choose parts rather than specify to which agile approach (eg. scrum, xp) you are referring.

We both agree that a very disciplined approach is necessary, and that this discipline need only be applied to a very few
rules.

Again, we disagree on intent and degree. How much discipline is too much or too little?

Pay attention to the current circumstances, decide your next small step using the information to hand, always trying to improve things.

Here we agree. I think. Perhaps we have different definitions of small step. For me, three days is a small step compared to an iteration or closure over three or four weeks.

We agree that if something appears difficult, hard, convoluted, and complex it is not well understood. We agree that if something is simple it is understandable and communicable (even when encapsulating a great deal of complexity).


Here we are in agreement. No qualification.

Design-Code Strange Loop

Partly inspired by Hofstadter's GEB, this exchange loosely follows the form of a Socratic dialogue, probably best known as the form of Plato's Republic.

Hmmm I have some major concerns with these statements. First of all, there is no separation between design and implementation (coding). Each step of coding is detailed design. Each part of design is influenced by the code.

Ron Jeffries and I both agree with you that the way should be as you say it is.
XP does support up-front design--it's just that it recommends that design always proceed in the presence of code. The more effort we spend on design without feedback, the more likely that our design is incorrect. Design is done during the planning game, in addition to being done constantly throughout development. XP by no means prohibits us from sketching out UML diagrams prior to beginning an iteration, a task, or a test case. But we learn as we repeat, and our need to physically sketc diminishes as our understanding of good OO design and the system increases.
Abstractions and ideas. A body of actual code contains and includes its working existant design. They are one and cannot be separated.

I disagree with you about how well the code expresses its intent, hence the value and need to focus on OO and UML.

Quoting Ron Jeffries again:
[1] The design is treated similarly. The main place for the design to be is in the code. An Agile project is producing a number of "living documents", the most important of which is the program itself, which is produced in a growing, fully integrated form, from the beginning of the project right on until the end. [2] Working together, the team communicates design essentials through conversation supported by pictures and documents as needed.
My insertions of the numbers into Jeffries' words: [1] is essentially a restatement of what you have said; [2] is a restatement of what I have said.

I add that developer-to-developer communication and collaboration is essential for success but that it is inadequate for the customer. I also emphasise that the "supported by pictures and documents as needed" is contentious and applies equally if not more so to design as to the code.

As soon as the first line of code is written you have software. It has a design, it has functionality.

Indeed.

Any statement which lowers the supposed worth of code to "least-important" is fatally flawed in my view.

As you noted above, the code is inseparable from the design. I suggest that the design is actually a specification for the code, as is the test suite, as are the requirements; demonstrably we can implement the same design in more than one way.

The worth in monetary terms is in the contract. Okay, that is a bit cheeky! But the customer does not care about the code whatever we do. The customer cares about what it does for them.

You appear to be saying the scaffolds on the building site are more important than the building itself. Surely the entire point is to deliver a working system (working code) to a customer.

The designs and specifications are more important than the building itself is a statement I would unhesitatingly make. Any qualification would be for each stakeholder; the builder thinks the permits are more important, the subcontractors want to finish by 3pm, the kids want a backyard, Mom wants a kitchen and Dad a shed, okay, a study.

Be Like Water

Sensei: The Bruce Lee movie I allude to is Game of Death, where Bruce's character finally defeats Kareem Abdul-Jabbar because he realises there are no set forms, no correct response.

"Don't get set into one form, adapt it and build your own, and let it grow, be like water. When water gets into a cup, it becomes the cup, when it goes into a teapot, it becomes the teapot. It can flow, or it can crash. Be water my friend. Adapt!" Bruce Lee.

Student: Similar to OOD, what is the purpose of XP? Why is it used and followed? Because it is believed by some to aid in reaching the final goal of workingsoftware. Again, the practices of XP are not the point in and of themselves.

Sensei: At one extreme, any formalism can provide a crutch and a barrier to progress. Just as Lee regarded the practice of traditional style like Wing Chun as dogmatic. For most practitioners, and indeed for Jeet Kune Do (JKD) as taught by Lee to his latter-day students, it was vital to master the fundamentals.

Student: The purpose of OOD is simply to:
  • Manage complexity.
  • Divide the problem.
  • Hide details.
  • Model the real world.
Sensei: These concepts predate OO-practice in software development by decades and are vprobably fundamental to most fields, like mathematical and econometric modelling, financial and marketting planning and execution.

Student: If we do not follow XP exactly then my question would be: so what? As with coding and design, a decision to follow XP more closely would be made if it was thought it could actually *help*.

Sensei: Like in Karate where the three Ks, Kihon (fundamentals), Kumite (free-fighting) and Kata (forms) are all necessary, perhaps OO principles, XP and formal processes, like Kihon and Kata, are key to Kumite, the free-flowing practise you espouse.

Bad Tests are a Joke

Bad tests resemble the Microsoft joke about their help desk giving perfectly correct but useless information. Joke reproduced below to help our readers find the humour in this otherwise serious subject.

The Turing Theorem has something to say about this, too. Can anyone tell me what?

My Helicopter Is Lost

A helicopter was flying around above Seattle yesterday when an electrical malfunction disabled all of the aircraft's electronic navigation and communications equipment. Due to the clouds and haze, the pilot could not determine the helicopter's position and course to steer to the airport.

The pilot saw a tall building, flew toward it, circled, drew a handwritten sign, and held it in the helicopter's window. The pilot's sign said "WHERE AM I?" in large letters. People in the tall building quickly responded to the aircraft, drew a large sign, and held it in a building window.

Their sign said "YOU ARE IN A HELICOPTER OVER SEATTLE." The pilot smiled, waved, looked at his map, determined the course to steer to SEATAC airport, and landed safely.

After they were on the ground, the co-pilot asked the pilot how the "YOU ARE IN A HELICOPTER" sign helped determine their position.

The pilot responded "I knew that had to be the MICROSOFT building because, similar to their help-lines, they gave me a technically correct but completely useless answer."

Happy Path Tests

Someone posted an article to the relevant company internal news group at which I was consulting, referring to JUnit Antipatterns and happy path testing. Another colleague replied and started off by saying, "You'll find that he only uses the term 'bad test' in the conclusion."

He continues by saying the author states that "Writing good tests is the hard part," implying that many of the easy tests may be bad tests and goes on to show illustrative examples. He could define 'bad test' as 'not(good test)' I suppose.

After referring to the section Easy Tests my colleague quotes the article, "The result is lots of passing tests that don't exercise the system, which leads to a misrepresentation of the code's health," and this is a bad outcome which need not be spelled out.

He goes on to say that he can only assume that the author was referring to tests that don't work. e.g. 1000 assert(true)'s. "That is, it's hard to say whether he's considering a 'happy path' test to be a *bad* test. You can't be serious. You did read the examples and make such a trivial assertion(asserting(1000 assert(true)'s))) as satire of some sort, right?"

Well no, actually.

The author is quite explicit and says, "Actually, happy path tests aren't an antipattern. The antipattern is when the developer stops at happy path tests."

After a couple of examples showing why "happy path tests" are necessary but are not themselves sufficient for adequate testing, the author states, "The point is, it isn't difficult to get a test to pass coincidentally," and this is a bad outcome as well.

I disagree with the assertion that "One single test of one component that tests the 'Happy Path' gives a massive, massive gain in confidence in the system. Even if it only tests 5% of the system in one ideal case it actually *proves* something about the behaviour of the system, where before you knew absolutely nothing."

Such tests prove nothing and in fact are worse than having no tests because such a view engenders a false sense of security and confidence in the system that is misplaced. One counter-example, quoted directly from the article:

One happy path test verifies that Factorial.eval(3) returns 6.

public class Factorial {
public int eval(int _num) {
return 6;
}
}

How's that for a false positive? If you've never encountered test-driven development, you might object that no one would write such a simple-minded implementation. One of the practices of test-driven development (TDD) is to write the tests first, then "do the simplest thing that could possibly work" -- in this case, return 6.

In answer to my other colleague I choose to borrow from the legal profession, "If the facts are on your side, bang on the facts. If the law is on your side, bang on the law. If neither the facts nor the law is on your side, bang on the table."

Good tests demonstrate a requirement (user or discovered, user-actor or controller-actor) of the system, usually asserting on an invariant of the class. I disagree that tests are especially useful for construction and other static tests, and these are taken care of by the compiler and linker anyway, by default giving us the underhyped benefit of static-type safety.

The place for static setup is in, you guessed it, setUp(), so the test fixture is not polluted by such detail.

So what do we test in the fixture itself? Dynamic behaviour. Specifically, scenarios of unit tests, or bits of user stories, to test that the software works as expected. From the junit cookstour, "We found that the most challenging application of JUnit was testing its own behavior."

I can certainly believe it.

Friday, November 10, 2006

Howth Castle and Environs

The issue of big-daddy refactorings, if there are such things, versus the baby-step refactorings, like missing the wood for the trees, is one that expresses in the context of software design the ever-present tension between forces.

A friend an colleague of mine said, "I would love to hear a suggestion for one small concrete refactoring solving a demonstrable problem in the code (which does certainly have problems and will never be perfect). And after that refactoring has been made current and tested overnight I'd love to hear about another one."

My reply was to quote straight out of Richard Helm via James O. Coplien in the Forward to Pattern Hatching by John Vlissides:

"You also have to get the macro-architecture right; layering, distribution, functional isolation,...; and the nano-architecture right as Cope says: encapsulation, Liskov...."

Somewhere along the way we need to step up from nano-architecture and into macro architecture. This in my personal perspective as Daniel the guy who prefers to refactor early than get bitten later. My primary tools are collaboration and approaching design from a patterns perspective.

To pick a nano-level problem, the Liskov problem is a likely candidate. We should get the basic principles of object-oriented design right first. There is not a obvious and trivial refactoring for this problem which can be applied overnight. Not to me, anyway.

I would be looking for a design proposal followed by some work on a prototype to see the detailed design emerge, followed by a design review before the code is inspected prior to being made current.

To return to the beginning (*), if there is a problem it should be fixed in the pattern implementation.

(*) James Joyce's birthday was not so long ago. What is the pertinent literary allusion?

Friday, November 03, 2006

Principles, Practices and Processes

The three Ps that I want to treat are best taken in the reverse order. The reverse of the listing in the title and the reverse of the importance to which many practitioners put them. Process is the leader for software practice within teams from which follows adherence to principles.

More often than not the absence of process in any guise, formal or informal, means that the team members work in a process vacuum. Obviously it is not a mandatory requirmement to have formal, documented processes in order to successfully develop software. All you need is a small, tight-knit group of very smart software developers. Sure.

For one thing, the informal processes of a small group do not scale well. It is all good and well to decide that good practices can be passed on by word of mouth and the exchange of information between seasoned and novice developers exchanging verbal and nonverbal cues. However the communication overhead, the fragility of memory and the difficulty in attracting and retaining that magical, high-calibre of staff (if they even exist) makes the trite statement that formal, documented processes are unnecessary a nonsense.

In reality, most hires are well meaning, hard working, technically adept and competent in their fields of experience. The cited norm of hiring exceptional candidates is unrealistic at best in a tight job market where your competition is offering the same salary packages and perks (more on this later).

The two important facts that are often ignored by technical professionals who are running the software development factory in the organisation is that firstly they are likely bound by a fiduciary obligation to capture organisation knowledge and secondly should consider the importance of balanced teams.

If not that manager personally then their boss (the CIO or CTO) has or should have a policy for knowledge management. The sustainability of any organisation depends upon its capacity to remember how to maintain and develop legacy and newly-developed systems.

One of the biggest mistakes is to ignore the documentation and maintenance of legacy systems while a replacement is being rolled out. Nominally in a short timeframe, to provide all of the extant functionality of the dinosaur model and more. The truth usually falls far short of the ideal and the new system will be delivered late, if at all, lacking basic functionality.

Balanced teams are all about finding the right people to work on projects together. Usually a gender, age and background mix adds strength as negative monocultures are avoided while positive synergies emerge. In this respect, balanced teams need high performers and steady workers, high-level designers and low-level hackers(*), planners, doers and influencers.

Process offer guidance for new inductees and set boundaries, preferably nonprescriptive guidelines, for novices and experienced practitioner alike. To define the set of practices that the team uses to build and deliver the solution.

The design and construction of the solution is less a mechanical exercise than one which is by its very nature creative. The entire software development lifecycle involves negotiation and compromises (more later). By the people, for the people. The modern approach to software development is to apply object-oriented and patterns design. At a certain level the business objects and domain ontology can be understood as an ensemble of collaborating objects.

Pattern poetry. Emergent systems.

The day-to-day software development is sharply focussed on the The Principles of OOD that have been communicated so well by Robert Martin. Uncle Bob to his friends and admirers. To him and Martin Fowler I defer on many issues in discussion with my peers and colleagues as I hold them in the highest respect (more later).

The gist of Agile development is eminently supportable (eg. test-driven development) even if some of the practices are debatable. More later about Barry Boehm, David Parnas and the ascendency of the traditional approach of lore. By recent graduates not forgotten; never learned that there is little new (more later).

Building on the seminal work by Grady Booch in bringing object-oriented design into the mainstream after decades of being perceived as a niche technology or a novelty. Where the C++ language invented by Bjarne Stroustrup made OOD a practical reality even if the market-driven successors Java and C# promised so much more while delivering, in many ways, so much less (more later).

(*) Hackers not crackers. See A Brief History of Hackerdom by Eric S. Raymond if you seek clarification.