You're viewing all posts tagged with software engineering

Practicing Prime Factors

Uncle Bob’s tweet from a few weeks opened my eyes to the value of practice in honing my development skills, and led me to read a lot more about practicing and code katas. One of the things I love about having a kata, like the prime factors kata, is that you can focus on different skills and techniques each time through. You can do it in different languages, with different testing frameworks, using different text editors or build systems, on different operating systems, etc. I’ve done it a few times in the last few weeks, mostly in C++ (my current language for work projects). But I’ve used Visual Studio, vim, my work’s build environment, and other variations. I still haven’t used a decent testing framework, but that’s one I want to add.

Having gone through the prime factors kata a few times now, there are some changes that I feel should really be made near the end. Ok, the rest of this post isn’t going to make much sense until you at least read through the prime factors kata. Go do it now.

Commenters rightly pointed out that having the additional

if (n > 1)

clause added in test 4 isn’t intuitive and violates the TDD principle of just adding enough code to make the tests pass. But they then added the caveat that without it the step to loops and the candidate variable in test 7 is pretty big for the refactoring step, mostly in terms of the mental leap. I agree.

But if you really do the easiest thing you can as you add tests, you don’t run into that problem at all. The key is when you get to the test for finding the prime factors of 9 (i.e. the seventh test in the slides). The slides have you first creating a variable, candidate, to represent the number 2. This may makes sense as a refactoring step done before writing this test, but lets ignore that for now, and assume you haven’t done it. Then the easiest way to get the seventh test passing is just to copy the entire while loop below itself and change the 2’s to 3’s. Not only is that dead simple (Ctrl-C, Ctrl-V), it also makes the following steps much more obvious. At this point it’s easy to see how important the candidate variable is, so you create it, and increment it between the two while loops. Then the two while loops are identical so you just nest them inside another while loop. The remaining refactoring work to for loops is the same as the slides.

Platforms vs. Solutions

From http://www.flickr.com/photos/tripu/Jeff Atwood recently discussed The Dark Side of Extensions, and advocated taking the five most useful popular extensions to Firefox and integrating them into the product as features. Before the Firefox team decides to go do that, it might be useful for them to step back and consider what they want Firefox to be: a solution or a platform.

The platform vs solution question eventually rises in any successful software project. The reason is that successful projects are used and usually by a lot of people. The more people use your software, the more people want to change it to fit their particular needs, address weak areas in the product, or adapt it for new uses. Once this pressure starts being applied, from your customers, you have a choice. You can hire extra people, work extra hard and try to take care of the most frequently requested changes as quickly as possible. Or you can do the work to make your software “extensible”, essentially to turn it into a platform. Of course, you can also do something in between.

The most obvious example of a platform in software is the operating system for a personal computer. Windows, Linux, and Mac OS X, are all platforms for personal computing. Of the three Linux is probably the most pure as a platform. Mac OS X is the least pure, because Apple sees it more as one part of a larger solution that they offer to users, and so don’t give their developers the same level of support that either Microsoft or the open source community does.

Those who advocate a pure platform approach like Linux, because you can include or exclude “bloat” (i.e. features) that you never use. This was very apparent in the comments to Jeff’s post, where tons of people said, in effect, “I don’t want a bloated browser like IE or Opera - I just want it to include the things I use.” Mozilla is definitely going down the platform route with Firefox, and the users who therefore choose Firefox reflect that.

Microsoft tends to take a middle of the road approach with its software. Windows and IE both attempt to be solutions and platforms at the same time. And both had great success because they got the killer “apps”: Windows had Office and IE had all the websites that only worked in IE for years. Heck, even now my wife won’t switch to Firefox because some pages “don’t look good”, despite Firefox’s apparently better support for standards.

Apple obviously approaches the problem by trying to produce a solution, and that has been extremely successful for them in certain markets (can anyone say iPod? iPhone?). In the past they haven’t had as much success as a platform, so it will be interesting to see how the new iPhone SDK works out for both Apple and it’s developers.

Ultimately, I’m not sure that creating a platform for end-user extensions is the right way to go in the browser market. Platforms are market makers. Windows created a huge market for all types of software by creating a successful platform for personal computers. The real market that browsers enable is the market for cool web sites and services, not the market for browser extensions (how many browser extensions are profitable products?). A browser needs to focus on becoming a platform for web sites and services, and becoming an easy way to get to those for end users. Browsers are not the only user interface to the internet, but they are the primary one and can remain so only if they provide an excellent platform for creating web sites. Of course, there are huge problems in trying to do that. But if a browser can do that, in a way that gives it a competitive advantage, it will become the platform for the web and be more successful, and probably more profitable, than Windows.

