Showing posts with label Software Philosophy. Show all posts
Showing posts with label Software Philosophy. Show all posts

Monday, December 8, 2008

3-Stage Software Testing

One of the 7 Habits of Highly Effective Software Developers relates to Unit Testing. Software Unit Testing is one of the most important but least popular and poorly employed aspects of software development. Often we developers put too much onus on QA (Quality Assurance) teams to test software for bugs. Unfortunately QA's role is only to test things that are not possible for every developer to test e.g. making sure the software runs on all versions of all Browsers or OSs.

The 7 Habits talks of considering unit tests after completion of every quarter of your task and having a test completed by the end of the task. When you write a test, how should it be written? Should it be a full fledged test that can be run and verified with automated testing frameworks or should it be informal tests? While the answer may vary with the size of development groups and the complexity of applications, I like to think of 3 stages in writing unit tests - stages which progressively lead to complete tests.

Stage 1: Crash Test - The first stage is to write a test that can simply verify that things don't crash i.e. no unexpected exceptions are thrown and no abnormal conditions occur. Here's a simple test that invokes a service on another server that returns a value object. Successful running of this test merely ensures that nothing is fundamentally broken.


@Test
public void testReport() throws M13Exception
{
Report report = (Report)svs.getReport("foo");
}


Stage 2: Eyeball Test - The next stage is to examine the results of invoking the functionality being tested. In the example test below, the contents of the Report object returned from the server is printed to a log output. What this test lets you do is to "eyeball" the results printed to get some sense of whether the functionality is working right or not. This stage is an extension to the Crash Test stage.


@Test
public void testReport() throws M13Exception
{
Report report = (Report)svs.getReport("foo");
logger.info("Report: " + report.toXML());
}


Stage 3: Automated Result-Comparison Test - This is the final stage in writing a test and extends the Eyeball Test stage. In this stage, you write code that compares the results of invoking the functionality being tested with pre-defined, expected results. In the example below, the XML representation of the Report object got from the server is compared against a XML data file; the test passes if the XML matches the XML in the data file and fails otherwise. Once this stage is done, the test is complete.


@Test
public void testReport() throws M13Exception
{
Report report = (Report)svs.getReport("foo");
String fooXML = report.toXML();
logger.info("Report: " + fooXML);
// read from data file to compare results with
String reportXML = FileUtils.readFileToString(file, "UTF-8");
assertTrue(fooXML.equals(reportXML));

}


One of the common mistakes people make is to attempt to get to the third stage directly right from the beginning. While this may work for some simpler tasks, for more complex tasks and software, this results in constantly having to change the data set being compared against since it is likely that the definition of the data is changing continuously until the later stages of development.

So the best practice may be to ensure you have Crash tests and Eyeball tests in place after each task (making sure you at least think of testing at each quarter of the task). If the data sets are stable enough, you can also write Automated Result-Comparison tests at this stage. If not, come back to this stage later in the development cycle.

Note that for all stages you employ a formal testing framework such as JUnit. So you start right from scratch with a formalized test that can be run in an automated fashion even if you are only at the Crash Test Stage. This way you are simply expanding on and extending the tests at each stage.

Happy testing! ... and give those QA guys a break!

Sunday, February 17, 2008

7 Habits of Highly Effective Software Developers

