Geek in a Suit

Thursday, February 11, 2010

Shu-Ha-Ri not harmful... it's misunderstood and mis-applied.

Rachel Davies, for whom I have incredible respect, posted this post called "Shu-Ha-Ri considered harmful". In it she points out that the basic notion of Shu-Ha-Ri from Aikido of graduated styles of learning - the novice, the advanced student, the master should learn in different ways, the novice learning more by rote, and more importantly, from a single master, the advanced student trying the techniques in varying ways and comparing styles, then the master innovating new combinations and techniques. Cockburn, often cited in the Agile community somewhat gets this, but his adaptation varies from this concept just a bit.

Rachel has an important critique, which I accept. She points out that agile boot-camps and other styles of training for organizations and groups and teams in Agile practices often cite Shu-Ha-Ri, and require that teams do "just the scrum by the book" (or whichever method is to be used), at least initially. Then, as they master these techniques as they are put forth, they can adapt. She, however, sees a disrespect for the unique circumstance of the student in this. In her words, "I'm uncomfortable with approaches that force students to follow agile practices without questioning." I agree. But this is not what Shu-Ha-Ri implies. Shu should always include questioning... but the student should test the technique as presented, and part of the discovery is finding out its limits - how it works, and how it does not. But it's an experiment. It requires a control... and the control is the basic form, attempted as instructed, to get a baseline. Teaching any technique, physical or mental has a similarity in that respect. Is the metaphor limited - yes, and I argue that it is the agile-boot-camp folks who often mis-apply the martial arts concept.

I understand the concern she raises, especially the respect for context and unique character of the teams and the flexible nature of knowledge work... but this betrays a misunderstanding, or mis-application of Shu-Ha-Ri. Shu doesn't imply that the students are fungible. Technique is still taught in the context of the student (team). Sutherland has it wrong when he days "only when you have mastered the basic practices are you allowed to improvise." In Aikido, the students who are in a "Shu" mode are not improvising with "different" techniques, but they are applying them in different situations, and seeing how they fit. One adapts HOW to do the technique for a tall person, for a short person, a heavy person, and advanced Aikidoka, an unranked novice, etc. Likewise with Scrum, you apply the technique, but the coach helps the team use the technique in context. That's Shu. Ha then is where a team combines the techniques in unique ways. They may remove a practice, or replace it and see how that fits. Ri (mastery), they are inventing new techniques, or altering the basic forms in different ways. This is all quite reasonable, even in an agile coaching context.

In Aikido, especially, students practice the techniques in multiple contexts, so they can get a sense of the suppleness. Students are asked not to innovate initially, nor combine techniques before they have at least mastered the basic technique itself - so they're not thinking through each step of a move while they're doing it - they "get" it. They they can move closer to innovation.

Rachel's post, while understandably compassionate, confuses two separate things... models of instruction, training, and practice with notions of respect, oppression, and dominance. Telling a student to try the basic move and get it better before expanding isn't disrespectful, it's understanding the learning models of the student. In practice, it is quite possible for a student to grok the technique more quickly, and if Sensei observes this, he will show the student something slightly more advanced, and have him practice this. Or, Sensei may see the student struggling to apply the move, and may change the context to let the student appreciate what's happening. The point of Shu-Ha-Ri is, as Rachel points out, to ensure that one doesn't miss the basics while playing with the innovative and the expansive. If it's being used to hold a student back, or somehow contains a disrespect for the student - that's a failing of the teacher (coach). Student-sensei relationships are adaptive to the needs of the student, else they become a mere posture of dominance and submission, without the deeper communication that's supposed to occur within a relationship of trust.

I feel Rachel is (unintentionally and understandably) mischaracterizing Aikido and Shu-Ha-Ri based on mis-perceptions prevalent in the community on how its concepts can be applied. Aikido is taught in many forms, but Agile is not looser, nor is Aikido tighter a discipline. Aikido is, in any good Dojo, taught with great sensitivity to the needs, the capacities and readiness of the student. It is taught to groups, individually - that is to say, it is demonstrated to the group, then practiced in pairs, with Sensei observing, correcting... coaching. If anything, rolling Agile out in a large organization, and taking large groups through paces in boot-camps which do one-size-fits-all is UNLIKE Aikido training.

