Headshot-color me@jbrains.ca Find out where I'm appearing
« Previous 1 3 4 5 6 7 8 9 13 14

Three Steps to a Useful Minimal Feature

Recently, in the mailing for Steve Freeman and Nat Pryce’s book Growing Object-Oriented Systems Guided by Tests, I followed a discussion that included this comment:

I guess the skill is knowing in what makes as small as possible but valid slice (as you say in the book)

Even though I’ve written before about splitting stories, I have refined my ideas about how to deliver a useful, significant, but small slice of software. I use a simple technique which I describe this way:

  1. Write out any, and I mean any, meaningful end-to-end scenario in detail with concrete values at every step.

  2. Now that you’ve chosen one real scenario, go to each step in that scenario and ask the question, “What would I need to assume to eliminate this step?” If you find those assumptions make for a reasonable scenario, then use that assumption to simplify the scenario.

  3. Repeat step 2 until exhausted or unable to come up with a simplifying assumption with five minutes’ thought.

I’ve used the example of online bill payment in many of my classes and applied this algorithm. You’d be surprised how simple, but useful a bill payments system one can build.

In fact, let’s look at this example in a bit more detail.

What does a typical bill payments system look like? I imagine that TD Canada Trust has a fairly representative system, so I’ll use that as my example.

First, select the “Pay Bills” option.

Selecting Pay Bills

Next, select the account to use to pay the bill.

Selecting the account

Next, select the bills you’d like to pay. Once in a while, I want to pay multiple bills at once, such as when my business has to pay the property taxes on a handful of rental properties.

Selecting the bills to pay

Next, enter the amount you want to pay, the date on which to pay it, the account from which to pay it (why again?) and whether you want to repeat this payment automatically.

Entering the details

Verify the payment details: the amount, the account, the creditor, the date, and the system schedules the payment.

Verifying the details

From this, I can describe the scenario in a form that looks like an executable example:

Pay a bill online

  • Given that Joe has already logged in
  • Joe says "I want to pay a bill"
  • Joe selects chequing account 12345 from which to pay the bill
  • Joe selects Visa bill with account ending in 2222 as the bill to pay
  • Joe says "I want to pay $5,000"
  • Joe selects a date 14 days from now as the date on which to pay the bill
  • Joe selects "only once" as the frequency with which to pay the bill
  • After Joe sees a summary of the bill payment he has asked to schedule, he says "I confirm that I want to pay this bill"
  • Now the system schedules to pay the bill as requested and sends Joe an email to confirm the transaction with a link to cancel or change the scheduled payment

Apart from the numbers, this scenario perfectly accurately reflects how I pay my bills online, although I only wish TD Canada Trust would send me the confirmation email I threw in as the system’s response. We have completed step 1 of the algorithm: we have specified a complete scenario with concrete values at every step.

You'll notice that we didn't specify Joe's username and password. We don't intend to re-test logging in here, so we don't bother with those details. We will have tested that elsewhere.

Now we move to step 2 of the algorithm: looking for assumptions we could make about paying a bill online that would eliminate steps in the process. To do this, we have to be prepared to sacrifice any semblance of a decent user experience. Don’t worry: once the Walking Skeleton runs, you’ll be able to add all the bells and whistles that will make this feature a pleasure to use. For now, we want to eliminate any detail that distracts us from connecting our feature to the key interfaces it must deal with. In this example, I know I want to expose an HTTP interface to clients (eventually the web) and that I need to connect to the Big Ugly Banking System, but beyond that, I don’t know that anything else matters. Within this context, then, we can start making our simplifying assumptions.

That is, until someone remembers that, being a bank, we need to keep a strictly accurate record of all transactions. That means we should add a final step to the scenario: the system records the transaction in its log. While some might consider this a superfluous detail, banking regulators would call it quite essential, and so I find it hard to ignore. This means that we have a third essential interface to which to connect: the transaction logging facility. For our purposes, I’ll assume that we have one and that it has the usual properties: transaction posting date, description, and debit or credit amount, who performed the transaction and when.

This itself makes me ask whether we need to log the transaction yet, because in our scenario we’ve scheduled a payment, and not made one. Scheduling a transaction leads to issues of canceling, editing, and building a process that completes the transaction on the day the customer scheduled it. This leads to our first simplifying assumption: Joe pays the bill immediately.

This simplifies the scenario because we no longer need Joe to tell the system when to pay the bill: the system always pays the bill immediately. Our revised scenario looks like this:

Pay a bill online

  • Given that Joe has already logged in
  • Joe says "I want to pay a bill"
  • Joe selects chequing account 12345 from which to pay the bill
  • Joe selects Visa bill with account ending in 2222 as the bill to pay
  • Joe says "I want to pay $5,000"
  • Joe selects "only once" as the frequency with which to pay the bill
  • After Joe sees a summary of the bill payment he has asked to schedule, he says "I confirm that I want to pay this bill"
  • Now the system schedules pays the bill as requested and sends Joe and email to let him know that the bill was paid

I like to start at the top and look for simplifying assumptions. First, I see that Joe has to select the account from which to pay the bill, which implies that the system presents a list of accounts to Joe, which we recognize we need to do, but not in the Walking Skeleton. Ultimately, Joe simply needs to specify the account number to debit to pay the bill, so for now we’ll make him type that in. Our revised scenario looks like this:

Pay a bill online

  • Given that Joe has already logged in
  • Given that Joe has already logged in
  • Joe says "I want to pay a bill"
  • Joe says "I want to pay from account 12345"
  • Joe selects Visa bill with account ending in 2222 as the bill to pay
  • Joe says "I want to pay $5,000"
  • Joe selects "only once" as the frequency with which to pay the bill
  • After Joe sees a summary of the bill payment he has asked to schedule, he says "I confirm that I want to pay this bill"
  • Now the system schedules pays the bill as requested and sends Joe and email to let him know that the bill was paid

Next, I see that Joe again has to select the bill to pay, which implies that the system presents a list of bills to pay. We could simplify this by requiring Joe to enter the payee identification number and the account number, even though this means saddling Joe with knowledge of payee identification numbers. In particular, the system neither has to store nor present a list of bill payees, and Joe doesn’t need to register a creditor before paying them.

I'm making up this notion of payee identification numbers because I don't know how banks really implement this. I imagine whatever they do, it boils down to companies registering as payees for bill payments, which results in issuing them some kind of identification number. If some kind soul wants to educate me on how this really works, I'd gladly edit the article to bring it closer to the banking industry's real implementation.

Our revised scenario looks like this:

Pay a bill online

  • Given that Joe has already logged in
  • Given that Joe has already logged in
  • Joe says "I want to pay a bill"
  • Joe says "I want to pay from account 12345"
  • Joe says "I want to pay to payee number 66666"
  • Joe says "I want to pay to account number 2222"
  • Joe says "I want to pay $5,000"
  • Joe selects "only once" as the frequency with which to pay the bill
  • After Joe sees a summary of the bill payment he has asked to schedule, he says "I confirm that I want to pay this bill"
  • Now the system schedules pays the bill as requested and sends Joe and email to let him know that the bill was paid

Next, I notice that Joe has to specify the amount to pay, and I can’t think of how to eliminate that detail without complicating matters. It does make me think about potential future features, such as “pay balance off in full” and “pay minimum payment required”, which I note down before returning to this scenario. Joe will simply have to tell us exactly how much to pay towards the bill.

Next, I notice that Joe has to confirm that he only wants a one-time payment. We can eliminate this detail by assuming that Joe can only pay the bill once. We know that customers want recurring payments, but that only distracts us from implementing the Walking Skeleton. We can eliminate this step, and our revised scenario looks like this:

Pay a bill online

  • Given that Joe has already logged in
  • Joe says "I want to pay a bill"
  • Joe says "I want to pay from account 12345"
  • Joe says "I want to pay to payee number 66666"
  • Joe says "I want to pay to account number 2222"
  • Joe says "I want to pay $5,000"
  • After Joe sees a summary of the bill payment he has asked to schedule, he says "I confirm that I want to pay this bill"
  • Now the system schedules pays the bill as requested and sends Joe and email to let him know that the bill was paid

Next, I notice that Joe has to confirm the bill payment before the system will process the payment. While this step might seem essential for security reasons, remember that we don’t necessarily have a graphical web interface for our Walking Skeleton implementation, and so we might not even have the opportunity to ask for confirmation of the bill payment. On this basis, we eliminate this step by assuming that Joe has looked over the details before pressing the button to pay the bill. Our revised scenario looks like this:

Pay a bill online

  • Given that Joe has already logged in
  • Joe says "I want to pay a bill"
  • Joe says "I want to pay from account 12345"
  • Joe says "I want to pay to payee number 66666"
  • Joe says "I want to pay to account number 2222"
  • Joe says "I want to pay $5,000"
  • Now the system schedules pays the bill as requested and sends Joe and email to let him know that the bill was paid