1. Design before Coding - We developers often tend to get straight to code spending little time on design. While too much formal and complete design is useless in building software systems, some basic design before coding is extremely helpful. Design does not imply writing a text document with pictures or UML models. Design can be skeletal code that is the basis for the full implementation. I try to first build data structures that hold system state and service interface contracts (not implementations) for the entire application as part of my design exercise. I then try to think through the application flow for various usage scenarios. Good design typically pays off in the end in terms of good quality code developed in a shorter period of time.
2. Re-factor continuously - This seems contradictory to the first habit of designing before coding. After all, if the design is good why should there be a need for re-factoring? The answer is - it is impossible to design software systems accurately and fully at the very beginning. Designs tend to evolve as the system is being built. Often times requirements keep changing. No one can get it right the first time unless you are building a very simple, trivial piece of software. Re-visiting previously written code and continuously re-factoring it is one of the keys to building good software.
3. Unit Test every quarter - No that's not company's fiscal or calendar quarter. It is the 1/4 milestone of your task completion. Break up your task into four, and after each quarter, think of how you can unit test the code you have written. Note that I'm not suggesting you should write a unit test, rather you should have a good mental idea of how one would go about effectively testing what's built so far. If what you have written so far is not unit-testable, re-factor the code to facilitate unit testing before moving on to implementing the next quarter of your task. At the end of a complete task, make sure a formal unit test gets written.
4. Write squeaky-clean code - All brilliant developers that I've seen write very clean code. Clean code means paying attention to removing dead code, avoiding compiler warnings, indentation, spacing, naming, and consistency in code style. This is more than just cosmetics - if your code is not clean, it is unreadable, unmaintainable, less likely to be reused and more likely to get re-written by someone else.
5. Comment code - The only documentation we developers will ever write is comments in the code. Keeping this up to date is of critical importance for the long life of the code. Any code where the logic may not be obvious to a new peer programmer should be documented. Documenting your code is not just for others but for yourself too - how many times have you gone back to your own code written a year ago and wondered what the heck the implementation logic was?
6. Be lazy - Yes, be lazy! Don't write your own code when there is good code already available for the same task. Don't re-invent the wheel. There are a ton of open-source utility libraries that are extensively used; make use of these wherever possible. Why waste time writing code when you can borrow someone else's code which may even be better that what you could write?
7. Get your code peer-reviewed - Peer reviews often help generate new ideas and perspectives on implementation that you may not have thought about. Set your ego aside and solicit peer reviews of your code.

Saturday, January 5, 2008

Why Software Quality Sucks

It was an important customer demo at a big industry trade show. We had worked franticly all through the previous night to get the systems setup for this demo. The demo began very well but halfway through, the program crashed. It was a disaster! Since the demo was for a large and important potential customer, there were a whole bunch of my company suits (non-techie executives) present and they were clearly upset. Apparently they had seen this all too often and couldn't understand why we developers were so incapable of producing bug-free software, and why we should be paid so much money for the junk we produce! At the hospitality suite that night, after everyone was happily buzzed and relaxed, I accosted some of these suits to try and explain to them what had happened and provide excuses for the failed demo. After the usual excuses about demo'ing with the latest unstable dev build, lack of sufficient resources to code, this other team not delivering their fixes on time, bad hardware etc. (all of which they had probably heard many times before), the conversation drifted on to a more generic discussion on software and bugs. I found myself trying to impress upon them the difficulty of building bug-free software. Now this was easy and I felt confident I could convince them because I had Bill Gates on my side. That's right, Bill Gates of Microsoft. It so happened that around the same time as this trade show, Bill Gates was demo'ing Windows 98 at COMDEX, introducing it to the whole world for the first time. And it crashed and burned in the middle of the demo! An embarrassed Gates mumbled something about needing to iron out some more bugs. I mentioned this incident to my suits. Some of them had read about it in the papers. I told them Microsoft had 2000 very bright engineers working on Windows 98 and asked rhetorically why they still couldn't guarantee bug-free code. The software we were building and demo'ing was incredibly complex large-scale, real-time, network monitoring and control systems with hardware interfaces to receive 100s of thousands of data points, and heavy-duty number crunching in mission-critical, high-availability systems. I reminded them that we had just 3 engineers working on the portion of the demo that crashed. The point was that if Microsoft, with its huge army of the world's best developers, cannot produce bug-free software, how could we, with far fewer resources to build equally complex systems, produce anything better? The suits seemed to see the point. By the end of the night I had totally convinced them that software is, by nature, very complex and it was virtually impossible to build systems of better quality than Windows. One had to just live with the fact that software systems will malfunction and crash periodically. Thanks Bill!