Bootcamps fail to do Shu-Ha-Ri if they insist that all steps of Shu are learned at equal pace by all learners. This is not Shu-Ha-Ri being harmful - this is Shu-Ha-Ri being ignored.

Labels: , , , , , , , ,

Saturday, March 28, 2009

Hiatus... should be over

So apologies that I haven't posted anything - I never intended to be away from the blog for this long, so soon after starting it. I have recently been in transition, and am now working for Google as an internal software development coach. Having disclosed that, I should mention that this blog isn't Google-specific, and mostly the things I'm speaking about here are things I have seen elsewhere, fodder from discussions with other Agile Community members and practitioners, and may include aspects from my current job, but not specifically, and usually blended in with the former. In other words, I won't be leaking Google's secrets on this page. ;)

Having said that, it's so far quite a fun place to work, with a wide variety of development cultures. There's a great spirit of exploration and experimentation in my new firm, which allows different teams to try different things. I leave it as an exercise for the reader to see how this can be challenging, organizationally, but it certainly allows G to innovate - as is nicely shown in the marketplace.

Anyway, I have four or so articles partially written from before that I hope to publish in the next couple of weeks. It's been an adjustment (haven't been an actual employee for quite some time now), but I'm starting to get a bit of a stable schedule so I can pay attention to this sort of thing (blogging).

Cheers,

Christian - the Geek in a Suit

Oh, P.S. I wore a suit to my first meeting with the team I'll be working with, and got soundly ribbed for it. lol. It's ok... I'm a big boy. I can take the heat. -cg.

Labels: ,

Friday, January 23, 2009

Are mocks just stubs by another name, or something more?

[An older article that I published internally to a client community, reprinted with changes to remove client references]

An opinion that I've run into among some of my clients is that mock objects are just what we've always called stubs, and that it's an old approach. It's actually a quite common perspective - one I have held myself for part of my career. In fact early mock object approaches were very much like "fake implementations", but modern mocks are different. While they can both be considered "test doubles" or "test stand-ins", stubbed out interfaces or provide expected data for the system under test, mock objects provide behavioural expectation. These terminologies can be confusing, but we can sort that out.

jMock and EasyMock are two examples of mock object frameworks which allow for the specification of behaviour. jMock uses a domain specific language (DSL) such that you code the expectations in a fairly verbal syntax (often called a fluent interface). Something along the lines of

myMock.expects(methodCall(SOME_METHOD)
.which().returns(A_VALUE))
.then().expects(methodCall(OTHER_METHOD))

... which should be vaguely like english to the initiated. EasyMock, on the other hand, uses a "proxy/record/replay" approach instead, which some find easier. The point is that they both define a set of expected interactions, rather than a first this state, then the next state only.

Martin Fowler, around the middle of his article "Mocks Aren't Stubs", after describing fakes vs. mocks approaches in more detail. He starts to use a clarifying terminology which I like:

"In the two styles of testing I've shown above, the first case uses a real warehouse object and the second case uses a mock warehouse, which of course isn't a real warehouse object. Using mocks is one way to not use a real warehouse in the test, but there are other forms of unreal objects used in testing like this.

"The vocabulary for talking about this soon gets messy - all sorts of words are used: stub, mock, fake, dummy. For this article I'm going to follow the vocabulary of Gerard Meszaros's book. It's not what everyone uses, but I think it's a good vocabulary and since it's my essay I get to pick which words to use.

"Meszaros uses the term Test Double as the generic term for any kind of pretend object used in place of a real object for testing purposes. The name comes from the notion of a Stunt Double in movies. (One of his aims was to avoid using any name that was already widely used.) Meszaros then defined four particular kinds of double:

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
  • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.
  • Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

