Saturday, July 5, 2008

Sometimes Older is Proper (not better)

Thursday was an interesting day - I found myself challenged in a completely unexpected manner. We are in the final stages of formal QA before they enter into regression testing, and I have been doing a lot of work in the legacy application.

Throughout this whole project, I have been able to develop in a TDD fashion far more than I ever have before. It has gotten to the point where even to look at a problem is to solve it in a testable fashion. Where the legacy application was concerned, we generally took an approach of creating "targeted" presenters using a customized version of the MVP pattern.

When I say "targeted" presenters, I mean that we layered the new functionality directly into the existing pages, but decoupled it with a presenter that was used solely for the new functionality. This approach has created several problems, and I'm not sure I would recommend it for future use: it turns out that where the two styles of development interact, the code is very brittle.

One place where the interaction is most sensitive is in the save processes. The legacy code looks back to .NET Framework 1.1, and performs validation and persistence of the existing objects before the MVP code gets persisted. Because we don't want the 2.0 code referencing the 1.1 code, and the 1.1 code cannot recognize the 2.0 code, we cannot wrap the persistence between the two sets of components into a single transaction. Needless to say, there is some really ugly code involved in these processes.

The defect I was working on on Thursday was even worse. It involved code in a class that was termed a presenter, but a crucial step in the processing had been missed. So I spent about 8 hours layering in a fix that referenced a specification and other existing code, and did some ugly hoop-jumping to maintain the state of the view until all the persistence processes were complete in both code-bases.

I was testing and getting ready to check in when... oops. The presenter was used by two different views, both of which were parts of very different implementations of the legacy code! Since this was a legacy app, and the "presenter" was not fully tested, I was not aware of this through failed unit tests. Further, the presenter and views are defined in a separate solution, so I wasn't easily to see where the view was used via Resharper. Suddenly my state preservation model didn't look so good, and with much agonizing and advice on the part of a co-worker, I threw away my solution and stopped treating the "presenter" code as a presenter. Instead, 3 hours later I had the code-behinds on the aspx pages calling into business methods on the "presenter" class and all worked appropriately - at least as far as I could tell.

What bugs me is that it seems that I've become so committed to coding in a testable fashion that I couldn't even see this solution in the first place. And when it was suggested to me, I agonized for a few minutes before I could accept that it was the right way to go. I don't think that this is necessarily right and proper. As developers, we want to do the best job for our employers that we can, and when producing new code, that means TDD. However, when working with legacy code, sometimes TDD is probably more like putting lipstick on a pig - it looks good but does it really lead to the best solution?

Chad Myers recommended that evening that I read Michael Feather's book on Legacy code. Ironically, I've started the book already, just haven't gotten too far into it. I think that I will make a point of finishing it this month.

Subscribe in a reader

Wednesday, June 18, 2008

Factory Patterns and Lightweight Entities

We're coming to a milestone in our current project at work, and it's had some real eye-openers. In one project, I've seen the effects that traditional, heavy-handed development can have and the freedom that can be bought through the exercise of best-practices.

Of all the areas that I've grown these past few months, the one I want to focus on tonight is my appreciation of Manager and Factory objects and lighter-weight entities. Coming into the project, I had an academic understanding of factories, and had used them in the past because they were part of the overall project architecture. However, on this project, we were teamed with several developers who preferred to code with heavy entities. Now, the project is coming to a close, and we have discovered the following:

1) Entities can get too big. When you have an entity that has all of it's properties and methods in one object, it tries to do too much. Changing any part of it has risk for every consumer of that entity, and the bigger the entity, the larger the risk (because the size is probably a reflection of the amount of use).

2) An entity's properties should be limited to the core properties that define what that entity is. Period. An automobile object should not have a property of "stereo". That's just clutter.

3) Property getters and setters should be very simple and basic. We have run into more than one case where the setter of a core property had too much purpose-specific logic in it that prevented the property from being used in any other purpose or context. Corrections were done, but in a fashion that is less readable and understandable than it should be (why does a manager class have to call a method called SetPropertyValue on the entity instead of the Property setter itself?). The technical debt keeps piling up.

4) Manager classes are not limited in the number of methods and the manner in which they operate on entities. Care should be taken to make sure that duplicate code is not written, but even if it is, the refactor is easy and the impact on other code is much lighter. If a Manager class starts to serve too many purposes, break off another one: PersonManager works great for basic Person functions, but you may need a CarpenterManager or DeveloperManager to operate on a person in a different capacity.

5) Factory classes are also flexible for returning the very "properties" that you've resisted putting on the entity. The AutomobileFactory can have a "GetCarStereo" method, but can also repurpose to have a "GetCDPlayer" method as well - both of which work with the same Automobile entity.

I know that a lot of this is really basic stuff, and most developers who've been coding TDD and Agile-design for some time are wondering why this should even be part of a post. Well, there's a difference between knowing something from a book or because you're told to do it, and learning it first-hand through experience. I am much better able to defend these ideas today than I was a couple of months ago, and am a better programmer for it.

Working through the basics as you learn to develop can be extremely beneficial. This is part of the growth process I spoke about in my first post- the difference between acting on faith and trust and actually owning what you're doing.

Subscribe in a reader

Sunday, June 1, 2008

This is my first real blog entry, and so it seems like a good opportunity to unload some thoughts I've had about Agile, Agile Practices, the agile community and TDD in general.

