Posts Tagged agile
I had a really amazing experience this week. I had an opportunity to pair with a gentleman who has been programming since I was in diapers. He had only ever test driven code once before, but is really excited about learning. I think that in and of itself is amazing, but I learned a few things during the experience as well.
Being it was his second time test driving, I navigated while he drove. He’s seen our team pair, and asked if we were going to ping pong (one of us would write a failing test, then the other make it pass), but I wanted him to build as much muscle memory as possible. A smile lit up his face and I can’t tell you how happy it made it me.
Our task was to parse a version number and increment the patch number on it. He immediately created a new file for the test, but I stopped him.
“Hey, wait a sec. Why don’t we take a few minutes to write a test list first?”
“What do you mean?”
“Well, I like to take a minute to think about the inputs, outputs, and edge cases, then write down any tests we think we should write. The test list is our roadmap. It keeps us on task. I don’t like Test Drive Design. I like to do just enough design first, then test drive the development.”
“Okay man. I can dig that. That’s smart. I never understood how TDD makes better code when you weren’t taking the time to design anything. So, what you’re saying is that TDD doesn’t mean “don’t design at all”, but “design just in time”, right?”
“Yeah. I guess I am. Something like that anyway.”
I’ve said before (perhaps not on this blog, but before and relatively often) that TDD doesn’t excuse us from good design. What I hadn’t realized is the “just in time” part. Just like everything else in my development cycle, I’m doing just enough, just in time to be most effective, yet still as lightweight as possible. It’s interesting and I’m surprised it never dawned upon me before.
Anyway, we create a test list and finally he gets to create that test class he was so eager to create. Even though he’d barely done this before, he instinctually created the test class first. I know I had to have been smiling as I watched him. “This guy’s a natural”, I thought to myself. He copied just enough code from a neighboring test class to get the test runner to pick it up and then I had him delete any extra remnants of the other test. We paused for a moment and I asked him to pick out what he thought would be the simplest test to write. He picked one and began writing the test, beginning with creating an instance of our not yet existent class.
“Whoah! Hold up! You’ve got a failing test.”
“What do you mean? There is no test yet!”
“Yes there is, and it’s failing. That code won’t compile. A test that won’t compile is a failing test.”
He smiled. “Okay man. I’m picking up what you’re laying down. Write just enough to make it fail, right?”
There it is again. Do just enough, just in time….
So, he created the new class, ran the no-op test, and went back to writing the test. Soon, we also had a no-op function and a failing test that actually tested something meaningful.
“Okay. Now let’s make this pass.”
“Sure. Now let’s see. We’re going to need to split this string and..”
“Whoah! Slow down a little. Is that really the simplest way to make this test pass?”
“What do you mean?”
“Well, why not just hard code the return value?”
He looked at me like I was totally nuts. Maybe I am….
“Huh? I don’t get it.”
“If we hard code the return value, the test will pass. Then, we’ll go back and add a new test that forces us to change the code. If we start splitting strings right now, we might accidentally write code that isn’t tested.”
“Hmm…. I don’t know man, but you’re the expert. All right, let’s just hard code it for now.”
He reminded me of just how counter intuitive TDD can be when you’re new at it. Heck, it can be counter intuitive when you’re experienced at it. I’ve been doing this for years and I still feel weird hard coding that return value, but I’ve also burnt myself by writing more code than absolutely necessary to make a test pass. This is a nice reminder of just how much discipline it takes to test drive.
We test drove this little function for about an hour, writing just enough code to make it fail, then just enough to make it pass. At the end of that hour, we had about a dozen tests and I could see the concern growing on his face. I had a feeling that I knew what was coming.
“Man. These tests are great, but the implementation code sucks. It’s downright awful. Are you sure about this TDD stuff?”
“Yeah. That code does suck, doesn’t it. Let’s fix that. So far we’ve been skipping the refactor step. I wanted you to get the feel for the red/green cycle. I probably should have stopped us sooner, but you were having fun and building muscle memory.”
We spent 15 or 20 minutes refactoring the code until we had something not quite as nasty as before.
“Dude. That was awesome! We just completely tore the guts out that code and it still works! More importantly, I know it still works, because the tests are green. Not bad man, not bad at all. I can see why you young guns like this so much.”
This is something that strikes me often about test driven code. Test driven implementations tends to be ugly as sin. I mean, downright bad, rotten, spaghetti code implementations, but we have the tests. We can refactor. Hell, I’ve deleted entire implementations and started over, knowing that when I was done, I’d have working code again, because I have the tests. The ugly implementation is only a problem if we skip the refactor step. Don’t skip it. Even if there’s nothing to refactor, be sure to take a moment to double check that there’s nothing to refactor yet. It’s really important to build that habit. Next time I teach someone TDD, I’ll be sure to make it an explicit part of our cycle from the very beginning.
We continued on like this for the rest of the afternoon. By the end of the day, we had a really bullet proof implementation ready to merge in.
“So, Chris… This is really great and all. I mean, I had fun man, but it took us all afternoon to write that one function. Is this really worth it?”
“Well, you’re right. It took a long time to write that, but let me ask you something. All those edge cases we found… If we had whipped up the 30 minute version of this function, without test driving, would we have thought of them?”
“No. No way.”
“When would we have thought of them?”
“Months from now when someone reported a bug.”
“Right. Months from now, when…”
“When we’ve forgotten all about this code and not only will it be more expensive to fix, but it’s also caused real damage out in production.”
“Exactly.” I said with a smile on my face.
An old dog learned a new trick this week, and this pup learned some valuable lessons as well. I’m looking forward to pairing with him again. I’ve a feeling we’ve got a lot to teach each other.
For the last several months, I’ve been working on a project that involves reading data from a device via Bluetooth and then sending that data to another machine via RS232 serial port. At least, that’s my piece of the puzzle. It’s proved to be a challenging, but not insurmountable, task for someone who is traditionally a CRUD programmer.
Yesterday, we discovered that we built the wrong software. I built what I was asked to build, but it isn’t what the client needs. The sensor device that was chosen doesn’t provide appropriate data for the use case. I spent the day salvaging whatever code I could for reuse in the end product, but the application itself (and several months of development) will be thrown away. This has prompted me to have a personal retrospective on this project to see if there were things we could have done to fail faster.
A few months ago, I wrote about the beginning of our journey on the path to agility. Since then, we’ve made some major strides and also had some serious set backs.
Our process was getting better, but we continued to struggle with the legacy code base. I was writing tests and refactoring the code base, but was still the only one doing so. The team was seeing the benefits I was getting out of it, but simply didn’t know how to do it themselves.
Agile isn’t just a methodology or a big buzzword but it is a mindset which encompasses a set of values and principles which are light-weight and if implemented correctly Agile can pave the way for creation of great products. Organizations which stay true to the agile principles not only promote flexibility and openness but are also fun to work
If you follow me on Twitter, you’ve probably noticed that I’ve been talking and reading a lot about Agile lately. I’ve drunk the koolaid; I’m a believer. If you actually know me though, you know that this wasn’t always the case. I used to mock all of the zealots and their precious Agile methodologies. So, what changed? There has to be a reason for this. There is and it’s also why I’ve not been as involved in Rubberduck and this blog lately.
I used to work on a small team for a large retail grocer. We were agile (little “a”). We had to be. In the retail grocery world, if you’re not, you’re sunk; you’re done, go home. There’s a constant flow of products being introduced and retired, new sales and marketing strategies, etc. This means that not a day goes by without someone needing more from the system you’ve built. You have to be able to react quickly and accurately to the business’ needs.
Like I said, we mocked the Agile folk, with their daily stand ups, planning meetings, etc. because we were truly agile. We worked in small batches, kept our code clean, tested, and flexible, refactored as needed, had direct access to our users (and took advantage of it), deployed early and often. Man, we were good and we knew it, even if we were working with ancient tech, we consistently delivered value to the business on a regular basis.
Fast forward a year, and I’m no longer working for that grocer. I started working for a small manufacturing company closer to home. No tests, no documentation, no real deployment process, and a ton of legacy code where the previous idea of reusing code was Ctrl+C, Ctrl+V. Some of this stuff is a real nightmare, but I’ll just refer you to The Daily WTF to get a feel for what the existing code base was like. But hey, I get to work with .Net instead of VB6!
At the interview, they told me they were going through an Agile transition. I was pretty excited about that, so needless to say I was a little disappointed at the state of things when I arrived. The only “Agile” thing in sight was a kanban board without any WIP limits and a daily stand up that was really just management’s daily status update. There was lots of talk about wanting and needing to be more agile though. That was a good start.
So I dove in. I created a shared folder in our google drive and started putting whatever notes I needed to make for myself to implement a feature in it. I started making small refactorings to the code, mostly renaming variables and extracting methods. I was just trying to make sense of things.
Pretty soon, my team mates were putting their own notes in the shared drive too, but every time I released a new feature, something broke. “This can’t continue.”, I thought to myself. “I’ve got to get this under test.”
So I asked our OPs team to set up a VM for testing and tried. I did manage to get the code (in the area I was working in) under test before adding a new feature. The number of critical bugs released to production had significantly dropped, but the work was slow. It was taking far too long to get anything done and released. It was then that I remembered some things I had read in “Working Effectively with Legacy Code” by Micheal Feathers.
Good is the enemy of better.
I didn’t need to get all of the code under test, but I did need to make sure that I wasn’t breaking any existing code. I stopped trying to wrap tests around the one layer legacy system (Everything was stuffed in a webforms code behind). Instead, I created a new class library in the solution. Any new or changed behavior went into a new class in that library and got a full suite of tests wrapped around it. I didn’t change the legacy code beyond swapping out or adding a call to the new, tested, code. Now I knew that the whole system worked because new code was tested and the old code hadn’t really changed.
The number of bugs released had dropped further, but bugs will always happen. Fixing them had become easier too, because I could add a new (failing) test case and it would point me right to the lines of code that needed to be changed. Deploying those changes however, was still far too manual, risky, and time consuming.
I set about migrating our ASP.Net web sites to web apps so that I could leverage config transformations and deployment packages. This was a huge boon to my productivity and worth every second it took to do. I no longer had to manually check the diffs to see which files I needed to copy/paste deploy. All I needed to do was build a new version in RELEASE configuration and deploy. Within an hour of finding a bug in prod, I could deploy a fix for it. I was starting to gain the trust of my users. Yes, we were still going to have occasional regression issues, but we could fix them quickly.
The code I worked on was getting better, but that was just my code. The rest of the team was still mucking through the legacy system with their cowboy boots on. Everyone understood the value of tests, good designs, and being agile, but just didn’t know how to do it. Unfortunately, I didn’t know, and perhaps still don’t, know how to teach what I knew about being agile. Our process was still a mess and I didn’t know how to fix it, or communicate my knowledge. I needed to find a way to do both.
The team knew a bit about big A Agile, so I figured my best hope was to communicate in those terms, but I was loathe to do it. So much of the talk about Agile these days focuses on the processes and tools that it’s sickening.
Individuals and interactions over processes and tools
The other thing I always hated about “Agile” was the time boxing. I knew from years of experience that time boxing the development of systems for internal use was a death march. It may work fine for new or external facing development, but working in sprints with hard deadlines for internal systems will just never work. The business needs this functionality, or this bug fixed now, not a week and a half from now. I needed a way to keep what agility we had, but also needed a way to move forward.
I started reading. A lot. I began with the manifesto and that’s where my first glimmer of hope was. No where was there any mention of any specific process. No where did it say that you must work in two week sprints or hold a daily meeting. It simply said that there are ways to increase your agility, and that there is no one true way to do it. There’s hope, but what is this? Everything I’d ever heard about Agile involved silly things like time boxing and mob programming… I kept reading.
It’s turns out that everything I knew about Agile was really everything I knew about Scrum. There were other Agile methodologies out there that I knew nothing about. “Surely one of these will work for us.” I kept researching.
Then I found it! I found kanban. No time boxes, limited context switching via WIP limits, a prioritized backlog that still let us re-prioritize on the fly. This is what I had been doing all along without ever knowing it. I already had the process, but now I had the words. “This will work.”
I immediately shared what I had learned about kanban with management and explained what was wrong with our current board. I got permission to revamp it. I got the team together and explained what I wanted to do. We groomed the backlog and worked with the stakeholders to prioritize it. Things were starting to look up.
At least until I received some very vocal feedback and concern about our “new” process.
“WIP limits! But what if something is broken and needs fixed now?! You’re telling me I’ve gotta finish these other things first?!”
“Well… Yes and no. By all means, if something is broken, fix it now. Don’t make the business wait.”
“But then the WIP limits are worthless!”
I had a problem. I knew that the WIP limits were important in order to reduce cognitive context switching. We couldn’t afford to lose them, but we were still living with a shody system. Things broke at least several times a week. At least I had the weekend to think about it.
I spent my weekend doing more research on kanban. I watched several videos and read several articles before I found my answer. Swimlanes. What we needed was an “urgent” swim lane. Whenever something was broken, or a VIP made a request, we could use this swim lane. Anything in that lane was the priority. It would allow us to “safely” break our WIP limits and, perhaps more importantly, keep track of the number of urgent tasks the team was dealing with.
I went back to work on Monday, solution in hand. The team reacted well, and we implemented immediately. The goal was to track the number of urgent tasks we faced and reduce them over time. Fantastic! We’re making progress.
At least until the next complaint rose.
“So, if we just keep pulling the highest priority task, how am I supposed to tell the business when something will be done?”
“How do you estimate that today?”
“… based on my workload and priorities.”
“Okay, so keep doing that.”
“But, if the priorities can change, how can I tell them?!”
“Don’t our priorities already change fairly often?”
It was evident to me pretty quickly that this wasn’t going anywhere. I needed a way to make this team member feel comfortable doing the same thing he’d always done, but with a new name. I had nothing. Well, I did, but I really didn’t want to go that route. I really, really, didn’t even want to think it. Time boxing.
Now, I knew the effects that time boxing has on quality. As you get closer to the end of a sprint, people will cut corners in order to “just get it done”. This was absolutely the last thing I wanted to happen. I couldn’t have this happen. The department had traded quality for speed for years. Which in the long run is exactly what had slowed development to a crawl. “No. There has to be another way…”
I discretely pulled our most senior dev aside and explained the situation, both my co-workers concerns and mine. He had some experience with “Scrum light” and I valued his input. After some discussion, he convinced me that there was a bit of merit to having a two week iteration. (We use the iteration because it doesn’t have the same psychological effect as “sprint”. We didn’t want anyone rushing anything to meet an artificial deadline!) It would allow us to better track how much work we actually got done and improve our estimations over time, but he agreed that it would put quality at risk. We decided on a compromise. We have the team estimate what we could get done in two weeks, but with the explicit understanding that those estimates would be awful (we had no historic data after all), and no one was to sacrifice quality in order to meet an artificial deadline. If an item didn’t get done, it simply got pushed to the next iteration as the highest priority item in the queue. It was still kanban and it wasn’t a real timebox. Grudgingly, I agreed that this was a win and rounded up the rest of the team to discuss the idea.
To my surprise, this has worked very well. To the best of my knowledge, no one has felt pressured to get something done just because the iteration is over. As an added benefit, we had a built in time to hold a retrospective. They’ve been entirely invaluable to our team. It’s a chance for us to reevaluate whether or not our process is working frequently, as a team.
So, what we ended up with is some bastardized hybrid of Scrum & Kanban. I’m sure there are many Agile zealots out there who will tell me that we’re doing it wrong, but I don’t care. What we’re doing works for us. We’re a better team with a better process than we’ve ever had before. We deliver more value, more frequently than before. We’re more agile and that’s more important than being “Agile” in my book. After all, no where in the manifesto does it say “Here’s the one true Agile way.”, it only points towards what some of those ways may be. It’s something that I think we’d all be wise to remember. I hated the idea of Scrum, but was able to see value in some of its practices, so we borrowed the ones that work for us and left out the things that wouldn’t. We iterated quickly, recieved feedback with an open mind, and then acted on that feedback. If that’s not Agile, then I don’t know what is.
Of course, I still need to find a way to teach people how to test drive their code, but that’s a problem, and topic, for another day.