Visual Studio keyboard bindings and text editor requests

I recently started experimenting with emacs on the many suggestions of Steve Yegge. Unfortunately, just getting up to speed in emacs (i.e. working as quickly as I do in Visual Studio or even vim) was taking a long time and when time is short, as it is during milestone development here at Microsoft, that’s not the best way to spend it. So I’m putting off that experiment until work slows down, or until my parental leave in January. In making the decision to stick with VS for the next few months of coding, though, I definitely want to improve how I use the text editor and as I began working to do that I ran across this post by Noah Coad. So I thought I’d respond to Noah and share some of the things I’m doing to improve my keyboard use (and therefore, speed) during development.

My familiarity with the vim and emacs commands has led me to seek a keyboarding compromise. I love vim’s ability to navigate very easily on the home row keys. But I like the fact that emacs doesn’t have two separate “modes”, command and insertion. I always get thrown off in vi because of that. So my navigation keys are basically the vi navigation keys while holding down the Ctrl key. I’ve also adopted a hybrid of the emacs convention of using Alt to switch from characters to words, lines to sentences etc. So Ctrl-Alt-J moves my down a page, etc. And then, I like the windows convention of holding down Shift while nagivating to select text, so I’ve added mappings to do that, e.g. Ctrl-Shift-K to select up a line. I’ve also added some other navigation keys that follow this same pattern, such as Ctrl-0 to go home. Of course, I’ve also remapped the commands that are at those bindings by default to other bindings depending on how often I use them.

So far this seems to be going well. I’m still learning these keyboard shortcuts and that’s taking a little bit of re-education, but I feel faster than when I use the mouse, which is a good baseline to start from.

Now to respond to Noah. I am now trying to use visual studio for more of my text editing needs, and customizing it more so that I can. As I’ve started doing this I’ve been impressed at how much power is there that I didn’t know about, but also frustrated by the little things that are possible but not easy to do. Searching, replacing, and regular expressions is an area that could definitely stand to be improved. Opening files from the explorer or from visual studio into new instances easily would be nice (an SDI type mode). One thing that would be useful is to have keyboard mappings that can be applied and then removed easily. This would be similar to modes in other text editors, but allow for some specific behaviors in certain cases.

Old Construction Prerequisites

Code CompleteSo it’s now been two weeks since my last post committing to a daily post about what I’ve been reading. I went on vacation to Bear Lake for a family reunion that was great fun except for the bad sunburn I got. I’ll have to tell you sometime about my bad burn from ‘91 (Florida).

Anyway, I did manage to get chapter 3 of Code Complete read at some point during the vacation. I was half-surprised by how much of McConnell’s advice parallels the agile principles that are so popular currently, given that it was written about 15 years ago. While the focus is still on waterfall development the value of iterations is recognized and the need for using development practices that “accomodate changes.” I did get a kick out of how many of the questions in the checklists are things that might not even be considered up front by many agilists nowadays. The most dated section was the section on programming languages, with no real discussion of modern programming languages like Java, C#, or any functional languages. I’ll have to go get me a copy of the second edition.

Becoming

Taken by http://www.flickr.com/photos/simonov/Because I’ve been working to develop myself as a professional software engineer, I recently read Designing Learning by by Andy Hunt and Dave Thomas, the authors of The Pragmatic Programmer. It inspired me to go a little further and write down a specific plan for the things I want to learn and how I’ll learn them. Then I noticed that the meme has been making its way around the blogosphere. Ok, now I feel like a copycat. That’s out of the loop. Ah well. I’ll be documenting my plan and how well I follow it here on this blog. I’ll also be using the blog to record things I learn and my thoughts about them.

Just for background, here’s a bit about my current state as a developer. While, I don’t consider myself a great developer, I do want to improve more than most of those I know. At work I code in C++ and do some powershell scripting.

After brainstorming, I’ve decided that the areas where I would like to focus my learning are:


  • .NET framework and C#

  • TDD and unit testing

  • Software classics - CS theory and software engineering especially

  • MicroISV and software entrepreneurship

  • Automating everything

  • Other programming languages, such as Erlang

  • Regular reading of journals and magazines, such as Dr. Dobbs


