What's the Minimum Requirement for a Successful Project? 3

Posted by Berin Loritsch Mon, 21 Jul 2008 15:15:00 GMT

This is in preparation for a newbie series on the Ruby language; however, it’s included for your benefit here. There are certain things that any project needs to keep track of if it is going to be successful:

  • What needs to be done?
  • What was done?
  • And how close are we to being done?

I’m going to avoid all the project management and tech speak as much as possible. For you college students, pay attention, if you aren’t doing these things you are writing a recipe for disaster. So let’s look at these things one by one.

What needs to be done?

To answer the question you need to ask yourself, “if I get nothing else done what will make me successful?” These things are called requirements and every project has a different focus, so the requirements will not be the same. The next thing you have to ask yourself, “what is standing in between me and something I am proud to show off?” These things are called issues and they can be your own doing, or they can be external forces. Now, you can lump everything together, because you have to address both your requirements and your issues in any given release. A release is a version of what you are doing that addresses a list of things—whether they are requirements or issues. It’s kind of a package deal.

I’ve managed a few projects where we put everything in an issues list, and assigned version numbers to those things. The issues could be further classified into requirements, enhancements (aka new requirements), bugs (mistakes we made), configuration problems (environment problems), and risks (something that may or may not happen that we need to be aware of). The important thing is that you assign these things to a version so that you can have an idea of what the package is. The complete list of things to be done is your release, and as you knock them off, you can see how close you are to making a release. The list can grow as your are working and new bugs come up, or the people you are writing the application for come up with something new.

What was done?

To track what was done, you not only need to track how you are doing in your issues list, but you also need to make sure that your workspace (source code, etc.) matches what you said is done. You need to make sure that when you have a number of people working together on the same project, that no one accidentally deletes someone else’s work or added something extra. For software projects, the chief tool to keep track of what was in older releases and merging multiple people’s work together is called a version control system. Some common free version control systems include CVS and Subversion. For software projects, there really is no excuse for not using a tool for version control. The two listed are free open source projects, and the second one is a little better to manage. There are commercial software projects, but the important thing is that you are using something other than people to manage the process. Your version control software has to support the following functions:

  • Tagging (marking a set of code at a version in history so you can say what was done for a previous release)
  • Merging (when more than one person is working, their work should be merged into the controlled version and not just replacing the whole file)
  • Logging (every commit should have a message summarizing what was included in the change)

Document management systems usually don’t support tagging and merging. They just keep a history of versions of a document. They also usually require you lock a file so that no one else can touch it while the changes are happening. While that may be necessary when you are working with word processor documents, with source code it is an unnecessary restriction that gets in the way of doing work.

So how do we make sure we did what we said we did? You test. Testing is essentially checking the application is doing what we expect it to do. You can do testing manually, automatically, or some combination of the two. The best option is to combine automated testing with manual testing. The automated testing will always check the “blessed path” or expected way to use an application. That way you make sure you don’t accidentally break something with new changes. The manual testing (people actually using the application) will have people trying to break it or abuse the system. If they succeed in breaking it in an unexpected way you have a new bug and a new automated test to deal with.

How close are we?

Hopefully, the list of open issues is getting smaller and the number of passing tests is increasing. Eventually, the number of open issues for a release will reach zero, and all the new stuff will be fully tested and given the green light. That’s when you know you are ready to cut a new release. It helps to chunk up the work so that you can get a feel for how close we are getting a little at a time. Let’s say you have a long list of things for a release, and you think it’s going to take about six months to get it all done. If you wait until the end of those six months to begin checking your work, you are probably going to get overwhelmed with the list of things that went wrong. It’s better to break up the work into smaller chunks.

I find that one or two week cycles give a good balance between getting the work done and testing early. If you do too much work before testing, it will take a lot of work to fix the problems. If you do too little work before testing the work becomes tedious and unpleasant. It’s best to have a good battle marching speed to do little mini releases so that people testing will be able to catch and report problems before they get too big.

