Friday, September 07, 2007

Joel Spolsky, EBS and a Bit of Nyquist / Jolie


I'm totally impressed by the evidence-based scheduling (EBS) proposed by Joel Spolsky and his team. Their approach to scheduling is so simple and usable that people may adventure to try it out. And then there is a good chance that the EBS will make accurate predictions.
For accurate predictions, the project has to broken down into fine grained half-a-day sized tasks



Huh, is that small print? In all fairness, during his FogBugz 6.0 presentation, Joel was quite clear about the small print: EBS makes accurate estimations if the project is broken down into many fine grained half-a-day sized tasks. Yes, Joel was quite clear about this and I hope everybody got the message because Joel is in fact answering one of the most difficult questions of software project management: how much do we need to know in order to build an accurate plan?

Friday, August 31, 2007

Interface, Behavior, Implementation


I always break down my code in interface, behavior and implementation. Code structured this way scales up naturally while its dependencies are easy to manage. This breakdown also promotes code testability and reusability. But what always amazes me is that, when the code is structured correctly, one can achieve a lot with very little code. On the negative side of things, the code is fairly fragmented. That's because system behavior is implemented via interaction between objects instead of monolithic algorithms wrapped by classes. Sometimes it may be difficult to figure out how a particular piece of behavior is implemented, but that is usually a symptom that the object model is not quite right and it is in need of redesign and refactoring.

Here are some ground rules on how to structure the code as interfaces, behavior and implementation:
  1. Describe your domain with a hierarchy of interfaces. Interfaces should be correctly segregated.
  2. Describe behavior in abstract classes. They should depend only on interfaces and other abstract classes within the same closure.
  3. Implementations are derived in most cases from abstract behavior and sometimes from interfaces.
  4. Clients should have access only to interfaces, utility classes and class factories. Some abstract behavior classes may be visible too if you want to allow the client to extend behavior via inheritance (this is typically framework).
  5. Implementations are not visible to client classes.


PROS:
  • code scales up well and it is easy to package.
  • code dependencies are easily manageable.
  • code is testable. The tests follow the hierarchy in the code, hence the tests are hierarchical.
  • code is highly reusable.
  • one can achieve a lot with little, simple code.


CONS:
  • code is fragmented according to the above mentioned rules.
  • tests are fragmented same as the code.

Wednesday, October 11, 2006

Bad Number Four: The Has-It-All-SwissKnife Anti-Pattern

The Bad:All-in-one interfaces, which address multiple concerns. Such interfaces are likely to change. They force changes in unrelated code. Large interfaces create artificial dependencies.
The Good:Segregated interfaces, with only one reason to change. When an interface changes, only relevant code changes.

Thursday, September 28, 2006

Bad Number Three: Can’t See the Tree for the Leaves … Never Mind the Forest

The Bad: Making abstractions depend on implementations. Dependencies are inverted, instead of running from implementation towards interfaces they go backwards. The consequences are dreadful: very quickly the code becomes spaghetti like and each change request is going to look like a 9th Richter scale codequake.
The Good: Have abstractions dependent only on other abstractions. Have implementations depend on abstractions and never the other way around.

Tuesday, September 26, 2006

Bad Numer Two: Can't See the Forest for the Trees

The Bad: Diving too soon into implementation details. The end result is a rigid design. Each time a new feature needs adding it’s a major piece of work. The rigidity derives from the fact that the design lacks abstraction.
The Good: A good software design contains both abstractions and concrete artifacts. The first are interfaces and abstract classes, the second are concrete classes, also named implementation classes. The abstractedness and stability of a code base is measurable and quantifiable (see Martin Metric). A good design will have its Martin Metric along the main sequence.

Monday, September 25, 2006

Bad Number One: Class Exhibitionism

The Bad: Adding a non-public type to the signature of a public type. In order to get the code to compile, the non-public type has to be refactored to public, thus weakening the package level encapsulation. With time, if the same wrong is repeated enough, all classes and interfaces in the package become public. Package level dependencies quickly become unmanageable. All classes in the package are visible to the public.
The Good: Avoid exporting classes from a package; export interfaces as much as possible then use a class factory to instantiate objects.

The Good, The Bad and The Ugly

Somebody requested me to post some examples of badly engineered code compared to well engineered one. It’s a fare request. Plus, writing about that makes things clearer for me too. It's a good learning exercises to explain why the bad are bad and why the good are good. So, the next few posts will focus on making that comparison.

Tuesday, September 19, 2006

Functional Programming in Java


A simple interface can take a program a long way.
public interface Expression {
Expression evaluate();
}

This can a starting point for an implementation of functional programming in Java. Java has enough firepower to implement functional programming and dynamic typing. Following I've tried to provide an elegant solution to the above problem. The article discusses some of the design issues. The complete code and test cases are provided (see at the bottom of the page).