"Of these kinds of doubles, only mocks insist upon behavior verification. The other doubles can, and usually do, use state verification. Mocks actually do behave like other doubles during the exercise phase, as they need to make the SUT believe it's talking with its real collaborators - but mocks differ in the setup and the verification phases."

This use of behavioural expectations for "Test Double" objects is quite handy, especially in systems where you have components which make use of heavy service objects or (gasp) singletons which may do more than a simple "call-and-answer" method-call on the object. Deep call chains may require stronger behavioural testing. Being able to provide an object to the system under test that expects to be called a certain way and in a certain order can create a much more precise test, and reduce the amount of code you have to write in a "fake" object. Otherwise, to fully and precisely test, one ends up with severely large number of Test Dummies and ballooning "Fake Objects", which themselves could have error and often have to be tested. Having a system like jMock or EasyMock can reduce the size of your testing code, thus removing some code that could easily become out-of-sync with the system under test, and either introduce false-positive errors or become insufficient and therefore meaningless. So less code, less maintenance, less syncing problems, and the tests are able to be more precise at the same time.

Another approach is to radically re-factor your code into smaller components, each of which is substantially simpler to test. If a component does one thing and does it well, and components interact and collaborate, then you often can use simpler mock behaviours, or simple fakes without as much effort on the testing. Great resources for testable code are available here .

Mocks aren't always easily intuitive for someone used to building fakes (it wasn't for me, and it still occasionally trips me up), but once you're comfortable with the approach, it can be much crisper. This is especially true as people try to get better coverage in isolated unit tests, or who are trying to test-drive their software. The linked Fowler article is a good one, and certainly worth reading for those trying to figure out how to more meaningfully test components without having to start up external servers or simulators.

Labels: , , , , , , , ,

Friday, January 2, 2009

Why I hate the java.util collection library.

<rant>

It's very simple. While it was a vast improvement over Vector and Dictionary of their day, the Collections library as of Java 1.2 did what all new Java APIs from Sun seem to do... require lots of code to use simply and allow the user to do invalid things.

I won't spend a lot of time on the former, since it could be the topic of a whole other blog post, and I should preface all of this by saying that Java is my most proficient language. So this is not an anti-Java bigot speaking... just a frustrated user who wishes Sun and the community wouldn't keep heaping bad APIs on top of bad APIs.

Sorry... back to the point.

Immutability - it's all backwards... what's with that?

The big issue I have with the Collections API are about allowing the user to do wrong things. This amounts to Sun having inverted Mutability vs. Immutability in the class heirarchy. Immutability, in any language that wants to guarantee semantics, ease concurrency and resource contention, and otherwise clean things up should have immutability as a default. Something set shouldn't be volatile or mutable unless specified as such, and the semantics should enforce this. But with the collections API, we have immutability as an optional characteristic of Collections. To use a simplistic example, you can do this:

Collection c = new ArrayList();
c.add(foo);
c.add(bar);

Collection has an add() method. This means that, by definition, Collection is mutable. "But wait!" you cry, you can obtain an immutable version of this collection by calling Collections.immutableCollection(c);. Sure. At which point you have something that conforms to the contract of Collection, but which may throw exceptions if part of that contract is relied upon. In other words, you should not have access to methods that allow you to break the contract. To allow this is to have written a bad contract with ambiguity. Now, to properly guard against the possibility of stray immutable collections, you may be forced to check immutability before executing the contract (the add() method) or you may have to guard against the exceptions with try-catch logic and exception handling. You can see some of this in the concurrent library's implementations of concurrent collections. It's not bad, but could be simpler with an immutable collection interface.

Additionally, consider how hard it is to create anonymous one-off implementations of Collection. You have to implement not only size, contains(), iterator(), but all the storage logic. If you're wrapping an existing data structure and merely wanted a read-only view on the structure, you are forced to implement all that extra API in your code purely to satisfy the optional contract provided in the Collection definition.