I don't know that I deserve much credibility, but I am a professional software developer with over a decade of experience. Roughly two years ago, I was exposed to TDD and agile concepts by Chad Myers and Ray Houston when they were hired as consultants with a former employer. Prior to that, my experience had been all waterfall, beginning with the company that wrote the book on waterfall, EDS.

Shortly after learning some basic agile concepts from Chad and Ray, I left that employer for the promise of professional growth in a more open environment. After that didn't work out, I found myself at a company where I am allowed to try and improve my skillset and practice what I have learned. However, this is in the context of diving back into old methodologies and practices as the current projects demand. At present, I have yet to see a project through to completion using agile methodologies. I have written several hundred unit tests, but often these were done post-development, so I could also say that I have not done any true Test Driven Development in a project from start to finish.

It is interesting that when a changing influence comes into your life, the first thing that has to change is you. This is true no matter what the changing influence is: whether Christ, family, diet, or agile. If you are not open to receive the new ideas and embrace them within yourself, then that change cannot happen. Also, the change isn't something that's going to just happen to you: you have to work at it to supplant the old with the new. Having embraced the new idea, you have to work to rid yourself of existing habits and practices that are contrary to the new ideology, and practice those tasks brought by the new ideology until they become habit. This takes a high degree trust that the new ideology will deliver what it promises, because invariably, change involves risk.

Once you have begun to change yourself, it is natural to want to influence others to believe as you do. Working with others who share your beliefs makes accomplishing tasks much easier. Additionally, you do not feel isolated or alone, and as you experience more success, others witness that success and are drawn to the ideas, empowering even more and greater success. However, anyone that you share your beliefs with has to overcome the same challenges that you did when you first received the new ideas. There are several factors that influence how readily someone can accept new ideas:
  1. The strength of the idea

  2. The degree of trust in the messenger who is communicating the new idea; and

  3. The degree of success and comfort the individual has with his previous ideas (a negative factor).

In order to gain acceptance, any new idea or philosophy must have some inherent appeal. For agile, this appeal is the hope of future success and the promise of remediation to existing failures and frustrations. For a software developer or team that has been mired for years in the frustrations typically associated with development (unrealistic deadlines, long hours, tense relations with management, difficult code maintenance cycles, etc. etc.), this is a tremendously strong appeal. But is it enough?

Why should anyone who has been developing for years immediately trust that this new idea can work? Sure, all the big software companies do it, or so they say. But if you've been caught in the mind-numbing cycle of meeting deadlines and then fixing the bugs generated trying to meet those deadlines, you're probably not able to do a lot of research on your own. How can you verify that the new ideas are valid? This is where trust in the messenger comes in to play. You can learn to trust someone for several reasons: previous associations, good reputation, well-reasoned arguments. All of these serve to build that trust, but ultimately it comes to an act of faith - saying that the strength of this person's argument alone is worth a risk.

So, ideas 1 and 2 have been established - there's a strong new concept and it has been proposed by someone you trust. Step 3, giving up what you "know", is often the hardest. Even when "what you know" is unreasonable and demonstrably unproductive. We see this played out in life all the time: the battered wife who continues to return to the home of the batterer; the alcoholic that continues to ignore help; ignoring calls of creditors rather than changing your spending habit. People want to stay with the familiar because it is more comfortable than the risk of change. From experiencing various needs to change personally, I've learned that overcoming yourself is the most difficult and challenging task of all.

You can start a project using TDD, but when it gets close to the deadline, you're instructed to leave off the testing for now because it takes too much time to write the tests. Or developers around you don't see the immediate benefit of testing because it's too hard (you do, after all, have to re-learn the way you write code). Or, you sabotage yourself: you're in the middle of a project, the deadline is coming up but you get pulled into too many other tasks and aren't meeting milestones. So you resort to what you know, and the tests get forgotten.

If you're going to become successful, you need to go back to the tests at every opportunity. Try to keep them going, evangelize to those around you. Ultimately, with patience and time, you will build momentum and start to see real change. Other developers will start to trust the quality of your work. Management will see the shorter testing cycles. Demonstrating solutions to problems becomes much simpler, and light-bulbs start to go off around you. Factors 1 & 2 start to become evident through you, even as you work out factor 3 within yourself.

This is a process, and there may be many short-circuited projects during this time. But when you've seen the goal, all you can do is to keep pressing. To borrow from Paul in the book of Phillipians, Chapter 4:

12 Not that I have already reached [the goal] or am already fully mature, but I
make every effort to take hold of it because I also have been taken hold of
by Christ Jesus. 13 Brothers, I do not consider myself to have taken hold of it.
But one thing I do: forgetting what is behind and reaching forward to what
is ahead, 14 I pursue as my goal the prize promised by God's heavenly call
in Christ Jesus.


You just have to let patience be its own reward - for a time.

Hello and Please Bear with Me

I've been encouraged to blog for some time now, and certainly don't seem to be much at a loss when talking with my co-workers about our life and habits as developers. So, why not share some of them on a blog? I don't expect to change the world, and if you're looking for revelations about the best, latest way to build software, you're probably in the wrong place.

Rather, I would like to think of myself as developing from the trenches. I have definitely caught the TDD bug, and can see the merits of agile-related practices. However, I have not yet had the opportunity to work in a fully-agile shop. So where does that leave me? As someone that is trying to teach those around me the benefits of what I've learned and trying to grow further in this "new" path myself.

It may be an interesting trip, and you're welcome to follow along.