Search This Blog

Showing posts with label Development. Show all posts
Showing posts with label Development. Show all posts

Sunday, August 3, 2008

Builder's shouldn't mix

Those few who have read my past posts, may have noticed my preference for the use of builder style methods, which look something like this:

publisher.uses(anOutputStream).toPublish(aPublishableItem);



I like the expressiveness the builder style method gives me; Bringing me closer to chaining methods that resembled sentences.

While I like the way the above line reads, it's not enough to endorse their use any longer ... and here's why.

Publisher is a concept and like all good concepts, its definition is defined through an interface (or abstract class, but most likely an interface). If I was to look at this Interface, I'd see something like the following:

public interface Publisher
{

void toPublish(PublishableItem aPublishableItem);
Publisher uses(OutputStream outputStream);
}

The first thing that stands out is the uses builder method that takes an output stream and uses it in some fashion.

"What's so bad about that?" I hear you ask.

Let's assume I was not the author of this interface or any of its implementations. I would have to read the implementation to understand how this type was used. Or, more precisely, what sequence behavior should be called in, when required.

So it's possible that sequence of calls is very important. For instance (pardon the pun)

publisher.uses(anOutputStream).toPublish(aPublishableItem);


Could publish the publishable item to the passed in output stream.



Alternatively, I could have tacked the methods together differently:

publisher.toPublish(aPublishableItem);


publisher.uses(anOutputStream);


Might publish the publishable item to a default output stream and nothing to the passed in output stream.



What I dislike is that information about sequence of calls is only discernible in the implementation of our Publisher's behavior. That's an abstraction lower than I should have to go when reading clear, written code. The interface gives no clue.

One could argue the case, "Oh but anything that returns an instance of itself would be called first ... blah, blah, blah"; But, the fact still remains - One can't be sure by simply looking at the method signatures - and that extra trip to an implementation is a trip I'd rather not make if I didn't have to.

So what was the publisher refactored to?

After looking at an implementation, it was found that the publisher is created with a default output stream and if one is not provided at the time of publishing, the publisher defaults to the output stream created at its time of construction.

A middle ground was struck that didn't read quite as well as the first attempt, but provided an interface which told a better version of the truth than its predecessor.

To publish to the default output stream: publisher.publish(aPublishableItem);

To publish to a given output stream: publisher.publish(aPublishableItem, outputStream);

In summing up, I chose to favor clarity of type behavior, for developers who may want to use the Publisher type in future, over a nice sentence, for readers of existing code. In this case the trade-off is subtle, as the refactored code is still quite readable. What's important is that I value both readers of my code and users of my types. In doing so I try to satisfy both sides in a reasonable way. In this example, I felt that users of my type were unfairly disadvantaged to satisfy a really nice sentence like chaining of behavior.

Wednesday, July 9, 2008

Selfish Development

I think one of the main reasons we, as developers, start programming is to express ourselves creatively. It's the creative process that keeps us coming back for more: There is nothing more rewarding than distilling down a seemingly complex problem to a simple solution. This doesn't need to happen in the code first. The reward usually happens much earlier during the discussion about the problem with your team members (fellow developers, analysts, customers etc).

Of course this is true in any field. Oliver Wendell Holmes Jr. (1841 -1935) Once said "I wouldn't give a fig for the simplicity on this side of complexity; I would give my right arm for the simplicity on the far side of complexity". If he was talking about software development, I believe he would be saying that it's not enough to solve the problem. We need to solve problems in ways that other people can understand and use.

As a developer this means writing code that's explicit about your intent. In Bob Martin's words, "The name of a variable, function or class should answer all the big questions. It should tell you why it exists, what it does and how it's used". Eric Meyer sees it as trying to tell as much truth about your intent as possible. You can't tell the whole truth, due to the nature of abstraction, but you must try. Doing so will help you avoid the over abstraction syndrome that plagues source code (Over abstraction occurs when important information is abstracted away too early, requiring the reader to divert down a side path to understand the solution). I'll talk more on this in a coming post as it requires a discussion of encapsulation and cohesion.

So what does this have to do with selfish development? Put simply it is this - If you are worth your money as a developer, your code will be read many times and there's a cost to reading and understanding code. So good code is easy to read and understand. If its easy to read and understand then its more than likely easy to extend. It also needs to be correct in a manner that can be proven when required. This provides confidence to you and those who will use or maintain the code in future. So it needs automated tests and excellent (not good) coverage. With this in mind, team members should avoid writing dirty, untested code under the premise of - "I just need to get something out. We can come back and clean it up later"; or my favorite, "I'll get it working and you can make it pretty". What's actually being said is, "I'm too lazy to do a proper job but I want to enjoy being creative. You can deal with my stuff once it works (do maintenance). Oh and don't ask me to explain how it works. I'm not good at explaining things".