The point here is that an immutable Collection is a sub-set of the functionality of a MutableCollection. That should be obvious, but apparently wasn't to Sun. Consider had Sun used the model used in NeXTSTEP (and now Apple's Cocoa) APIs. Collection would have been defined as (simplified):

public interface Collection<T> extends Iterable<T> {
    public Iterator<T> getIterator();
    public int getSize();
    public boolean isEmpty();
    public boolean contains(T object);
    public boolean containsAll(Collection<T> object);
    public T[] toArray(T[]);
}

and

public interface MutableCollection<T> extends Collection<T> {
    public boolean add(T object);
    public boolean addAll(Collection<T> objects);
    public boolean remove(T object);
    public boolean removeAll(Collection<T> objects);
    public boolean retainAll(Collection<T> objects);
    public void clear();
}

This would, ultimately, mean that a Collection instance, typed as a Collection would not have any mutable methods available to invoke, let alone that would need guarding against stray immutable invocations. There would then be two strategy for guaranteeing immutability. One... cast the stupid thing as Collection, and onothing that has access to the cast can get at the MutableCollections methods (except by explicit reflection). Alternately, add a "getImmutableCopy()" method to the MutableCollection interface that creates a shallow copy that is NOT an implementor of MutableCollection... merely of Collection. Then you have a safe "snapshot" of the mutable object that can be freely passed around without worry that something else will modify it.

Ok, why is this such a big deal? It's about having code that means what it says. If I have to guess about the run-time state, or more concrete type of an instance to know if it's OK to invoke one of its methods or not, then I'm working with implicit contract, and that's murky territory. Java, by making an ImmutableCollection a special implementation of Collection, has inverted the hierarchy of contract, and exposed methods that are not truly available for all implementors. Optional interfaces are fine, but you don't expose the optional interface above where it's true. It's a basic piece of encapsulation that the Java folks just seemed to forget.

Now, this is a decade too late, this little rant. Truth is, I made it when I worked at Sun, but was quite the junior contracting peon, and had no real voice. Now, I have a blog, and am free to whine and be annoyed in public. :) But I hope to make a more general point here about contract. Optional contracts (APIs) need to be handled very carefully, and in a way such that an unfamiliar programmer can understand what you meant from how the contract reads. Look at your interfaces from the perspective that it should not offer what it (potentially) cannot satisfy. Polymorphism doesn't require "kitchen-sinkism" in an API. Just careful, thought-out layers.

The issues of testability also arise here, insofar as a class that has to implement optional APIs must, therefore, have more tests to satisfy what are, in essence, boilerplate code. If I made a quick-and-dirty implementation of Collection as a wrapper around an already existing, immutable data structure, then I have to implement all of those methods and test them, when in fact, half of them will throw an exception. This means (to me) that they probably shouldn't even exist. Code with a lot of boilerplate code (lots of extra getters and setters, or over-wrought interfaces) tend to be hard to test, and one of my big annoyances in life (these days) is testing boilerplate code. Ick.

</rant>

Labels: , , , ,

Thursday, December 11, 2008

Testability - re-discovering what we learned and forgot about software development.

(or, why agile approaches require good old-fashioned O-O)

What are we all talking about? (the intro)

Testability comes out of an attempt to understand how agile processes and practices change how we write software. Misko Hevery has written some rather wonderful stuff on his blog, and starts to get into issues around singletons, dependencies, and other software bits that get in the way of testability, and starts to look at testability as an attribute. (Full plug at the end of this post) But in particular, he also starts looking at what design and process changes can we start to use to make code more testable. And while we're at it, what is the point? Is testability the point? It's important, especially as a way to remove barriers from working in an agile environment, if that's what we've chosen. There are reasons related to quality as well. But I think there are some deeper implications, which Misko and others have implied and, on occasion, called out. It's that we've forgotten the point of Object-Orientation and what it was trying to achieve in the 80's and 90's, and are re-discovering it.

What have we forgotten? (the reminiscence)