I can’t see any further simplifications, so I choose to stop here. I suspect this constitutes a close-to-minimal protocol for the “pay a bill online” feature. The programmer in me sees this as a single message, which pleases me, because of the simplicity of the interaction. The customer in me can see clearly all the extra stories we need to complete to make this feature available for public use, which makes planning easier. It feels like a win for everyone, except perhaps for Joe, who has a crappy interface to work with.

Now that we have a Walking Skeleton bill payment feature, we can identify the stories we want to deliver beyond the simplest case, and can decide which ones we need to roll this feature out to paying customers.

  • Let Joe choose from a list of available payees which company to pay
  • Remember the payees that Joe has previously paid and present them as “favorites” so he doesn’t have to search for them
  • Remember the payee accounts that Joe has previously paid so that he doesn’t have to enter them each time
  • Let Joe delete accounts he no longer needs to pay
  • Give Joe the option of paying the minimum payment required by the payee
  • Give Joe the option of paying the full balance owing
  • Let Joe schedule his payment in the future
  • Let Joe cancel pending payments
  • Let Joe change pending payments
  • Send a reminder to Joe to pay a bill he has paid at least three of the past six months (try to detect a recurring payment)
  • Let Joe schedule a payment to recur every month (same day each month)
  • Notify Joe in advance of automatically detected recurring payments and ask him if he wants us to pay the bill for him
  • When emailing Joe about a bill payment, include links to review the scheduled payment, change it, or cancel it, if appropriate

I imagine we could come up with more together, but I find one common thread with all these stories: once we implement the Walking Skeletion, we can implement most of these stories independently of the others. We know that more independent stories means greater opportunities to change priorities as needed as well as greater opportunities to drop features in favor of other more lucrative options. Once again, everyone wins.

Would you like to work with J. B. Rainsberger to realize revenue sooner and lower costs from delivering software? Schedule a workshop with J. B. today.

July 16, 2010 08:00 stories, planning, design, article

Design your own TDD learning experience

Join me in Chicago in September to learn whether test-driven development will work for you. In this course, you will learn the secrets of modular design from one of test-driven development’s master practitioners. Bring your laptop and be prepared to change the way you write software.

Using integration tests mindfully: a case study

Gus Power commented about the way he uses integration tests in his work.

Interesting series of articles & comments. I also read Steve Freeman’s article in response to the same topic. It’s got me thinking about how we work and I thought I’d take the time to describe it here.

You define an integration test as “… any test whose result (pass or fail) depends on the correctness of the implementation of more than one piece of non-trivial behavior.” We have many such components that exhibit such non-trivial behaviour in the products we create, many of which are not developed by us. And we have integration tests to verify they work. I’m not just talking about 3rd party libraries and frameworks here, I’m referring to the whole system: caching layers. load balancers, DNS servers, CDNs, virtualization etc. When we build software it only becomes a product or service for our users when it has been deployed into a suitable environment; an environment that typically contains more than just the software we have written and packaged. Since our users’ experience and perception of quality result from their interaction with a deployed instance of the whole system, not just their interaction with the software at a unit level, we have come to value end-to-end integration testing. I believe there’s merit in testing these components in symphony and will attempt to clarify what kind of integration testing I’m talking about.

For a given piece of functionality we write an executable acceptance test in human readable form (for web projects we typically use some domain-specific extensions to selenium, for services we have used FIT and it’s ilk, sometimes we roll our own if there’s nothing expressive enough available). We run it against a deployed version of the application (usually local though not always) which typically has a running web/application server and database. The test fails. We determine what endpoint needs to be created/enhanced and then we switch context down into unit-test land. A typical scenario would involve enhancing a unit test for the url mappings, adding one for the controller, then one for any additional service, domain object etc. When we’re happy and have tested and designed each of the required units we jump back up a level and get our acceptance test to progress further. The customer steers the development effort as he sees vertical ‘slices’ of functionality emerge. The acceptance test is added to a suite for that functional area. The continuous build system will then execute that test against a fully deployed (but scaled down) replica of the production environment, with hardware load balancer, vlans, multiple nodes (session affinity) and so forth. Any additional environmental monitoring (e.g. nagios alerting) is also done as part of this development effort and is deployed into the test environment along with the updated code.