I will be using the following proven techniques to learn:

  • Reading (includes watching presentations)

  • Writing

  • Projects

  • Experiments

  • Discussion with others


So I’ve chosen 5 specific things to do that include a mix of the things I want to learn and the techniques for learning.

  1. Project: Write unit tests for EGX, begin TDD, avoid Mocks

  2. Read one article, book chapter, etc per day (at work or on commute)

  3. Write about what I’ve read about on blog, even if it’s just a link and a note

  4. Experiment: Spend a day thinking about every activity I do, and how it could be automated

  5. Discussion: After reading an article or two, talk to David about starting a MicroISV


And to make sure it happens, I’ll do the following in the next few weeks:

  1. Do an hour of EGX development on each driving day of my vacation

  2. List chapter, article, etc. to read on Joe’s Goals each morning

  3. Joe’s Goals checkbox for writing about reading

  4. Spend tomorrow considering how I could automate everything I do

  5. Find some MicroISV articles to read on vacation, forward to David


The Quality Feature

Waterfall photo taken by http://www.flickr.com/photos/dirgon/In typical waterfall software engineering, the development team goes away for a while and writes a bunch of code. They usually do this after a bunch of planning to estimate how long it will take to write the features they’ve decided to work on. Usually, near the end of their coding time they start cutting corners to get things to work, and if they’re lucky the software will compile and seem to work reasonably well.

At this point it is time to write the code for the Quality feature. This feature presents some challenging when you approach it:


  1. The Quality feature affects all code in the product. It may require work in every class, every file, at every level of depth, and written by every developer on the project.

  2. The Quality feature has a huge test matrix. Basically, think of the cross product of all the test matrices of all the features ever added to the project.

  3. The Quality feature has an unknown scope. Scope is defined only as quality assurance finds bugs and the severity, reproducability, and frequency of those bugs is determined.

  4. The Quality feature determines the success of the product. No other feature, no matter how cool and amazing, can make a product successful long-term if that feature does not have quality.


In the latest IEEE Software, Ron Jeffries and Grigori Melnik present some of the research done on the effects of practicing TDD on both code quality and effort expended. With only a couple of exceptions, the studies summarized show that practicing TDD increases the effort (i.e. time) required to do the work of writing code, and also increases the quality. Although the numbers vary from project to project, the implication is that you spend some more time to get higher quality code. In essence, you have one more feature that you’re developing, the feature called Quality. That feature takes some amount of time to write, and it is roughly equivalent to the increased effort required when practicing TDD. Measuring the difference in time or effort expended can provide some idea of the cost of Quality as a feature.

Quality is a feature, like performance, that can sometimes be very hard to tack on after the fact. Implementing the Quality feature while writing code through TDD and other good practices can increases our understanding of how much effort it requires. Because of the unique challenges around the Quality feature, leaving it to be done later not only decreases that understanding, but it also increases the effort required significantly.

TDD and Negative Splits

Taken by http://www.flickr.com/photos/matt_alt/As a sometimes cross country and track runner, I was taught and quickly learned that the best way to run any longer distance is to do the second half faster than the first half. In distance running, we call this negative splits. Those of you who have run negative splits know how different it feels from “positive splits” where you run the second half slower. When you finish a race in which you run negative splits you generally finish more quickly, feel much better, and are excited about running the next race. When you finish a race with positive splits you feel wiped out, beaten down (by everyone who managed to do negative splits), and uninspired. Although there may be and probably are physical reasons for these results, much of it is definitely psychological. If you spend the first half of the race conserving energy and then the second half passing people, it doesn’t matter that much that you spent the first half further back than many others. As you begin passing people the psychology of lots of small wins kicks in and really makes you feel good.

But negative splits are hard to do. When the gun goes off your adrenaline is already racing and you want to win. You don’t want people to pass you left and right while you run conservatively. Even when you know through experience the benefits of negative splits, it’s easy to get caught up in the moment and start too quickly. Or it may just be that the race leaders seem so far ahead of you that you have to go faster or lose all hope of remaining competitive. Pride may drive you to run faster earlier. Or it might be a justified fear that by trying to run negative splits you actually start off too slowly and never make up the time.