But what is the essence of what Misko is saying? Martin Fowler (coiner of the Inversion of Control term) and others have written wonderful articles on "Law of Demeter" and other principles. In general, they are all looking at how we grow software, and between all the thinkers and talkers and doers, it seems to me we're re-discovering some key concepts that we all learned in college but forgot in the field. The key points are:

  1. Manage complexity by separating concerns and de-coupling code.
  2. Map your solution to the business problem.
  3. Write code that is not brittle with respect to change.
  4. Use tools that empower your goals, don't change your goals to fit the limits of the tools.

In other words, what we all learned when we were taught textbook Object-Oriented Analysis and Design and Programming. Now, O-O has had its various incarnations, and I would contend that all the architectural threads of AOP, Dependency Injection, as well as a more conservative take on O-O have all stemmed from these key principles which were at the core of the Smalltalk revolution and early attempts to get rapid development cycles, and what is often now called "agile" development.

How did we forget all this? (the rant)

So why did we forget all this? Five reasons, I suspect:

Selling Object-Orientation

We did a crappy job of selling O-O. You might not think so, since O-O is so prevalent (or at least O-O languages are). However, we didn't sell the 4 notions I mentioned above, we sold business benefits that were, in essence, lies. They didn't need to be, but usually were. These are things like "O-O will make you go faster because of reuse," or "O-O will help you reduce costs because of reuse," and on and on. These can be true, but are usually the result of a longer evolution of your software in an O-O context, and the costs of realizing the benefits that we sold often would be too high for businesses to stomach. In fact, O-O won, in my view, because managing complexity became fundamentally necessary when software scale became huge.

Cheap, fast computers

Fast computers have allowed us to do so many bad things. Room to move and space to breath unfortunately gave us less necessity for discipline. We removed the impetus to be efficient and crisp and to think through the implications of our decisions in software. However, we're catching up with hardware in terms of real limits. Moores law may still apply, but we are starting to have limits in memory. A client of mine observed that Google is an interesting example. He works for an embedded software firm, and noted that they have probably similar scaling issues as an embedded (say, phones or similar devices) device company, because sheer volume of traffic forces Google against real limits, much the way resource constraints on a telephone forces those companies against their constraints. However most of us live in a client-server mid-level-traffic dream of cheap hardware so that we can always "throw more hardware at it".

Java

Java is an O-O language, and really was the spear-head that won the wars between O-O and structured programming in the '90s. However, bloated processes and territorialism have kept Java from fixing some of its early issues that prevented it from solving problems such as I mention above in efficient ways. The simple example is reflection. If a langauge requires that I create tons of boilerplate code (try-catch, look-up this, materialize that) to find out if an object implements a method, and then to invoke it, it needs to provide a way for me to eliminate the boilerplate. If it can't do it in a clean way, it should at least provide convenience APIs for me to do the most common operations without all that boilerplate. Sadly, the core libraries of Java were bloated even in the beginning, because of the tension between the Smalltalk, Objective-C people on one hand, the C++ people on the others, and Sun not caring really, because they were a hardware company. So because Java won the O-O war (don't argue, I'm generalizing) its flaws became endemic to our adoption of O-O practices. I'm going to mention that J2EE bears about half of Java's responsibility, but I'll leave that for another flame. Nevertheless, the design and coding and idiomatic culture that spawned from these toolsets have informed our approaches to O-O principles for over a decade.

The .com bubble

The dot-com bubble compounded our Java woes by introducing 6-month diploma programmers into the wild - nay, into senior development positions, and elevated UI scripting a-la JSP and ASP, which allowed for the enmeshment of concerns beyond anything we'd seen for a while in computing. All notions of Model-View-Control separation (or Presentation, Abstraction, Control) were jettisoned while millions of lines of .jsp and .asp (and ultimately .php) script were foisted onto production servers, there to be maintained for decades (I weep for our children). While this was invisible in early small internet sites, the Dot-Com bubble which careened the internet into a primary vehicle for business, entertainment, culture, and these days even politics caused a growth in number, interaction, and complexity of these sites that has caused unmitigated hell for those who found their "whipped-up" scripted sites turn into high-traffic internet hubs. Much of this code has been re-written out of necessity, and yet it caused the travesty that is Model-1 MVC and other attempts to back-into good O-O practice from a messy start. These partial solutions were propagated as good practice (which, by comparison with the norm, they were) and a generation of students learned how to do O-O from Struts and other toolsets. Ignored in that process were wonderful tools like WebObjects or Tapestry which actually did a fair job of doing O-O AND doing the web, but I'll leave that point here.