For any project where money is involved (i.e. just about anything other than an open source project) it’s important to see how close you are to your estimate. Unexpected problems can really affect when you get the work done. Let’s say you are marching along really well and it looks like you might even beat your 6 month deadline, but suddenly the database is imploding under the work load. Now you have to find out why it’s misbehaving and fix it. The problem might be a configuration problem, or it might be that you have too little database for your application, or it might be that you are doing too much with the application. Finding the problem and fixing might set you back a month, so meeting that six month deadline is starting to look like a pipe dream. Now you have to figure out how you are going to handle the schedule problem. You can move the schedule, change what’s required for the release, change the way you are doing things to speed it up, or make people work longer. There’s a physical limit to how long people can work, and the longer the work the exponentially more inefficient they become. If you can work smarter instead of harder, go for it. It will help you with the next release.

With open source projects, the schedule is less of an issue, and things are released when they are ready. However, if someone wants to help out and start helping, it’s always best to give a clear answer of what you are trying to do. If they can see the list of issues and help get it done quicker, more power too them.

Test Driven Development 101

Posted by Berin Loritsch Thu, 03 Jul 2008 11:47:00 GMT

I’m back after a long hiatus, and I’m probably talking to the air right now, but that’s OK. For the two or three of you actually listening to me, listen on. We have a number of projects of different sorts at the company I work for, and you’d be surprised at how few do test driven development—and how few even write unit tests. In this day and age, not writing tests that can be automated at all is inexcusable. At the very least, the tricky stuff should be thoroughly tested. This article is for anyone who is either skeptical about or interested in Test Driven Development. That includes managers as well as developers.

What’s the Problem, Man?

We all ( at least should ) want to write quality software, and we will take this as the “given” in geometry. Since we want to write quality software, how do we do it? In the early cowboy days of software engineering, it was painful to write code. First you did all your planning, wrote what you hoped would work, printed the stacks of punch cards, and then loaded it on the main frame. Which explains all the texts on the huge design up front methodology. However, that was before my time. When I first started, we wrote a little, ran the software, and debugged. Which explains why debuggers were so important at that time. However, times have changed. We now have a plethora of unit testing frameworks and maintainable build scripts to incorporate the tests into the build process.

As soon as we ran into some hard problems that really couldn’t be completely designed up front (like TCP/IP stacks), we developers had to figure out a way to make sure things worked properly. I mean, the spec is pretty complete, but there are a lot of details and practical limitations imposed by hardware that you won’t find in the spec. If you attempted to alter the spec to include all these corner cases, you would also lose any way of understanding how it is supposed to work. This is how the automated unit test was born. All these corner cases had to be accounted for so that future changes to the code won’t break the existing functionality. I think everyone recognizes the importance of testing. It’s just how, when, and where testing happens that people disagree on.

Another problem that is often brushed under the table is code slipping in that doesn’t need to be there. How often have you tracked down a problem only to discover code that was never supposed to exist be the culprit. You remove the offending code, and violá it works! The code could have been there from old legacy requirements that no longer apply, or it could be a developer without experience over thinking a problem. Nevertheless the code is there and it is causing problems.

If you adopt a clean as you go philosophy to developing software, you had better make sure you don’t accidentally breaking anything as you refactor your code. Even with “safe” refactoring, there is an inherent risk that you can inadvertently break something you didn’t think you would. If only there was a way to make sure all the important functionality keeps working…. Oh yeah, you do have a full test suite don’t you? Oh, it didn’t get written because you were under the gun and you were on a roll? Too bad.

Test Driven Development (TDD) was designed to not only address these issues, but to instill a discipline so that your unit tests would get written. Let’s face it, all people are lazy. It’s in our nature to not do anything we think is a waste of time. Sure we’ll do a little work now to avoid a bunch of work later, but we need to know it really is going to avoid a bunch of work later. What some people fail to realize about TDD is that the time will be spent somewhere. Either you write your tests up front or you spend time in a debugger later. Either you prove that your approach will work now, or you do it later. Either you break up your work into testable chunks now, or you attempt to do it later and fail at it.

OK, So How Does It Work?