Setting up the infrastructure to do this kind of testing takes investment, both initial and ongoing. The continuous build needs to be highly ‘parallelized’ so you get feedback from a checkin in 10 mins or less (we’re heavy users of virtualization, usually VMWare or OpenVZ). The individual acceptance test suites need to be kept small enough to run quickly before check-in.

Benefits of this approach
* The continuous context-switch between acceptance test and unit test is key to our staying focused on delivering what the customer actually wants.
* The customer has multiple feedback points that he can learn from and use to steer the development effort.
* It confirms that the whole system works together – networking, DNS, load balancing, automated deployment, session handling, database replication etc.
* We create additional ‘non-functional’ acceptance tests that automatically exercise other aspects of the system such as fail-over and recovery.
* Upgrades to parts of the system (switches, load balancers, web caches, library versions, database server versions etc.) can be tested in a known and controlled way.

We’ve caught a number of integration-related issues using this approach (a few examples: broken database failover due to missing primary keys, captcha validation not working due to a web cache not behaving correctly, data not persisting because one database server had the wrong locale) and stopped them before they have reached our users. We have used the feedback as a basis for improving our products and their delivery at a system level.

OK this reply has now become far too long :-/ It would of course be good to discuss this in person sometime :)

—Gus Power

Thanks for the substantial comment, Gus. For those who don’t know Gus, he is one of the joint recipients of the 2009 Gordon Pask Award for contribution to Agile practice. I invite you to follow his work and learn from his example. On to the substance of Gus’ comment.

Gus, it appears you do not use integration tests to check basic behavior exhaustively. While I try not to use integration tests to check basic behavior at all, I mostly hope to stop programmers from attempting to write exhaustive integration tests that check basic correctness conditions. I wrote in Not Just Slow: Integration Tests are a Vortex of Doom about the vicious cycle I see when teams rely on integration tests to check basic correctness. I encourage them to stop that particular insanity. I would hesitate to use integration tests as even smoke tests for basic correctness, but if I found myself in a situation where I needed to write such tests, I’d do it, then look for ways to render them obsolete.

Also, you mention writing “human-readable acceptance tests”, and I certainly use such tests in my work. When I counsel against using integration tests, I advise it within the context of programmer tests only. While I strongly encourage teams to allow even some of their acceptance tests to check policy or business rule behavior directly and in isolation, I understand and agree that one generally needs to write some acceptance tests as integration tests.

In general, you describe using integration tests quite purposefully, mindfully, and responsibly. I expect no less from a practitioner of your caliber. I would truly enjoy working with you on a project.

Finally, you mention that your integration tests catch system-level issues, such as a broken database schema, mistaken cache integration, and so on. I expect integration tests to find only, or at least mostly, these problems. None of these sound like basic correctness problems.

So Gus, I appreciate you for writing a great description of using integration tests well. I wish we had more examples like this. I truly wish I saw more examples like this. Sadly, I don’t: I see teams trying to check basic correctness issues with plodding, brittle, misleading tests. For those, I stress the need to eliminate integration tests.

January 31, 2010 08:00 testing, agile, design, integrated tests are a scam

Not Just Slow: Integration Tests are a Vortex of Doom

“Aha! So @jbrains is really against the integration tests just because they are too slow for hourly use”

It reminds me about the Ferrari IT story (XP team, dozens of deployments a year on many continents) that started from getting a big visible counter of a total number of tests and wrote just big amount of any tests first. You need to start somewhere and getting large integration tests is definitely better than nothing. As long as you are prepared to improve the testing practices later. —Artem Marchenko

I agree with this sentiment. I tell the story of my very first attempt at test-first programming1, how I wrote about 125 tests, many of which fit my definition of “integration test”, and which took 12 minutes to execute. This meant that, on average, I only made 8-12 edits per hour when writing that code. I recognized then, and I still recognize now, that even making only 8-12 edits per hour—4-6 edits per hour towards the end—that I produced better software than I did when I would write code almost continuously for several hours at a time. As much as I disparage those integration tests today, I appreciated them a great deal at the time I wrote them. I find integration tests useful for finding system-level problems, as the first step in fixing a mistake, and if I genuinely can’t write a focused object test, then I will usually write an integration test.

As you say, Artem, I simply don’t stop there.

When I label integration tests a scam, I mean to emphasize the self-replicating nature of integration tests. It starts simply enough: you write a handful of integration tests, which give you a lot of freedom to implement your design in a way that introduces unfortunate dependencies, which makes focused object testing quite difficult. As a result, you will probably resign yourself to writing more integration tests, which do nothing to improve your dependency problems, and the cycle begins again.

