When refactoring is no use

TDD is about design. It’s not about testing.

Last week an interesting thing happened while I was working with a team. They had been writing a new application with TDD since last June, and they were successful in that the application was nearly ready, on time and on budget. But there was one thing that was worrying them.

They had been following an article on the Model-View-Controller pattern, and had structured their application accordingly. There was a controller for every window, and there was a model too. (It’s not a web application, it’s Swing.) The view object was extremely thin, as it only contained code for constructing and laying out widgets; absolutely no other logic in there. The controller hooked listeners to the appropriate UI widgets.

So everything was OK, no? A clear architecture, a clear separation of concerns, everything tested. There was a problem: the controllers were big objects, with intricate logic. The tests were difficult to read and difficult to write. It was difficult to understand what was being tested. My worry was that maintenance of that GUI could quickly turn into a mess. Also my customer was worried about that. The fact is, whenever anything had to be changed about the logic of that window, the controller would have to be changed, and the view would have to be changed, and the model would have to be changed. We had three big monoliths that were involved in everything. This ran against the open-closed principle.

My first reaction was to call upon my refactoring skills. Take the unreadable tests and make them readable. Use extract method and expressive names to make them read like a story about what was being tested. Some success there, but not a clear victory. It was still difficult to find the code being tested, as those were end-to-end tests. Then I found the bit that contained the important logic and wrote proper unit tests for it. Some success, as I was able to test with precision all the corner cases that the end-to-end test was not exercising. But still not a clear victory. Then I started to work on the big controller. I took the worst one with me in my hotel room, and I spent a chunk of my evening trying to refactor it. Extract method, rename, extract class. Extract, extract, extract. It was still big and ugly. With a bit of polish, but still big and ugly. Still running against the OCP.

The next day I pulled a different set of tools. I remembered the lessons in Francesco Cirillo‘s Emergent Design Workshop. I showed the team how to use Francesco’s technique, which is to start with a design of the communicating objects on the whiteboard. After one day of practice with this technique, we attacked the real problem. We started designing the main application window from scratch.

The team was surprised, and I was surprised as well, because the big controller evaporated. It was clear, when working at the whiteboard, that it was not necessary. The model object evaporated too. We designed the main scenarios, and we saw that a simple design could accommodate all of them. At first I sketched the design for them, but they were soon criticizing my design and changing it to suit their needs and tastes. Then we started coding; it went smoothly because the design was already done, and we were taking advantage of all the low-level objects that they had implemented already.

We learned is that when the design is wrong, no amount of refactoring moves will get you out of the hole.

7 Responses to “When refactoring is no use”

  1. Gicappa Says:

    So interesting. But I’m curious: at the end the mvc pattern evaporated with the big controller or it was still there?

  2. matteo Says:

    Controller gone. “Model” gone. They were not needed :-)

    You could say that there’s now a collection of objects that take the place of what was the controller.

  3. Franco Lombardo Says:

    Matteo,

    you say: “TDD is about design. It’s not about testing.”
    Well, it seems that (also) this time TDD failed to create the right design! :-)
    I think that TDD is sometimes overestimated as a design tool. Well, I mean, it’s a great tool for “tactic” design, not for the “strategic” one.

  4. Mikalai Alimenkou Says:

    At first, I want to note, that it is very strange not to use MVC pattern with pure MVC framework like Swing. It is hard to identify what was the root of your problems, may be you don’t have any other layers in your application and have all business logic inside controllers, may be you don’t use other UI design patterns like Mediator, Observer, etc. But it definitely not MVC itself. From my experience all elements of the MVC with Swing should be as thin as possible, delegating all logic to other layers. In this case they are easy to test with both unit and funtional tests.

    At second, I agree with previous comment. TDD a great tool for “tactic” design, not for the “strategic” one. TDD has little impact on overall architecture, much more on low level design decisions. But it is again strange to me why you ignored the pain of testing your large controllers and didn’t do something to reduce it. TDD doesn’t solve problems (you fix them yourself), it makes them visible.

    At last, I want to add some words about refactoring. It is rarely possible to fix architectural problems with basic refactoring practices like method and class extraction. They are used to improve local design, not the whole approach. Design meetings are the best tool to improve application design and architecture. After decisions are made you also apply refactoring (I hope :) you will not rewrite application from scratch), but this is another type of refactoring. It is refactoring to patterns, refactoring to new design solution. This refactoring is also based on small simple steps, but the goal is different – apply new better solution. There is great book on this theme: “Refactoring to Patterns” from Joshua Kerievsky.

    In any case I’m happy for you, because you identified issues early and avoid large problems in future.

  5. matteo Says:

    Hi Mikalai,

    Thank you for your comment.

    It’s difficult to reason here about which is the best way to solve a coding problem, as I don’t have an example of your style of Swing coding, and I can’t show you my customer’s code.

    I agree that whiteboard design sessions are important, in fact that’s what we did in our case. I agree that knowing patterns like MVC and others is important. But I also think that it’s better not to start with a preconceived idea like “it’s a Swing app so I will apply MVC”. It might work well without MVC. It’s better to let MVC emerge from the design session, if indeed it’s needed.

  6. Extreme Enthusiasm » Blog Archive » When refactoring is no use « ZenAndSoftwareDevelopment Says:

    […] Extreme Enthusiasm » Blog Archive » When refactoring is no use. Pagine […]

  7. matteo Says:

    @Franco: I would not call it a complete failure. We still were able to reuse all of the lower level pieces that were built with TDD and had the responsibility of displaying various bits of information. It was the high-level design that was lacking. It was the first TDD project for that team.

    I would say it was partially successful, and I took a few days more of training-on-the-job to turn it into a success. :-)

Leave a Reply