At its heart, TDD is pretty simple—you simply perform a prove, fix, prove cycle. First you prove it doesn’t work . Then you fix the problem. Finally, you prove your fix worked . If you are lucky, your first “prove” step will prove the code already handles what you were thinking about. Write the test anyway, because you need to make sure that any changes will still support that test case. It’s fairly easy to see how to perform the mechanics of TDD, but less about it’s design implications without a little more explanation.

One of the side-effects of writing your tests before you write code is that it makes your code easier to test. Remember, people are lazy? Since a proper unit test sets up the environment and checks the effect of the call after, we make it easy to set up the environment. In fact, the less we rely on network connectivity or environment variables, the better. If a bit of code can be tested just by passing objects into it and examining the return value, then you aren’t going to be overly clever in your implementation. Easily testable code, also happens to be more modular. If you can pass in a mock object for something that would normally talk to the network so that you can have the mock imitate the situations you would encounter there, you can more easily test just the small unit you are working on.

Unit Tests vs. Integration Tests

In the best of all worlds, a project would have both. Unit tests make sure that the smallest unit (such as a method on a class) is doing what we expect it to do. A proper unit test also tests only one aspect of that method. It’s not uncommon to have several tests for the same method to make sure that all the corner cases are taken care of. Integration tests make sure all the different units work together like we thought they would. A unit test uses mock objects to isolate the thing we are testing from everything else. An integration test sets up the complete environment and runs the test against that environment. They are different tools for different problems. To do TDD, unit tests are required, but additional testing is a plus.

More often than not, code that is properly unit tested will work just fine. However, there are some issues that only crop up when the whole system is put together. Perhaps there is some race condition, or some complicated event loop that only appears in certain conditions. Once you track down the cause of the problem, you can write a unit test to reproduce the condition that caused the mess in the first place. With the new unit test, you fix the code, and everything should work in integration again.

Many times, the integration tests are all done manually. The challenge is that it is hard to set up a complete environment, test the user interface or system messaging, and evaluate the results automatically. The problem is that over time the number of tests that people have to do for a release becomes daunting. People are less picky than machines. If there is a tool to support integration testing like Selenium or some other testing framework, use it to at least catch regression issues. You know those issues where you accidentally break something that used to work when you add a new feature? Anything that was working and gets broken needs to be tested every time. Machines are good at doing rote repetition like that.

Battle Rhythm

When you sit down and start doing TDD, you’ll develop a battle rhythm. Most IDEs these days have support for running your unit tests without going through the whole build process. It’s pretty convenient, and it makes TDD a lot easier to do. So, you’ve got a new requirement and you need to get it working. It’s good to have a general idea of where you want to go, or how it is supposed to work, but don’t be a slave to that idea. We start writing the unit test at the easiest place it is to begin—whatever that may be. I personally find it is best to start with the happy path (the path where everything works as expected). For example, look at the pseudo code below:

Test String is a URL
--------------------
1. Use the string "http://bloritsch.d-haven.net" 
2. Pass the string to the String Utility "isURL" method
3. Assert the response is "true" 

Of course, it’s pretty easy to make this one test pass. All we have to do is write the StringUtility.isURL() method to simply return true. No evaluation or anything. When we run the test, we prove that solution is good enough for now. So we need to start thinking about the next test. What if the string is not a URL? So we add the next test:

Test String is NOT a URL
------------------------
1. Use the string "I'm not a URL, ignoramus!" 
2. Pass the string to the String Utility "isURL" method
3. Assert the response is "false" 

Now we’ve proven we have some work to do. So we change StringUtility to return true if the string starts with “http:”. It’s the simplest thing, right? But what if we want to include SSL encrypted URLs, or mailto URLs? So you add tests for them, and make them pass—without breaking the other tests you’ve written. All these tests are cumulative, so while they may have been extra work at the beginning, they can save your bacon later. Don’t forget about those corner cases, what if the string is null ? Etc.

By the time you are done with this method, you’ll have done the following things without even realizing it:

  • Documented what you consider a URL and what is not a URL (design documentation side-effect)
  • Proven the design works (design proof side-effect)
  • Tested the implementation (implementation proof)
  • Provided a safety net to do refactoring (supporting implementation malleability)