Design Patterns

A small corollary to the dot-com bubble is that combining Java, and Patterns concepts from the Gang of Four, these new developers managed to create a code-by-numbers style of design, where you don't describe architecture with patterns, you design with patterns up-front. This has led to some of the worst architecture I've ever seen. Paint-by-numbers has never resulted in a Picasso or Monet, and rarely results in anything anyone would want to see except the artist's mother. Design Patterns and pattern-languages aren't bad - far from it. However, they are a language to discuss architecture, they are not an instruction manual. They should be symptoms of a good software design, not an ingredient.

Really big, bloated projects

Lastly, really really big projects have taken all of the above and raised the stakes. We are now finding that the limits of software aren't the Hardware (thank you Moore), but rather the people. A whole generation of us attempted to solve this by increasing the process weight around the development effort. This satisfied some contractual issues with scale, but in general failed to attend to the issues raised in The Mythical Man-Month, despite 40 years of its having been published.

A side-effect of really big projects is that when you have that much money on the table, risk-mitigate goes into high-gear, and people are bad at risk analysis and planning. We tend to manage risk by telling ourselves stories. We invent narratives that help us manage our fears, but don't actually manage risk. So we make very large plans. Idealistic (even if they're pessimistic) portrayals of how the project shall be. Then, because we want to "lock down" our risk, we solicit every possible feature that could potentially be in, including, but not limited to, the kitchen sink, to make sure we haven't forgotten it. It goes in the plan, but by this point we have twice the features any user will ever ever use and 80% of the features provide, maybe, 20% of the value. So we actually increase risk to the project's success while we are trying to minimize and control it. This kitchen-sinkism leads to greater and larger projects, but then large projects bring prestige as well, so there are several motivation vectors for large-projects. Most of them aren't good.

Enter Agile (the path to the solution)

Agile software started to address the human problem of software, and I won't go into it much here, as it's well covered elsewhere. However, one can summarize most agile methods by saying that the basics are

  1. Iterate in small cycles
  2. Get frequent feedback (ideally by having teams and customers co-located)
  3. Deliver after each iteration (where possible)
  4. Only work on the most important thing at a time.
  5. Build quality in.
  6. Don't work in "phases" (design, define, code, test)

This is a quick-n-dirty, so no arguments here. It's just an overview. But from these changes there are tons of obstacles, issues, and implications. They are, indeed, too numerous to go into. But a light non-exhaustive summary might include:

  • You can't go fast unless you build quality in
  • You can't build quality in unless you can test quickly
  • You can't test quickly if you can't build quickly
  • You can't test quickly if you aren't separating your types of testing
  • You can't test quickly if your tests are manual
  • You can't automate your tests if your code is hard to test (requires lots of setup-teardown for each test)
  • You can't make your code more amenable to testing if it's not modular
  • You can't ship frequently if you can't verify quickly before shipping
  • You can't build quality in if you ship crap
  • You can't get feedback if you can't ship to customers
  • etc.

Lots of "can't" phrases there, but note that they're conditionals. Agile methods don't actually fix these problems, they expose them, and help you do root-cause analysis to solve them. For example, look at some of those chains there.

If I, for example, take my "Big Ball of Mud" software system and re-tool it to de-couple it's logical systems and components into discrete components in its native language (say, Java), then I suddenly can test it more helpfully, because I can test one component without interference from another. Because of this, my burden of infrastructure to get the same test value goes down. Because of this my speed of test execution improves. This causes me to be able to test more frequently (possibly eventually after each check-in). This causes me to be able to make quick changes without as much fear, because I have a fast way of checking for regressions. This allows me to then be less fearful of making changes, such as cleaning up my code-base... Oh wow - there's a circle.