That was 10 years ago. Windows has come a long way since those days and we now see less of the infamous "blue screen of death". Software in general has matured more in the last decade. However, we are nowhere close to the level of maturity in commercial software where high quality is taken for granted. Any enterprise-scale software is fraught with bugs requiring continuous application of hot-fixes, patches, upgrades etc. Ask any IT professional in any company about the quality of software he supports and invariably you will hear complaints. Complaints are likely to be more vociferous when it comes to business applications software.

So why does software quality in general and business applications software in particular suck so badly? Why is it that software is not as reliable as (say) bridges? Some would argue that the software industry is still very new. After all, they argue, we've been building software for less than 50 years while we've been building bridges for 1000s of years. Others would argue that software is much more complex than building bridges or cars, and as complexity increases, so does the probability of defects. There are some who would point to lack of standardization in the way certain common software elements are built resulting in defects when trying to integrate elements into one application. Then there is the camp that feels that a significant number of software engineers lack the proper training and skills and that the software industry is probably the only industry where we don't make a distinction between technicians and engineers (think of electrician versus electrical engineer, plumber versus civil engineer etc.) - in software, anyone who works with code is a software engineer and problems arise when technicians try to do the job of engineers.

While all of the above reasons may be valid, I believe the fundamental reason for poor software quality is the way we build it. This is more true for applications software than systems software. In spite of all the advances in languages, IDEs, design patterns, open-source libraries, runtimes and standards, building large applications software remains horrendously tedious, labor intensive and error prone. I think we need a radical shift in the fundamental mechanics of writing code in a programming language. We need a new vocabulary for application developers to state their business logic rather than code it. Modeling tools have attempted to address some basic part of this but have been very code and programming oriented e.g. thinking in terms of classes in UML. Model Driven Architectures (MDA) never lived up to the hype and simply provide a language to describe the high-level problem without providing a real-life solution.

I have no idea what this new vocabulary and mechanics for building software would look like in its final form (although I have a name for it - Yeti), but it seems to me intuitively that it will have the following characteristics:
  • Rules would figure in it prominently, and application building will primarily involve describing business rules
  • Application logic would be expressed in a natural language syntax
  • Application builders would not be programming (as we know it) in languages such as Java, C++ or JavaScript
  • Applications would be able to process incomplete information employing fuzzy logic, and learning

Maybe I'll never see Yeti in my lifetime. Maybe Yeti simple cannot exist given the basic von Neumann model for computers.
Or maybe not.

Sunday, December 30, 2007

Who produces the most Applications Software?


If I asked you who produces the largest amount of business applications software, you'd probably say Oracle, SAP, Microsoft, IBM or one of many software vendors that produce and sell such software. Until recently I would have agreed with you completely. But I'm beginning to realize that the maximum amount of applications software is produced by IT groups within companies and not by large software vendors. Although companies spend millions of dollars on buying software applications to run their business, they also produce a lot of code internally for their own consumption. This could be enhancements or customizations to applications they purchased because the purchased software did not meet their business needs out-of-the-box. It could also be brand new applications they build specific to their business for which no commercial software is readily available. Most of these applications tend be simple (and interestingly short-lived) since IT groups tend to be very constrained in terms of budgets and skills to engage in full-fledged, large-scale software development. However, given the massive number of companies globally that use software for their daily operations, the total number of such applications is very large. Interestingly, it is because of this large market for simple, light-weight applications that light-weight frameworks such as Spring have gained a lot of popularity.

So who produces the most applications software? Combined IT groups of companies do.

Saturday, December 29, 2007

A Few Good Men


One of the classic problems in software development is to accurately estimate how many developers are needed to complete a task. In my 15+ years of commercial software development in small and large companies, I've yet to come across a project where the resource estimate at the beginning of the project ended up being anywhere close to being correct. Invariably the estimate is much lower than what it actually takes to complete the project. Why is this? I think it is because some of the intangible aspects of developer efficiency are overlooked in the resource estimation process.