Integration tests help cause pain, even though they appear to help reduce pain. Therein lies the scam.

I must acknowledge this: if you started writing tests this week, or this month, or even this year, then you will probably benefit more from writing integration tests than trying to write perfectly focused object tests. I have said and written elsewhere that I believe a programmer needs to write about 1500 to burn into her brain the basic patterns of good tests. Even so, as you write those tests, I want you to remain aware of the cost. Even if you don’t know how to write a good, focused object test, if you want to write more such tests, and especially if you try to write more such tests, then I will have completed the first phase of my mission to eradicate programmer reliance on integration tests to show the basic correctness of their code.

Join us! Turn one integration test into a small suite of focused object tests today. If you don’t yet see how to replace an entire integration test with equivalent focused object tests, then write at least one or two focused object tests along side the integration test. Try it. I promise, you’ll like it.

1 I use the term test-first programming to refer to test-driven design without the evolutionary design part. With test-first programming, I develop a specific design, then I use tests to help me type it in correctly.

One last comment to my good friend Artem: please don’t put me to sleep with the word “just”!

How test-driven development works (and more!)

It surprises me, from time to time, how much I still need to justify test-driven development to prospects and would-be course attendees. Many feel that TDD has crossed the chasm, while others still see TDD as a cultish practice worth marginalizing. I take some blame for those who find TDD cultish, because until now I haven’t had a strong, sensible, theoretical basis to justify TDD as an idea. I could do no better than “it works for me” or “my friends like it”. That has changed since I’ve started giving my talk “Introduction to Agile with the Theory of Constraints” in which I use concepts from Theory of Constraints to motivate the practices of agile software development, notably those of extreme programming. If you buy in to ideas from Theory of Constraints or Lean Manufacturing, then I think I now have a stronger argument to justify the core programming practices in extreme programming in particular and agile software development in general. I don’t even need all of the Theory of Constraints but rather a simple appeal to fundamental concepts in Queuing Theory.

Queuing Theory?

Yes, Queueing Theory. (And I don’t plan to capitalize that any longer.) I don’t proclaim to have any particular expertise in this area, but I have already seen how to use queuing theory ideas in optimizing network-based systems, and I see no reason we couldn’t extend that to software delivery systems. Better, I only need to appeal to a single idea from queuing theory to make my point.

Given a process B, which follows a process A, sometimes in performing B we need to perform some of A again. We can remove the need to rework by taking some portion of process B and performing it before process A1.

This merits a diagram. If we have this problem

then we can solve it by doing this

and the resulting system will work more efficiently by removing wasteful rework. I assume here that we derive no significant benefit from the rework itself, which I suppose I must justify, but let’s not ruin a good story with the truth. Here I’ve described the general problem, and by applying it to software development, I can… well, I find it more effective if I save the punchline for the end.

Winston Royce, 1970, revisited

I imagine you know this diagram

and appreciate that Royce wrote in his now infamous paper that this single-phase waterfall is risky and invites failure. If you don’t appreciate that, then I cannot strongly recommend enough your reading the original paper in its entirety, rather than stopping after page 2 as most people have done2.

We can apply the queuing theory result I’ve just cited to this diagram and generate some interesting conclusions. I’ll start by focusing in on this portion of the system

We write code, then we test it. Sadly, we occasionally find a bug3 which makes us change the code we wrote after we thought we’d finished it. That makes a loop of the type we can unravel with our queueing theory result.

Since “coding” is process A and “testing” is process B, we need to do some testing before we start coding.

It doesn’t take long for this to become a virtuous loop where we writing only the code we need to write in order to pass the tests we write.

I use the term test-first programming to describe this cycle4. When we practise test-first programming, we design as much detail as we can before writing the first test, then use the tests to help us type in our implementation correctly. Most teams most of the time can use test-first programming to reduce their defect mistake count to near zero, which increases their productivity and improves their ability to deliver, by helping them waste less time agonizing over whether to fix mistakes late in a release. I started this way in 2000 when I first discovered JUnit and stopped making silly mistakes in the code I wrote, which I found significantly beneficial in helping me code more confidently. I still designed most of what I built mostly up front.

After a while, though, I recognized a new process loop: I found some parts of my design difficult to test, or I found some parts of my design didn’t fit together when I tried to type them in.

Returning to our queuing theory result, since “designing” is process A and “doing test-first programming” is process B, we need to do some test-first programming before we start designing.