In fact, it is a positive feedback loop. Starting to make this change enables me to more easily make the change in the future. But once I'm moving along in this way, I start to be able to ship more frequently, because my fast verification reduces the cost of shipping. This means I could ship after three iterations, instead of twelve... or eventually every iteration. It means I can make smaller iterations, because the cost of my end-of-iteration process is going down... There are several feedback loops in process during any transition to a more agile way of doing things, as the agile approach finds more and more procedural obstacles in the organization.

But... and here's the big but... if you start to do an agile process implementation and don't start changing how you think about software, software delivery, how you write it, and how it's designed, you're going to run up against an internal, self-inflicted limitation. You can't move fast unless you're organized to accommodate moving fast. Your code base is part of your environment in this context. So starting to help developers subtly move in this direction, and increase the pace at which they transition the existing code-base into a more suitable shape for working efficiently is critical. This, as it turns out, involves our dear old O-O.

What are testability, O-O, and other best practices today? (the recipies)

There's a wealth of info out there. These don't just include software approaches but also team practices. Martin Fowler, Misko Hevery, Kent Beck, (Uncle) Bob C. Martin, Arlo Belshee, and a host of others I couldn't name in this space provide lots of good text on these. These include dependency-injection, continuous code review (pair programming), team co-location, separation of concerns. On the latter point, Aspect Oriented Programming is a nice approach which I see as another flavour of O-O, conceptually, in that it attempts to get at some of the same key problems. It is often mixed either with O-O, or with Inversion of Control containers. Fearless refactoring, continuous integration, build and test automation (I'm a big fan of Maven, btw, since, for all its problems, it makes dependency explicit). Test Driven Development (and it's cousin test-first development). Also, the use of Domain Specific Languages has become quite helpful in both mapping the business problem to technology, but also eliminating quality problems by defining the language of the solution differently. And of course, wrapping this development in a management method that helps feed the process and consume its results - such as Scrum, or the management elements of Extreme Programming.

These are a sampling of practices that affect how you organize, re-think, design, create, and evolve your software. They rely on the basic principles and premises of agile, but require, in implementation, the core elements that O-O was trying to solve. How can we manage complexity, address the business problem, write healthy code, and be served, not mastered, by our tools.

Prologue (the plugs)

I'm a big Misko Hevery fan these days (I can feel him cringing at that appellation). There's a lot I've had to say to my clients on the subject of testable code, designing for testability, and tools and technologies, but Misko seems to have wonderfully summed up much of my discussion on the topic on his "Testability Explorer" blog. He explains issues like the problem with Gang-of-Four Singletons, why Dependency Injection encourages not only testable code, but it does so by separating concerns (wiring, initialization, and business logic), and all sorts of good stuff like that. It helps to read from the earlier materials first, because Misko does build on earlier postings so later ones may assume you have read the earlier ones and that you are following along. Notwithstanding, his posts are cogent, clear, and insightful and have helped to crystallize certain understandings that I've been formulating over my years of software development into much more precise notions, and he's helped me learn how to articulate and explain such topics.

Misko has also recently published a code-reviewer's guide to testable code. Sorely needed in my view. I also want to make a quick shout-out to his testability explorer tool, which is fabulous, and I'm working on a Maven 2 plugin to integrate it into site reports.

Also, I've built a Dependency Injection container (google-code project here and docs here) suitable for use on Java2 Micro Edition CLDC 1.1 platform, because I had to prove to a client that you could do this in a reflection-free embedded environment. It's BSD licensed, so feel free to use it if you want.

Lastly, Mishkin Berteig and co. have a decent blog called Agile Advice (on which I occasionally also blog) which nicely examines the various process-related, cultural, organizational, and relational issues that working in this way brings up. My posts tend towards the technical on that blog, but occasionally otherwise as well.


Labels: , , , , , , , , ,