Monday, July 17, 2006

An Unfit Industry

In the last 16 years I've done software development for quite a few companies. I've seen good and well engineered code only in a couple of places. The norm is poor code.

The software industry requires highly trained people. In almost all cases, senior developers have a B.Sc. or M.Sc. from an accredited university. Self-taught rarely works above the intermediate programmer level. Education is never a bad thing and in fact it is highly required. Further on, in this industry one can never stop learning. Instead developers have to constantly adopt and create new technologies. Learning by creating is inherent to the nature of development work.

Learning is not easy though. All people fail at it sooner or later, as soon as we reach our own level of incompetency. What happens beyond the point of failure depends on the individual: some try hard to break through, others give up or few give up and blame it on the others. In that respect I've seen half-knowledgeable people dismissing a good thing [design patterns] just because they didn't have the patience nor the will to learn the about the topic. It's pretty childish to dismiss something as a bad thing just because you failed at it, but people do that.

With all that said, why all the poor code in the software industry? Maybe development is beyond the average level of competency. But I somehow doubt that. I'm guessing instead that most of us stopped learning. That because the only places I've seen good, elegant code was where people were still learning.

Thursday, July 06, 2006

Managing Complexity

After reading G. Chaitin's book "Meta Math - The Quest for Omega" (summarized in SciAmer ) I realized that in order to completely verify a software system it is required to employ resources which have at least the same complexity as the system itself. Complexity means effort(and that means $$$$), therefore an organization should spend the same effort for verification as it spends for development.

Specification and design are not in the same ballpark because, by their nature, they require a certain level of incompleteness and ambiguity. After all, the specs and the design are just a blueprint for the program and not the program itself.

At the end of the day though, we have a problem at hand and a solution for it in the running code. The complexity of the problem is directly reflected in the complexity of the solution. Further on, getting the proof we have a valid solution is as complex as the problem itself.

Thursday, October 13, 2005

Functionality, Quality and Timeframe: Pick Two

There are three things required to sell a product: functionality, quality and timeframe. You miss any of the three, next you don't have a selling product. In an perfect world, you would plan to cover all three aspects and then manage the development accordingly. In reality you cannot manage all three aspects. That puts hard boundaries on each of functionality, quality and timeframe. The resulting plan is rigid and cannot be changed nor adjusted when required. Instead, put only two aspects in the box and leave the third one floating.

For example:
  • planned timeframe (ie budget) and quality. Functionality negotiated. Low risk.
  • planned functionality and quality. Timeframe negotiated. Average risk: it may become an open wallet project; proceed in small iteration to control your budget.
  • planned timeframe and functionality. Quality negotiated. High risk: low quality on components with too many outgoing dependencies dramatically increases development costs (ie you miss your timeframes and enter panic mode); manage dependencies and lower quality ONLY on components with no outgoing dependencies (they can be easily replaced).
Agile methodologies promote only the first two approaches. But reality is different. Many times businesses are forced to take unhealthy risks and go against any rules of sanity.

Monday, August 01, 2005

Variations on a Turing Halting Theme: Beyond the Turing Machine

A program is a list of instructions. Each instruction is equivalent to a sequence of operations ran by a turing device. We can detail the program in its equivalent sequence of turing operations, which can be encoded as the equivalent turing machine. The output of a turing machine is a sequence of bits. That can be seen, of course, as another turing machine. With that as a prerequisite, can a turing machine create another turing machine that will generate a given output? If that can be done, then a turing machine could re-create itself to generate a turing machine that generates a given output. Assuming things can go this far, can a turing machine choose what output to generate then recreate itself to match the desired output? In other words, can a turing machine change itself in ways that would benefit its own existence? Can a program re-program itself?

Sunday, April 24, 2005

Variations on a Turing Halting Theme: Superbug Hunting

SuperBugs inherit their "intelligence" from the system complexity. It is the system complexity where we have to look for weapons against superbugs. Here are a few things programmers and their managers can do to prevent superbugs from occurring in the code and to bring down the ones that haunt complex systems.

Thursday, March 17, 2005

Variations on a Turing Halting Theme: Bug Anatomy

The turing halting problem: "Given a description of an algorithm and its initial input, determine whether the algorithm, when executed on this input, ever halts (completes). The alternative is that it runs forever without halting." [ wikipedia.org ]

There is no such thing as bug free software. No matter how much quality we try to put into our software, bugs always seem to find their way back into the code. And the more quality we put in, the subtler and harder to fix these bugs become. In a complex enough system, given enough time, programmers will fix all dumb, trivial bugs. At the same time though, impossible to catch superbugs evolve in the code. Is this a paradox? Not at all. To understand why not, we need to look at what really makes a software bug.