What is the optimal size of a developer group engaged in a project with shared, common, inter-dependent tasks? I believe the optimal size is 1! One brilliant developer with all the required skills produces software in the most efficient fashion. Of course, it is going to take forever for a team of one developer to complete any reasonable size project, and obviously most teams need to have more than one developer. My thesis is that with every additional developer, efficiency of the group decreases by 5% over the previous level. So if a team of 1 developer works at 100% efficiency, a team of 2 developers works at 95% efficiency (0.95 x 100), a team of 3 developers works at 90.25% efficiency (0.95 x 95), a team of 4 developers works at 85.74% efficiency (0.95 x 90.25), and so on. This is shown graphically in Figure 1 below where the yellow line indicates the decrease in efficiency level as the number of developers increases.

Figure 1. Developer Value


Why is it that efficiency decreases as the number of developers increases? The decrease is due to many factors. The principal factor is related to source code dependency and version management. When a group of developers share a common code base, there is invariably inefficiencies related to:
  • module inter-dependency ("my change broke his code"), and
  • code sharing ("I have to rebase to his changes first before delivering my changes to the same file").
Other factors leading to lower efficiency of larger group sizes include:
  • difficulty in ensuring a common understanding of the project design goals and principles ("I thought you meant this rather than that 3 months ago"),
  • differing developer backgrounds, styles and skill levels ("this guy's code is so cryptic, I'd rather re-write it than try to figure it out"), and
  • the increasingly distributed nature of development with language, culture and time-zone related communication problems ("wish I could quickly and easily get this guy in Bangalore or Minsk to understand exactly what I mean")

As the number of developers increases, the output obviously increases. This increase is linear and is shown graphically in Figure 1 as the blue line. However, this output does not factor in the cost of inefficiencies mentioned above. The inefficiencies introduce a cost penalty that needs to be factored in to the calculation of true "developer value". This value is shown in Figure 1 as the green line. Note that the rate of increase of developer value is lower than the rate of increase of developer output as the number of developers increases. Resource estimates are typically based on the output curve, when in fact they should be based on the value curve. The difference between the output curve and the value curve is called the "estimate gap" and is indicated by red double-arrow lines between the blue (output) and green (value) curves. Missing this estimate gap is the reason for poor and lower estimation of required resources for projects.

So what does this value curve mean? Let's say you estimate that you need 5 developers to complete a task based on amount of code that needs to be written. In Figure 1, assume that the y-axis is re-scaled so that 20% output is 100% for your task - so 5 developers are estimated to be required to complete 100% of the task. From the output and value curves, you actually need 6.7 developers when you factor in the estimate gap. The table below shows the mapping between estimated number of developers based on output and required number of developers based on value. This mapping can be used to arrive at more accurate resource estimates without just going with the incorrect estimate based on output.

# Developers Estimated
(Output)
# Developers Actual
(Value)
1
1
2
2.1
3
3.3
4
5
5
6.7
6
9
7
13

An interesting aspect of the value curve is that there is an inflection point around 21 developers (indicated in Figure 1) after which the value actually starts decreasing! This means that a group of 21 or more developers working on shared, common, inter-dependent project tasks is likely to be a loss making proposition. In terms of estimated number of developers based on output corresponding to 21 developers based on value, the number turns out to be between 7 and 8. This means that if your estimate arrives at more than 7 developers based on output (which is more than 13 developers based on value), you run a huge risk of failure. In such a scenario, re-visit your project tasks and try to break it up into more independent sub-projects. If that is not possible, you are dealing with an immensely complex software system that given today's state of the art, is impossible to build effectively. Hopefully such systems are rare.

So for your next project, bridge the estimate gap to arrive at better resource estimates, and remember the magic upper limit numbers - 7 and 13.