In the past couple months as I’ve started practicing test driven development of a new feature that I own, I’ve felt very much like I’m in the first half of a distance race. I know that I’m progressing slower than I have in the past, if you simply measure my progress in writing code that will ship. I can feel that I’m taking longer to do what I’m doing. However, I also feel like I’ll be able to complete the feature faster, which mostly means that the bug tail will be much shorter. Looking at the code I’ve written vs what I’ve done in the past I know there is an improvement. The daily process of writing tests, and then writing code that makes them succeed is as addictive as passing people in a race and keeps me excited about the end result, even if I’m moving slower by some measures. At the same time, I feel like a better developer, I’m able to make changes more quickly, and I’m more excited about my work.

Unit Tests and Asserts in Legacy Code

Because I’m currently learning about unit testing, TDD, and other agile practices, I admit that I recently considered the possibility of doing some greenfield development. While a part of me would love the experience, the challenge of using these techniques in legacy code to improve the product is very motivating to me. Along those lines, I recently followed an interesting debate about the relationship between unit tests and asserts in production code on an internal discussion list. Microsoft is big enough that you hear from people using all types of technologies and at lots of different stages in the transition to unit testing and TDD.


Assertions


Years ago, the state of the art practices for verifying code included a liberal dose of assertions scattered through the code that would run in a debug build. Generally, an assertion would notify the user and abort the program. When an assertion failed, you could debug to find out what was wrong. Generally the assertions are closer to the problem in the code than any other bad behavior you might see if the assertion hadn’t fired. Often assertions were used to verify contracts along an interface. Unfortunately, in some cases assertions were used in place of proper error handling. At other times assertions were used to verify global state assumptions. Assertions generally lead to two sets of binaries for your program - an official set and a “debug” set that developers and testers can use to find bugs.


Good Guy, Bad Guy


Assertions can often be categorized as either error handling or as there-is-no-way-this-could-happen. Assertions that are used for handling errors are bad. Instead errors should be handled either via a proper return value, or via exceptions. I won’t get into the debate here about which should be used. Assertions that fall into the there-is-no-way-this-could-happen category are probably better handled via code correctness tools that analyze code paths to verify certain properties of the code, though there is probably little harm in using these assertions.


Often assertions were used to do in-place unit testing. External unit testing is a natural next step from such assertions. Before discussing unit tests, however, it’s helpful to note that these assertions were a good thing. They tested the code every time it was run (at least in the debug version) and was often easy to understand with the surrounding code as context.


Unit Tests

Unit tests are a different way of validating code correctness from within the code. Instead of creating two sets of binaries, you essentially create two sets of source code: one that is the program, and one that verifies the program (Note: two sets of binaries may still be created). Unit tests, if written well, can verify the code in ways that are not easy to do with asserts alone. Unit tests are run regularly while writing code, while asserts fire only if a given code path is exercised in some way. Unit tests can specifically test code paths that might not be exercised normally. Unit tests can be written with the code to shape the code itself, if you are doing TDD.


Unit tests can do a lot more … Oh wait, like I said, I’m still new to all this agile stuff. So it was interesting to listen to the agile believers argue against any assertions while others made the case that assertions were useful in some situations. I must admit that the idea of having no assertions at all was shocking at first. But it immediately became appealing as an ideal once I understood how clean code without assertions might be. However, in a large legacy code base with lots of asserts you’re not going to escape assertions easily. And honestly, after some thought and experience, I believe that the two methods are complementary.


Asserts in existing legacy code are an excellent starting point when trying to get the code under test. Using the techniques in Working Effectively With Legacy Code, write tests for your code that exercise it well. Initially, if you’ve got a good set of asserts, positive unit tests can be written that don’t even verify much, as long as your framework interprets asserts as unit test failures. Of course you’ll want to write tests that cover more than just that, but it is a start and will provide some level of safety when refactoring. If your unit test does not interpret asserts as unit test failures you can use a seam (link seam, preprocesser seam) to redefine assert in the binary you compile for unit testing.


Do unit tests make assertions redundant?

In writing new code within the legacy code base I definitely find myself using less asserts because I am writing unit tests as I go. However, certain things are much easier to test with a well placed assert in the code, rather than the unit test. In talking with a colleague the other day I was reminded that making code unit-testable can make it more complex than it has to be. Because of this, I believe that assertions in code can have the beneficial effect of simplifying the code you write by making it somewhat easier to write unit tests around it. This can lead to better factored code that doesn’t make it so obvious that it’s been engineered in order to accomodate unit testing.


Resources


_ASSERT macro , assert , Java’s guide to Programming With Assertions


Unit Testing , Unit Testing Legacy Code