It doesn’t take long for this to become a virtuous loop where we check our design ideas as we think of them and implement only the parts of the design we can justify needing. When we include refactoring in our practice, we can confidently “under-design” compared to the level of design we expect to need by the end of a task, which I believe amounts to designing appropriately for the code we need to implement right now. This virtuous loop combines test-first programming and evolutionary design, including guiding principles like “you aren’t gonna need it” and the four elements of simple design into test-driven development, where we check our implementation by running tests and we check our design ideas by writing tests.

Where test-first programming helps most teams most of the time reduce their mistake count to near zero, test-driven development helps them reduce their design inventory—mostly code that gets in our way because it doesn’t actively help us deliver a feature—to near zero. This further increases productivity and improves their ability to deliver by helping them waste less time agonizing over design problems they find costly to fix. I waited until I’d spent an entire release practising test-first programming before doing more test-driven development. My transition consisted of trying to do less and less up-front design for each task, letting myself feel comfortable with each new step. Within two years I estimate I designed about 5% as much up front as I did before I started practising test-first programming. I can’t measure the corresponding improvement in my design, but I look back at projects that took 3 months before I practised test-driven development that I now feel confident I could complete—truly complete—in one week. Of course, we can’t stop here!

Enter our friend analysis. To simplify the discussion, I will treat analysis as “discovering the features we want in our software” without forcing myself to state too precisely how that happens5. Once again, we have our familiar situation.

Once again, we face the situation where in the process of implementing features we discover new features we need, current features we don’t need, and learn new things about features we know we need to build. This adds to our analysis, meaning that we should try test-driving some features before we try to implement others.

It doesn’t take long for this to become a virtuous loop in which our desire to implement (and deliver!) features drives them ever smaller, as we extract more concentrated value out of each one6. When we implement feature 12 we learn something about features 23, 30 and 52. We might decide not to deliver feature 30 any more. We might decide to expand feature 23 to encompass a few more key cases. We might decide to rush feature 52 to the top of the pile. Most teams most of the time find that this cycle helps them reduce the number of rarely- or infrequently-used features in their system7. This yet again increases productivity and improves their ability to deliver meaningful software to their stakeholders by eliminating the time wasted on delivering too much of a feature too soon, the time wasted on entire features we thought we needed but realized we don’t, and the time wasted arguing about what a feature means, rather than writing examples together: business-oriented tests that describe how a feature works in enough detail for the business and technical project community to agree on the conditions of satisfaction for delivering the feature.

I call this behavior-driven development, and refuse to spell it with the u that provides as much value to the word as your appendix does to your body8.

Once again, I didn’t coin the phrase, and some might argue against the way I use it, but I find it apt. This cycle include practices like business and technical people writing examples together, feature injection, feature splitting, and value-based (rather than cost-based) planning.

At this point, I think I’ve done my job. I believe I’ve justified not only test-first programming or test-driven development, but full-on behavior-driven development, using only a single result from fundamental queuing theory. I’ve made only a single assumption—that we agree on the appropriateness of applying queuing theory to a software development system. I’ve tried to add as little as possible to my reasoning in order to keep it as context-free as possible. As a result I claim that most teams most of the time will benefit from moving along the path from code-and-fix to test-first programming to test-driven development to behavior-driven development.

Now, for homework, what happens when we consider these processes?

Surely at least one you’ve needed to deliver more features for software you’d already deployed. How well does that work? What problems do you encounter? What if you applied our new favorite queuing theory result to that rework loop?


1 I really need a citation for this, and when I find it, I will place it here.

2 I digress, but I really can’t help myself on that one.

3 Also known as defect or, for the truly congruent, mistake.

4 Clearly I didn’t coin the phrase, but I know many people who treat “test-driven development” as a simple renaming of “test-first programming”, and I believe making a stronger distinction adds real value to the conversation.

5 I don’t think “gathering requirements”, as though we could pick them like berries, fits as a metaphor. I like “trawling for requirements”, which I believe I first read in Mike Cohn’s User Stories Applied.

6 We can easily apply the “Pareto Distribution” here in that we can deliver 80% of the value from implementing 20% of the feature.

7 You recall that Jim Johnson of the Standish Group reported in 1994 that 45% of developed features are “never used”. As I recall, only 7% of features were used very frequently.

8 My Canadian and British brethren and sistren be damned. I assert my right as a Canadian to choose the British spelling when I prefer it and the American spelling when it saves me time.

« Previous 1 3 4 5 6 7 8 9 13 14