Sure the method is just a part of the overall design, but you’ve thought about and decided how the method is going to be used from the perspective of someone using the method. It’s a shift in thinking from being the “implementer” of the method, which usually results in code that is easier to use elsewhere. You’ve also introduced a level of trust in this method that you wouldn’t have if you just reached for that regular expression you found on the net to determine if something is a URL or not. You’ve also introduced a boundary where the implementation can be as simple or complex as you want—but code that uses the method won’t care about those details.

The battle rhythm of proving, fixing, and proving actually improves your development speed. It may not seem like it at first, but by testing all along the way we’ve minimized the time we will have to spend in a debugger. The ramp up time is a little slower, but as you get your battle rhythm going, you stay at a constant pace. Without TDD, I find myself working with bursts of productivity interrupted by long periods of finding out exactly where I went wrong in a debugger. With TDD, I find myself working at a steady pace, and those occasions where I missed something I spend much less time in the debugger. Once I’ve discovered the culprit, I add the test case that reproduces the error condition and then make it work. Now, when I refactor code I can make sure I don’t reintroduce the problem accidentally.

Silver Bullet?

There is no silver bullet, and no golden hammer that will make things work perfectly the first time. There are only tools that help you get closer to the ideal. TDD is a tool that helps improve quality from the start. Most detractors of TDD look at the claims of documenting your design as being false—or at least unreadable to normal people. I may give them that argument, but TDD isn’t about documenting design, it’s about building a better quality product with the minimal amount of investment. It’s about improving your productivity over the course of a software project. It’s about reducing the number of “doh!” bugs to virtually none saving your brain cells for the more complex problems. Finally, it’s about minimizing the risk involved in refactoring or even rewriting your software.

All of these benefits are things that the text books say are a good thing. It’s also done in a way that is less painful to developers. Writing documentation is a pain in the butt, however, writing test cases is something that directly benefits the developer. It benefits the project over it’s period of performance. Bottom line? More bang for the buck.

RAD to a Point

Posted by Berin Loritsch Tue, 16 Oct 2007 13:30:00 GMT

Software engineering is a lot like a marathon. If you all out sprint at the beginning you’ll ruin your chances of finishing first, if at all. If you simply walk along the route sniffing daisies and waving at the people on the side of the road, you also won’t have any hope of finishing first—although you stand a good chance of finishing the race. The key to a successful project is maintaining a realistic, yet brisk pace.

The Rapid Application Development (RAD) tools of the past were more like sprinting toward the goal. It might work if you are running 100 yards, or maybe even 400 yards. If you are running a 10k there is no reason to start out at a sprint. In fact you’ll find that the limitations of the RAD tools will get in your way and keep you from finishing the 10k.

The point of doing at least a few projects using test first practices is to develop some conditioning so that you can bring your pace up. With a comprehensive test suite you tend to do things so that as few red flags appear as possible. That means that your software is more truly modular. It helps develop good practices and style, which all contribute to the overall goal of increasing your normal pace.

What’s important is that you choose tools that compliment what you are doing. A good pair of running shoes will do you better than some basketball high tops. Same goes for the frameworks you use. Sometimes the framework can get in your way and hold you back. Sometimes it helps keep you on point and even helps push you forward. One isn’t necessarily better than the other in every situation.

Now, when you race cars there is a counter-intuitive concept where good braking will actually help you go faster. As you round the corners, you brake going into the turn so that you can accelerate coming out of the turn. It’s the same concept when you manage your software team. By adding review periods so that you can take stock of where you are and where you are headed, you can refocus the team so that they can perform quicker. The trick is to provide just enough of a slowdown to keep the team under control while not grinding them to a halt.

A well tuned team of developers can get the job done quicker, more consistently, and using less cost. The trick is careful tuning and tweaking of the team and how you do work to make sure that everything just works. To truly do rapid application development, you don’t need a tool to do everything for you. You need a team of professionals who know how to get the most out of the project.

Why I Hate Writing Microsoft Programs

Posted by bloritsch Wed, 21 Feb 2007 18:11:00 GMT

