Pages

Wednesday, November 6, 2024

Can I discover how good is someone at TDD without watching them or looking at their code?

I've come to believe that the best way to interview a developer is to pair program with them, giving them a problem in their technical strength and being a questioning, supportive pair partner in order to see what their best is. And I want to make it a good experience for the candidate. The things I look for in a pairing session include, of course, TDD. Besides gauging their skill, I am looking for things like how they handle difficulty, if they can accept direction, how they handle disagreements, if they are curious, if they are careful and detail-oriented.And I want to see if they can have fun while working on something. 

If the candidate is not strong at TDD, I can help them learn and leave them with suggestions and resources on how to improve if they would like to apply again.

If the candidate is good at it, we can talk about TDD styles, or code patterns, or architecture or really anything relevant to the position. I'm looking to see their curiosity, and what they would. be like to work with.


Recently, a client asked me to participate in a panel interview for a senior developer they were considering. The panel was a mix of non-technical and technical people, so no pair programming was going to happen. I got to thinking how hard would it be to gauge whether a person' was good at TDD if their resume listed it as a skill. I proposed this to a colleague, D., who is learning TDD and we decided to see if I could do it. Here's a slightly edited transcript of what we did over instant messaging:


Jeff Hoover

Imagine you are Test Driving new functionality, say adding a student to a course, and I can't see your screen. Describe what I would see as you go from first test to first implementation to second test to implementation.

 

D.

I'd tell you that I'm assuming that the student and course classes exist, so that I don't have to go through the exercise of creating the "data bag" classes.  Then I'd tell you that I am going to assume that I'm creating a service class that will sit behind a controller or some other "exposing mechanism" so that I'm not having to deal with that nonsense...

 

Jeff Hoover

OK. So what do you type first in your editor?

 

D.

then I'd probably tell you that I'd start with a JUnit test case, and I'd say that I'd have a testAddingAStudentToACourse test method.  I'd tell you that.

 

Jeff Hoover

What goes in the test case?

 

D.

So the first thing that I do is something like: boolean studentAdded = registrationService.add(new Student());

 

