When I’m maintaining a code base, sometimes I’ll get a big idea about a redesign that will improve the code. And because I strongly prefer to make changes in small steps, this means that the new design may be done in only a small part of the code for some time, living alongside the old design it’s replacing. In fact, the old design may never be completely replaced. How do we feel about that?
I’ll point to a concrete example. I was working with the open source CloudFoundry app called UAA. There were a few tests for the web UI that were relevant to some changes I needed to make to the code, and I saw that the tests had some duplicated code that could be cleaned up. Rather than simply extract the duplicated code to some common methods, I decided to introduce the Page Object Pattern (I’ve had a lot of success with this pattern on other projects, but I won’t go into all the details of how it works here.) You can see the first two tests that I redesigned in this pull request: Rearchitect two integration tests to use page objects.
After that pull request was merged, we had two tests using page objects, but many more that did not. I had agreement from my team that this was a good change, but we then had a mix of different designs in the tests. Even after later converting several more tests in the same test class to use page objects (current version: SamlLoginIT.java), as of now, not all of the tests in the class are using this design, and there are several other unconverted test classes as well. It’s now almost a year later with little further progress, and I’m no longer working on the team. The redesign will probably not be complete across all of the relevant tests for the lifetime of the repository.
We had a great discussion about this at a Legacy Code Rocks meetup (thanks Scott, Chris, Sarosh, Steve, Jacob, and Jeremy). The incomplete evolution seems to be a very common experience for developers (I’ve encountered it myself several times). The general feeling was that doing them is the right decision, even knowing that the rest of the code may never catch up. It was suggested that we should document these design choices so that in the future, maintainers will know which of the existing design approaches should be used for new code and further refactoring. Finding the best place to put this kind of documentation and keep it up to date can be a challenge, however.
So my advice is to make the code you’re working on better than you left it, even if you don’t clean up all the other code.
Thanks to Bruce Ricard for making some great contributions to the UAA redesign mentioned above and for inspiring this post.
featuredphoto credit: spaceamoeba, CC BY-NC-ND 2.0
I have been rapidly adding books to my office library recently, and I realized that it’s getting hard to remember which books I already have. Scanning the shelves and other stacks where I may have books isn’t enough, because I also have ebooks from several different sources. Surely there is software that can catalog all of my books? Here I’ll document my experience with a library management app to try to wrangle all of my work-related books.
I did a quick search and decided to try Libib. A free Libib account gives me plenty of features for managing a small book collection. I can use the web interface or a mobile app. Once I set up my account, I grabbed my phone and grabbed a book off my shelf to scan the barcode using the mobile app. The book didn’t have a barcode, but it did have the ISBN printed on the back cover. I set it aside and grabbed another book. Success! The moment the barcode was near my camera, the book was added to my Libib collection.
I was able to add maybe as many as two-thirds of my books by barcode. In a few cases, there were multiple barcodes on the back of the book, so I learned to to slide the camera over from the side that had the ISBN barcode. I had to check the information about each book, though, because there were some misspellings in the title and some missing co-authors. A few titles included the subtitles, but most did not. I added the subtitles in most cases. I found it a little odd that for titles starting with “The,” this word was removed (not just moved to the end for collation purposes), but I left them that way. Several entries did not include cover art, or the cover didn’t match my edition, so I either took a rough picture or found a picture online.
For many of the books I couldn’t add by scanning, I was able to enter the ISBN printed somewhere in the book and that worked. For ISBNs that weren’t in the Libib database, I had add the books manually (the pro version of Libib allows for lookups by Library of Congress number, which the older books did tend to have).
There were a few interesting things about entering books manually. For example, a minor mystery was how to translate this word art into a title:
I settled on “PL/I Programming: A Manual of Style” with an “I” instead of a “1.”
There is a field for the number of pages in the book. I had to do a bit of study to conclude that I was supposed to count everything except the covers, including blank pages and probably the end papers. So I took the last page number and manually counted the pages after that and before page 1. One fun case was the Programming Perl Quick Reference Guide, which had a page number on the inside of the back cover and both inside covers had printing. Then there’s Computer Lib/Dream Machines, revised and updated, which is two books printed back to back and upside down from each other, with two separate sets of page numbers that are added together in the page count (it only has one ISBN and one entry in the database). But I also have a scan of the first edition, which includes Dream Machines in reverse and upside down compared to the physical book so you can read it easily on the screen. There is a separate set of page numbers for Dream Machines, and also a continuing page count for the book as a whole upside down in the opposite corner, counting backwards from the highest page number in the book because of the way the pages were reversed.
There’s an interesting anomaly for the book I am a Bug – the text on the spine is printed from bottom to top. Every other book I have has the text reading down the spine instead of up. I can choose to either keep the rest of the book right side up, or align the spine text the way all the other books on the shelf read (I chose to keep the cover and contents right side up instead of the spine). In any case, it was an easy decision to make the cover picture in my book database right side up.
Several of my ebooks books have ISBNs but none have a barcode to scan. The Kindle ebook Dominating ChatGPT in 30 Days, though, has an ISBN for the print version but not the Kindle version. Books I bought on Leanpub often don’t have an ISBN, though they do have a publication date, which can change often as the book is updated. Examples include Developer Hegemony, and everything by Jerry Weinberg from Leanpub, like What Did You Say?, second revised edition. However, What Did You Say? is also available from Smashwords where it oddly does give an ISBN. The Smashwords version has a publication date of 2013, but Leanpub has an update from 2015. All of the books I’ve bought on Smashwords, however, do not include an ISBN. Here again, I was able to find an ISBN for one of them (Aremac Power: Inventions at Risk), for the Nook version from Barnes & Noble, which might or might not be considered the same publication.
A new category of publication I’ve fairly recently become aware of is the “zine.” I have a handful of these visually-oriented ebook zines that relate to my work, and none of them have an ISBN or any date printed inside. Examples include the “Pocket Guide to Debugging: Stellar strategies for sticky situations” and “How to Quit Vim.”
Most of my ebooks that are faithful reproductions of printed books seem to have proper ISBNs, including those from Pragmatic Bookshelf, Barnes & Noble, Manning, and InformIT. But some from more unusual sources, such as a scan of a 1949 book from Project Gutenburg (Giant Brains; or, Machines that Think) and a PDF sourced from GitHub (Uncurled: everything I know and learned about running and maintaining Open Source projects for three decades) have no ISBN and the publication date was not easy to track down.
By the way, I had to get creative with ebooks in Libib so I would know not to look for them on my bookshelf. I added an “ebook” label to all of my ebooks, and I added a note indicating where I got it from. For books with digital rights management, that tells me what app I have to use to read the book.
I really enjoyed having an excuse to thumb through my books, because I found some interesting things inside. I buy a lot of used books, some of which have a previous owner’s name written inside. I’d really like to talk to them to ask where they were in their career when they used the book. Alas, the person I’m pretty sure I tracked down successfully died in 2008.
I found library stamp cards in several former library books, like The Medusa Conspiracy (bonus points if you know why that one would be in my office library). In Computers in Business, I found an unpunched punchcard, perhaps used as a bookmark. And in The Aramac Project there was a letter I forgot I’d gotten from Dorset House thanking me for proofreading the book. In fact, it strokes my ego to find my name printed somewhere in the front of a book, and in some cases I didn’t even know I had influenced the content of a book. There’s one more going to print soon – stay tuned.
Surely there are established library science practices that have worked out how to solve some of the questions about cataloguing books that I’ve run into. I’d love to hear from people who are familiar with this.
At the last few companies I worked for, my organizations happened to follow a similar agile methodology. It worked really well for us, but I don’t have a name for it. Maybe you can help name it. For now, I call it “Beyond Scrum.”
I’ve followed the Scrum methodology on a handful of teams in the past, including all of the ceremonies (colloquially – daily standup, sprints, sprint planning, demo, retro). I was even a certified scrum master.
But over time, some of the ceremonies seemed more useful than others. For one, sprints (a.k.a. iterations) especially created friction without providing any benefit. At the end of a sprint, there would almost always be stories that either had to be split, taking partial “credit” in the current sprint and the rest in later sprints, or pushed wholesale into the next sprint without getting any recognition for any of the work in the current sprint. We could get better at fitting stories into one iteration, but you know what makes more sense? Don’t arbitrarily chop our work up into sprints and then fret about whether the last few stories fit into the time at the end of the sprint.
My recent teams evolved into more of a Kanban sort of flow, where we would finish a story then pull the next story off of the backlog. There was no sprint backlog, just the overall project backlog. I don’t really know how much of Kanban we followed, because I don’t know much about Kanban. But I do like the continuous flow of starting a new story as soon as people were available to take on something new. Caveat: one team used a project management tool to groom at least a week’s worth of work at a time, which looked on the surface like a 1-week sprint, but the reality was that they always wanted at least a little more than a week of stories so they wouldn’t run out and have to groom more stories mid-week. Any stories that weren’t finished at the end of the week would flow smoothly into the next week with no anxiety about what sprint it was a part of.
Speaking of stories, story sizing seemed to lose its value. With no sprints, we didn’t need to calculate velocity in order to know how many stories we could cram into a sprint. Discussing size may have had value in terms of making sure the team understood the scope of a story, but ultimately the teams didn’t care what T-shirt size or Fibonacci number was put on each story. What did still matter, though, was writing good stories that had clear acceptance criteria and weren’t too large. Large stories that couldn’t be finished in a week were difficult to manage. But writing small stories was often difficult to do. Sometimes we’d split a story after seeing that it was taking a long time, or we’d just march on for two or three weeks and get it done.
My last few teams were working on infrastructure software–someone that a product owner might have difficulty relating to customer-visible features. So we found that the product owner role wasn’t very active or useful. Typically we would have a manager or designated person on the team take care of accepting stories, which was often rather informal. Often we wouldn’t even have clearly written acceptance criteria, and often the acceptance process was a rubber stamp that didn’t provide any value to the team. On a related vein – the sprint demo didn’t make much sense. We might demo individual stories as needed to facilitate story acceptance, but with the diminished participation of the product owner, we often skipped the demos. In their place, we might demo significant new features or newly introduced technologies to our team as needed. One team had a weekly time slot for this and other technical discussions.
Besides Kanban, another methodology we borrowed from was Extreme Programming (XP). We didn’t strive to follow all of the essential XP elements, so it would be disingenuous to say we were an XP team. But we did follow the most commonly known XP practices, test-driven development and pair programming. Another element of XP is the 40-hour workweek, and we did pretty well at that one too. Many of the other elements were there, like collective code ownership and continuous integration. But not the iterations and not much of the iteration planning.
We kept some of the other Scrum ceremonies. The daily standup was still useful, especially with a remote team. There were experiments with going more lightweight with an asynchronous standup in a chat tool, and in the other direction with adding a daily stand-down. And the retro was still popular, at least once every two weeks but more likely once every week. It wasn’t hard to find recent things to celebrate or improve on.
So there you have it – the elements of “Beyond Scrum” that were remarkably similar at two different companies. Maybe many companies have evolved toward something similar? Let me know if any of this sounds familiar to you.
feature image photo credit: Tom Natt (CC BY-NC 2.0)
Here’s one for the programmers in my audience. At a recent software crafters meetup, someone brought up the fizzbuzz coding exercise, and how funny it would be to code in bash. Examples of solutions in bash were easy to find, but I didn’t see any that included unit tests. So I tried a test-driven (TDD) solution for fizzbuzz in bash. Here’s how it went.
I updated Bats using homebrew on my Mac. There is now a GitHub organization serving as a home for Bats. I uninstalled an older Bats version I already had, made sure to remove the old tap (“kaos/shell”), and reinstalled from its new home using the brew instructions:
$ brew install bats-core
...
==> Installing bats-core
==> Pouring bats-core--1.11.0.all.bottle.tar.gz
Error: The `brew link` step did not complete successfully
The formula built, but is not symlinked into /usr/local
...
Pay attention to those errors (the “Error:” label was in red on my terminal, but it was buried in a large amount of other log output). I needed to follow the instructions in the error output before I had the new bats-core in my path:
brew link --overwrite bats-core
I also wanted to use the bats-assert library, which depends on bats-support, so I ran:
The fizzbuzz exercise asks for 100 lines of output, printing out the numbers 1 to 100, with these modifications: if the number is divisible by 3, print “fizz” instead of the number. If the number is divisible by 5, print “buzz”, and if it’s divisible by both 3 and 5, print “fizzbuzz”. Rather than try to process 100 lines of output in each test, I planned to write a function to return one number in the sequence. I started with this test in a test subdirectory:
I created a dummy function in fizzbuzz.bash so the test could run:
#!/usr/bin/env bash
function fizzbuzz { : }
And now the test does its job:
✗ fizzbuzz 1 returns 1
(from function assert_success' in file /usr/local/lib/bats-assert/src/assert_success.bash, line 42, in test file fizzbuzz-test.bash, line 12) assert_success' failed
-- command failed --
status : 1
output :
--
1 test, 1 failure
Getting the test to pass was easy enough:
function fizzbuzz { echo 1 }
Not shown below is the satisfying green color on the last line showing 0 failures (it was red before):
function fizzbuzz {
local num=$1
if [[ $((num % 3)) != 0 && $((num % 5)) != 0 ]]; then
echo "$num"
return
fi
if [[ $((num % 3)) = 0 ]]; then
echo -n fizz
fi
if [[ $((num % 5)) = 0 ]]; then
echo -n buzz
fi
echo
}
I was happier after a refactor to remove the redundant logic:
function fizzbuzz { local num="$1" local output=""
if [[ $((num % 3)) = 0 ]]; then output=fizz fi if [[ $((num % 5)) = 0 ]]; then output="${output}buzz" fi if [[ -z "$output" ]]; then output="$num" fi
echo $output }
There are far more concise solutions out there, but I like the readability of my solution. Now, as for getting 100 lines of output when running the scripts directly, I tacked this to the end of my script. This allowed it to meet this requirement while still not affecting how the unit tests work with the fizzbuzz function at all:
if [ "${BASH_SOURCE[0]}" -ef "$0" ] then for i in $(seq 1 100); do fizzbuzz "$i" done fi
I tested this chunk of code manually rather than try to automate a test for it.
$ ./fizzbuzz.bash
1
2
fizz
4
buzz
fizz
7
...
And there you have it, a testable fizzbuzz in bash. Some people add a few extra rules to continue the fizzbuzz exercise, and I’m confident that the tests would help support any further additions to the code.
I know you can’t tell from the looks of it, but I’ve been hanging around here a lot lately, working on a post that wants to turn info a book of its own. I’m making good progress getting it tamed into a reasonable length. But first, inspired by LinkedIn posts from James Bach and from Jon Bach and subsequent comments, I want to explore another idea rattling around in my head.
I’m going to talk about three examples where members of a community were accused of groupthink of some sort. In many cases, some people observing the communities say that they see cult-like behavior. I’d prefer not to use the derogatory term “cult” here for a couple of reasons. One, because cult leaders actively encourage groupthink and blind obedience, and I don’t see that happening in these cases (even if their followers are picking up some of these traits). And also, because real cults have done a lot of damage, such as permanently ripping families apart. Let’s not try to equivocate that with what I’m talking about here.
Example 1: I learned a lot from the author and consultant Jerry (Gerald) Weinberg. I am of his followers. People outside his circle often don’t understand the level of devotion that many of his followers exhibited during his life and afterward. Someone even coined a term for it: “Weinborg”, which many of us have adopted for ourselves. (If you don’t get the reference, look up the fictional “Borg” in the Star Trek universe – we have been assimilated).
I attended three of Jerry’s week-long workshops. Every time I’ve been through an intense experiential training, it has been a deeply moving experience. That’s true of Jerry’s workshops, plus other experiential trainings I’ve attended (several Boy Scout training sessions come to mind, for example). Once you’ve recovered, you want more. But you can’t effectively explain why it was so moving to someone who wasn’t there, in fact, for many of them, you can’t give away too many of the details, or you may ruin the experience for someone who attends later.
There are likely many other behaviors among Jerry’s followers that looked odd to outsiders. Perhaps we would invoke his laws by name, like “Rudy’s Rutabaga Rule”. Or we might reference “egoless programming” and point to the book where Jerry wrote about it. We might get ourselves into trouble, though, if we recommended that people follow his ideas without being able to explain them ourselves. “Go read his book” isn’t very persuasive if we can’t give the elevator speech ourselves to show the value in an idea.
Early in my career, a wise leader cautioned me to build opinions of my own rather than constantly quoting the experts. That has been a guiding principle for me ever since, and one that I hope has steered me away from groupthink.
Example 2: James Bach is a consultant and trainer who has influenced a lot of people in the software testing community, along with his business partner. I have learned a lot from James, and I continue to check in with him periodically, though I have never chosen to join his community of close followers. Incidentally, he has also been influenced by Jerry Weinberg.
James has grown a community of people who agree on some common terminology, which streamlines their discussions. It gets interesting, though, when someone uses that terminology outside that community without explaining what it means to them. I remember attending a software quality meetup that advertised nothing indicating that it was associated with James Bach or his courses. But then I heard the organizers and some attendees use terminology that I recognized as originating from James. It’s been several years since the meetings I attended, but I think I remember them presenting other ideas that closely align with what James teaches, not always identifying where they came from or why they recommended them. I vaguely remember that I stood up once or twice and told them that I hadn’t accepted some of those ideas, and I don’t recall the discussion going very far.
If a group has an ideology that they expect participants to adopt as a prerequisite for participating, that’s fine, but it needs to be explicit. Otherwise, they need to be prepared to define their terms and defend their ideas.
Example 3: I participate in the “One of the Three” Slack forum and often listen to its associated podcast created by Brent Jensen and Alan Page. They have spoken out about James Bach and his community a few times. At one point, some participants piled on to some negative comments that seemed to have no basis other than “because Alan and Brent said so.” I called them out for groupthink, not unlike the very thing they were complaining about. Fortunately, I think they got the message.
I remember talking about the “luminary effect” with author and consultant Boris Beizer years ago. This is where people hesitate to challenge an expert, especially a recognized luminary in their field, because of their perceived authority on a topic. But in fact, all of the experts I’ve mentioned love for you to give them your well-reasoned challenges to any of their ideas. Granted, the more they love a debate, the more exhausting and intimidating it can be to engage with them. There are smart, after all, and you need to do your homework so you can competently defend your ideas – that’s not asking too much, right? In fact, one of the best ways to get their respect is to challenge them with a good argument. I just hope that a few of my ideas here will survive their scrutiny.
In this post I’m talking about some controversial people and some controversial topics. Where I’ve stayed neutral here, I’ve done so very deliberately, and though I have some further opinions unrelated to the topics I’m discussing, I’m not going to muddy this post with them.
Software developers, have you had this experience? You start to fix a bug or add a feature to some existing code, and you have a hard time working with the code because it’s poorly designed. It might not have decent unit tests. It might be full of code smells like long functions, poor naming, and maybe even misspelled words in names and comments. It’s really difficult not to complain about the state of the code you have to work with. If you’re pairing like I often do, you’ll complain to your pair. Or maybe you’ll whine about it to your whole team.
I’m going to make a case for developers to tone down the whining.
I can find peace when I’m annoyed by software that’s hard to maintain by remembering Boulding’s Backward Bias from Jerry Weinberg’s book The Secrets of Consulting: “Things are the way they are because they got that way.” Jerry attributed this to his mentor, the economist Kenneth Boulding. It was possibly inspired by biologist D’Arcy Wentworth Thompson, who said, “Everything is the way it is because it got that way.”
Boulding’s Backward Bias, in a tautological sort of way, reminds us to consider the potentially complex history that got us to where we are now. Weinberg points out “There were, at the time, good and sufficient reasons for decisions that seem idiotic today.” And, he says, the people who created the problems might still be around, and they might be in a position of authority. This leads to what Weinberg calls Spark’s Law of Problem Solution: “The chances of solving a problem decline the closer you get to finding out who was the cause of the problem.”
So resist the urge to track down who committed the code you’re concerned about. But do try to put yourself in their shoes when they were writing the code. Let’s consider a number of possible factors that could lead to awful code.
Maybe the developer wanted to do better, but they had constraints that prevented them from doing so. Common examples are schedule pressure or not thinking they have permission to write unit tests. Frequently I’ve seen that these constraints are imaginary; management probably wants developers to take the time to do it right the first time, but the developer nevertheless puts pressure on themselves to finish faster.
Maybe the developer was inexperienced at the time they developed the code, and there wasn’t enough technical leadership oversight to notice and correct the problems.
Maybe the developer thoughtfully chose a different design standard than the one that you’re judging the code by.
Maybe the developers who worked with the flawed code after it was initially written didn’t feel empowered to improve the design.
When dealing with organizational issues, you might want to learn about the history of how you got here. But with internal code design decisions, I find that it’s sufficient to understand that there probably were good reasons for them without knowing what the reasons actually were. Granted, if you can’t figure out why a particular feature works the way it does, that may require some historical investigation, and that’s beyond what I’m discussing here.
Complaining wastes time and distracts from getting the work done. What if some of the people who wrote that code hear your complaints? Some people are good-natured about such criticism, but not everyone is likely to appreciate these complaints about their work.
Typically when I start working with problematic code, I’ll grumble about it either out loud or to myself, but then I’ll get to work on it. My approach was heavily influenced by Michael Feathers’ book Working Effectively with Legacy Code. I will do enough refactoring to make sure I can write unit tests to cover the code I’m working with. I might do additional refactoring to make the code more readable. But I have to make tough choices about how deep to go with improving the code, or else I wouldn’t ever get much work done. I think I’ve done pretty well with this.
When I asked about this on Twitter, some of the responses indicated deeper issues than hearing whining about bad code.
How often do you or someone on your team complain about the state of code that someone else wrote? How often do you think it's not healthy to do that? (blog post brewing)
There was a report about developers who said the code was too far gone to fix. There was some discussion about code ownership – it’s better to talk about how the team’s code has problems, rather than complaining about the output of one specific person. There was even a mention of a developer who wouldn’t fix the code because it was written by someone else. A few people didn’t think the complaints were much of a problem, and they suggested having a dialog with the original authors to get help improving the code.
Have you or will you ever write code that isn’t perfect? Could your own code be the subject of someone else’s complaints? Surely it will, and my hope is that the team will focus on making whatever improvements are necessary to get the job at hand done effectively without worrying about why the code is harder to work with than they’d like.
My team is committed to Test-Driven Development. Therefore, I was struck with remorse recently when I found myself writing some bash code without having any automated unit tests. In this post, I’ll show how we made it right.
Context: this is a small utility written in bash, but it will be used for a fairly important task that needs to work. The task was to parse six-character record locators out of a text file and cancel the associated flight reservations in our test system after the tests had completed. Aside: I was also pair programming at the time, but I take all the blame for our bad choices.
We jumped in doing manual unit testing, and fairly quickly produced this script, cancelpnrs.bash:
#!/usr/bin/env bash
for recordLocator in $(egrep '\|[A-Z]{6}\s*$'|cut -d '|' -f 2)
do
recordLocator=$(echo -n $recordLocator|tr -d '\r')
echo Canceling $recordLocator
curl "http://testdataservice/cancelPnr?recordLocator=$recordLocator"
echo
done
The testing cycles at the command line started with feeding a sample data file to egrep. We tweaked the regular expression until it was finding what it needed and filtering out the rest. Then we added the call to cut to output the record locator from each line, and then put it in a for loop. I like working with bash code because it’s so easy to build and test code incrementally like this.
After feeling remorse for shirking the ways of TDD, I remembered having some halting successes in the past with writing unit tests for bash code. We installed bats, the Bash Automated Testing System, then wrote a couple of characterization tests as penance:
#!/usr/bin/env bats
# Requires that you run from the same directory as cancelpnr.bash
load 'test/libs/bats-support/load'
load 'test/libs/bats-assert/load'
scriptToTest=./cancelpnrs.bash
@test "Empty input results in empty output" {
run source "$scriptToTest" </dev/null
assert_equal "$status" 0
assert_output ""
}
@test "PNRs are canceled" {
function curl() {
echo "Successfully canceled: (record locator here)"
}
export -f curl
run source "$scriptToTest" <<EOF
Thu Apr 02 14:23:45 CDT 2020
Checkin2Bags_Intl|LZYHNA
Checkin2Bags_TicketNum|SVUWND
EOF
assert_equal "$status" 0
assert_output --partial "Canceling LZYHNA"
assert_output --partial "Canceling SVUWND"
}
We were pretty pleased with the result. Of course, the test is a good deal more code than the code under test, which is typical of our Java code as well. We installed the optional bats-support and bats-assert libraries so we could have some nice xUnit-style assertions. A few other things to note here–when we’re invoking the code under test using “source“, it runs all of the code in the script. This is something we’ll improve upon shortly. We needed to stub out the call to curl because we don’t want any unit test to hit the network. This was easy to do by creating a function in bash. The sample input in the second test gives anyone reading the test a sense for what the input data looks like.
Looking at the code we had, we saw some opportunity for refactoring to make the code easier to understand and maintain. First we needed to make the code more testable. We knew we wanted to extract some of the code into functions and test those functions directly. We started by moving all the cancelpnrs.bash code into one function, and added one line of code to call that function. The tests still passed without modification. Then we added some logic to detect whether the script is being invoked directly or sourced into another script, and it only calls the main function when invoked directly. So when sourced by the test, the code does nothing but defines functions, but it still works the same as before when invoked on the command line. We changed the existing tests to call a function rather than just expecting all of the code to run when we source the code under test. This transformation was typical of any kind of script code that you would want to unit test.
At this point, following a proper TDD process felt very similar to the development process in any other language. We added a test to call a function we wanted to extract, and fixed bugs in the test code until it failed because the function didn’t yet exist. Then we refactored the code under test to get back to “green” in all the tests. Here is the current unit test code with two additional tests:
#!/usr/bin/env bats
# Requires that you run from the same directory as cancelpnrs.bash
load 'test/libs/bats-support/load'
load 'test/libs/bats-assert/load'
scriptToTest=./cancelpnrs.bash
carriageReturn=$(echo -en '\r')
setup() {
source "$scriptToTest"
}
@test "Empty input results in empty output" {
run doCancel </dev/null
assert_equal "$status" 0
assert_output ""
}
@test "PNRs are canceled" {
function curl() {
echo "Successfully canceled: (record locator here)"
}
export -f curl
run doCancel <<EOF
Thu Apr 02 14:23:45 CDT 2020
Checkin2Bags_Intl_RT|LZYHNA
Checkin2Bags_TicketNum_Intl_RT|SVUWND
EOF
assert_equal "$status" 0
assert_output --partial "Canceling LZYHNA"
assert_output --partial "Canceling SVUWND"
}
@test "filterCarriageReturn can filter" {
doTest() {
echo -n "line of text$carriageReturn" | filterCarriageReturn
}
run doTest
assert_output "line of text"
}
@test "identifyRecordLocatorsFromStdin can find record locators" {
doTest() {
echo -n "testName|XXXXXX$carriageReturn" | identifyRecordLocatorsFromStdin
}
run doTest
assert_output $(echo -en "XXXXXX\r\n")
}
You’ll see some code that deals with the line ending characters “\r” (carriage return) and “\n” (newline). Our development platform was Mac OS, but we also ran the tests on Windows because the cancelpnrs.bash script also needs to work in a bash shell on Windows. The script ran fine under git-bash on Windows, but it took some tweaking to get the tests to work on both platforms. There is surely a better solution to make the code more portable.
We installed bats from source and committed it to our source repository, and followed the instructions to install bats-support and bats-assert as git submodules. We’re not really familiar with submodules and not entirely happy with having to do a separate installation of the submodules on every system we clone our repository to (we have to run “git submodule init” and “git submodule update” after cloning, or else remember to add the option “–recurse-submodules” to the clone command).
Running the tests takes a fraction of a second. It looks like this:
$ ./bats test-cancelpnrs.bats
✓ Empty input results in empty output
✓ PNRs are canceled
✓ filterCarriageReturn can filter
✓ identifyRecordLocatorsFromStdin can find record locators
4 tests, 0 failures
Here is the current refactored version of cancelpnrs.bash:
#!/usr/bin/env bash
cancelEndpoint='http://testdataservice/cancelPnr'
doCancel() {
for recordLocator in $(identifyRecordLocatorsFromStdin)
do
recordLocator=$(echo -n $recordLocator | filterCarriageReturn)
echo Canceling $recordLocator
curl -s --data "recordLocator=$recordLocator" "$cancelEndpoint"
echo
done
}
identifyRecordLocatorsFromStdin() {
egrep '\|[A-Z]{6}\s*$' | cut -d '|' -f 2
}
filterCarriageReturn() {
tr -d '\r'
}
if [ "${BASH_SOURCE[0]}" -ef "$0" ]
then
doCancel
fi
There are two lines of code not covered by unit tests. Because the one test that hits the loop body in the doCancel stubs out curl, the actual curl call is not tested. Also, the doCancel call near the bottom is never tested by the unit tests. We ran manual system tests with live data as a final validation, and don’t see a need at this point to automate those tests.
It’s been too long. Hello again. I’m still working on the next installment of Jerry’s Story. I’m going to restart it once again, and some day I’m going to get it right. Meanwhile, I dug up a list of my articles, presentations, and other content from 1995 – 2009, both self-published and otherwise, and found working URLs for them (I appreciate archive.org so much!). I’m putting them here mostly for future reference, but if you do delve in, please let me know if any of the links still don’t work.
One bonus if you scroll all the way down–my very first feature article, from 1987.
There are also some posts on myTejas Software Consulting Newsletterblog that I may republish here. Many of the articles linked below have outdated contact information. I’d love to hear from the modern you either in a comment on this post or via Twitter.
So with no further ado, in reverse order of musty outdatedness, here is my long list of nostalgia.
March 2008 Gray Matters podcast (mp3) Jerry McAllister interviewed me for this podcast, where we talked about the testingfaqs.org Boneyard and the strength of the worldwide test tools market.
Trip Report from a USENIX Moocher Dallas/Fort Worth Unix User’s Group, July 2003, reprinted in the Tejas Software Consulting Newsletter, August/September 2003, v3 #4
Developing Your Professional Network The Career Development column in the January/February 2001 issue of Software Testing and Quality Engineering magazine. References from the article are available on the STQE web site.
Software Defect Isolation Co-authored with Prathibha Tammana. Presented at the High-Performance Computing Users Group, March 1998, and InterWorks, April 1998.
Tester’s Toolbox column: The Shell Game Software QA magazine, pp. 27-29, Vol. 3 No. 4, 1996 Using Unix shell scripts to automate testing. Basic information about the available shells on Unix and other operating systems.
Tester’s Toolbox column: Toward A Standard Test Harness Software QA magazine, pp. 26-27, Vol. 3 No. 2, 1996 The TET test harness and where it fits into the picture.
Tester’s Toolbox column: Testing Interactive Programs Software QA magazine, pp. 29-31, Vol. 3 No. 1, 1996 A concrete example of using expect to automate a test of a stubborn interactive program.
Tester’s Toolbox column: Using Perl Scripts Software QA magazine, pp. 12-14, Vol. 2. No. 3, 1995 The advantages of using the perl programming language in a test environment, help in deciding whether to use perl and which version to use.
Apple Kaleidoscope, Compute! Magazine, pp. 111-112, issue 91, Vol. 9, No. 12, December 1987. I was interviewed about this article for “The Software Update” episode of the Codebreaker podcast, released December 2, 2015.
As I sit here listening to Christmas music, I’m giving myself the gift of extra time to write. I want to respond to something Paul Maxwell-Walters recently tweeted:
If there is such a thing as a Tester’s Mid-Life Crisis, I think I may be in the middle of it….
Paul cited the director of a counseling center who said mid-life crises are likely to happen between age 37 through the 50s. Paul, approaching his 40s, worries that his crisis is here. As I see my 50s getting large on the horizon, I don’t know if my crisis has past, is still coming, or will never come. I was actually around Paul’s age when my consulting business dried up and I ended my 16-year run in software testing. Four years later, though, I went back to my comfort zone, and had four consecutive short stints in various testing jobs.
That last testing job morphed into a development job. I’m very happy with my current employer for encouraging that path to unfold. Over the years, I have fervently resisted several opportunities to move into development, some of them very early in my career. I had latched onto my identity as a tester and staunch defender of the customer, and I wouldn’t let it go.
Paul wrote:
I have also come across people around my age and older who are greatly dissatisfied or apathetic with testing. They feel that they aren’t getting anywhere in their careers or are tired of the constant learning to stay relevant. They feel that they are being poorly treated or paid much less than their developer colleagues even though they all work in the same teams. They hate the low status of testing compared to other areas of software development. They regret not choosing other employers or doing something else earlier.
That’s surely the story of any tester’s career. Low status, low pay, slow growth. I embraced it, because I loved the work and loved what it stood for. The dissatisfaction seems to be more common now than it used to be, though. My advice, which you will know if you’ve been reading things on my blog like “The black box tester role may be fading away“, is: get out! Don’t transition to doing test automation. Become a developer, or a site reliability engineer, or a product owner, or an agile coach, or anything else that has more of a future. I think being a testing specialist is going to continue to get more depressing as the number of available testing jobs slowly continues to dwindle.
Because I’m writing this on Christmas Eve, I want to put an It’s a Wonderful Life spin on it. What if my testing career had never been born? In fact, what if the test specialist role had never been born?
Allow me to be your Angel 2nd Class and take you back to a time when developers talked about how to do testing. Literature about testing was directed toward developers. What if no one had worried about adding a role that had critical distance from the development process? What if developers had been willing to continue being generalists rather than delegating the study of testing practices to specialists, while shoving unit testing into a no-man’s land no one wanted to visit?
And what if I could have gotten over the absolute delight I got from destroying things and started creating things instead? I’m sure I’d be richer now. I’d have better design skills now. But alas, I’m not actually an Angel 2nd Class, and more to the point, I haven’t dug up enough historical context to really play out this thought experiment. But I’ll try to make a few observations. Within the larger community of developers, I might not have been able to carve out a space to start a successful independent consulting practice, which I dearly loved doing as a tester. Maybe I wouldn’t have developed my appreciation for software quality that I have now. Maybe I wouldn’t have adopted Extreme Programming concepts so readily as I have, which has now put me in a very good position process-wise, even if I’m having to catch up my enterprise design and architecture skills.
How about not having any testers in the first place? Maybe the lack of critical distance would have actually caused major problems. Maybe the lack of a quality watchdog would have allowed more managers to actually execute those bad decisions. And maybe those managers would have been driven out of software management. Would the lack of a safety net have actually improved the state of software management by natural selection, and even allowed some companies with inept executives to die a necessary death? I think I’m hoping for too much here, and perhaps being too brutal on Christmas Eve.
It has been a wonderful career. It could have been a different career, but I’m just glad that it has taken me to where I am now. Paul, I wish you a successful outcome from your mid-career crisis. I realize that my advice to get out is much easier said than done.
I have given myself a challenge that I’m enjoying right now – immersing myself more deeply in the Wikipedia community. The culture that has developed around the people who add to and edit Wikipedia bears some resemblance to the culture that we see with users of services like Facebook, Twitter, and Slack, but it’s much more complex. I aim with this article to convey a sense of the richness an online community can develop, and the frustration that an outsider can feel, much like a physical community. Maybe I’ll convince you to be a Wikipedia editor too.
First I’ll mention another online community that encourages its members to make contributions that everyone can benefit from. I made an attempt to become a productive contributor on Stack Overflow, a web site for asking and answering questions about computer programming.
Stack Overflow uses a reputation system, where various contributions you make will increase your reputation score. Additional features becomes available when your reputation grows. I thought that building a good reputation on Stack Overflow could be something I could add to my resume. I answered a few questions, and was able to especially build reputation when I answered questions that no one else had answered. But I had trouble finding questions that hadn’t already been thoroughly answered within minutes of being posted. And I was chastised a few times for not strictly following the rules when I posted answers, which was disheartening when I had put effort into answering the question. I understood that a community should have rules, but I lost interest before I really learned enough of the rules to be productive on the platform. I went back to only reading the content on the site, which, like many programmers, I do often.
I created my Wikipedia account way back in 2003. According to the contribution log, which shows almost every detail from the beginning, this must have been because I wanted to add a new article about load testing. I had made some edits to other pages without an account, but I needed to have an account to create a new article. That article is still there today, and I’m happy to see that after hundreds of edits, some of my original phrasing is still there.
Wikipedia newbies would be wise to hold off on creating new articles, however. I have added a total of seven articles. Of those, two have been converted to redirects to other articles with a broader scope, and one was deleted. I created an article about Brian Marick in 2007. Amazingly, it lasted until 2016 before someone claimed that he was not notable and eventually got it deleted. Recently, someone created an article about Janet Gregory, and within 24 hours, someone started a proposal to delete it. It was deleted 8 days later. For many topics, perhaps especially for articles about people, it’s quite difficult to prove that they meet Wikipedia’s notability guidelines. It must be sad when someone sees that they’ve been declared non-notable. I will probably not be adding new articles any time soon.
My long tenure on Wikipedia has caused others to assume that I’m well-versed in the rules of the road, but with my off-and-on interest in contributing, I’m just beginning to absorb the elaborate set of rules and conventions that editors are subjected to. In response to an edit I made to the article on Jerry Weinberg that was not up to snuff, one editor told me “Surely you know by now that ‘he told me so himself’ is not considered to be adequate sourcing for anything here…” Unlike a traditional encyclopedia with articles written by trusted experts, everything on Wikipedia is expected to be backed up by published independent sources.
I find it rewarding to make edits that improve the content on Wikipedia. Doing the research to justify the edits helps me to build my knowledge. I can refer to the article later when I want to refresh my knowledge of a subject, and so can anyone else. But the benefits are greatly reduced if the changes aren’t accepted by the community. In the case of my edits on the Jerry Weinberg article, I swallowed my ego and asked how I could change my approach. This led to a healthy discussion, and I was successful when I used a different approach. It’s often not easy to engage in this sort of discussion when I’m still smarting from having someone erase my work.
I recently decided to get more involved with Wikipedia because of efforts by Noah Sussman and Alan Page. Noah put a lot of effort into improving the Wikipedia article on software testing. Then Walter Görlitz, another volunteer Wikipedia editor, removed a big edit that Noah made to the article, an action that Wikipedia calls a “revert.” A heated discussion ensued, and little progress was made on overhauling the article like Noah wanted to. Alan issued a call for help to figure out how to effectively improve the article (A call to action: let’s fix the Wikipedia page on software testing), and I and several others joined a discussion on a chat group outside of Wikipedia to strategize.
I decided to take an agile approach; make small changes and watch to see how the broader Wikipedia community responds. A few of us have also branched out and looked at the many other articles related to software testing and found that as a group, they aren’t very well coordinated or well written. We’ve made numerous small changes to these articles now with a great deal of success, though we haven’t made substantial progress on the original goal of completely revamping the software testing article. I’m starting to make somewhat larger edits now and I hope others do too.
Instead of wondering how Walter would react to my edits, I decided to engage with him directly, so I invited him to open a dialogue. I’ve seen that he puts a great deal of effort into improving and reverting vandalism on a large number of Wikipedia articles. Walter tells me that the small edits I’ve made successfully has improved my reputation with him, and he is now less likely to revert the changes I make. Unlike Stack Overflow where reputation is tracked in one place, on Wikipedia your reputation is earned individually with each editor.
I’ve found that the best way to get consensus on Wikipedia is to use the “talk page” feature on the site itself, so everyone who is following the changes on the page have an opportunity to respond. In fact, the Wikipedia community prefers for discussions like this to take place on Wikipedia, otherwise some editors may get suspicious that one person is trying to recruit a cabal of “meatpuppets” to artificially amplify their influence. Our chat group is very loosely coordinated, and the edits we make are all an individual decision, so I’m not worried that we’ll raise this suspicion, especially now that we’re using the talk pages more.
The response to an edit can vary significantly based on who is watching for changes on the article. Many areas don’t happen to have anyone watching closely, so whether the edit is useful or not, it may stay around for years. If someone feels strongly about maintaining the quality of a particular article, you’ll be held to a higher level of scrutiny. I’ve also found that newly added information may be held to a higher standard than what is already in an article. Once I added some information to an article without including a citation to back it up, and the information was removed, despite the fact that most of the information already in the article was also not referenced. It was just easier for the editor who did it to revert a recent edit than to address the broader problem. You can avoid this if you scrutinize your own contributions very carefully.
If you’re afraid you’ll have difficulty getting edits on Wikipedia to stick, take this to heart, from the instructions to “Be Bold” with your edits:
Think about it this way: if you don’t find one of your edits being reverted now and then, perhaps you’re not being bold enough.
Now if you’ll excuse me, that load testing article has become a bit of a mess.
My thanks to Simon Morley and Walter Görlitz for helping me improve this article.