Some Principles
I decided that before I post further thoughts, it's important to talk about four of the important principles Dave and I use to guide our thinking. I've reproduced them here from a post on pliantalliance.org. You may want to visit there to see some of the discussion. The text below was written in July 2003.
Solve the real problem.
Whenever possible, solve the real problem. It's hard to explain it better than the following. [Yes, I'm going to quote a "capital-A" Agile guy. It's the dogma that bothers us, not all of the ideas. I'm not entirely sure where this quote of his idea is originally from.]
Managing Technical Debt
Ward Cunningham sometimes compares cleaning up the design with paying off debts. Going further, he discusses managing the technical debt on the project.
Making hasty additions to the system corresponds to borrowing against the future, taking on debt. Cleaning up the design corresponds to paying off debt.
Sometimes, he points out, it is appropriate to take on debt and make hasty changes in order to take advantage of opportunity. Just as debt accumulates interest and grows over time, though, so does the cost to the project of not cleaning up those hasty design changes.
Cut corners in the design, he suggests, when you are willing to take on the debt, and clean up the design to pay off the debt before interest grows too high.
Solve the whole problem.
This is the corollary to solve the real problem. If you are building a function, a class, a library, an application, or a system, by solving the real problem, you make sure you clearly delineate the areas of responsibility of yourself and others using a well-defined interfaces. By solving the whole problem, you ensure that the well-defined interface best represents the most appropriate division of responsibility. This idea can be represented by the notion that a system component should make the work it does significantly easier for clients if they use the component versus doing it themselves.
The implementation is more flexible than the interface.
If you have well-defined interfaces dividing areas of responsibility, then you “write to the interface”. Doing this properly and taking advantage of the abstraction requires designing the interface to match the division of responsibility (i.e. solving the whole problem) and then writing the code to implement as much of that interface as is necessary (maybe all of it). The wrong thing to do is to write an interface that matches (read “exposes”) your current implementation out of convenience and then be jailed to that implementation forever.
Never let perfect stand in the way of very good.
It may be tempting to wait until you have the time, the knowledge, or the inclination to design the “perfect” solution, especially if you aim to solve the real problem. However, you must temper this instinct with practicality. While it may look like you are holding out for “the perfect solution” (that solves the whole, real problem), what you are really doing is preventing “the very good solution” that can be applied in the interim. We must recognize that perfection can only be approached asymptotically through evolution of design and implementation: in essence by refining our deployed very good solutions to make them more perfect. Solving the real problem should be applied where scope allows and it should guide our path to tell us where perfect is, but we are allowed to get there in more than one step. Very good solutions have value, and value delayed is value lost.
0 Comments:
Post a Comment
<< Home