(EDITORIAL - I would really like to hear: "To make sure my framework is configured properly. I'd write a test with assertTrue(false) , run the tests and see it fail, then I'd delete the test." Lack of this remark doesn't prove anything, but if they say it it gives me confidence in their experience with things that might go wrong, and that they can think about a process and describe it well)


Jeff Hoover

Does your test case compile? (Ed: I wish I'd figured out a way to get at this without handing to him. His answer doesn't give me much)

 

D

Nope.  So now I let the IDE generate the RegistrationService class.  Once that happens (and the add method is created as well) then the test fails.

 

(Ed. I probably should have asked why the test failed, to get at what assertion he had.


Jeff Hoover

Bonus point for IDE automation!

 

D.

Then I implement registrationService.add(Student student) { return true;}

 

D.

Then the test passes.

 

D.

Now for the next test, I realize that I've added a student, but haven't specified what course to add them to.  So now need to refactor the test and the class to add them to a course.  So now I have in the test:  boolean studentAdded = registrationService.add(new Student(), new Course());


(Ed. If I had asked: "let's say you don't have a Course class - would you stop working on the Service and take a side-track to TDD the Course, or stay with the Service and added tests to the Course as needed?", it might give a senior dev a chance to comment about outside-in vs inside out)



D.

Now I need to test that the course contains the new student after I register the student.  At least I think that's the right thing to do...

 

D.

That's the next smallest test that I can think of...

 

( Ed: Nice! Candidate has shown me a basic ability to TDD. If they were applying for a junior position, this would go a long way towards receiving an offer. Candidate has the basics. They will hone their skills when they pair program with senior developers on all their cards. For a senior position, more conversation would, of course, be necessary.)

 

D. 

I now have to look at whether or not the registration should return a boolean or an altered course object.  I'm gonna go with the latter.  So now, I change the return types, etc...



Thursday, February 15, 2024

Don't know where to start on TDD?


This post got me thinking about how difficult it can be for people new to Test Driven Development to know where to start. I started writing this post as a reply to the post, but I ended up 29 characters beyond the limit, so here it is:


You might start by coming up with an example of what you want your code to do, what its behavior should be. Here's an example:

"When I give [specific complex input] I will get [specific output]"

Then imagine this example sequence of test-writing:

1. a test that asserts that I can call newCode
2. a test that asserts that calling newCode returns nothing
3. a test that asserts that calling newCode with any argument returns nothing
4. a test that asserts that calling newCode with any argument returns six (or some other dummy result)
5. a test that asserts that calling newCode with simplest argument returns the correct result
6. a test that asserts that calling newCode with more complex argument returns correct result

Each test should fail when you first write it. Then you add implementation to newCode to make only that test pass. Don't add code to make future tests pass - remember, small steps. As you write implementations for subsequent tests, 1-4 will fail (maybe even to the point of not compiling). That's expected. Delete any test that is superseded by one that moves you closer to your goal.

You might not get to 6 with the same steps as me, or you might gain enough TDD experience to start at 2 or 3 or 4 instead of 1. But hopefully this example shows a means of tests "driving" behavior.


Friday, September 1, 2023

"Don't Make Me Think" about your code.

Sometime after 2008 or so, I started telling software crafters that I was coaching that I have a guideline for code quality: “Don’t make me think.” Often I used the phrase to reference one of the many well-known code smells. Recently, I wondered where I first learned that expression. Unfortunately, online searches are dominated by Steve Krug's 2000 UI/UX book (with editions in 2005 and 2013) by the same name.The earliest use that I could find of “Don’t make me think” as applied to code quality is a blog post from 2011, If you know of an earlier use, please get in touch.

As I thought more about "Don't make me think", I've come up with many examples. Here's my list so far. What's your favorite?

  1. Have a readme that tells me how to run tests and install and run your application
  2. Use a test naming convention that gives the action, initial state, and expected state.
  3. Name things well
  4. When possible, use the names that are the same as in the business domain
  5. No commented-out code
  6. No comments, except to explain something that you have to do in an unexpected way (Debunk “good” comments)
  7. Unless you squash, have meaningful commit messages. If you squash, have meaningful merge messages.
  8. Write short methods
  9. Write with idiomatic use of your language
  10. Don’t write “clever” code
  11. Don’t over architect or over generalize
  12. Have sufficient types and amounts of automated tests
  13. Maintain your tests as you maintain the rest of your code
  14. Methods should have few arguments, and probably no boolean arguments.
  15. The interface of a class should “hang together” without methods that “don’t belong”
  16. Avoid “primitive obsession”
  17. Find solutions besides switch statements
  18. Don’t go crazy with inheritance. Prefer composition.
  19. Use a rich domain model, not an anemic one.
  20. Avoid conditional nesting
  21. Avoid subclassing unless you need all (or most) of the parent class (avoid Refused Bequest)
  22. Declare things where they are used.
  23. Arrange Act Assert
  24. One assert/concept per test
  25. Avoid double negation
  26. Use a Formatting Convention
  27. Short feedback cycles
  28. Don’t mix levels of abstraction




Tuesday, September 29, 2020

Rough Post - Styles of Pair Programming

This post is probably a little rough, but I wanted to get it out, so here it is:


Fowler’s article on pair programming lists four "styles" of pair programming:

  • Driver and Navigator

  • Ping Pong

  • Strong-Style Pairing

  • Pair Development


and then goes on to throw out Pair Development as not being a style. Ping-Pong to me is totally a different style from anything else. There are also "Evil Pair Partner" and “Silent Ping Pong”, which are variants of PingPong, so I'd say there are three styles related to Ping Pong.


Fowler doesn't list "Just Pairing” as a style. Likewise, I can’t see the difference between “Just Pairing” and D/N - I consider them different names for the same one style.


The rule of Strong Style from Llewellyn Falco and Clare Macrae is: "For an idea to go from your head into the computer it must go through someone else's hands". I'll restate this as "Driver may not type their own ideas; driver must only type the navigator's ideas." That sounds like D/N with a restriction, so maybe a separate style.

All that said, I’m seeing five distinct styles:


  • Driver and Navigator aka “Just Pairing”

    • Strong Style 

  • Ping Pong

    • Evil Pair Partner (more for practice, not writing prod code)

    • Silent Ping Pong (more for practice, not writing prod code)



Saturday, June 22, 2019

Bangle - Next Steps

I haven’t solved the bridging issue yet, so I’ve started printing the first of two cut-down halves to see if I can glue them together seamlessly. (CA/Superglue is said to be good for this).


Here's the filament I chose. It's non-metalic PLA but looks a lot like copper:



If the result of gluing the two cut-down halves is good, I'll sand the inner diameter to make it a comfortable thing bangle, then print two full halves in copper and try gluing them. If gluing doesn't end up being a good solution, I'll have to solve the bridging issue, perhaps with supports, or with tweaking print/extrusion/fan speeds.


Saturday, June 15, 2019

Designing and 3D Printing a Chunky Bangle

Initial Concept

My wife likes big, chunky jewelry, so I thought I would try and 3D print something for her. I started with this as a basic idea:


This bangle looks to be in 3 pieces. My wife has similar multiple-part bangles. The pieces are held together with elastic. It allows for a smaller inner diameter, but the elastic is a potential point of failure. My wife also has one-piece bangles that she can slide her hand into, so I figure I can make a one-piece for her.

Tools

Printer: I have access to a Prusa i3 MK3, the predecessor to the MK3S.

Modeling: OpenSCAD - open source, "The Programmers Solid 3D CAD Modeller". Unlike traditional CAD apps that rely on drawing and/or moving around shapes, in OpenSCAD, you build designs by writing lines of OpenSCAD language code (some examples) Once you have a design in OpenScad, you have the software render it and export it in STL format.

Slicing / exporting gcode: Prusa has their own version of the popular open source app, Slic3r. Slicing is the process of taking a 3D model (usually an STL file) and converting it to a stack of slices that a printer can print, one on top of the other, to create the object. Gcode is the low-level instruction language used by printers to control the motions of the bed, the extruder, the arms and so on. Exporting gcode is the process of taking slices and converting them into those low-level instructions


Iteration 1:

Design a basic ring-ish shape. The easiest way I found was to design a disk and subtract from it a disk of same thickness but smaller diameter, centering the disks on each other. It looked like this:


Iteration 2:

I wanted to improve the ring design from a harsh geometric shape into the first inkling of a bracelet design. This involved learning to rotate a 2-D circle in 3-space around an axis, using the rotate_extrude() function. The result looked like this:


Iteration 3:

To create the first printed prototype, I reduced the thickness of the bracelet design, keeping the diameter. Thinness causes a quicker print and uses less plastic. Took about 40 minutes to print. (No pic)

Iteration 4:

Discover that diameter of first physical prototype is about 1/3 of useful bracelet diameter. Scale design to 300% and print again. When my wife put it on, it was a little too big, I decided to measure a favorite bracelet of hers and use its inner diameter (ID), 65 mm, for my design. Going back to the Iteration 1 design, I quick-printed something with the 65 mm ID. Holding it up against the favorite bangle, the ID matched. My wife put it on and called the fit perfect.  So, I had confirmed the final ID. (No pics)

Iteration 5:

Learn to go from circular cross-section to something more complex.
First, I set out to relearn parabolas:

https://www.symbolab.com/solver/parabola-equation-calculator
https://www.mathwarehouse.com/geometry/parabola/
https://www.mathwarehouse.com/quadratic/parabola/interactive-parabola.php
https://www.mathwarehouse.com/geometry/parabola/standard-and-vertex-form.php
https://www.desmos.com/calculator/dz0kvw0qjg
https://www.omnicalculator.com/math/parabola#how-to-use-the-parabola-equation-calculator-an-example
https://www.desmos.com/calculator/mey71rif1d


I created a curve that I liked. It turned out to be Y = 0.3*(x-6.1)^2 + c.


Then I enclosed the curve into a shape with a perimeter and area:


Then I plugged some points into the equation and added 3 corners. It was simple to turn those points into a polygon call in OpenSCAD (in a future iteration, I would calculate and add more points in between these points, to smooth out what was a faceted curve:

            polygon(points=[[6.0, 0.000],
                            [5.5, 0.108],
                            [5.0, 0.363],
                            [4.5, 0.768],
                            [4.0, 1.323],
                            [3.5, 2.028],
                            [3.25, 2.773],
                            [3.0, 3.518],
                            [3.0, 0.000]
                        ]);

The rotate_extrude() call stands up a shape and rotates it around the z axis. Applying this to my polygon resulted in a shape like a volcano:


In OpenSCAD, I duplicated the "volcano", fliped it 180 degrees, and lined the duplicate up with the base of the original:



I showed the double-volcano model to my wife for her blessing, which I received. A first real physical prototype is in sight.

Iteration 6:


I decided to test-print only one volcano, just to have something to handle before committing to the whole print (which the slicing software projected would take 13 hours at 7% infill). It turned out that because of the nature of the curve I chose, the edges taper to a very thin edge are are not comfortable to wear:




Iteration 7:

I decided I needed to find some way to smooth the sharp edges and reduce/eliminate the faceting artifacts shown above. I had already figured out how to reduce the faceting down the face of the "volcano" by using more points to express the curve that became the polygon that was rotated through space. The faceting around the perimeter turned out to be easy - I had to add an extra argument, $fn=360, (number of fragments) to the rotate_exclude() call. Of course, the extra computation changed the rendering and slicing time from seconds to several minutes. I smoothed the lips edges of the volcano by smoothing the polygon itself using the offset() function before the rotate_extrude() call. The resulting model looked like this:


I started to print, but not long after it started, the print separated from the bed. I suspected that there was not enough surface area on the bed, so I stopped the print and restarted it with a 15mm brim (seen below) After 13.5 hours, the first "real" prototype print looked like this:





But the print had some issues:





It might be because the sharp angle of the base of the upside-down volcano extends out too far for support good bridging. There are lots of ways to address bridging issues. But before I tried any of those, I noticed that the model had a weird asymmetry. Displaying each volcano in a different color: 


and shifting one of the volcanos along the x-axis make the asymmetry more obvious:



It turns out that before I used the offset(), the two halves would remain on the z plane when one was turned upside-down. But adding the call resulted in one of the volcanos impinging across the z-plane. Could the impingement be responsible for the defect?


Iteration 8:

I moved the offset()'ed volcanos so their bases both sit on the the z-plane, but that resulted in a "bumpy" circumference, so I didn't print it - it needed more adjustment:


Iteration 9:

By making each volcano cross the z-axis an equal amount, I got to  this:

To determine whether there is a bridging issue, but not have to print the entire 13.5 hours, I "chopped off" some of the top and bottom of the model (by intersecting it with a box)

I printed iteration 9 and the bridging issue remains:


 So, next is figuring out how to fix it.






Monday, February 13, 2017

Tuesday, December 13, 2016

Things an "Expert" C Developer Knows: Undefined Behavior

This is the first post in my series: Things an "Expert" C Developer Knows


The 2016 SEI CERT C Coding Standard [free download] gives  this definition of Undefined Behavior:
Behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which the C Standard imposes no requirements.
An example of an action that causes undefined behavior is signed integer overflow. Professor John Regehr at U of Utah has a blog post that gives several possible examples of what C compilers *could* do with this C statement in a program:
printf ("%d\n", (INT_MAX+1) < 0);
The possibilities Regehr lists include printing 0, 1, 42 or formatting your hard drive. While the latter won't actually happen, undefined behavior can cause data corruption and/or security vulnerabilities.

The CERT C Coding Standard lists. in Appendix C, all undefined behaviors in the current C standard: C11 (more formally known as: ISO/IEC 9899:2011) There are 203.

Since I've mentioned the C11 standard here, I should probably have a Things an "Expert" C Developer Knows post about C standards.

Things an "Expert" C Developer Knows...

In January, my engagement with my current client will be taking a real change in direction. Rather than being a software craftsmanship coach, I will be joining a large team as an embedded C developer.

In that vein, I submitted a talk to my employer's internal conference in February about things that developers need to know if they are going to say they *really* know C.

In preparation for that, watch for a series of posts from me on: "Things an "Expert" C Developer Knows"

Tuesday, November 15, 2016

Always TDD? But Certainly Not for Startups, Right?


I just read "Uncle Bob is Smoking His Socks", in which the author, Joe Rounceville, starts by taking issue with Uncle Bob's statement that you are not a professional software developer if you don't TDD. I understand how that statement makes people bristle. Rounceville goes on to suggest that there are contexts (in particular lean startup) that don't merit TDD. He goes as far as to suggest that TDD in that context might be "gold plating."

Of course, startups need to be ultra-agile and able to get fast feedback on experiments by getting work out in front of customers rapidly. There's an element of racing against time when a company has to achieve success with only a certain amount of capital to burn through. Startups need to be able to pivot quickly in response to what they learn in their experiments.

But people who argue against "always TDD" seem to think that they save time up front by not using TDD. Among experienced TDD developers, I have not found that to be the case. And well test-driven code allows for rapid pivots because it's easy to understand and different concerns in the code are not tangled-up among each other.

Another risk for startups that decide not to TDD is that prototypes tend to be pushed into production as-is. Many young companies have met serious pains when their initial rushed-to-market release could not handle the load that came with the product's rapid success. And there's the old saw that if you don't have the time to write it correctly the first time, you are not going to have the time to re-write it later.

The relationship between TDD and startups is tricky. Learning TDD takes time. It's not a simple matter to start using TDD if you aren't good at it. Developing software with TDD when you are new to it is slower than developing without TDD. There is a non-trivial learning curve. As a developer, I would not want to try to get good at TDD while working for a startup. And I personally would not want to join a startup if I wasn't already strong in TDD. And because of the ever-changing nature of the market, if I have a startup idea and I want to get good at TDD before starting on it, I'm likely to miss my window of opportunity. And if I develop my startup without TDD, I'm likely to end up with a hard to change and hard to maintain project that's prone to defects. So while Uncle Bob's statement about "always TDD" may sound dogmatic, I find the statement that "startups don't need / can't afford TDD" to be false.


Wednesday, October 19, 2016

The Importance of Saying "I don't know and..."

As a technical coach, especially when I am embedded in a team, it is hard to overemphasize the importance of using the phrase "I don't know," plus "let me go find out." or "I'll ask someone who I think can help us."

Being able to say "I don't know" is an important skill for developers if they want to be on a high-performing team. Without it, the team risks creating an environment of fear and delayed learning. So when I say "I don't know ..." as a coach, I am modeling behavior that will help devs succeed. And adding the second phrase models taking initiative. While many, perhaps most, devs are good at finding answers to things they don't know, there can still be cultural and other reasons why a developer might not step out of their usual domain.

If you have coaching responsibilities, either formal or informal, and you don't say "I don't know..." maybe ask yourself if you are missing an opportunity to model important behavior.

Tuesday, October 11, 2016

Guest Post: Thoughts on Re-evaluating Spring as a Framework for Building Well Maintainable Applications

I'm hosting this post for Brandon Zeeb, another software craftsman at Pillar

Thoughts on Re-evaluating Spring as a Framework for Building Well Maintainable Applications


Spring Boot is good for the general populous.  Not sure I’d recommend it for any greenfield development.

Spring Boot wraps up a lot of auto configuration that most developers, in my experience, do not understand.  Spring Boot enables 3-6 annotations, 2-3 of which you cannot enable outside of Spring Boot.  Those 3-4 common annotations explode out into at least 10 more annotations.  In total, all those annotations create a large number of beans very few developers know exist.  Only if you had been using Spring since version 2.0 and had followed it's growth through MVC (and its ViewResolvers) into the RESTy side (with its MessageConverters) will you know what it's actually doing.  In short, very few developers I've found can actually be effective within Spring.  If they try to find the right place for a thing, they spend more time reading documentation to find the right extension point (what base class does my @Configuration need to extend so that I can put this bean in the ContentNegotiationManager!?!?) and if they work around the framework they pay later when Spring works against them

If you're writing small micro services and you finish them and forget them I don't see a problem.

Though these days there are many more frameworks which are slimmer and offer a more obvious and easily testable environment which makes, in my view, Spring less relevant. (edited)

In short, I'm looking at more ala cart solutions.  An example might be using Guice (for IoC) with Vert.x 3.  There is pretty minimal frameworking, whatever your app does, you see very clearly.  The examples online don't show what I believe to be the best use of Vert.x (and Guice), they put all the codes in one file and say "hey look how easy this is!".  The Java APIs of Vert.x are incredibly clean, all interfaces, easy as hell to mock, which ties in well to properly abstracting and dividing the application into components

The built-in Hazelcast solves a lot of problems in architecture N-tier apps have.


Brandon Zeeb:
Twitter: @phasebash
Github.com/phasebash

Monday, September 26, 2016

SonarLint for Eclipse Does Not Analyze Test Files by Default

I say unit tests are code like any other code. As such, they deserve to be refactored and kept clean.

I learned today that, by default, SonarLint for Eclipse disagrees. If you select Window->Preferences->Sonar Lint, you will see that "Test file regular expressions" contains "**/*Test.*,**/test/**/*":


For SonarLint to behave the way I want, which is to treat any file whose name ends in ".java" as code, I have to clear the regex.

Wednesday, September 21, 2016

Legacy Code: Characterize or Test Protect?

In the context of working with legacy code, I often hear the terms "Characterization Tests" and "Test Protecting". Here is how I think about each term:


Characterization Tests: a suite of tests that I build against legacy code that uses sufficient representative inputs to convince myself that I have characterized the behavior of the object I am testing. This suite also documents the behavior.

Test Protecting: building a suite of tests against legacy code that covers the object under test sufficiently that I am confident that the suite protects me from inadvertently changing the object's behavior during refactoring or when I add features.


One thing that is interesting to me is that the exact same suite of tests can both characterize and protect an object's behavior.

I was running a workshop today and introducing the concept of test protecting. Here are the steps I introduced today. If you are new to test protecting, they should be a good starting point. If you've done some test protecting, I hope you'll find some new nuance to your understanding. If you're already awesome at test protecting, maybe you'll want to teach from my steps, or comment with your insights about test protecting.

Steps I Teach to Test Protect Legacy Code:

Select a class to protect. 

Usually this is the class that I need to fix a bug in or add a feature to. Or maybe I have a few spare cycles and I want to add coverage to a frequently edited class. I do not select a rarely-modified class to test protect - that would be a waste of time (and thereby money, mine or my employer or client's)

Create a test class.

Legacy code, by one definition is code that doesn't have tests, so it follows that a legacy class won't have a test corresponding class. Create a test file with a name and location that follows your unit testing framework's conventions.
(Commit to your version control.)

Choose a public method to start with.

Often legacy classes are large and complex enough that it is difficult to test protect them well through only the public methods. I see several options (of varying risk) in that case:

  1. Change private methods to public
  2. Change private methods to package protected
  3. Don't protect private methods that are difficult to test through public methods.
  4. Refactor the class so that it is easier to test.
  5. Use a testing tool that allows you to test non-public methods.
  6. Protect as much as you can in a reasonable amount of time, assess risks posed by unprotected code, then decide on your next action.
The only answer that I am comfortable with is number 6.

Write a test that exercises the method you selected.

If you are at least somewhat familiar with the behavior of the method you selected, think about edge case inputs and happy and sad path inputs. Choose one to start with. Some devs like to start with edge cases, some like to start happy or sad path. I'm not sure it matters.

Give your test an absurd assertion against the result.

This step is as much like characterization as it is like test protecting. I want to be sure my test fails before it passes, so I choose a value that is unlikely to be correct. For instance, if the method I am exercising is:
int lookupDogAverageWeightLbs(string breed);
then I will choose a number like 5000:

assertEquals(5000, lookupDogAverageWeightLbs("Goldendoodle"));

Run the test so it fails.

In the previous example, your test should fail with a message something like:
Expected [5000] got [40].
If it doesn't, check your assumptions and look for mistakes in your exercising of the method or in how you wrote your assertion.

Fix the test so it passes.

When we test drive, we assert what the expected value should be, run it to see it fail, and then write the least amount of code needed to make the test pass. In test protection, the existing implementation is the standard behavior, so we fix our expectation to make the test pass. From absurd fail message above, you can easily see the actual result delivered by the implementation and fix the expectation with it so that the test passes:
assertEquals(40, lookupDogAverageWeightLbs("Goldendoodle"));
(Commit to your version control.)

Examine the test coverage of the method you are exercising.

If your coverage tool doesn't automatically run when you run tests (why doesn't it?), then run your coverage tool. Find which lines of your exercised method are not executed by your first test. If there are none, your method is test protected.

Write another test that hopefully increases coverage. 

Write a new test and give your exercised method new input data. Use inputs that, based on whatever understanding you have of the method so far, will exercise a non-covered line. Sometimes the name of the method will give you an idea, sometimes you will have gleaned some tentative understanding by scanning the implementation code while looking at coverage, and sometimes you still won't have a clue. Pick new inputs however you can and run your test.

Get the new test passing.

You might have needed to write a green test in order to increase the method's coverage. That's ok. In test driving, a test that doesn't go red before going green is worthless and time-wasting at best and possibly dangerous at worst. But in test protecting, such a test is only worthless if it does not add to the method's coverage. If your new test didn't increase the method's coverage, change your inputs until it does. This is another difference from when we test drive. When we test drive, we choose a set of inputs that fail and implement code to make it pass. We change the inputs only if the ones we chose did not make the test fail.
(Commit to your version control.)

Check the coverage of your method again.

If you have not covered all the lines in the method, jump back to "Write another test that hopefully increases coverage." If you have covered all the lines in the method, start over again with"Choose a public method to start with." If you've covered all the lines in the class, you have test protected the class. Congratulations!

Make sure new tests are run in CI.
If there are already other test classes in your project, your continuous integration server may already be configured to run tests in all test classes. If not, adjust your CI server's configuration to be sure that it runs your new tests. Kick off a build and inspect the build output to be sure your new tests ran.


Monday, May 9, 2016

Jokes and Fun and Safety at Work

Something happened at a conference I recently attended:

Between sessions, there was a long line at the men’s room. There was little or no line at the women’s room. Behind me in line, out of my vision, I heard a man say:

“Maybe we're all starting to feel transgender right now.”

If you didn't cringe when you read that, I get it. In a way, it is a clever nod to all the happenings related to the recent North Carolina bathroom law.

But I cringed when I heard it, and I cringe each time I read it again. Unfortunately, that comment is hurtful. Would a transgender man feel welcome hearing that? I can't imagine one would.

I'm sure that the comment was not intended to be hurtful. But if a transgender man heard it, and I can't be certain that one did not, I expect it would have a hurtful impact. And if we want a better workplace and a better world, I believe we have to take responsibility for the impact of our actions, not just our intentions.

I believe everyone wants a fun work environment, and jokes can certainly be part of that. But I'm hoping that we can create cultures that push women and members of marginalized groups out of IT less. When someone is outnumbered in a setting, it can be hard to speak up about what feels OK and what doesn’t, especially if the rest of the group seems to (even if silently) condone something. I understand - rocking the boat about culture can be a risk to employment. As white, heterosexual men, we are probably not experienced with what can feel hurtful to people different from us. I try to honor and believe people’s life experiences. Even if I can’t see how something can be hurtful, I try to believe people when they say something is.

All of this means that I try to avoid humor that is sexually charged, comments on people’s bodies, or touches on race, religion, sexual orientation, gender presentation, and other characteristics of members of marginalized groups.

And if a joke like that slips out and I notice, I don't wait for someone else to point it out. I call myself out on it and acknowledge the possible impact, even though it wasn't my intention.


Saturday, May 7, 2016

Slides from my Diversity in IT talk at Agile and Beyond 2016 are Now Available

At Agile and Beyond 2016, I presented: "Why Should I Go See Another White Man Talk About Diversity in IT?" I spoke mainly about how women and members of marginalized groups are often made to feel unsafe or unwelcome at IT companies, usually without men realizing the impact of what we are doing.

I've put my slides up (link below). If you are interested in the topic, please take a look. I have included many links to information and resources.

slides - scroll down below my profile to find the .pptx



Wednesday, May 4, 2016

A New Adventure - Same Employer, Same Client!

For the last ten months, I have been a software developer on a high-functioning, reference team at a major enterprise client. The team is a great group of people, with solid processes, and has been delivering defect-free code and providing mission-critical business value since before I joined them. But my time as a member of that team has drawn to a close.

Starting Monday, I will be moving within that same client to join the coaches in their Agile Center of Excellence. This new role will allow me to use more of my personal, social, coaching, and training skills, and will no longer involve creating production code on a cadence.

I am really excited for my employer, my client, and myself. The change comes at a time when the development team is beginning a pre-planned size-down, so my employer has somewhere to place me immediately. My client can continue to benefit from my accumulated domain experience. Both will be getting their best value by paying for work that I excel at, and I'll be doing work that I have a deep passion for.


Thursday, March 31, 2016

On Privilege

What do I mean by “Privilege”? Privilege is a set of advantages that I receive, without earning them, merely because of my birth circumstance(s). Privilege is different than prejudice or “ism”s. As a person of privilege, I get the advantages even if don’t have prejudice and don’t engage in overt oppressive activities like sexism or racism.

Some examples of my privilege as a white man include:
• I can go to any workplace and expect to see people who look like me
• If I make a mistake, no one will say that it’s because white people are stupid
• The lead roles in movies and tv shows are usually of my color and gender
• If I have children and a career, I won’t be called selfish for not being home with the children
• I am more likely to get a job than an equally qualified applicant who is a woman and/or person of color

There are many types of privilege, including (specific examples and details can be found by following the links):
White Privilege
Male Privilege
Heterosexual Privilege
Able-bodied Privilege
Class Privilege
Religious Privilege
Cisgender Privilege (contrast with transgender)

You may say: “But I have struggled! I don’t have privilege!” Privilege isn’t about whether I am powerful, wealthy, have a good job, have worked hard all my life to get where I am, or struggle every day. Privilege is simply unearned advantages I get because I belong to a group.

Sometimes when people learn about the concept of privilege, people feel guilt?  If this happens to you, ask yourself where the feeling is coming from. Is it because:
• I never noticed it before?
• I act in ways that make this worse?
• I don’t do things that make it better for people without privilege?

Now that you know about privilege, what can you do about it? You can:
• Read about privilege
• Stay on the lookout for other privileges you might discover you have
• Belive the life experiences of people with less privilege
• Ask yourself if your actions or policies reinforce the advantages of certain privileged groups
• Don’t “help” less-privileged groups - it robs them of agency. Instead amplify their voices (retweet, etc)

Wednesday, March 2, 2016

Pair Programming "Out Loud"

One element of Pair Programming that I find key to success is "Programming Out Loud". When I have the keyboard and mouse (driving), I want to be explaining my train of thought to my pair partner, moment-by-moment. Conversely, if I am the non-keyboard partner (navigating), I want my partner to be explaining their thoughts moment-to-moment. If my partner is practicing "silent pair programming", I try to ask ask questions like: "What's next?" or "Where are we headed?" to try to get them talking about what they are thinking or doing. Or if that doesn't help, I can ask to drive for a while and try and draw out their ideas that way. Usually things don't go this far, unless my partner is new to pairing. When I start to go off silent and solo, my partner usually invites me back into collaborative mode.

But what if my partner is the navigator and they are disengaged? I can ask (slightly tongue-in-cheek): "Are you with me?" It can require courage to "call out" my partner in that way, but sometimes it takes courage to be on a successful agile team. And if I'm the disengaged navigator and I catch myself being distracted, I can ask to write the next test to try and focus myself back on what we are doing.

Do you have ways that you draw a distracted partner back into collaboration? Or do you have ways to get yourself back into collaboration?