It's of no difference if you're a team of one or many; Pairing or flying solo; It's not done until it works, is testable and understandable.

One of the "tells" of this process is someone who wants to provide a solution in a technology before they understand or have defined the problem (bottom-up programmers): The implementation nuts, who are crazy about the latest api or library out there. It's like a person who needs to hang a picture on the wall. Straight away s/he runs out, grabs a nail and a hammer and starts banging.  Unfortunately, the picture must be hung in a specific area and a reinforced steel girder occupies that area. Our friend keeps banging away, saying "hang on, I've almost got it" ... you can see where this is going.

The alternative is to talk about the problem; Code by intention; top-down; Write the class or function that answers "Wouldn't it be cool if I had an object that did..." and worry about the implementation afterward. While your at it, you might as well test using Test Driven Development (TDD). Doing so requires you to understand the problem you need to solve before you try to solve it with the detail (take a squiz at testing with mocks, interfaces and abstracts). It also allows you to concentrate on defining the problem in an abstract easy to understand way before getting into the noisy detail of exactly what you will use to provide the solution.

My personal experience is that a well defined, high level solution to a problem, written in the conceptual layer, leads to less coupling in your system allowing you to dangle implementation details off you abstractions, one at a time. For complex systems, it's much easier to understand a small bit of implementation detail, trusting that the system is correctly communicating among its components to solve the greater problem, than to think of everything at once (the details and how its going to communicate with other parts of the system).

I've worked with people who have criticized other developers because they talk about the problem too much before putting something down. Personally, I like these guys, they use the best computer, our brains, which are largely configured to quickly compile and optimise language, before proceeding to the more expensive writing of code. Refactoring helps but is no match for an open discussion about the problem and how to simplify it among people.

One further comment: I was careful in my post to use developer and programmer in specific places. I believe a programmer is someone who knows how to solve a problem with a given language or api without regard for the true elegance required in a well designed system that is very easy to understand and change. A developer is something again. S/he is someone who is considerate to the users of the code base, customers and other developers. In doing so, they seek clarity of intent and elegance in design of a maintainable system. They have values (I'll rant about this in a future post), principles and tested practices. They can justify their values and principles and constantly test their practices. aahhh, this is getting a little to zen.

Stepping down from my high horse: Before I was a developer, I was a programmer. I've been guilty of everything I've spoken of in the negative. I try to improve the readabilty of my code, with every line I write, leaving in a better state than I found it. My success varies, but over time I'm improving ... I have a long way to go. I would appreciate your comment.

Sunday, August 12, 2007

Conceptual Integrity

Conceptual integrity of your software solution is arguably one of the more important factors to focus on, increasing understandability, maintainability etc.

One way to maintain conceptual integrity is to apply the Domain Pattern. Two good resources covering Domain Driven Design (DDD) are:
•    Domain Driven Design, Eric Evans 2004
•    Applying Domain-Driven Design and Patterns, Jimmy Nilsson 2006

An application’s conceptual language takes centre stage in DDD, allowing business owners and developers to talk the same language. A clear, simple conceptual language shortens the divide between business analysts and developers, during their feature discovery cycles.

I can’t stress enough: Good software is all about good communication. Agreement between project stakeholders, to work toward a simple conceptual language modelling real-world processes will improve the project's chances of success.

Maximise understandability by minimising overloaded meaning.
If there is more than one word or phrase to describe a process, try one of the following:

  • Look for a more specific word, that narrows meaning. "Entity" is a pet hate of mine. Seriously, unless accompanied by a domain specific context, get rid of it. Substitute it for "Thingy" - at least it will scream attention as you increase domain fidelity.

  • Agree to use one word and actively ensure all conversation participants support the decision. This decision should come from the business owners.

  • When neither word suitably describes the overloaded meaning, the issue is usually one of context. Extract the contextual meanings and distill into one description for each context. Agree on a single abstract word for the original overloaded word. The abstracted word and one of the context descriptors will then be used to narrow meaning in the domain.


Step 12 in “The Joel Test”, which documents 12 steps to better code, asks if you do hallway usability testing. I feel this testing is really challenging your Domain Model using your conceptual language.

Continually test your domain model's power, flexibility, complexity and conceptual integrity:

  • Can it answer a question required for your next iteration?

  • Is the answer way to complex or a little vague?

  • Are there several ways to answer the question, and if so, is it an early indicator of a domain model or conceptual language smell?


This is my way of hallway testing…as a spontaneous design session. Don't be afraid to put your domain model, or parts of it, on the wall for all to see. Put post-it notes on the model with questions you expect the model to answer (i.e. Can user x searh all assets owned by business unit y?). Encourage others to comment, question and amend through this shared process.

Remember, the more "cold" eyes on a problem area, the better chances that problem has of being resolved properly.