First off, I don‘t dislike Microsoft for religious reasons or claim that the only true software is open source. I dislike Microsoft because they have made my life difficult on many occasions when it could have been easy. The one area where Microsoft is the most painful to use is when you write software. The first problem is the horrendous API documentation and how to articles. Before I provide anechdotal evidence, let me provide something to contrast against.

As a Java developer, there are a couple things I have come to appreciate about the Java‘s contributions to the world. The first is the first rate JavaDoc tool. If Sun did nothing else right, they spent the right amount of time to flesh out the JavaDocs for the core API, and even provided links to tutorials to help you understand how things are supposed to work together. Second, the Java community as a whole has contributed excellent articles on how to solve different problems with Java. I‘d love to see another community surrounding a language match the level of quality of the articles. As a result of all these rich resources, developing Java programs is far less of a pain than any language supported by Microsoft.

As a Visual C++ developer, there are a couple of things that I have come to loath about Microsoft‘s contributions to the world. The first is the abysmal API documentation, which has gotten worse with the advent of the .NET framework. The descriptions are barely useable and there is no clear idea of how to use some of the parts of the API. It might be forgivable if the remainder of the Microsoft community (including magazines and such) picked up the slack, but rarely can you find what you need and everyone wants money. You can‘t even evaluate if the magazine will be good for you without spending money on it. The second is the lagging IDE technology. Things that I take for granted in any Java IDE like refactoring tools are not to be found in Visual Studio (unless you pay for a third party plugin or a new version finally includes limited support for it). You can‘t really build anything without the IDE, but it is a pain when you do something as simple as rename a method to clean up the class signature.

One of the first events that really helped me to dislike programming Microsoft programs was when I had an assignment to add features to the print dialog. I searched the IDE help for how to do this, and all I could find was that it was “possible” but that was it. I had a coworker look through his several hundred dollar thick reference guides and they gave the same answer as the online help. I searched the internet and came up with the same answer. Eventually, I ended up just creating a new dialog box with just the controlls I needed and secretly controlled the printer settings behind the scenes. The printer settings are set by the dialog box which is required by Microsoft to exist — but you can instantiate it without making it visible. After I created the custom print dialog with just the controls we needed I finally found the answer I originally needed, sort of. It turns out you need to copy the Print Dialog resource descriptor from the API source code included with Visual Studio into your project and manually edit it with a text editor to add the controls you need. There were no instructions on how to edit the resource file, but at least there was some description of the process. Events like this repeated themselves ad nauseum .

Another event that solidified the fact that I didn‘t want to do .NET programming and spend time learning C# had to do with a copy of Visual Studio 2003 given to me by my company. Despite the fact that this IDE is supposed to be Microsoft‘s cutting edge offering, they didn‘t learn a thing from the success of Java. Java‘s success has nothing to do with the language! It has everything to do with the community surrounding it, and the attention vendors pay to making our lives easier. Visual Studio 2003 had zero refactoring tools. None, zilch, nada. The open source community did help in some respects by providing NANT and NUnit, but no refactoring plugins. Eventually JetBrains created one, and perhaps Microsoft has licensed it for the next version of Visual Studio. I asked the Microsoft rep about it the response was, “No, it doesn‘t have anything like that… but don‘t you think that being able to display a screen on the desktop or on the web with the same code is a useful thing?” I couldn‘t believe it. How many people do that? You either develop for the web or you develop for the desktop. Worse, despite the cool factor that might have, it doesn‘t make my life easier for what I do each and every day. Refactoring tools are useful even when there is no UI and you are writing a library.

There are some very clever things that Microsoft has done, but they are lost in a sea of irrelevant features and horrendous documentation. The documentation problem is made worse by the community of money grubbing publications filled with articles that are no better than what Microsoft provides. Will things get better? Not if they continue to stick their heads in the sand when truly useful innovations come around. Is Java the bees knees? No, but it is a useful tool if only for the fact that you can find a real solution somewhere on the web without having to shell out money just to look at it. For the record, I‘d really like someone to implement something as elegant as Microsoft‘s web service solution in Java. Of course, you lose that very elegance if you don‘t use the overpriced and underpowerd Visual Studio….