 This talk is entitled How TDD and CD Make Your Apps Suck Less. And so just by way of introduction, my name is Matt White. I work at Money Desktop. I just started there this week. So far, it's pretty awesome. They do mostly Rails. They do some Sinatra, other web stuff. I've been doing web programming for about five years now, three plus of that in Rails. And I'm what I would call a TDD convert, meaning, thank you. Meaning, I started out not ever writing any tests because no one made me. We thought that was for QA, right? That's what tests are for, right, they're for QA. So that was kind of how I grew up in the Rails community. Fortunately, I moved over to Mosey where I met Mike Moore and some other people that are awesome. And they helped me see a better way. So as I give this talk, I'm going to assume that you at least know what test-driven development is. If you don't, here's a diagram that kind of gives you a summary. Otherwise, I would suggest going to Wikipedia. They got a great article on it. But sometimes other people have different ideas of what TDD is. But I think the one on Wikipedia is pretty good. Pretty good summary. So, by show of hands here, who can admit that they write apps that suck like all the time? No, I mean like all the time, yeah. Okay, anyone that didn't raise their hand is a liar. That's my assertion right now, is that you all write apps that suck. You just either don't know it or you can't admit it. It's one of those two things. So, as we go along, you'll probably, you'll be able to understand my point. So, I'm talking about TDD, but what is CDD? That's the other part of my talk here, the CDD thing that you may not have heard of. And if you go on and Google CDD or comment-driven development, you're going to get some weird results. And I disagree with most of what you find there. So don't waste your time with that. If you want a good definition, I would suggest going to the Scotland on Rails talk that was given by Jim Weirich two years ago. And he has a much better way of doing it. So, a basic summary of how it works is basically you take whatever code you think needs testing, and you comment it all out, and then you write your tests while your code is still commented. And then you uncomment your code until your tests pass. And so, that kind of seems maybe a little bit silly or simplistic, but that's the idea is to make it easy. This is for, like I said, I was a convert. This is kind of one way that I learned to do it the right way. And so, if you're one of those people that doesn't test all the time, or has no tests at all, then this is a great way to get into it. So, why would you use it? That's a good question. Why would I use CDD? There's a number of reasons, and these are just some of the top ones. One, it defines your app. During the demo, you'll see that CDD and TDD are totally inseparable. CDD is useless if you're not testing. And anyone who's ever written a decent amount of tests know that a well-written test suite will define your app. An easy way to evaluate a Rails project, or Sinatra, or anything else that has tests is to go look at them, and if there aren't any, well, then you got a problem. But usually the test of their well-written will define the app pretty well. So, that's a great way to make sure your app is properly defined. Another thing it does is it adds test coverage as I mentioned, is you comment it out and add your tests. You'll automatically have your coverage. It encourages simple design. TDD by default does that. If you're testing, then you gotta write your methods so that they can be tested. Otherwise, you're gonna be in testing hell. It gives you confidence. You know that if you add something or take something away from your code that your app isn't gonna blow up. You got something that's verifying that it actually works so you're not always in fear that, oh shoot, is this, we're moving this line gonna break everything, or we're moving some gem or whatever. I find that as I do this CDD, I'm entering a state of flow much easier, and I'm enjoying what I'm doing. It's like you're fighting against this thing that's telling you you're failing, and then once you beat it, then it's kind of fun and cool to see that you've been able to write better software. And then finally, most importantly, your apps will suck less. And one of the big reasons for that is that you're not wasting your time debugging things, and you actually know what your objective is. You'll have these tests written that have defined your app and you know where you're going and you know when you're done. Instead of working on something that's kind of beating around the bush and not actually getting to where you need to be. And I think that this technique should be a lot more common. It's something that I think is easily overlooked and just kind of an afterthought, testing in general kind of is. But how many of you here by show of hands have ever looked at your code from two years ago and thought, wow, this really sucks? Okay, it looks like everyone's raising their hand. And I'm pretty sure that, okay, so if you raise your hand, if you didn't raise your hand, then you're probably really super awesome and you don't belong here. But if you did raise your hand, that means two years from now you're gonna look back and think, wow, I sucked back then, right? Okay, so then you all write apps that suck. So now you know, now you know how it works. Now you know how it works. But the point of this is that, so here's a very scientific graph of how much you all suck. And you can see the legend up there with TDD, CDD. The line is much lower over time. You see the gap also widens, meaning that people who are using it are not already ahead of the game to begin with, but over time they're actually even further ahead because they have been able to learn more and get better at writing apps. And so you're somewhere along this continuum right now. And if you make this switch to using TDD and CDD, you'll jump from the black line to the red line almost immediately and so you will suck less really fast. And so that's kind of why I say that you all do suck now and you won't suck as much. But the fact of the matter is that nobody writes perfect apps and as you can see, I made that point at the very end there by leveling the line out at a really low level of suck. So when do you use it? I think I kind of explained that, but just to be clear, a great time to use it is if your test coverage is weak or you have non-reusable or non-dry code. Anyone not familiar with the term dry is don't repeat yourself. So any code that has been repeated throughout the app and anytime you want to do any kind of general refactoring, it's a great technique to use. Another great time to use it, hopefully the sound is working okay, is if your boss ever reacts like this. Oh, I don't think I'm getting sound like, you know. Yeah, I've got it plugged in. Anyone have a good level reader? Sounds like it's working. I don't know, I might have to skip that one, but. Oh, I just closed it now. Yeah, I don't think it's working. Anyway, basically the guy turns in some really crappy work and the boss tells him, I should fire you and burn out your freaking house. So if your boss ever tells you that or you're in a code review and you hear that, then yeah, that would be a great time to start using testing and comment driven development. So as I'm doing it, there are some tools that I find really useful. One is Autotest, we've kind of already seen that used in the demonstrations that have been going on. For some reason it looks a little bit different in Linux. It didn't always used to. I don't know what happened. It seemed to happen when I changed to 192, although when I searched for Ryan Davis comments on that, he insists it's not. So I'm not sure what the deal is. I don't want to make him grumpy though, so I'm not going to go accuse him of not updating it correctly. So Autotest is great, so you don't have to go constantly run back and forth between your tests. It'll just run them for you every time something changes. And the other one is FLOG. FLOG is really awesome. If you've never seen FLOG, this is by the Seattle RB guys. Yes, this is awesome. So basically the way FLOG works, so if this is your code here, that little square here, I don't know if you guys can see that, but basically FLOG sees it as a score and it follows this ABC Assignments Branches and Calls and it assigns a score to your code based on how many assignments, branches and calls there are. And so it's like golf. The higher your score is, the worse off your code is. That's why FLOG clearly still backwards is golf. So basically a high FLOG score is really bad. Oh, I forgot to sign out of. I almost got trolled by my colleagues here. There we go. Okay, so here's my crappy code that I'm going to demo now. And this is kind of a small demo. A lot of times you'll have methods that are a lot longer than this that could use some refactoring. This one, in my opinion, is too long. I usually try to aim for five lines or less. And my methods helps for better design. So this one's too long if you ask me and it needs some help, it needs some love. It's not the prettiest thing in the world. So I'm going to start out by turning on Autotest. I might already have it on, but it looks like I do, but I'm going to restart it here. So you can just run Autotest. If you don't want Autotest by default, when you first run it or turn it on, we'll run your whole test suite. And if you have a big test suite, that can be kind of annoying if you just want to run it quick. So you can run it with the dash F if you want it to not run your whole test suite. And so it just kind of sits there in the background while you're coding. And there you can see at the top it ran my test. There's one test right now, so kind of weak. But if I go and flog this code, so I just say flog. And in this case, I'm just interested in the models because that's what I'm writing tests for. So sorry if you can't see that. Is that better? A little bit better. So I just run flog dash A. A says do it against everything and not just the worst ones and show the score for everything that is. And there you can see the total is 20.5. The average per method is 6.8. And the worst one you can see there, account transfer amount two is 18.3. Now, according to the Jake Scruggs, what a good flog score is. Sorry if you can't see that. Zero to 10 is awesome. 11 to 20 is good enough. 21 to 40 might need refactoring. It gets worse and worse than that. 200 plus, someone please think of the children. So, and at previous jobs I've had, I've actually flogged some of the code and it has been well over 200. So I wouldn't be surprised if some of us here could flog our code and find really high scores. And there are only few cases in my opinion where your score should be over 20. They should be rare exceptions and you should be aware of those. 11 to 20, like it says, I think is good enough. In this case, I don't think it is because that's the only method in the whole class. So it can be better. Zero to 10 is what I'd like to go for but occasionally 11 to 20 would be okay. So as you can see here, I got 18.3. I'd like to get that lower. And so what I'm gonna do is I'm gonna comment this whole thing out and when I do that you would think because I have a test, right? It should fail, right? Okay, so I'm gonna save it and then all the test is gonna run its magic in the background, but it passed. Uh-oh, something's wrong. So I go look at my test and all it says, oh, I should make that font way bigger, huh? Okay, there we go. So all it's doing is assert true. Not very useful. So let's get rid of that. You don't need that garbage. And so what we need to do here is look at this method and see what we wanna test. Now we don't need to test find. We're gonna trust that Rails can find properly, so that's okay. But we do need to test these little validations that are going on here. They're raising under certain conditions. So I'm gonna say, well, here we go. Raise transfer amount must be greater than zero if amount is less than zero. So if amount being transferred from account is less than zero, looks like one of the rules here of this app is that you can't transfer between accounts if the amount is less than zero. So we need to write a test that goes and verifies that. So I've cheated a little bit here so I don't have to waste your time writing all that. So I'm gonna do a little setup here, a little setup method and one easy test of that. And so here you see test transfer with amount less than zero should fail with exception. Now, I know a lot of people like to use RSpec. I'm using test unit just to keep this simple. It's already built in. You don't have to do anything extra if you're not doing testing. It's already there if you're doing Rails. And so here's this test. It's expecting an exception to be raised of runtime error and it's running this method transfer amount to with a negative value to Joe's other account here. And so we're expecting this exception to be raised and then the message on that exception should be transfer amount must be greater than zero. And if we go back and look at that, we can see that that's what we're expecting. So if I go and uncomment that and say that we saw the test failed as it was run there, now that I've uncommented that it works, magic. Okay, so now we've added test coverage to that part. So let's keep going. We see this other one here. Raise unauthorized transfer if self user ID is not equal to to account user ID. Okay, so this app is also stating that we can't transfer money to other users. So this is a very simple app that only transfers money between a user with multiple accounts. So from his first account to his second account. So I'm gonna go back and grab another test here that I previously wrote that goes and checks for that. Okay, so transfer to another user's account fails with exception. And so it's pretty much the same as the previous test. We're trying to transfer from Joe account one to John account one. So two different users. So now let me just make sure. So that's still commented there. I say this auto test should fail as I'm going back here to uncomment. So there's the failure and comment. And I save, it runs again and there we go. So now we've got test coverage on that part at least. So as we go through this, we can see that there's some things that can be moved around. You can see that this section down here, there's kind of some repetition. It's not very declarative. They're pretty much doing the same thing, just one with a positive, one with a negative. Those could easily be renamed to withdraw or deposit and it would be more declarative, right? And this one here, as we go to test that one, we would realize this is really just a validation because it's saying that if we try to transfer out more than the account actually has, it should fail. And in Rails, that's just a simple validation to this is gonna have, I'm gonna go and jump ahead here and copy all of this so we can see how much prettier it can be. Okay, so there's all my tests in place. They should all fail pretty miserably. At least one of them did. And then after some refactoring and looking at things, this is how it ended up looking using this CDD, TDD approach. Okay, so we save that, auto test will go run again and everything's happy, five test, nine assertions. Okay, so now we can see that validation up there. We can see that we renamed these, we pulled those transfers out and named them withdrawal and deposit so that made it more modular. Another thing we noticed as we were writing tests here, if we transfer and one succeeds and one fails, well we need to make sure that they both get back to the previous state, otherwise some will end up with more or less money than they deserve. So we wrapped that in a transaction here and made sure that that would roll back if one of them failed. So, and that kind of saved us there. Another thing we noticed as we were doing that is this method here, self.user.verify accounts integrity. Wanted to make sure that that was getting tested properly and so we tried to go figure out what it did as it was commented out before, right? Go and look and it's just kind of stubbed out. Figure out at some point what this does and if we need it. Kind of useless, huh? And that's the kind of thing you might notice sometimes when you're doing CDD is that you have code that either does nothing or doesn't do what you expect and so you can take it out and put it other places. So we took it out and we were left with this. So this looks a lot better. This is a lot more, it's tested. More testable, a lot cleaner, more modular. And as we were doing it, we were able to enter more state of flow and enjoyment as we were doing it. It was easier to understand what's going on because now we can just go look at the tests and see what's going on. Oh, transfer to account succeeds. So when I transfer using this method, I expect it to succeed. Okay, accounts have correct amount after failed transaction. Oh, okay, so we don't have any invalid data. We can then comment this one now. Yeah, things are a lot happier now. Things look a lot better. So, oh, I mean you need to reflog it. Yeah, that's what I was just gonna do. There we go. Flog total 21.6. So the total is about the same and that's expected but the average per method now is about half of what it was before and that big method that had 18.3 is now at 6.6. So everything's less than 10 now, much better. So the complexity of the code is much better now. So I encourage you to use this flog and there's other stuff. If you ever use metric foo, flog is part of it and there's a lot of other cool gems that are included in metric foo that you can use and get some good reports on the state of your app and whether or not it sucks or not or how bad it sucks, I should say, because it sucks, so. Okay, so you probably have all heard this quote before, perfection is achieved not when there is nothing more to add but when there is nothing left to take away and that's kind of one of the aims of CDD. Try to take out as much as we can and have it still work at a minimum level and that way our app is much more streamlined and a lot easier to work with and so that's pretty much what I have. Does anybody have any questions? It's actually the same, the growl notifier I'm using is the same as what we were seeing earlier. For some reason in Linux it shows up funny. It didn't use to, like I was saying earlier, I don't know what happened to it but it still works at least, so. Yeah, I'll look into that later. I don't know if that works because growl doesn't actually run on Linux, so. Oh, well this one's through lib.notify, on Ubuntu or any Linux flavor, so. Yeah, it used to look just like growl but I don't know what happened. Is there a chance for that? Yeah. What's it called? I don't remember offhand. If you Google auto test and there's a blog post about getting started with auto test and it's pretty long and detailed and it goes into it really well, it's on there somewhere. Thank you. No problem. Anything else? Okay. That's it.