Sunday, July 19, 2015

GetRouteData() in ASP.NET WebApi

I've been trying to get System.Web.Http.HttpRouteCollection.GetRouteData() to work in ASP.NET WebApi recently, and had a hard time of it. In ASP.NET MVC it's really easy, but there are additional details I couldn't figure out in WebApi. There was even a detailed set of answers on StackOverflow, but when I tried them, they all failed in ways that didn't make sense to me.

And now I have seen it work, so I want to document it. Here's what I did:

  1. In VS 2013, New Project -> Web, ASP.NET Web Application
  2. Select WebAPI. Check "Add unit tests".
  3. Add the following unit test:

And here's a Git repository with the complete working solution.

(Thanks to this blog post for unblocking me.)

Thursday, May 14, 2015

The relationship between DRY and Coupling

I think that the DRY principle is a subset of* "Low Coupling".

DRY & Coupling:

If one rule is expressed in two places in your code (violating DRY), and you want to change the rule, you must edit both places. This is coupling.

byte[] OpenFile(string fileName)
{
    // Is it our file type?
    if (fileName.Extension == ".foo") ...

void AutoSaveFile(byte[] contents)
{
    path = Path.Combine(directory, DateTime.Now.ToString("dd_MM_yyyy") + ".foo");

If we decide to change our file extension to the much more reasonable ".bar", then we must edit both.

*possibly equivalent to

The Prime Refactoring

I used to believe that the two most important refactorings were Extract Method and Rename. The way they deliver value and the way they are used are quite different, so it's hard to compare, so I figured they had equal value.

Recently I've decided that Rename is slightly more urgent, if not more important. It is the first refactoring to learn; the first to teach; the first to apply. (Just slightly)

The problem is code that lies to you. It says it's doing one thing, but actually it's doing another. You either have to think really hard to figure that out (slow) or you misunderstand the code and write bugs.

Fix that first. It may lack cohesion, have tight coupling, and lots of duplication, but first introduce good names. Rename to make the code stop lying to you.

(Soon afterwards, start using Extract Method to give you more things to name.)

Monday, April 6, 2015

"good" names - a minbar

In code, naming things well is incredibly powerful. Names help with expressing intent, increasing cohesion, and identifying duplication.

Bad naming can do a lot of damage. Names that lie, mislead, or obfuscate will confuse a programmer, or at least make her work harder to get the job done.

I think a name is "good" when you don't have to examine what is behind the name to know what it does. It doesn't have to add additional value, it just has to avoid obfuscation. For example:

void AThenB()
{
    A();
    B();
}

If you see AThenB() in code, you'll know exactly what it does. Not a great name, but not a damaging name, either.

This is the minimum bar when naming a new entity in code. It's not a hard bar to meet. You can often do way better. But never check in any code that doesn't meet this bar.

JBrains calls it it "accurate names".

Arlo Belshee calls this "tweetable names":




Wednesday, March 18, 2015

The zeroth rule of software estimating

I realized that before even the first rule of software estimating must come:
Know why you are estimating.
We take it for granted that software estimating is something we must do. For many people, this is obvious. But when we start talking about why we estimate, I see many different answers. Perhaps it is not so obvious after all.

Some of the answers I have heard:

  1. To decide which work to do next.
  2. To decide how many items to start working on in an iteration.
  3. To decide how many people to hire.
  4. To sync up long-lead work (e.g. marketing).
  5. To evaluate and reward the performance of individuals.
  6. To evaluate and reward the performance of teams.
  7. To measure the impact of changes in process, tools, technical debt, etc.
  8. As a lever to push people to work harder.
It's common to choose more than one. This can produce really wacky results.

Whatever your reasons are, it's worth understanding them deeply. Is that something you really need? Is this approach really going to give you that result? Are there other ways that are more effective? 

Thursday, February 26, 2015

The second rule of software estimation

The more error there is in your estimates, the less precise you must be.

That's based on my past experience with being wrong a lot, and seeing other people be wrong a lot. If I tell you I can write a feature in a day, and sometimes I'm right, and sometimes it takes a month, then there's no reason to differentiate between 5-hour and 6-hour features when estimating.

I suspect that powers-of-n is a good model for many teams, where n depends on some combination of team familiarity with the code, technical debt, domain complexity, etc.

A statistician could certainly give some guidance here. Something about standard deviations.

A lot of teams like to use Fibbonaci numbers for their estimates, which seems weird to me. Why is this a good sequence? Why jump from 1 to 2 (a 100% increase) then to 3 (a 50% increase)? Can you really tell a 2 and a 3 apart, reliably enough to be useful?

In Fibonacci, the next number is "twice the average of the last two numbers", which is pretty close to "twice the last number". I doubt your estimates are reliable enough that the difference will matter. And powers of two are culturally familiar in software, easy to remember, and easy for programmers to add.

See also: the first rule.

Tuesday, February 24, 2015

The first rule of software estimating

Take a list of pieces of work you might do. Stories, features, products, I don't care. Find two that are the same size. Approximately.

Do them both. Measure how long they took. Did they come out the same?

If you can't reliably recognize two items as being the same size, then nothing else in estimation will work for you. It all builds on this.

How I write "contract tests"

This comes up in conversation often enough that I want to write it down..

Context:

My code talks to an external dependency that is awkward to use in unit tests.

I can refactor most of my code to eliminate the dependency. (See DEP and Whole Value). But I still have some code that talks to the external dependency. I wrap the dependency with an adapter (see Ports-and-Adapters) of significant thickness and abstraction (see Mimic Adapter). In test, I replace the real dependency with legitimate, but simplified test double (see Simulators). 

Problem:

I can't be certain that my simulator has fidelity with my real system. They may behave differently, allowing my tests to pass when my system has a bug. (This is a common problem with mocks.)

Solution:

Write one set of tests for the port, running the tests against both the real and simulated implementation.

In C#:


Tests on the simulator are fast enough to run with every build.

Tests on the real system may be slow; they may require awkward setup; they may cost real dollars to run. You may decide to run them only in your CI or once per sprint or whatever. Since adapters are relatively stable, that can be OK.



Tuesday, February 17, 2015

Bug metrics

Metrics are tricky. Plenty of ink has been spilled on that topic, so I'll leave it for now.

Around bugs, I know of 4 interesting metrics:
  • A: Count of active bugs
  • B: Time to fix
  • C: Fix rate
  • D: Injection rate
When I want to sound like I understand queuing theory, I call them Peak / Latency / Throughput / Load.

(I'm ignoring the disconnect between what we can measure and what is true. For example, bugs in the system that are impacting customers but are not currently tracked by the team. See http://jbazuzicode.blogspot.com/2014/11/measuring-bug-latency.html)

Customers only care about A and B.

Companies that I have worked at often give a lot of attention to A. For example, I've seen "Bug Hell", where any dev (or any team) with more than a certain number of active bugs must stop working on features until the bug count is lowered. 

In the orgs I'm familiar with, we tend to go immediately from A to C, with bad consequences. Focusing on C means devs will tend to choose narrower fixes; they'll allow tech. debt to accumulate; they'll forego testing; they'll fix cheap bugs before important bugs; they'll work when tired; they'll multitask. The inevitable bug bounce will be higher. This is all bad for customers; it's bad for business..

Getting B (latency) down is great, but it's not always directly actionable. You can prioritize bug fixes before feature work. You can strictly assign bugs back to the devs that created them, throttling the most prolific bug creators.

I see D (injection rate) as being a valuable thing to focus on (although it's difficult to measure). As you write fewer bugs, A and B will get better, which is good for customers. And C will become irrelevant.

Because A->C is such a deeply ingrained habit in our corporate culture, if you don't want that to happen, you have to actively exert effort to take things in a different direction. Every time someone says "we have N bugs", make sure they also say "remember to treat each bug as a learning experience - what can we do to make sure this kind of bug doesn't happen again?" and never say "we fixed M bugs this week."

(Thanks to Bill Hanlon for putting a lot of these ideas out there.)

using MS Fakes safely

MS Fakes can generate something called "Shims" which can override virtuals, and "Stubs" which can override anything, including statics and members of sealed classes.

If you decide to use them, I recommend using these rules:

Only generate the fakes you care about

Use Disable, Clear, and !.

<StubGeneration Disable="true"/>

<ShimGeneration>
  <Clear/>
    <Add FullName="Foo.Bar!" />

Enable diagnostics:

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/" Diagnostic="true">

Treat Fakes warnings as errors


Sadly, there's no easy way to do this. Edit:

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Fakes\Microsoft.QualityTools.Testing.Fakes.targets

Target BuildFakesAssemblies, the GenerateFakes task sets the FakesMessages property, which you always want to be blank, so add:

<Error Condition="@(FakesMessages) != '' Text="Error generating fakes" />

Saturday, February 14, 2015

Write your own unit test "framework"

If you haven't already done it, I recommend you try writing your own unit testing framework. Actually, do it several times, in several different ways.

The existing unit testing packages are sizable pieces of software, and I'm not recommending you spend weeks on this effort. Keep it simple. In fact, the bare minimum to get started with TDD is almost nothing:


Sure, there is value in automatic test discovery, in rich asserts, running all tests even when one fails, reporting, etc. But you don't have to have those things to get started. (Remember this next time you are away from WiFi and have a programming idea.)

Starting from this point, experiment with different ways to write a unit test framework. Some ideas to consider:
  • What's the #1 feature you miss the most in the above example?
  • A natural way to extend asserts in to your domain.
  • How easy is make the mistake of writing a test that never gets run?
  • If my tests are super-fast, how much overhead is there in test discovery and reporting?
  • Reporting that points directly to the site of the failure.
  • How much boilerplate does a developer have to write?
  • Test discovery: reflection ([Test]), inline functions (describe(()=>{})), or something else?
  • If you only supplied one built-in assert, would it be "Assert True", "Assert Equals", or something else? What are the implications?
  • Try both traditional asserts (AssertFoo(result...)) and fluent asserts (Assert.That(result).IsFoo(...)).
Let me know what you find.

Thursday, January 29, 2015

Exceptions are a primitive type

I hold that Exception is a primitive type, and so using one directly in your code is a common example of Primitive Obsession.

The antidote is to wrap the primitive in a Whole Value. It's a pretty straightforward transformation when your code looks like this:

- Make a new exception class, typically nested at the current scope.
- Name it based on the message text.
- Parameters to the message become parameters to the constructor and properties on the new class.
- Override the "Message" property to hold the string.Format() call.

Like this:
Like all good design moves, this helps testing.

Note that in the 2nd example, I'm separating "I expect this exception with these properties" from "the exception should be able to format itself like this". There's a nice separation of concerns.


Wednesday, January 14, 2015

Why I write horrible code. (And so can you!)

EDIT: I may have been too subtle.

Some readers think this is a list of excuses for writing bad code. It is not. Instead, I want to analyze the reasons I have written bad code in the past, so that I can look for ways to make future code better. I want to acknowledge my own limitations, so that I can find ways to compensate. I also believe that many programmers have similar challenges, and may be able to learn from this analysis. Furthermore, I hope that by hearing about my imperfections, you can become less afraid of sharing yours, and that can open up opportunities for your growth.



Today I overheard a friend say something like "Who would write code like this? How could they think it was a good idea?"

I've written a lot of bad code, which makes me a kind of reluctant expert on the topic. It's possible that I'm just worse than average, but I've seen some great programmers write bad code too.

Here are the reasons I can see:

  1. Expediency

This is the most common reason that programmers cite. As in "I could spend some additional time to make this code more beautiful, but we need this change right away." 

I agree that the value of our work is time-sensitive, so delivering it sooner is better. And I agree that we are not being paid for the beauty of our code, but only for the value delivered to customers.

However, encoded in that statement are certain beliefs, about the cost to make the code more beautiful, how much better the code could possibly be, the value offered by that better code, and the risks in getting there. I say "belief" because I think they could vary by programmer, project, technology, market, organization, etc. I'll try to cover these beliefs as I go.

  1. Good design is unfamiliar

While all programmers have suffered from poorly-designed code, well-designed code is all too rare. We may know what we hate about this code, but we have a hard time knowing what "great" would look like. My college professors talked about "low coupling and high cohesion", but that conversation was always in the abstract - I didn't know how to make sure my code actually had those attributes.

I've often thought I knew a great design for something, only to discover that I missed many important details. If I ever get my code to the point where I can use it, I have compromised the design so much that it's not the huge win I was hoping for. I believe most programmers have had similar experiences. This feeds back in to the belief that attempting to make code beautiful won't give much return.

  1. We don't know what we need yet

When I start on a programming task, I usually have a bunch of questions I can't correctly answer yet:
    • What does my customer really need from my program?

    • Will the feature I have in mind really meet that need?

    • What is the true behavior of the externals I intend to depend on? (Do they have the capabilities I need? How do I call these APIs correctly? Do they scale? Are they reliable? Any bugs that will sting me?)

    • What is a good design for my code, based on answers to the above?

    • What future work will be difficult because of design decisions I make now?
Whatever I write, I will soon discover that I was wrong about my answers to these questions, and my design is no longer well-suited to the new answers. If I worked hard on that design, that hard work is wasted. If I work hard to revise the design, I may discover tomorrow that my new answers are wrong, too, so the revised design is also waste. This means I should take shortcuts to get my work done and in use, so I can get that feedback sooner and more cheaply.

Of course, when I get finally get the feature right, customers will not be interested in paying me to go back and rewrite it for no reason.

I used to think this meant that instead of working on good designs, I should learn how to work in poorly-design code, getting great at analyzing it in the debugger and finding minimal fixes. Now I know how to refactor.

  1. We don't know how to refactor

One time you tried to clean up a mess in the code, and you broke something. Your boss yelled at you. Customers were unhappy. You had to work extra hours to fix things up. Now you're wiser, and when someone says "I want to refactor this", you say "only a little, and only if you have great tests, and only if there's plenty of time." Which means it seldom happens. So we don't get any practice refactoring.

But refactoring is key: if you don't know what good design looks like (in general or specific), then the only way to get a good design is to start with a bad one and refactor your way to good.

More generally, remember that it's up to you to invest in your own skills. Refactoring isn't inherently slow or risky, but learning refactoring and other skills takes time and temporarily reduces your performance. You can't count on your employer to cover that, but it still matters.

  1. Too-big steps

Suppose you decide to clean up that code mess, once and for all. Part-way though, you get in interrupted. Maybe the live site goes down and you have to fix it, and that eats up the rest of your day. And tomorrow you have to work on some important new feature. By the time you get back to the cleanup, much of your work is no longer valid.

The antidote is to work tiny and get done. Do the smallest cleanup you can, check it in, and get back to work. Don't aim for "good", just for "better". Make things a little better each day. See Two Minutes to Better Code.

  1. We don't know what we're missing

So you're a smart programmer. Fueled by caffeine and isolated by headphones, you can get your job done. The code you work in is a mess, but you're still delivering value. Sure, you wish the code was nicer, but how much difference would it really make? Is it really worth the investment?

If you're only accustomed to working in code that is a mess, you're in no position to make this judgement. I know that is hard to accept. Really well-designed code doesn't just make things better; it makes things different. Ways that just aren't visible from the old way of doing things. For example:
  • No need to track bugs in a database, because there are no bugs.
  • No need to keep a list of future work (product backlog), because you can just pivot as needed.
  • Easy to test everything with super-fast unit tests, because everything is appropriately decoupled.
  • Ship at will, because you can verify ready-to-ship in a matter of minutes.
  • Any complexity in the code indicates an opportunity to reduce essential complication, since there is no accidental complication. (See 7 minutes, 26 seconds for definitions)
If you've never seen this it sounds impossibly far-fetched. A pipe dream. So of course you wouldn't invest the effort required to get there. (You probably believe that most of your code system complexity is essential; you're wrong again. Sorry.)

  1. We incorrectly compare short-, medium-, and long-term impact

Code mess creates a drag on development. As development gets slower, pressure increases. You take a shortcut. The mess gets worse. A vicious cycle. Exponential growth of the mess. (See Nobody Ever Gets Credit for Fixing Problems that Never Happened.)

In the (very) short term, we can deliver value sooner by taking shortcuts.

In the medium term, we will deliver features more slowly. Less value to customers = bad business.

In the long term, the cost of new features is so great that you must throw things away and rewrite, which you should never do. This isn't "pie in the sky" thinking; this is "we want to stay in business for more than 5 years".

  1. We don't ask for help

Even when my programming is going really well, as soon as another person sees my work, they'll notice a problem that I missed. Each person can offer a different kind of insight in to the design. I can learn a lot from that.

So turn that dial up, from code reviews, to pair programming, to mobbing.

  1. The code is just too horrible

How fast you learn something is heavily dependent on how fast you can iterate.

If you don't know what great design looks like, and you're not already good at refactoring, and your code is really really horrible, and your build takes forever, and your tests are crap, then every step you take will go extremely slowly.

If this is your situation, you could practice your skills in side projects and code katas, or you could switch jobs. Develop those design and refactoring skills in a better environment, then come back to this legacy code when you're ready for that challenge.

Thursday, January 8, 2015

Saff Squeeze on recursive code with NCrunch

NCrunch makes all unit testing better, but there's something cool that happens when combining it with the Saff Squeeze, and something even cooler when the code under test is recursive.

In case you missed Kent Beck's Saff Squeeze:

The Saff Squeeze, as I call it, works by taking a failing test and progressively inlining parts of it until you can't inline further without losing sight of the defect. Here's the cycle:
    1. Inline a non-working method in the test.
    2. Place a (failing) assertion earlier in the test than the existing assertions.
    3. Prune away parts of the test that are no longer relevant.
    4. Repeat.
(I add Step 0; make a copy of the failing test.)

NCrunch helps because you can quickly see how far in the test you're getting until an assert fails. If the code under test is recursive, then:
Repeatedly incline the recursive call until NCrunch's code coverage dots show uncovered code.
Now your test does its job without any recursion, and you can continue to apply the Saff Squeeze as normal.

Wednesday, January 7, 2015

Why we Test, part 7, The Dead Horse

I've seen a wide range of practices that the practitioner claimed was TDD. (Arlo Belshee identifies 7). Obviously, outcomes vary.

People claimed that TDD was or was not effective in some way based on those results. To make it worse, I see wide variation in the stated purpose of TDD. If we don't see the same purpose, then we aren't measuring effectiveness the same way. For example:

  1. ensure correctness of new code
  2. prevent regressions due to future work
  3. point me directly at my mistake
  4. be fast enough to run often
  5. a safety net during refactoring
  6. the only way to be sure my tests are comprehensive
  7. to stop me from writing code I don't need
  8. make code coupling obvious
  9. make DRY problems visible
  10. support cohesion
  11. create the context for entering a Flow state
  12. regular rewards as I make progress
  13. confidence (possibly false!) that my program will work
  14. explain to another human what my code is intended to do

(I sometimes group these into Bugs, Design Feedback, Psychological Benefits, and Specification.)

If you start by practicing TDD a certain way, and see it succeed at one of the above, you'll be tempted to argue that is the "true purpose" of TDD.

If you start with a belief about the true purpose of TDD, and select a practice that doesn't do that, you'll think TDD doesn't work. (See We Tried Baseball...)

I say all this because I hope people will shift to an "all of the above" mindset, and adjust their understanding and practice to make that happen.

Why we Test, part 6: BDD vs. TDD

I've seen BDD advocates say "BDD is just TDD done right." (e.g. here and here) They seem to be saying "It's important to write your unit tests at the appropriate level of abstraction, using language from the problem domain, phrased for a human reader. Domain experts (e.g. users, business analysts) should be able to read, and perhaps write the tests."

More recently, I've seen "TDD is just BDD done right." (e.g. here and here) These people seem to be saying "It's important to use your unit tests to drive the design of your code. BDD is missing that important action." I think they're noticing that BDD doesn't include a Red-Green-Refactor cycle.

I think they're both right. Striving to write tests for humans provides the best guidance for refactoring, carrying the Ubiquitous Language deep in to the system and improving DRY and Cohesion in the system.

Tuesday, January 6, 2015

Why we Test: part 5: Two reasons for regression testing

In Part 1, I wrote: "I count on tests to catch mistakes before our customers do" and "Having tests means I can refactor safely".

In both cases, I want tests to catch my mistakes, but I now realize I should consider these separately.

Regression

In the first case I'm relying on the tests to confirm that I have written my code correctly, or that future functionality changes don't break previous functionality changes. I have written bugs plenty often, and I'm looking to the tests to tell me about. I'm definitely going to keep writing new features and fixing old bugs and shipping software.

When we decide to change old functionality, we'll want to change the old tests. So they should be malleable to provide their value. They should be readable and granular, so when they fail I can decide whether to change the product or change the test.

Pinning

In the second case, my decision about whether to refactor is heavily influenced by whether I have those tests. In a legacy (i.e. tightly coupled) system without good tests, most people will just leave things as-is instead of refactoring.

If I'm just looking for a safety net while I refactor, I can use Pinning Tests. They tests don't need to be malleable, since the product behavior is not changing. If they are fast, they don't need to be granular, since I can run them really often. They do need to be very reliable. They need to cover as many cases as I can manage, but only in the sections of code I'm touching. It's OK if the tests are ugly, if I'm just going to delete them at the end of my refactoring session (when my decoupled code is now amenable to unit testing.)

(In this context, when I say "refactor", I don't mean "a highly disciplined process of changing code without changing behavior, according to a recipe", or "using a high-fidelity automated tool that will safely change code without changing behavior". You could say I mean "tiny rewrites.")

Sunday, January 4, 2015

Simplest possible Git workflow

I'm working with a group that is getting ready to transition to Git, from a traditional centralized version control system.

Some thing I learned while working to revitalize endangered languages is that the first lesson should get students working in the new system, for real, as soon as possible. Applying that to Git, I want to ask "what's the minimum to get you started using Git in a real way without creating a mess that is difficult to clean up later."

A friend complained to me that every time he asks a Git expert a question about Git, the response starts with, "Well, first you have to understand how Git works". I want to offer a workflow that does not require understanding how Git works.

In Scott Chacon's "Introduction to Git" video, he says if you're comfortable with a version control system that is not Git, you're going to hate Git. How can we get around that?

Can I create a microverse where Git is simple and easy to understand, and yet still comprehensive and self-consistent?

"in a real way" for us means "a team of people with a central 'official' repository, that anyone can push to", so I can't ignore remotes/pulling/pushing for now, but I can ignore pull requests. A single person working alone on a single machine can simplify even further than what I describe here.

Prerequisites

I assume that an expert is available to set things up and teach these basics. I advise the expert to avoid talking about any additional details of Git, no matter how juicy.

Whether you choose rebase or merge (linear or non-linear history in master) is up to your expert. If you want to use rebase later, you should use it now, to avoid "creating a mess that is difficult to clean up later", at the cost of expanding "minimum to get you started". Personally, I like rebase.

In our case, everyone sets up their development environments in the same way, and we're using Windows. We push these settings to every machine:

    git.exe config push.default simple
    git.exe config pull.rebase true
    git.exe config core.autocrlf true
    git.exe config core.safecrlf true
    git.exe config rebase.autosquash true
    git.exe config core.editor '"%ProgramFiles%\Windows NT\Accessories\wordpad.exe"'
    git.exe config merge.conflictstyle diff3


and we assert that git config user.name and git config user.email are set.

The expert should create the central repository and instruct everyone on cloning it and help maintain the .gitignore.

Simplest Development Workflow

We can treat Git like an old-fashioned centralized system with a single branch. Let everyone work in master. (Branches are awesome, but understanding them is more than the newbie is ready for.)

You only need these Git commands:

> git pull
When you want to update your machine with the latest from the central repository.

> git add FILENAME
When you create a new file

> git status
> git diff
To see what changes you have pending (ignore the difference between staged and unstaged changes, but watch out for unstaged adds)

> git commit -a
> git pull
> git push
When you like your changes and want to share them with the world

> git reset --hard
> git clean -fd
When you don't like your changes

> git log
To see what has been done

The biggest risk I see here is if there's a merge conflict when you pull before pushing. Stand by to help people through that the first time.

Release Workflow

Release from master. If your team needs time to stabilize master before you can release, make everyone stop what they're doing and focus on completing the release. When you are done, add a tag, then let everyone get back to work.

What's next?

As needs arise, you can build on this model. A dev can start making multiple commits before pushing, or work in a feature branch and merge it, without anyone else needing to learn something new. So you can grow incrementally.

You'll probably want to use branches for releases pretty soon.

At some point, you'll need to have a big conversation about the underlying model of Git, and what rebasing means, etc. Put that off as long as you can, and then go deep.

I find gitk helps people visualize what is happening as things get more interesting.

Tuesday, December 30, 2014

Why We Test, part 4: Specification

Warning: this post doesn't feel great to me. A bit disorganized and ill-balanced. But I needed to get it out for completeness. Feedback welcome, as always!

Llewellyn Falco (here) and Arlo Belshee (here) both talk about the way that tests can provide the value of "specification": that tests can explain to another human what the program is supposed to do.

How does this compare to the values of catching bugs, informing design, and psychological reward? There's a subtle way in which focusing your energy on specification is hugely important.

What does it mean for to be a good spec?
  • Name of a test is business value oriented.
  • Expresses a single example of a business rule.
  • Uses terminology from the problem domain.
  • Meant to be read by a human. (programmer, not customer)
  • At the appropriate level of abstraction for a human reader.
  • Test doesn't make any non-business-value demands
This gets you the design feedback you need. You can only meet the goal of "test as spec" if you listen to the design feedback. You can't have a bunch of setup code (including mocks); that would distract from the core message of the test. The correct terminology gets pushed in to the system under test. These short, simple, straightforward tests are only possible when code is decoupled, and when each business rule is expressed in exactly one place (DRY).

Simple, decoupled tests are inherently fast.

When they fail, they tell you clearly why the failure matters.

It also gets you the comprehensive safety net: if you're focusing your attention on writing and satisfying the spec, all your code will have purpose and will will be covered by tests.

Because the test only makes demands for for business value, you are free to refactor without unnecessarily breaking tests.

This value appears when you focus on how tests are read, but also delivers value when tests are run and written.


Monday, December 29, 2014

Why We Test, part 3: Psychology

Warning: I think this is a bit of a crappy blog post. I needed to get the ideas out there for completeness, but I haven't thought through this part thoroughly.

In "TDD as the crack cocaine of software", Jef Claes talks about the way that TDD (with really fast tests) can create the preconditions for Flow. Flow is emotionally rewarding, so that becomes its own reason to write tests this way, in addition to the desirable outcomes of catching bugs and informing design.

Other psychological (not technical) reasons to do TDD or other types of testing:

  • Confidence. 
Knowing that I have tests makes me feel safe that I can make changes and ship working software. 

Note that this confidence may be false! For example, if I base that confidence on reported code coverage, even though code coverage often does not correlate with quality. (Even worse, if I ignore other quality-ensuring activities because I focus my attention on code coverage, my quality will suffer while my confidence increases.) It's very tempting to celebrate coverage numbers. Don't do it.

Interestingly, following The Three Rules of TDD will tend to result in very high coverage and good quality.
  • Incrementalism
TDD with a tiny Red/Green/Refactor cycle helps you take small steps. You get the feeling of making progress all that time. You're always a minute or so away from a reverting to a green bar.
  • Tracking progress/status
If I get interrupted, I can look at my most recent unit tests to remind myself what I was doing. I can write my next failing test as a note to my future self about what I wanted to do next. (While I'm away, I'll probably change my mind, but the note is still valuable.)

Also, if I commit each passing test, another programmer can read the history to see the path I took. But I'm getting off topic for this post.

Maybe you can think of more examples, or a better way to organize these ideas - let me know!

Thursday, December 25, 2014

Two kinds of safety

While comparing the use of tests to catch bugs vs. improve design, I had a thought about safety (also inspired by Anzeneering).

The "catch bugs" approach provides one kind of safety - if I screw up, the tests will catch my mistake before it has a chance to do any harm.

The "improve design" approach provides a different kind of safety - I look for hazards and eliminate them, so the mistake doesn't happen.

To use a metaphor: if you have a high-wire to cross, the first kind of safety would come from a safety net under the wire; the second would come from replacing the wire with a wide, stable platform.

The wire + net is quick to install/change/remove; I can practice my balance on it; it's exciting. If I fall, I have to crawl back and try again.

The wide platform is expensive, but I can traverse it without taking great care; I can run across.

Working in untested legacy code is like living in a tree city where each home is connected by high wires.

Why we Test, part 2: Design

Previously, I talked about the perspective that the reason to write tests is to catch bugs, and this is a good thing all around. Now I want to talk about code design - about using tests to help me design my code well. Some people argue this is the "true meaning" of "Test-Driven Development".

Unit tests can point me towards good design: if its difficult to write a good test, it means the code is poorly factored, especially that the thing I want to test is inappropriately coupled with something I don't care about right now. Introducing indirection at this point can open my code up to a valuable abstraction.

The "good test" that is "easy to write" will look something like:

    testSubject = new Foo(/*initial state*/);
    result = testSubject.Bar(..);
    Assert(result...);

That's Arrange-Act-Assert, with one line of each. No need to write comments to that effect; no need for blank lines. (There are a few other similar forms in the 2-4 line range.)

This kind of test is only possible if the code under test is not tightly coupled to the rest of the system. More than just "programming to interfaces" so I can inject dependencies, I look to eliminate dependencies. I don't have much setup code. I don't use mocks because I don't need to.

It's not just about "testing a class", it's also about "testing a business rule". If I can test my business rules this way, they are DRY: each piece of knowledge has exactly one canonical expression in my codebase. I minimize emergent phenomena, so my whole system is easier to reason about.

Since I have unit tests that express my business rules, I use terminology from my business domain in the tests. That terminology will flow in to my system-under-test, giving rise to ubiquitous language (within my bounded context of course.)

This kind of test will naturally be super-fast and completely reliable, which supports the "catch bugs" value described before. But I also write a lot fewer bugs, because I have well-factored, well-named, decoupled, DRY code that is easy to reason about. Writing fewer bugs is more effective than trying to find-and-fix the bugs with tests.

I can treat bugs as another kind of design feedback: I can ask what made it easy for this bug to appear, and look for a way to eliminate the whole class of bugs. I may use a unit test for this purpose, but simply writing a test for the specific bug is not enough - I want to address the whole class.

Refactoring is still really important, and (unless I have great tools in C# or Java) I must count on the tests to protect me while refactoring. But now I have the advantage that a) my code is relatively well-factored already, and b) my tests are helping me figure out good ways to refactor, so refactoring is much more fruitful.

You only get this value if you listen to the feedback your tests are giving you.

This value appears when tests are written.


Why we Test, part 1: Bugs

I've noticed a small disagreement in the Agile world around the "true purpose" of unit tests? Mostly the two camps are "to catch mistakes" and "to direct design". I want to explore these ideas a bit further. Arlo Belshee gathered a bunch of great perspectives at What Makes a Good Test Suite?, and part of what I'm doing here is reorganizing those ideas, especially Llewellyn Falco's answer.

Value #1: Bugs.

Bugs ruin software, nullifying the value we work so hard to create. Tests catch bugs (sometimes called "checking" or "regression" or "validation"), so our users and our reputations are not harmed. If another person (or my future self) works on this code later on, I count on tests to catch mistakes before our customers do.

Having tests means I can refactor safely (for some definitions of refactoring). Refactoring makes future work easier. Programmers are happier. We can say "yes" to our customers more often. When refactoring, tests are especially important in languages without great refactoring tools (basically everything except C# and Java).

Speed matters. Faster tests => I run them more often => less has changed since the last run, and what has changed in fresh in my brain => easy to understand what a failure means.

Granularity matters. When a test fails, a granular test will tell me what is broken without a lot of investigation. (Some tests can also provide good diagnostics around a failure, which helps in similar ways.)

Reliability matters. If tests are flaky or broken, you either ignore them (so they deliver 0 value) or you rerun them (which acts as a multiplier on runtime).

Coverage matters. Luckily, strictly following TDD means you won't write any untested code, so you can be confident in your coverage, which is especially important in manual refactoring. Sticking with TDD requires discipline.

When I do find a bug, the responsible thing to do is add a test for it when I fix it. Now I can be sure I'll never have that bug again.

In this mindset, mocks are a great tool, because they let me unit test my code in isolation, which makes them faster, more reliable, and easier to write. I'm likely to introduce indirection ("program to interfaces") and use dependency injection, and maybe even the Service Locator pattern.

You only get this value if the have the right tests.

The bug-catching value appears when tests are run.

Monday, December 22, 2014

Extract Method to eliminate duplication

ReSharper recognizes duplication when I introduce a new variable:

But not when I Extract Method.

Consider these two rules:
  • When two methods in the same class are textually identical, they are semantically identical.
Here's my recipe for eliminating duplication with Extract Method:
  1. Extract Method at each site, giving the new methods nearly-identical names ("Foo", "Foo2" is fine).

  2. Use a text diffing tool to compare the two methods, including the signature.

  3. Use automated refactoring tools to normalize (eliminate the differences). For example, rename a parameter in one method to match the other.
  4. When the two methods are textually identical, except for their names, forward one to the other.
  5. Inline the forwarding method.
This may take several attempts, and the application of other refactorings (mostly Introduce Variable and Rename) to get everything just right.

Note that you don't have to understand what the code does to make this work. You just have to see the duplication and strive to eliminate it.

I like to do fully-automated, highly-reliable refactorings instead of manual edits, where possible. Because it makes me confident that I'm not breaking anything, I can do that without test coverage, which is key to recovering legacy code.

Monday, December 8, 2014

"We don't want to waste time on retrospectives."

As I've been running retrospectives, I've noticed something interesting around how much time we spend on them.

I've been on teams that decided to run retrospectives every 3 weeks. They picked a list of oversized changes, which didn't get implemented, so people stopped seeing the retros as valuable. If there was a scheduling conflict, a retro would get skipped, which just made the problem worse.

On my current team, we do retrospectives every week. The first one took 90 minutes, and triggered a lot of comments complaining about how long it took. I knew that if I wanted to get people to actually show up to weekly retros, I would have to limit the size to the more socially acceptable 60 minutes.

One tool I used was to ask each person to arrive with exactly one item (rant or rave) that they want to give attention to. That helped a lot with the time spent, without hurting value too much (although after a few iterations, we got more efficient from practice and people asked to go back to multiple items.)

I actually think we're optimizing the wrong way, though: if 90 minutes / week isn't worth it, then instead of reducing the cost, I'd rather increase the value. I prefer effectiveness over efficiency. There's a lot of latent potential upside here, focusing on efficiency optimizes for the status quo. I'd like to accelerate our improvements by having retros even more often (daily?!?!), but I am sure that idea would meet heavy resistance (and undermine my credibility.)

I've noticed an odd phenomenon that suggests that optimizing for time is not the right choice: at the end of each formal retrospective, when the meeting is officially done, about 1/2 the group sticks around and continues to discuss how we work. This sometimes goes on for another 90 minutes. Something similar happens at lunch. So clearly, there is a real need for more of this introspection. I just think that the formality of the scheduled retro is something people can't tolerate for more than 60 minutes / week.

It's strange that I hear the comments about "wasting too much time on retros", while people's actual actions show that they really are interested in devoting their time in this space.

How we do retrospectives

I've been facilitating formal retrospectives on this team for a few months now. We use this structure:

  • Meet at the end of each week for an hour.
  • Everyone writes observations on a sticky note (something that sucked, or something that was awesome by accident that you want to make sure we don't lose)
  • Dot-voting to select one item. 
  • Open discussion to deeply understand this item
  • Propose possible changes
  • Dot-voting to select one
  • Refine the item in to a crisp experiment

Most of the time is spent in the open discussion. I think this is the most interesting part. What causes this? What value do we get from it? What are some changes we could make, and what might the consequences be? How do other teams avoid or address this issue? Are there non-obvious changes that might make this issue disappear? (Causality may not be obvious.)

We have tried a few experiments with how we do retros:

  • Allow outsiders (including our manager) to attend. 

Learned that's OK if they stay quiet.

  • Each person can only put up one sticky note in the first round.

This speeds things up, and lets us devote more time to the open discussion.

  • Only call out good things that we want more of.

Assuming that bad things will fall away. Focusing only on fixing bad things will eventually get you up to "tolerable"; you need to increase good things to get to "awesome".

Overall I am very happy with our results. 90% of our decisions have been fully implemented; most produced valuable improvements. Where things didn't get better, we learned something valuable. For example, "Retrospective outcomes that require people to do more work are not likely to be adopted.", so at least for now, we should focus on things that don't require more work. (Maybe later, when our overload is reduced, and we can carve out more slack, we can start trying those things.)

Monday, November 17, 2014

Measuring bug latency

Many people like the metric of “how long it takes for a bug to get fixed”, indicating the health of your code, your team, your process, etc.

What exactly do you like to measure? Consider these points in time:

1.       Bug is written on a dev machine.

2.       Bug is pushed to source control.

3.       Bug is deployed to customers.

4.       Customers hit the bug.

5.       Someone recognizes that it is, indeed undesired behavior.

6.       Bug report is added to the bug tracking system.

7.       Developer starts working on a fix.

8.       Fix is pushed to source control.

9.       Fix is deployed to customers.

I think most dev teams at MS would default to measuring 6-8. Customers would measure 4-9. I can imagine measuring 1-9.

Maybe I even add:

0.       Developer had an incorrect thought.

And before that:


-1. The stage was set, which gave rise to incorrect thinking.

Share your thoughts here: https://twitter.com/jaybazuzi/status/534470023088050177

Resources for Coderetreaters

I held a Coderetreat in Port Townsend this past weekend as part of the Global Day of Coderetreat. Thanks to all who showed up, and to The CoLab for hosting us so generously.

I promised I would post a list of resources that came up in our discussion. Here ya go:



Sunday, September 28, 2014

Getting started in legacy code

This tweet got me thinking about how I make commits:
I started to reply in tweets, but decided I wanted a lot more than 140 characters.

Formatting

When I come on to a new project, one of the first things I like to do is bulk format all code. I don't care what the formatting is (your conventions are fine with me), as long as it's consistent an automated. Ongoing cost should be near zero.

What is refactoring?

A refresher on definitions of refactoring:
  1. Cleaning up the mess while working on something else.
  2. Cleaning up the mess while not working on something else.
  3. Highly disciplined manual application of a recipe for known-safe behavior-idempotent code transformation.
  4. Automated code transformation with a known-safe tool
Arlo Belshee argues that only #3 and #4 deserve to be called "refactoring". He calls #1 and #2 "rewrites", albeit highly localized. All the problems with rewrites still apply, just on a much smaller scale.

I'm inclined to agree with him, but most of the people I work with are happy to call #1 and #2 refactoring as well. I'm not interested in arguing about semantics, but I do find each of these definitions distinctly valuable.

I always advocate for removing barriers to Refactoring #4. They're unlikely to introduce bugs. They almost always make the code better (while most changes make the code worse). They're usually really easy to reverse if we change our minds later.

Refactoring #4 in traditional environments

My current project is sadly trapped in a non-distributed version control system, without lightweight branching. Code reviews are generally required before each change, but we make an exception for known-safe automated refactorings. For example, I might make a commit with this description:
R#: Extract Method `C.Foo`
The R# prefix indicates that this is a known-safe refactoring executed with ReSharper. The `back-tick` syntax is taken from Markdown. I check this in with the minimum of ceremony.

With practice, I have learned to make dramatic shifts in the clarity & structure of code using only the automated refactorings in ReSharper.

It's still my responsibility to make sure I don't break anything. Some ReSharper code transformations are not true refactorings. It's on me to know the difference, and to use the tool safely.

Usually I'll do this with a specific goal in mind: a feature or bug fix. Once the code is really nicely factored in that area, the behavior change is simple and obvious. I can send that out for code review by itself; doing that CR is really easy. (Sometimes people think I only ever work on easy problems!)

I will Refactor this way when I first come in to an area that I plan to work on. As I understand the code, I'll execute these refactorings to express my new understanding, checking in as I go. I'm much more conservative here, because I don't understand overall the intended design, and don't want to naively introduce an inappropriate structure. I pick the most valuable refactorings that I see. In this mode, if I get interrupted part-way through, at least I have made things better. (Thanks to Woody Zuill and Llewellyn Falco for that insight.)

Because I make these changes quickly, I rarely have to merge with someone else's changes. If they have a hard time merging with my changes, we can easily throw mine away and recreate them.

Modern environments

When I get to use Git or other modern source control, that allows an even better workflow.

First, I make a local branch.

Then I'll refactor as above, where each commit goes in separately. I still use prefixes like `R#:` to call those out. I can go a lot faster, because I can postpone any validation until the end of the process.

If I'm working on a feature or fix, goes in the same branch.

If I'm in a code-review-required situation, this pull request is easy to review if you look at each commit separately. 

If every change in the branch is cohesive, I'll merge like this:

> git rebase master
> git checkout master
> git merge --no-ff --no-commit FOO
> git commit    # provide a summary description

This gives a nice-looking commit history:

> git log --oneline --decorate --graph
*   ae593a5 (HEAD, master) Fix blah
|\
| * 67fa3f5 (FOO) Fix bug #2
| * fa27d00 R#: Rename `X` -> `Y`
| * 3a77461 R#: Extract Method `C.Bar`
|/

Just because I have a private branch doesn't mean I want to spend a lot of time there, though. No long-lived parallel development.

Refactoring #3

I'm still looking for a good way to call these out. What I want to tell you is "I didn't intend to change any behavior here. If you're searching commit history for a deliberate behavior change, look elsewhere. If you see a behavior change here, it was not deliberate." In the past I have used "REFACTOR: " as a prefix for this kind of commit.

other tags

Formatting changes are really dull. No one will ever want to read them. They affect many lines of code, so separating them out is really valuable. I commit them with a "FORMATTING: " prefix.

Sometimes I only change a unit test - not product code - and so there's no way I could be introducing a product code bug. Then I might prefix with "TEST: " (although if I extract a method from a unit test, maybe I tag that as a refactoring - still undecided).


Saturday, September 6, 2014

Examples of radical simplicity

With so much historical momentum around complex, poorly-factored code, it can be difficult to know what simple code looks like.

It can help to see some examples outside of your normal experience, to get you thinking in new ways. With that in mind, I really enjoyed this list:

  1. every JUnit test must run in under 1 second
  2. maximum of 1 if statement per class
  3. 0-2 fields per class
  4. 0-1 arguments per method
  5. 1-3 statements per method
  6. fewer than 6 imported classes per class
I'm not saying these should be hard rules, or even that they are ideals. Instead, try refactoring your code to fit within these constraints, and observe the result. Does anything useful appear? (Like a Whole Value?)

Saturday, May 31, 2014

Automating ReSharper's Inspect Code

If you write C#, I hope you have ReSharper.

If you have ReSharper, I hope you have tried its "Code Issues" feature. It has lots of good suggestions, and I need all the help I can get.

Staying clean requires running the analyzer over your whole solution. ReSharper doesn't update its results automatically: you have to explicitly click to make it happen. I hate explicitly clicking. It's also slow.

I decided to see if I could run it automatically in a Continuous Integration build server. I have figured out how to do so using TeamBuild on Visual Studio Online (VSO). Here's how:
  1. Create a new Repo in VSO. Clone it locally.
  2. Add your solution
  3. Connect to your VSO repo in Visual Studio.
  4. Create the PreBuild.ps1 script you see below, to install ReSharper's command line tools
  5. Create the TeamBuild.proj script you see below, to run InspectCode and build your solution
  6. Team Explorer -> Builds -> New Build Definition. Select the Process tab
    1. Set Build Process Template to GitTemplate.12.xaml
    2. Projects = TeamBuild.proj
    3. Pre-build script path = Tools\PreBuild.ps1
  7. Run the script once on your machine to establish a baseline. Add it to Git.
  8. Push
Now, when  the set of Code Issues changes, you'll get notified by the build.

Loading TeamBuild.proj from Gist 1d0e58acb2aeda163df3

Loading PreBuild.ps1 from Gist d5a71230d524965057be

I'd like to find a way to do this in Travis-CI, which would require running these tools on Mono. Let me know if you get it to work.


Thursday, May 29, 2014

The last responsible moment may be right away!

In Agile, they talk about deferring work and decisions until the "last responsible moment". Later I will know more (so I can make a better decision), and maybe I won't need the work at all (avoiding wasted effort). There may be other reasons.

Recently I've noticed a handful of decisions that should be made really early. Some examples:
  • 1-step build
  • unit test infrastructure
  • create a Git repo
  • 0 warnings
  • clean static analysis / lint
  • slow build tripwire
Some of these are important because they're easiest if you do them before you have any code. 

For example, I've worked on many projects where build + test time was quite long. The slow edit/build/test cycle makes me less productive, so I'd like to optimize it. But the optimization work is expensive for the same reasons: because verifying the changes is slow. 


The longest build I've worked with was 12 hours end-to-end. We all knew it *could* have been faster, but that optimization work seemed so daunting. I could optimize all day long, and get to 11h55m. 

I know there was a ton of duplicated code in different parts of the system. But refactoring to eliminate that duplication was also hard.

If we had maintained a fast build from the beginning, it would have been easier to keep it that way.

There is a balance to be struck. Maybe you're not sure if the idea you have is actually viable. You may want to spike it, or show a prototype to a user, before investing significantly in the idea. (See Lean Startup).

There are other tasks that should come soon but not necessarily immediately, like pushing that Git repo to another machine (so you don't lose it), or setting up an automatic CI build.

Wednesday, April 23, 2014

Spike and Extract

Sometimes I have an idea about how I want my code to turn out. In that case, I can use TDD in the conventional way: write a test, fill in the implementation to make it pass, refactor.

But a common situation is that I have only a vague understanding of where we're headed. I don't know how my dependencies work. I don't know how it should all fit together. So:
  1. Write a spike in the form of a "unit" test.
It's not really a unit test, because it's not testing a unit. But I'll still use a unit testing framework to write it. And I'll use FluentAssertions and ApprovalTests if they're useful

Like any good spike, it should hit the tricky parts, like an API that is poorly documented.

It may take a while to write. I'll try lots of different things until I get it to work.
  1. Extract 'till you drop
Use Extract Method, Extract Class, Move Method repeatedly.

Look at any primitives as potential new classes (Whole Value).

Keep refactoring until things read really cleanly. Avoid generality that you don't already need, though.
  1. Keep testing.
Write tiny tests for each new method.

If a method is hard to test, refactor.

If a method is hit by more than 4 tests, refactor.
  1. RGR
You now have code that works, passes tests, and has an appropriate structure.

Add new functionality with the standard Red-Green-Refactor cycle.

This is not new.

I'm basically describing "TDD as if you meant it" by Keith Braithwaite, but trying to be gentler.


Tuesday, April 8, 2014

Five ways to know when you're done refactoring & testing


  1. Test until bored. Ask "is there any chance this is wrong?" If so, write a test for it. If not, you're done.
  2. If the test and the system-under-test would look the same, skip the test, because of DRY.
  3. If the code reads like a spec, test by inspection: read it and confirm that it says the right stuff.
  4. If a function has a side-effect-free query, followed by an action based on the results of the query, split them and test separately.
  5. I'm not very good at figuring out how to design code well, but I can tell when code is hard to test. making code easy to test usually works out well for me. If I have to mock A to test B, that's harder than factoring out B and testing it directly. So, I refactor.

These are all different ways of saying pretty much the same thing.

Saturday, March 29, 2014

Testing progress reporting

I was trying to get some code under test.

Originally it had been written as one long method. It was a slow process, so another programmer had added progress reporting. He wisely created a new, decoupled progress reporting service, with an interface like:

Loading IProgress.cs from Gist 9867358

My first step was Compose Method: Applying Extract Method repeatedly until the code read roughly like English.

Loading Example1.cs from Gist 9867358

With the exception of the progress reporting, there's no point in writing a test for a Composed Method. The test would just repeat the code, violating DRY. Test by inspection.

But what about the progress reporting? How do I test it? Some options:

  • Don't test it.
  • Test manually.
  • Use an integration/end-to-end/system test.
  • Mock out the Do*() methods in a unit test.
  • Change the design.

Each option has pros and cons; each has a context in which it is the right choice.

TDD teaches us that difficult-to-test is a code smell; it's design feedback. All other things being equal, I'd rather choose the last option and improve the design.

There are several ways to think about the problem that could lead us to a new design:


  • The number "4" violates DRY with the (often implicit) parameters to ReportWork. How can I test that it stays correct when editing this code in the future?
  • Most difficult-to-test problems are solved by decoupling. What could I decouple here?
  • This method is responsible for both doing the work, and reporting on the work. Can I write those separately?
  • Think about the difficult-to-test aspect of the code; what if I made a new class that did only that, free of any context?

Suppose there was a class that was responsible for calculating & reporting the work to be done. You could just pass it a list of steps, with costs, and let it calculate the sum. The result might look like:

Loading Example2.cs from Gist 9867358

Since lambdas are capable of capturing locals, it's easy to use the output of one step as the input of another:

Loading Example3.cs from Gist 9867358

Consequences

The code is far easier to test:

  • DoWork, etc. are more testable now as separate methods, since they were extracted.
  • The ActiosnWithCostsList class is trivial to unit test. (You might not bother... up to you.)
  • The ExecuteWithProgress extension is non-trivial, but still easy to unit test.
  • The original code is now very simple, and should be tested by inspection.

I would like a lightweight end-to-end test to hit this code, just to make sure I haven't missed anything. Maybe that's just because I'm still new to TDD and haven't developed the confidence in my TDD skills yet.

If you're not used to coding this way, it may feel surprising. Once you understand it, it may seem clever. Those are both feelings I want to avoid in my code. (Well, I do like looking clever, but it's still a code smell.) I believe that any competent C# programmer can spend a few minutes reading my tests and understand what's going on here.

The 4 rules place "passes tests" above "expresses intent", so I think it's worth keeping until we find a better solution.

Next steps

You wouldn't be reporting progress if the operation was fast. Since it's slow, you may want to free up the current thread while waiting. That means async/await. Extend this solution to make that easy.

If you call a method that could report fine-grained progress, it would be nice to pass it a child progress. Something like:

Loading IProgress2.cs from Gist 9867358



Sunday, March 23, 2014

sorting out complex refactoring with the Mikado method

My understanding of the Mikado method:

Problem: You see a mess in the code so you decide to fix it. Part way through you realize you gotta fix some other thing first. So you start working on that, which leads to more.

Now your code is all torn apart and you're late for dinner and nothing works.

Solution: When you realize you can't finish problem A until you fix problem B, write down A and abandon your changes so far. When B leads to problem C, write down B and abandon those changes, too.

You're discovering a dependency tree. Eventually you'll get to the leaf nodes. When you fix a leaf node you don't have to fix something else at the same time. So fix it and commit. Now you've accomplished something.

Each commit prunes the tree. Eventually you get back to your original problem, which you can now safely fix without further difficulty

I think this is applicable to other problems besides software, like organizing your kitchen or car maintenance.

Sunday, March 9, 2014

Conways Game of Life in Gherkin

I am trying to learn Gherkin, and needed a problem to practice on, so I chose Game of Life. I think my current result is interesting enough to post.

Loading Gist 9454787

I'm struggling at the moment trying to figure out the right level of detail and correct organization in the tests. I'm curious what I'll think of my attempt a week from now / a year from now.


Saturday, March 8, 2014

Stages of TDD

I've often heard people say this sort of thing about TDD:
I really believe in the importance of TDD. It helps me catch stupid bugs right away, instead of waiting for testers or customers to report them. I especially like that it catches any regressions.
TDD lets me refactor safely. Also, writing tests first help me think about my design from the caller's perspective.
I still need a comprehensive suite of end-to-end tests to make sure the whole system works. I am concerned that, since I'm writing both the code and the tests, my blind spots will appear in both, allowing bugs to slip through. 
When requirements change, or we refactor a subsystem, a thousand tests break. Then we have to spend a lot of time fixing them.
TDD is expensive, but it's worth it.  
While others say something like:
TDD is not a testing activity; it's a design activity. "Test" is a misnomer. With TDD I write better code, faster. 
I don't need a bug database. My whole team only has a couple bugs per month. We do root cause analysis on every bug.
From the first perspective, the second sounds weird. It's hard to believe it's even possible. Maybe it only works in that specific context.

From the second perspective, the first sounds backwards and unenlightened. Why don't they get it?

I now believe that learning about TDD takes time and moves through stages. From the early stages, it's hard to fully understand the later stages. From the later stages, the you wonder why you ever wasted your time in the early stages.

The stages that I see are:
  1. TDD is about testing. I write tests to prove that my code works. Thanks to the safety afforded by those tests, I refactor more.
  2. I commit to 100% TDD. Every feature and every bug fix will be represented in tests. I immediately discover how hard this is, which leads me to mocks, dependency injection, marking methods as "public" for testing, etc.
  3. Tests give me feedback. I see "hard to test" as a code smell. I refactor to make things easier to test. Tests get easier and code gets cleaner.
  4. My tests are always easy to write, easy to read, and super fast. Code is clean. Every idea has a single canonical location. There is no duplication. Classes have great names. Cohesion is high and coupling is low.

    There are no bug farms, no part of the code I'm afraid to touch. Working in this code is inherently low risk. Since I rarely create bugs, testing for correctness is rarely fruitful. So yes, tests got me here, but not by catching bugs.

I found #4 very hard to grasp a year ago. I'm not sure if seeing this roadmap would have helped. Perhaps the right way to begin is just to focus on #1: write tests to catch bugs. Make developers responsible for proving correctness of their work. Don't count a feature as "done" until the bugs are gone. Build from there.

There may be an additional stage in the middle. If you know it, tell me and I'll fill in.

I'm really curious if there's a stage 5. Something I'm blind to. Got any?

Various definitions of "Refactoring"

In conversations with other programmers, I have heard people use "refactoring" to mean four different things.

0. (Not refactoring). Making the minimum necessary change.

This is often expedient and may feel safest, where safety = not breaking existing functionality, and not getting yelled at.

1. Doing more than the minimum necessary change.

You could hack in your bug fix or new feature. Maybe that's hard because the code is already convoluted. Or maybe your hack would make the code convoluted. So you clean things up a bit at the same time.

2. Cleaning up code without changing existing behavior.

At least, you hope you're not changing behavior.

3. A highly disciplined process of known-safe code transformations.

Within a method body I feel confident that I can rename a local variable with Search/Replace if I first check that the new name isn't already in use.

Feather’s book (aka WELC) includes some highly-detailed methods of doing this to legacy code to get it to the point where you can start writing unit tests. By detailed, I mean tedious.

4. Using a mechanized refactoring tools.

For example, select a line of code, RClick, Extract Method.

Only #3 and #4 are safe enough that I do them without fear.

My preference is to do #4, over and over again. (Also, I commit each one separately.)



Some will say that #1 and #2 aren't "true refactoring". I find that arguing about definitions to be counterproductive and off-putting. Each of these activities have value in some context, and each are worth discussing. However, it should be noted that Martin Fowler is one of those people.


Saturday, February 8, 2014

Variation in test terminology

When I observe conversations about testing with other engineers, I notice that latent disagreements about the meaning of testing terminology breaks the conversations. Latent, because people think they're talking about the same thing, but they are not.

Here are some examples:

"Integration Test" / "Integrated Test" - is there a difference?

  • I have tested Thing 1 and Thing 2 in isolation; now test that they work together.
  • A test for Thing 1, but Thing 2 is along for the ride.
  • I test the entire system, end to end.


"Unit Test"

  • Any test written by developers
  • A test that talks to the code directly (any size of "unit")
  • A test of a small portion of the code
  • A test of a single class, in isolation
  • A test of a single, tiny behavior


TDD

  • A practice where developers write automated tests
  • Tests get written before code
  • We look to tests for feedback on our code design
  • I follow a RED/GREEN/REFACTOR workflow

UI Test

    • I test my business rules by manipulating the UI
    • I test my program as a whole by manipulating the UI (assume the components are already tested)
    • I test my UI



    Why unit tests?

    In conversations around unit testing, I regularly hear someone assert the real reason we write unit tests. Sometimes the conversation goes like this:

    Alice: "We should write unit tests"
    Bob: "No. We can already achieve result X through some other approach." or "No. Unit tests fail to accomplish Y which is important."
    Alice: "But Z is the real reason we write unit tests."

    It would help me to have a list of supposed reasons for unit tests, so I'll catalog them here.

    Note that I'm not asserting specific definitions of "unit" and "test" and "unit test", although varying definitions here are a big part of the problem.

    Correctness: proof that my code does what I intended it to do.

    Detractors: There are many bugs that unit tests won't catch, e.g. integration bugs, security bugs, timing bugs. Also, the mindset that created a bug is also the mindset creates the test, so we can't rely on unit tests to catch all the bugs.

    Proponents: Developers often fail at basic correctness, leaving testers the tedious work of finding easy bugs. By using unit tests to ensure basic correctness, even imperfectly, testers can do the interesting and important work we need them to do.

    Regression: proof that my changes didn't break something else.

    Proponents: Much time and energy go to fixing regressions, which could be eliminated if we had tests.

    Detractors: Same arguments as in correctness.

    Refactoring: I can safely refactor without introducing regressions.

    Proponents: Refactoring is key to the long-term well-being of the code base, and the mental health of the programmers. Having unit tests in place makes that safe.

    Detractors: The obvious arguments from above, plus the burden of dealing with failing tests whenever you change something that should be innocuous. Do you fix 100 tests or just throw them away?

    Design: unit tests help me reduce coupling & increase cohesion

    Detractors: I don't see that happening.

    Proponents: When a test is hard to write, that's design feedback to refactor your code. When the test is short, clear, easy to write, easy to read, has a good name, and runs fast, you know your code is in good shape.

    Note that it can take a lot of practice to develop the skills required here.

    Efficiency: Unit tests affect the time it takes to get the job done

    Detractors: Time spent writing unit tests could be time spent creating customer value. Also, writing these unit tests takes a long time.

    Proponents: Highly skilled unit testers write less code because they have less redundancy and they only implement as much functionality as is required by the tests. They also spend less time fixing bugs, and can afford to perform root cause analysis on every bug.




    Sunday, November 17, 2013

    Logging an Item

    MSBuild items seem like the should be almost the same as properties, but they are different in some odd ways. Time to add support for them.

    Most of the time was just spent iterating with MSBuild, trying to understand what it expected, and how it would respond to different inputs. I'm glad I'm encapsulating that here in this class, so the rest of my Task code doesn't have to worry about it.

    Loading Gist 7523672

    EDIT: After posting this, I noticed a bug. I had hard-coded the name on line 7 of LogProperty. Some TDD gurus insist that you write more than one test before removing hard-coded results. If I had done that, I would have caught that bug before committing my code. I'm tired, so I'm looking for shortcuts; if I had a pair, I bet we wouldn't have let that happen.