1 2 3
Tick, tock (death of a ticket salesman)

This article originally appeared on earthli News and has been cross-posted here.


The following story tells tale of a day spent with the ongoing user-experience (UX) catastrophe that is the interface of the SBB/ZVV automated ticket machines.

While it's certainly possible that our experiences are unique and that others can easily purchase their perhaps simpler tickets, we have found that veering ever-so-slightly from the beaten path leads into some very deep and dark weeds.

Even were we to accept that the fault for the confusion engendered by the UI lay entirely with us, we can hardly be blamed for the mysterious time-delays and crashes.

In short, SBB/ZVV: give Encodo a call. We can help you fix this.

Prologue

When I renewed my monthly train ticket a month ago, the lady behind the counter told me that I could have gotten it from a machine instead.

She handed me a little pamphlet that explained how to use their ticket machines.

I gave it back. In hindsight, this may have been a bit hasty.

The setup

My monthly pass was about to expire. I didn't need to get a whole month this time, so I figured that I'd just buy a six-pack of one-day tickets, in order to save money.

How hard could that be?

Initial encounter

Instead of heading back to the human-powered ticket desk, I took the lady's advice and approached a machine.

I got to the train station about eight minutes before my train, confident that this would be more than enough time for me to exercise my not insignificant technical skills to prise forth a ticket.

On the screen, I poked a smiling SBB stock-photo customer in the eye to begin.

I chose a normal ticket. I entered my destination. Then I elected to change the starting point (I already had a ticket covering part of the trip).

Done. This was too easy.

Now to get a multi-day ticket. Multi-day, multi-ride...what's the difference?

I touch the info icon on each. A wall of text. Scanning doesn't elucidate anything.

Tick, tock.

I select multi-ride just to keep moving.1

I'm done early. All that remains is to pay for the ticket.

I slide in my card...

It's blocked. I turn it around and try again.

Blocked.

The card reader's apparently broken.

The machine is blissfully unaware of this and pops up a helpful message, asking whether it should just forget the whole thing and cancel my transaction.

I tell it not to cancel, but to go back to payment-method selection.

Going back incurs an interminable pause.

Tick, tock.

I'll pay cash!

The machine gives a maximum of twenty bucks in change. I have no twenties; only fifties.

Can't pay cash.

Wait! I have some change! What if I get a single-day ticket?

Type, type, type

That's only CHF5.80.

Scrounge, scrounge

I have CHF5.60.

Tick, tock.

Can I just tell the conductor that the machines are broken? Does that even work?2

I look over at the second ticket machine. There's a man standing in front of it, looking back and forth between two credit cards in his hands.

Tick, tock.

He seems to be having trouble deciding how he will pay for his ... CHF197.-- ticket.3

Tick, tock.

The dam has broken. He's decided.

Printing...

Tick, tock.

I jump on the machine after he's finished.

I type in the same commands as I'd typed in just a minute before on the other machine, my fingers flying over the keys, marveling at the comparative speed of the ZVV machine vs. that of the SBB one.

Wait...why can't I change the starting point of my journey like on the other machine? Where did that option go?

Start over.

Tick, tock.

Why can't I choose a multi-ticket from this machine? Where did that option go?

Fine. Get a single ticket. Anything at this point.

Type in both towns again, muscle memory helping me along.

Laaaaagggggg as I type. Why is searching a list of a few thousand items so slow?

Choose a single ticket. Wait, why is it cheaper now? What changed? I could pay for this one with my change now.4

No time to think about it.

Tick, tock.

The bell is ringing. The barrier is lowering. The train is coming.

There's the button for multi-card! It's on the final screen instead. I barely have time to register that this is a much better place for that button as I punch it.

I can feel the rumble of the approaching train in my heels.

I jam in my credit card. The reader works! Huzzah!

I type my code. Nope, the reader's still warming up. Please hold...

Tick, tock.

I type in my code. I can hear the train now. Start printing!

Tick, tock.

Oh, the machine wants me to decide whether I want a receipt before it will do anything else.

Yes, I want a receipt! Now start printing!

The printer has started!

I see the flashing light alerting me to the imminent arrival of my hard-won train ticket, still warm from the innards of the machine.

A paper drops into the slot, lying awkwardly in the tray. It looks rather large.

"One of your products could not be printed."

I reach in and pull out a forty-centimeter--long, curled monstrosity that purports to be my multi-ticket.

In my haste, I assumed that this was a valid ticket. That this hideous thing with ink staining the front of it every which way was the product that my labors had brought into the world. I assumed that the aforementioned product that could not be printed was my receipt. I thought that while I might be in trouble with bookkeeping back at the office for not having a receipt, that at least I had a ticket for the train.5

Never mind all of that. I had a multi-ticket. An overlarge and misshapen one, perhaps, but nonetheless a ticket.

Now, one final step to make it valid.

Tick, tock.

The train is gliding into the station

I hurried over and proudly punched my ticket, the ink delineating today's date mixing illegibly into the mess of ink already printed there.6 I didn't care. The ticket had printed. I had paid. And I was getting on that train legally. With a valid ticket.

Whether I could prove it to a conductor or not was another question, but my conscience was clear.

I folded my ticket three times and put it in my backpack -- because it wouldn't fit in my wallet.

Despite my misgivings, no conductor came to make me reveal my shame.

The ride home

I got to the office and somehow the SBB came up in conversation that afternoon. I remembered my ticket and hauled its weighty length out of my backpack.

My colleague laughed and expressed sincere doubt that this was a valid ticket. He thought I might slip by with it, but was almost certain that it had been annulled.

This seemed like a reasonable theory, but I was beyond caring one way or the other.

Still, when I arrived at the station three minutes early for my trip home, I spotted a ZVV machine on my platform.

Brimming with confidence, muscle memory and experience, I staged an assault, fingers flashing as I once again unerringly entered my order.

I was flying along, typing my destination, when...

"Your order encountered an error and could not be completed."

The screen froze, wiped itself clean and restored the introductory graphic. You know, the one with the people grinning their way through a day made more magical by having been able to purchase their tickets from one of these wonderful machines.

I turned and walked away.

I would take my chances with my malformed freak of a ticket, blissful in the knowledge that the SBB couldn't hurt me anymore.

I had stopped caring.

I napped on the way home. No conductor dared to disturb my peace.

The ticket purchase. Take #2.

The next morning, I was at the station early again. More level heads had convinced me to try again and a good night's rest had restored my optimism.

I went straight for the ZVV machine, avoiding the squat and grimly brooding SBB machine with its slow screen and faulty card reader.

Type, type, type.

Destination; starting location; multi-card; full days.

Wait. What? CHF70.-? For six days?

A whole month costs CHF119.-

Argghhh. Now what did I do wrong?

Back, back, back.

Tick, tock.

Choose multi-card; 1-hour.

CHF30.- For 6 trips? That's three days of commuting.

Dammit. This is a waste of money; I might as well just get a month.

Back, back, back.

Tick, tock.

One month. Personal or transferable? Personal is cheaper; personal.

Personal card number?

What? Oh, it's on my half-fare card.

Dig half-fare card out of wallet

Tick, tock.

Train's a'comin'.

Start typing my half-fare card number.

Crash. "Your order encountered an error and could not be completed."

Breathe.

Start over. The bell's ringing. The barrier is lowering. The train's a'comin'.

Just get a six-pack of 1-hour tickets. It's the cheapest option that lets me avoid having to buy a ticket again this evening. It's the least typing I can do.

Tick, tock.

Got it. Sweet Lord almighty, I think have a valid ticket.

No, really. This time I think I have a valid ticket.

STAMP

Now it's valid.

Epilogue

On my way to my destination, I did the math again and realized that I would need another ticket in three days.

A chill ran down my spine.

Time to give up. I would return to the SBB counter and just buy a monthly ticket from a human being.

At the counter, I asked for a renewal on the recently-expired monthly ticket I handed to him, but starting on the next Monday, my hand simultaneously cramping at the thought of how much touchscreen-typing that would have entailed.

In seconds, he renewed it and handed me the new ticket, chirpily telling me,

"You could have bought this ticket on the machine instead. Just three taps and you'd have been done!"

That night, as I lay in bed, I wondered whether his family missed him yet.



  1. This would turn out to be incorrect, but it would also turn out not to matter (keep reading). Multi-day is a 24-hour pass for the day on which you stamp it; multi-ride is a 1-hour pass. The former is obviously more expensive than than the latter.

  2. A colleague would later tell me that, if you call the SBB and tell them that the machine is broken, and give your name, that you're free and clear. This seems like a lot of work on the customer's part. You try to pay for a train ride and end up working for the train company.

  3. Considering how my purchase on that machine turned out, I was happy that it had elected to mess with my purchase and not his, as he seemed to have had a much longer trip before him than I.

  4. It turned out that on the first machine I'd selected a daily ticket whereas my flying fingers had selected a one-hour ticket on the second machine. It was about 20% cheaper and fit my coin budget but I would have had to do everything again in Altstetten had I chosen that option.

  5. It turned out that I was wrong. Another colleague would tell me that day that this is the machine's way of telling me that the ticket is invalid. When it said that the second item -- my receipt -- could not be printed, it was notifying me that it had nullified the entire transaction. With a whole screen of space, the machine could have told me that in much clearer terms.

  6. Not only was the ticket invalid, but I had somehow managed to purchase the wrong zones anyway. Of the text I was barely able to decipher, I was almost certain that I didn't have the zones I needed. It explained why my ticket purchases on the next day would seem so much more expensive.

Merge conflicts in source control

This article originally appeared on earthli News and has been cross-posted here.


I was recently asked a question about merge conflicts in source-control systems.

[...] there keep being issues of files being over written, changes backed out etc. from people coding in the same file from different teams.

My response was as follows:

tl;dr: The way to prevent this is to keep people who have no idea what they're doing from merging files.

Extended version

Let's talk about bad merges happening accidentally. Any source-control worth its salt will support at least some form of automatic merging.

An automatic merge is generally not a problem because the system will not automatically merge when there are conflicts (i.e. simultaneous edits of the same lines, or edits that are "close" to one another in the base file).

An automatic merge can, however, introduce semantic issues.

For example if both sides declared a method with the same name, but in different places in the same file, an automatic merge will include both copies but the resulting file won't compile (because the same method was declared twice).

Or, another example is as follows:

Base file

public void A(B b)
{
  var a = new A();

  b.Do(a);
  b.Do(a);
  b.Do(a);
}

Team One version

public void A(B b)
{
  var a = new A();

  b.Do(a);
  b.Do(a);
  b.Do(a);
  a.Do();
}

Team Two version

public void A(B b)
{
  var a = null;

  b.Do(a);
  b.Do(a);
  b.Do(a);
}

Automatic merge

public void A(B b)
{
  var a = null;

  b.Do(a);
  b.Do(a);
  b.Do(a);
  a.Do();
}

The automatically merged result will compile, but it will crash at run-time. Some tools (like ReSharper) will display a warning when the merged file is opened, showing that a method is being called on a provably null variable. However, if the file is never opened or the warning ignored or overlooked, the program will crash when run.

In my experience, though, this kind of automatic-merge "error" doesn't happen very often. Code-organization techniques like putting each type in its own file and keeping methods bodies relatively compact go a long way toward preventing such conflicts. They help to drastically reduce the likelihood that two developers will be working in the same area in a file.

With these relatively rare automatic-merge errors taken care of, let's move on to errors introduced deliberately through maliciousness or stupidity. This kind of error is also very rare, in my experience, but I work with very good people.

Let's say we have two teams: Team One - branch one Works on file 1 Team Two - branch two Works on file 1 Team One promotes file 1 into the Master B branch, there are some conflicts that they are working out but the file is promoted.

I originally answered that I wasn't sure what it meant to "promote" a file while still working on it. How can a file be commited or checked in without having resolved all of the conflicts?

As it turns out, it can't. As documented in TFS Server 2012 and Promoting changes, promotion simply means telling TFS to pick up local changes and add them to the list of "Pending Changes". This is part of a new TFS2012 feature called "Local Workspaces". A promoted change corresponds to having added a file to a change list in Perforce or having staged a file in Git.

The net effect, though, is that the change is purely local. That is has been promoted has nothing to do with merging or committing to the shared repository. Other users cannot see your promoted changes. When you pull down new changes from the server, conflicts with local "promoted" changes will be indicated as usual, even if TFS has already indicated conflicts between a previous change and another promoted, uncommitted version of the same file. Any other behavior else would be madness.1

Team Two checks in their file 1 into the Master B branch. They back out the changes that Team One made without telling anyone anything.

There's your problem. This should never happen unless Team Two has truly determined that their changes have replaced all of the work that Team One did or otherwise made it obsolete. If people don't know how to deal with merges, then they should not be merging.

Just as Stevie Wonder's not allowed behind the wheel of a car, neither should some developers be allowed to deal with merge conflicts. In my opinion, though, any developer who can't deal with merges in code that he or she is working on should be moved another team or, possibly, job. You have to know your own code and you have to know your tools.2

Team One figures out the conflicts in their branch and re-promotes file one (and other files) to Master B branch. The source control system remembers that file 1 was backed out by Team Two so it doesn't promote file 1 but doesn't let the user know.

This sounds insane. When a file is promoted -- i.e. added to the pending changes -- it is assumed that the current version is added to the pending changes, akin to staging a file in Git. When further changes are made to the file locally, the source-control system should indicate that it has changed since having been promoted (i.e. staged).

When you re-promote the file (re-stage it), TFS should treat that as the most recent version in your workspace. When you pull down the changes from Team 2, you will have all-new conflicts to resolve because your newly promoted file will still be in conflict with the changes they made to "file 1" -- namely that they threw away all of the changes that you'd made previously.

And, I'm not sure how it works in TFS, but in Git, you can't "back out" a commit without leaving a trail:

  • Either there is a merge commit where you can see that Team Two chose to "accept their version" rather than "merge" or "accept other version"
  • Or, there is a "revert" commit that "undoes" the changes from a previous commit

Either way, your local changes will cause a conflict because they will have altered the same file in the same place as either the "merge" or "revert" commit and -- this is important -- will have done so after that other commit.

To recap, let me summarize what this sounds like:

  • T1: I want to check in file1
  • TFS: You have conflicts
  • T1: Promote file1 so that TFS knows about (other users can't see it yet because it hasn't been committed)
  • TFS: Okie dokie
  • T2: I want to check in file1
  • TFS: You have conflicts
  • T2: F&#$ that. Use my version. Oh, and, f&#$ T1.
  • TFS: I hear and obey. T2/file1 it is.
  • T1: OK, I resolved conflicts; here's the final version of file1
  • TFS: Thanks! tosses T1/file1 out the window

I don't believe that this is really possible -- even with TFS -- but, if this is a possibility with your source-control, then you have two problems:

  1. You have team members who don't know how to merge
  2. Your source control is helping them torpedo development

There is probably a setting in your source-control system that disallows simultaneous editing for files. This is a pretty huge restriction, but if your developers either can't or won't play nice, you probably have no choice.



  1. This is not to rule out such behavior 100%, especially in a source-control system with which I am largely unfamiliar. It only serves to indicate the degree to which I would be unwilling to work with any system that exhibits this kind of behavior.

  2. Different companies can have different grace periods for learning these two things, of course. I suppose that grace period can be interminably long, but...

Programming in the moderncurrent age

This article originally appeared on earthli News and has been cross-posted here.


In order to program in 2013, it is important not to waste any time honing your skills with outdated tools and work-flows. What are the essential pieces of software for developing software in 2013?

Runtime

A runtime is a given for all but the most esoteric of programming exercises. Without something to execute your code, there is almost no point in writing it.

Debugger

Programming without an integrated debugger can be very time-consuming, error-prone and will quite frankly suck the fun right out of the whole endeavor. And, by "debugger" I mean a source-level single-step debugger with call-stack and variable/object/structure inspection as well as expression evaluation. Poring through logs and inserting print statements is not a viable long-term or even medium-term solution. You shouldn't be writing in a language without one of these unless you absolutely can't avoid it (NAnt build scripts come to mind).

Compiler/Checker

A syntax/semantics checker of some sort integrated into the editor ensures a tighter feedback/error-finding loop and saves time, energy and frustration. I was deliberately cagey with the "checker" because I understand that some languages, like Javascript1, do not have a compiled form. Duck-typed languages like Python or Ruby also limit static checking but anything is better than nothing.

Versioning

A source-control system is essential in order to track changes, test ideas and manage releases. A lot of time can be wasted -- and effort lost -- without good source control. Great source control decreases timidity, encourages experimentation and allows for interruptible work-flows. I will argue below that private branches and history rewriting are also essential.

Even for the smallest projects, there is no reason to forgo any of these tools.

Managing your Source Code

tl;dr: It's 2013 and your local commit history is not sacrosanct. No one wants to see how you arrived at the solution; they just want to see clean commits that explain your solution as clearly as possible. Use git; use rebase; use "rebase interactive"; use the index; stage hunks; squash merge; go nuts.2

imageI would like to focus on the "versioning" part of the tool-chain. Source control tells the story of your code, showing how it evolved to where it is at any given point. If you look closely at the "Encodo Branching Model"3 diagram (click to enlarge), you can see the story of the source code:

  1. All development was done in the master branch until v1.0 was released
  2. Work on B was started in a feature branch
  3. Work on hotfix v1.0.1 was started in a hotfix branch
  4. Work on A was started in another feature branch
  5. Hotfix v1.0.1 was released, tagged and merged back to the master branch
  6. Development continued on master and both feature branches
  7. Master was merged to feature branch A (includes hotfix v1.0.1 commits)
  8. Finalization for release v1.1 was started in a release branch
  9. Feature A was completed and merged back to the master branch
  10. Version v1.1 was released, tagged and merged back to the master branch
  11. Master was merged to feature branch B (includes v1.1 and feature A commits)
  12. Development continued on master and feature B
  13. Version v1.2 was released and tagged

Small, precise, well-documented commits are essential in order for others to understand the project -- especially those who weren't involved in developing the code. It should be obvious from which commits you made a release. You should be able to go back to any commit and easily start working from there. You should be able to maintain multiple lines of development, both for maintenance of published versions and for development of new features. The difficulty of merging these branches should be determined by the logical distance between them rather than by the tools. Merging should almost always be automatic.

Nowhere in those requirements does it say that you're not allowed to lie about how you got to that pristine tree of commits.

Why you should be using private branches and history rewriting

A few good articles about Git have recently appeared -- Understanding the Git Workflow by Benjamin Sandofsky is one such -- explaining better than ever why rewriting history is better than server-side, immutable commits.

In the article cited above, Sandofsky divides his work up into "Short-lived work [...] larger work [...] and branch bankrupty." These concepts are documented to some degree in the Branch Management chapter of the Encodo Git Handbook (of which I am co-author). I will expand on these themes below.

Note: The linked articles deal exclusively with the command line, which isn't everyone's favorite user interface (I, for one, like it). We use the SmartGit/Hg client for visualizing diffs, organizing commits and browsing the log. We also use the command-line for a lot of operations, but SmartGit is a very nice tool and version 3 supports nearly all of the operations described in this article.

What is rebasing?

As you can see from the diagram above, a well-organized and active project will have multiple branches. Merging and rebasing are two different ways of getting commits from one branch into another.

Merging commits into a branch creates a merge commit, which shows up in the history to indicate that n commits were made on a separate branch. Rebasing those commits instead re-applies those commits to the head of the indicated branch without a merge commit. In both cases there can be conflicts, but one method doesn't pose a greatest risk of them than the other.4 You cannot tell from the history that rebased commits were developed in a separate branch. You can, however, tell that the commits were rebased because the author date (the time the commit was originally created) differs from the commit date (the last time that the commit was applied).

What do you recommend?

At Encodo, we primarily work in the master branch because we generally work on very manageable, bite-sized issues that can easily be managed in a day. Developers are free to use local branches but are not required to do so. If some other requirement demands priority, we shunt the pending issue into a private branch. Such single-issue branches are focused and involve only a handful of files. It is not at all important to "remember" that the issue was developed in a branch rather than the master branch. If there are several commits, it may be important for other users to know that they were developed together and a merge-commit can be used to indicate this. Naturally, larger changes are developed in feature branches, but those are generally the exception rather than the rule.

Remember: Nowhere in those requirements does it say that you're not allowed to lie about how you got to that pristine tree of commits.

Otherwise? Local commit history is absolutely not sacrosanct. We rebase like crazy to avoid unwanted merge commits. That is, when we pull from the central repository, we rebase our local commits on top of the commits that come form the origin. This has worked well for us.

If the local commit history is confusing -- and this will sometimes come up during the code review -- we use an interactive rebase to reorganize the files into a more soothing and/or understandable set of commits. See Sandofsky's article for a good introduction to using interactive rebasing to combine and edit commits.

Naturally, we weigh the amount of confusion caused by the offending commits against the amount of effort required to clean up the history. We don't use bisect5 very often, so we don't invest a lot of time in enforcing the clean, compilable commits required by that tool. For us, the history is interesting, but we rarely go back farther than a few weeks in the log.6

When to merge? When to rebase?

At Encodo, there are only a few reasons to retain a merge commit in the official history:

  1. If we want to remember which commits belonged to a particular feature. Any reasonable tool will show these commits graphically as a separate strand running alongside the master branch.
  2. If a rebase involves too much effort or is too error-prone. If there are a lot of commits in the branch to be integrated, there may be subtle conflicts that resolve more easily if you merge rather than rebase. Sometimes we just pull the e-brake and do a merge rather than waste time and effort trying to get a clean rebase. This is not to say that the tools are lacking or at fault but that we are pragmatic rather than ideological.7
  3. If there are merge commits in a feature branch with a large number of well-organized commits and/or a large number of changes or affected files. In this case, using a squash merge and rebuilding the commit history would be onerous and error-prone, so we just merge to avoid issues that can arise when rebasing merge commits (related to the point above).

When should I use private branches? What are they exactly?

There are no rules for local branches: you can name them whatever you like. However, if you promote a local branch to a private branch, at Encodo we use the developer's initials as the prefix for the branch. My branches are marked as "mvb/feature1", for example.

What's the difference between the two? Private branches may get pushed to our common repository. Why would you need to do that? Well, I, for example, have a desktop at work and, if I want to work at home, I have to transfer my workspace somehow to the machine at home. One solution is to work on a virtual machine that's accessible to both places; another is to remote in to the desktop at work from home; the final one is to just push that work to the central repository and pull it from home. The offline solution has the advantage of speed and less reliance on connectivity.

What often happens to me is that I start work on a feature but can only spend an hour or two on it before I get pulled off onto something else. I push the private branch, work on it a bit more at home, push back, work on another, higher-priority feature branch, merge that in to master, work on master, whatever. A few weeks later and I've got a private branch with a few ugly commits, some useful changes and a handful of merge commits from the master branch. The commit history is a disgusting mess and I have a sneaking suspicion that I've only made changes to about a dozen files but have a dozen commits for those changes.

That's where the aforementioned "branch bankruptcy" comes in. You're not obligated to keep that branch; you can keep the changes, though. As shown in the referenced article, you execute the following git commands:

git checkout master
git checkout -b cleaned_up_branch
git merge --squash private_feature_branch
git reset

The --squash tells git to squash all of the changes from the private_feature_branch into the index (staging) and reset the index so that those changes are in the working tree. From here, you can make a single, clean, well-written commit or several commits that correspond logically to the various changes you made.

Git also lets you lose your attachment to checking in all the changes in a file at once: if a file has changes that correspond to different commits, you can add only selected differences in a file to the index (staging). In praise of Gits index by Aristotle Pagaltzis provides a great introduction. If you, like me, regularly take advantage of refactoring and cleanup tools while working on something else, you'll appreciate the ability to avoid checking in dozens of no-brainer cleanup/refactoring changes along with a one-liner bug-fix.8

One final example: cherry picking and squashing

I recently renamed several projects in our solution, which involved renaming the folders as well as the project files and all references to those files and folders. Git automatically recognizes these kind of renames as long as the old file is removed and the new file is added in the same commit.

I selected all of the files for the rename in SmartGit and committed them, using the index editor to stage only the hunks from the project files that corresponded to the rename. Nice and neat. I selected a few other files and committed those as a separate bug-fix. Two seconds later, the UI refreshed and showed me a large number of deleted files that I should have included in the first commit. Now, one way to go about fixing this is to revert the two commits and start all over, picking the changes apart (including playing with the index editor to stage individual hunks).

Instead of doing that, I did the following:

  1. I committed the deleted files with the commit message "doh!" (to avoid losing these changes in the reset in step 3)
  2. I created a "temp" branch to mark that commit (to keep the commit visible once I reset in step 3)
  3. I hard-reset my master branch to the origin
  4. I cherry-picked the partial-rename commit to the workspace
  5. I cherry-picked the "doh!" commit to the workspace
  6. Now the workspace had the rename commit I'd wanted in the first place
  7. I committed that with the original commit message
  8. I cherry-picked and committed the separate bug-fix commit
  9. I deleted the "temp" branch (releasing the incorrect commits on it to be garbage-collected at some point)

Now my master branch was ready to push to the server, all neat and tidy. And nobody was the wiser.



  1. There are alternatives now, though, like Microsoft's TypeScript, that warrant a look if only because they help tighten the error-finding feedback loop and have the potential to make you more efficient (the efficiency may be robbed immediately back, however, if debugging generated code becomes difficult or even nightmarish).

  2. Once you've pushed, though? No touchie. At that point, you've handed in your test and you get graded on that.

  3. According to my business card, I'm a "senior developer and partner" at Encodo System AG.

  4. With the exception, mentioned elsewhere as well, that rebasing merge-commits can sometimes require you to re-resolve previously resolved conflicts, which can be error-prone if the conflicts were difficult to resolve in the first place. Merging merge-commits avoids this problem.

  5. bisect is a git feature that executes a command against various commits to try to localize the commit that caused a build or test failure. Basically, you tell it the last commit that worked and git uses a binary search to find the offending commit. Of course, if you have commits that don't compile, this won't work very well. We haven't used this feature very much because we know the code in our repositories well and using blame and log is much faster. Bisect is much more useful for maintainers that don't know the code very well, but still need to figure out at which commit it stopped working.

  6. To be clear: we're only so cavalier with our private repositories to which access is restricted to those who already know what's going on. If we commit changes to public, open-source or customer repositories, we make sure that every commit compiles. See Aristotle's index article (cited above) for tips on building and testing against staged files to ensure that a project compiles, runs and passes all tests before making a commit -- even if you're not committing all extant changes.

  7. That said, with experience we've learned that an interactive rebase and judicious squashing will create commits that avoid these problems. With practice, these situations crop up more and more rarely.

  8. Of course, you can also create a separate branch for your refactoring and merge it all back together, but that's more work and is in my experience rarely necessary.

Windows 8: felled by a modem driver

tl;dr: if you can't read the BSOD message or need to examine the minidump files generated by Windows when it crashes, use the BlueScreenView utility to view them. Windows 8 kept crashing on shutdown for me because of an errant 56K modem driver. Sad -- so sad -- but true.

My Windows 8 installation went off with just one hitch: the machine crashed on shutdown. Every. Single. Time. This made it impossible to use the hibernation feature, which was a blocker issue for a laptop.

So, how to solve the issue? Well, the first step is to read the error message, right? In this case, the crash was a Blue Screen of Death (BSOD) so you can't copy the message or take a screenshot of it. You can take a picture if you're quick on the draw: for the last several versions, Windows has been extremely shy about crashing and will hurriedly restart before the user even realizes what has happened.

imageThat means you have to be fast to read the message, but it used to be possible. With Windows 8, though, the BSOD message has been improved to show a little sad face and a message that tells the user that Windows is gathering information related to the crash and will restart shortly. In the example to the right, you can see that the small text reads HAL_INITIALIZATION_FAILED. In the example, though, the error message takes up the whole screen; in my case, the blue area was limited to a 640x480 block in the center and the fine print had been scaled down into illegibility.

That tiny bit of text holds the salient nugget of information that can help a veteran Windows user solve the problem. This was, needless to say, quite frustrating. The Event Viewer showed nothing, which wasn't unusual in the case of a full system crash -- how would it be able to write an error message?

The system would still boot up fine and was perfectly usable, so I could search for help in finding that elusive message. Every halfway-useful page I found quickly ended in a forum moderator instructing users to upload their "minidump" files so that a Microsoft employee could examine them.

That wasn't acceptable, so I searched for help on how to read mini-dump files myself. Microsoft's instructions ran to multiple steps and installation of low-level debugging software. Frustrated, I jumped into a conversation called Blue Screen error delivers unreadable instructions (font too small); how to increase fontsize in Blue Screen? which also featured an unhelpful answer. Luckily, someone responded almost immediately with a tip to use the BlueScreenView to read mini-dump files.

Within seconds I'd found out that my crashes were caused by a driver file called CAX_CNXT.sys. A few more seconds and I'd found out that this was the driver for the 56K modem on my laptop. I disabled that device with extreme prejudice and restarted the machine. No crash. Problem solved. It took longer than it had to, but now my machine has been running stably on Windows 8 for days. And lacking a modem driver hasn't affected my workflow at all.


This article originally appeared on earthli News and has been cross-posted here.

Git Handbook 2.0 is now available

imageWe recently released the latest version of our Git Handbook. The 2.0 version includes a completely rewritten chapter on branch management and branching strategy (Chapter 11) -- including a branching diagram that's easier to understand than most (shown to the right). After having used Git for another year, we refined this section considerably to come up with a pragmatic strategy that works in many, if not all, situations.

If you downloaded an earlier version of the Git Handbook, it's definitely worth your time to take a look at this latest revision.

Warum wollen Programmierer immer so viel wissen?

Mit dem anrollenden Testen von ersten Munjari-Versionen ist mal wieder das Thema Testen von Software bzw. - damit verbunden das Melden von Problemen aufgekommen. Eigentlich ist das früher oder später bei jedem Software-Projekt ein Thema. Irgendwann gibt es immer Software-Probleme zu melden.

Also nimmt man als Anwender fix das Telefon oder Emailprogramm und meldet das kurz und bündig beim zuständigen Programmierer. Und schon geht's los! Was der alles von mir als einfacher Anwender seiner Software wissen will!

Aus der Sicht des Programmierers ist es oft so, dass dieser vom Anwender eine relativ kurze Problemmeldung bekommt (manchmal nicht mal ein ganzer Satz) und damit noch nichts anfangen kann. Er muss erst mal mittels Hin und Her die ganzen benötigten Informationen vom Anwender in Erfahrung bringen muss. Viel lieber würde dieser gleich beginnen in die Tasten zu hauen. Stattdessen muss er jetzt zuerst Emails schreiben oder gar telefonieren (machen Programmierer überhaupt nicht gerne).

Warum brauchen die Programmierer denn eigentlich so viele Informationen und können nicht einfach von sich aus das angetönte Problem beheben? Wie kann ich das Hin und Her zwischen mir als Anwender und dem Programmierer bzw. der IT reduzieren und komme schneller mein Problem gelöst?

Warum sind die Programmierer auf so viele Informationen angewiesen?

Um diese Frage zu beantworten, müssen wir etwas weiter ausholen. Heutige Computerprogramme (PC-Programme, interaktive Webseiten, etc.) sind, auch für den Programmierer, sehr komplex und werden, aus verschiedenen Gründen, immer komplexer.

Ein Programmierer entwickelt Programme indem er den Quelltext schreibt. D.h., er formuliert für alles was das Programm anzeigt oder machen soll, Anweisungen als mehr oder weniger lesbarer Text. In diesem Quelltext muss er für wirklich alles entsprechende Anweisungen programmieren.

Damit man eine Ahnung bekommt, wie detailreich diese Anweisungen sein müssen, im folgenden ein ganz kleiner Ausschnitt aus einem solchen Quelltext. Dieser zabert eine OK-Schaltfläche auf dem Bildschirm.

Button okButton = new Button(myForm);
okButton.Text = OK;
okButton.Left = 254;
okButton.Top = 154;
okButton.Width = 200;
okButton.Height = 23;
okButton.Show();

Wie zu sehen ist, müssen wir unserer OK-Schaltfläche alles einzeln erklären:

  • Erstelle eine neue Schaltfläche innerhalb des Fensters myForm
  • Beschrifte diese mit dem Text OK
  • Koordinate (in Pixel) der oberen, linken Ecke der Schaltfläche
  • Breite und Höhe (in Pixel) der Schaltfläche
  • Zeige diese Schaltfläche jetzt an

Wir brauchen also schon mal 6 Zeilen Text um, eine ganz einfache OK-Schaltfläche auf den Bildschirm zu bringen. Dabei haben wir noch nicht mal definiert, was überhaupt geschehen soll, wenn diese Schaltfläche vom Anwender gedrückt wird. Wie würde den dieser Quelltext aussehen? Keine Angst - dieser Quelltext würde den Rahmen dieses Artikels sprengen.

Der Umfang des Quelltextes von Programmen, welche wir täglich verwenden, liegt, bei kleineren Hilfsprogrammen, bei ein paar tausend Zeilen und kann bei grösseren Lösungen auf hundert tausende oder gar auf über eine Million Zeilen ansteigen. Um uns das etwas besser vorstellen zu können: eine Million Zeilen entsprächen über 15000 A4 Seiten ca. 38 Bundesordner für nur ein einziges Programm!

Damit nicht genug! Diese 15000 A4 Seiten ändern und wachsen stündlich, weil der Programmierer ja neue Funktionen einbaut oder bestehende ändert (z.B. um die Probleme der Anwender zu beheben). Für jede Version eines Programms (Version 1.1, Version 1.2, etc.) werden die zu dem jeweiligen Zeitpunkt gültigen 15000 A4-Seiten archiviert, so dass man später bei Problemmeldungen von Anwendern in den richtigen 38 Bundesordnern nachschlagen kann.

Nur noch leicht erschwerend kommt jetzt hinzu, dass vielfach nicht nur ein Programmierer an diesen Bundesordnern programmiert, sondern zeitgleich bis zu einem ganzen Team daran modifizieren und erweitern kann. Einige dieser Bundesordner können sogar von externen Firmen dazu gekauft und eingebunden werden. D.h., ein Programmierer hat selten den ganzen Quelltext selbst geschrieben und somit auch nicht auswendig im Kopf. Häufig arbeiten Programmierer auch in mehreren Projekten mit.

Kurz und bündig: als Programmierer ist man täglich mit tausenden und abertausende von Quelltextzeilen konfrontiert und wühlt sich durch diese. Das gute daran: das macht uns Programmierer sogar Spass!

Ein Problem tritt auf

Jetzt gehen wir davon aus, der Anwender schreibt uns zu diesen 15000 A4 Seiten: Das Einloggen ins Programm funktioniert nicht. Da stellt man sich als Programmierer erst mal die Frage, in welchem Kapitel unseres 15000 Seiten umfassenden Quelltextes man zu suchen beginnen soll? Ist es das Kapitel Login-Formular anzeigen oder eher das Kapitel Passwort mit dem auf dem zentralen Server gespeicherten vergleichen oder gar Netzwerkkommunikation zwischen PC-Programm und Firmenserver? Hätte der Anwender z.B. noch einen zweiten Satz dazu geschrieben wie z.B. Ich erhalte die Fehlermeldung, dass der Server nicht gefunden wird, so hätten wir Programmierer uns wohl direkt auf das dritte Kapitel gestürzt. Denke es ist klar, dass das die Dinge sehr vereinfacht hätte.

Hat ein Programmierer nur den ersten Satz, bleibt ihm nichts anderes übrig, als auf seinem PC zu schauen ob da irgend etwas den Anschein macht "nicht zu funktionieren". Da es andere PC-Systeme, andere Computer-Netzwerke, andere Benutzernamen und Passwörter sind, ist die Chance relativ klein, dass der Programmierer das Problem auf diese Art finden wird.

Nachvollziehbarkeit

Hat man mit Programmierer zu tun, so sprechen diese häufig von der Nachvollziehbarkeit bzw. wie sie das Problem "reproduzieren" können. Warum das?

Auch hier müssen wir etwas weiter ausholen:

Wie gehen die Programmierer denn vor, wenn ein Anwender ein bestimmtes Problem meldet? Er wird wohl kaum jedes mal die 38 Bundesordner Zeile für Zeile auf Fehler kontrollieren gehen? Ich kann da beruhigen: nein, das tun wir nicht.

Viel mehr versuchen wir Programmierer aus den Informationen, welche wir haben, uns den Weg vorzustellen, welches das Programm durch den Quelltext geht. Die einen mögen sich jetzt fragen, ob es da unterschiedliche Wege gibt? Ja leider. Ein Programm besteht eigentlich nie aus einem einzigen linearen Ablauf. Man kann sich ein Programm als ein Weglabyrinth vorstellen, welches aus tausenden von Wegabschnitten und Kreuzungen besteht. Kommt das Programm an eine Kreuzung entscheidet es anhand der aktuellen Situation, welchen Weg es weiter gehen soll.

Wir Programmierer müssen also genau wissen oder erraten, an welcher Kreuzungen der Anwender welche Abzweigung genommen hat um eine Vermutung zu bekommen, auf welchen Wegabschnitt wir mit der Suche begonnen werden soll. Dann gehen wir auf die A4-Seite(n) welche in Frage kommen und schaue, ob uns da was auffällt. Nehmen wir an einer Kreuzung eine andere Abzweigung, kann es sein, dass wir tausende von A4-Seiten entfernt vom Problem des Anwenders sind. Ist es uns möglich, denselben Weg wie der Anwender zu gehen und erhalten somit dasselbe Problem, so sprechen wir von Nachvollziehbarkeit.

Müssten wir Programmierer den ganzen Weg mit allem Kreuzungen im Kopf abschreiten, würden wir relativ schnell einen Nervenzusammenbruch erleiden. In der heutigen Zeit haben wir für das Abschreiten des effektiven Weges technische Hilfsmittel: Debugger-Programme 1. Wir verwenden also ein Programm, um den Weg in einem anderen Programm abzuschreiten. Unser Ziel ist es nun, mit diesen Programmen den genau gleichen Weg Schritt für Schritt durch das Programm zu gehen, wie vor uns der Anwender, so dass wir unmittelbar bevor das Problem auftritt anhalten und die Situation anschauen können.

Mit diesem Wissen um die Situation, welche beim nächsten Schritt zum Problem führen wird, können wir das Problem meistens sehr schnell beheben. Ohne dieses Wissen hingegen sind es häufig Zufallstreffer in der Art: Wenn ich mich richtig erinnere ist an der Stelle X der Weg etwas schmaler. Könnte ja sein, dass es dort zu einem solchen Problem kommt. Ich geh mal diese Passage des Weges genauer anschauen..

Je mehr bzw. genauere Informationen wir vom Anwender erhalten, desto besser bzw. korrekter können wir an den Kreuzungen entscheiden, in welche Richtung wir dem Anwender folgen sollen. Insbesondere an den grossen Kreuzungen ist dies wichtig.

Wie können wir das Hin und Her mit dem Programmierer reduzieren?

Der Programmierer muss erst wissen, welche der 15000 A4-Seiten für das Problem des Anwender relevant sind. Dazu muss er möglichst genau wissen, an welcher Kreuzung der Anwender sich für welchen Weg entschieden hat, damit er möglichst genau den Spuren des Anwenders mittels Debugger-Programm folgen kann. Je genauer der Anwender den von Ihm beschrittenen Weg beschreibt, desto besser und vor allem auch schneller kann der Programmierer ihm zum Problem folgen und dieses für den Anwender beseitigen.

In der Praxis sind folgende Informationen sicher mal ein guter Anfang:

  • Welches Programm
    • Genauer Name des Programms (wird hin und wieder auch missverstanden)
    • Welches Programmmodul etc. (falls vorhanden)
    • Welcher Version des besagten Programms?
  • Welche Umgebung
    • Betriebssystem
    • Betriebssystem Version
    • Für Web-Anwendungen: Browser inkl. Version (z.B. Internet Explorer 8, Firefox 3.6, etc.)
    • Mit welchen Daten wurde gearbeitet? z.B. Produktive Datenbank vom Datum X, mit der Datei Y. Falls möglich Daten der Problemmeldung beilegen)
  • Welches Resultat tritt ein?
    • z.B. Speichert man eine Adresse, wird diese zwar gespeichert aber nichts weiter passiert.
  • Welches Resultat wird erwartet?
    • z.B. Speichert man die Adresse sollte direkt eine Adressblatt auf dem Drucker ausgedruckt werden.
  • Welche Schritte braucht es, um das Problem zu reproduzieren?
    • Bsp:
      1. Als Benutzer X ins Programm einloggen.
      2. In der Symbolleiste mit der Maus auf Neue Adresse erfassen klicken.
      3. In der Adresserfassungsmaske folgende Daten eingeben: .
      4. Mittels der ENTER-Taste die Adresserfassung bestätigen.
      5. Nun wird kein Adressblatt ausgedruckt, sondern man befindet sich wieder auf der Hauptmaske des Programms.
  • Traten Fehlermeldungen auf und wenn ja, welche?
    • Falls es Fehlermeldungen gibt, diese möglichst komplett mitgeben. Man kann evtl. den Text der Fehlermeldung mit der Maus markieren, dann mittels Ctrl+C Tastendruck kopieren und mit Ctrl+V ins Email etc. einfügen. Oder man macht mittels der Print-Screen-Taste ein Bildschirmfoto (engl.: Screenshot) und fügt diesen in ein Word-Dokument oder direkt ins Email ein. Geht das alles nicht, kann man immer noch die Fehlermeldung abtippen.
    • Fehlermeldungen sind für den Programmierer sehr wichtig. Sie helfen ihm, den besagten Weg nicht von ganz vorne beschreiten zu müssen. Eine gute Fehlermeldung sagt dem Programmierer sogar, wo und um welches Problem es sich handelt inkl. den Weg, welchen der Benutzer genommen hat. Somit muss der Programmierer im optimalen Fall gar nicht erst auf die Suche gehen, sondern kann direkt an der Lösung des Problems arbeiten.
    • Es ist schade, wenn vom Anwender beschrieben wird: und dann kam eine Fehlermeldung. und die aufgetretene Fehlermeldung dann nicht beschrieben ist. In solchen Fällen wird der Programmierer höchst wahrscheinlich als erstes nach der genauen Fehlermeldung bzw. deren Text nachfragen.

Fazit

Je mehr Informationen dem Programmierer gleich beim ersten Kontakt gegeben werden können, desto eher/schneller bekommt man eine Lösung für das Problem. Ist die Problembeschreibung sehr knapp, wird sich der Programmierer wohl erst den Problemen annehmen, die besser beschrieben sind. D.h., das eigene Problem rückt erst mal etwas hinter andere. Kommt hinzu, dass der Programmierer viel motivierter an Probleme ran geht, um so ausführlicher diese dokumentiert sind. Auch der Programmierer vermeidet gerne, wenn nur irgnedwie möglich, ein Hin und Her, um an benötigte Informationen zu kommen.

Übrigens: das waren jetzt etwas mehr als 4 A4-Seiten ;-)



  1. Diese Programme werden "Debugger"-Programme genannt. Dabei handelt es ich um relativ komplexe Programme, mit welchen der Programmierer ein anderes Programm Schritt für Schritt ausführen. Mit einem Debugger ist es auch möglich, jederzeit ein Programm pausieren zu können und sich die Umgebung bzw. Situation anzuschauen, in welchem sich das Programm nach genau diesem Schritt befindet. Die Debugger-Programme sind in der heutigen Software-Entwicklung zwar nicht die einzigen, aber sicher eines der wichtigsten Werkzeuge, welches den Programmierer zur Verfügung stehen.

Warum Screenshots für Software-Produkte wichtig sind?

Wir von der Encodo arbeiten aktuell an Produkteunterlagen und dabei ist Frage nach Screenshots aufgekommen. Wir IT-Fritzen waren der Meinung, dass ein Screenshoot unbedingt in die Unterlagen gehört. Auf die Frage hin, warum wir denn unbedingt einen Screenshot relativ prominent in den Produkte- bzw. Verkaufsunterlagen wollten, antworteten wir: "Wir verwenden Screenshots als ein sehr gewichtiges Auswahlkriterium bei der Evaluation neuer Software".

Soweit so gut, aber müssen auf dem Screenshots Details sichtbar sein? Nein - es geht uns darum, dass wir mit einem Blick die Generation der Software abschätzen können. Aus der Generation wiederum schliessen wir, wie neu bzw. aktuell die Software ist und wie intensiv diese noch weiterentwickelt wird. Weiter zeigt es auch auf, ob die Entwickler Wert auf Details und somit Sorgfalt legen, was wiederum ein Indiz für die Qualität der Software ist.

Ist das Erscheinungsbild extrem schlecht, kann es sogar sein, dass es uns davon abhält überhaupt erst Details zu dem Produkt zu lesen. Insofern ist uns dann die Funktionalität ganz egal. Das ist sicher der Extremfall, aber er zeigt auf, wie wichtig das Erscheinungsbild einer Software werden kann.

Man könnte jetzt sagen, dann publizieren wir von unseren Programmen eben grundsätzlich keine Screenshots, so können diese auch nicht negativ wirken. Das wird mich als Entwickler allerdings misstrauisch machen. "Warum haben die keinen Screenshot von Ihrer Software veröffentlicht? Haben sie was zu verbergen?", frage ich mich bei solchen Programmen. Das hat für diese zur Folge, dass sie schon bei der allerersten Runde rausfliegen - noch bevor ich mir die unterschiedlichen Screenshots anschaue.

Beispiel: Taschenrechner-Evaluation

Machen wir ein kleines Beispiel und suchen uns eine neue Taschenrechner-Anwendung. Wir suchen einen einfachen Taschenrechner, welcher die wichtigsten Grundoperationen unterstützt. Wir googlen etwas umher und haben schnell zwei Produkte gefunden, welche die folgenden beiden Screenshots auf deren Webseiten publiziert haben.

imageimage

Wir überlegen uns jetzt, welches wir wirklich runter laden und auf dem eigenen Rechner installieren (und ggf. wieder deinstallieren, wenn er nichts war).

Bei welchen dieser beiden Programme klickt Ihr als erstes auf den Download-Link, installiert und testet es?

Tippe mal auf Nummer 2 - mindestens wäre das bei mir als Entwickler so. Beim ersten Taschenrechner habe ich das Gefühl, der Entwickler ist noch im Windows 95 Zeitalter stehen geblieben. Da wird vermutlich nicht so bald was Neues kommen, welches auch neuere Entwicklungen berücksichtigen wird (z.B. Integration in die neuen Windows 7 Funktionen).

Auch wenn wir Nummer 2 zuerst runter laden, kann es dieser immer noch mit der Funktionalität und anderen Dingen verpatzen. ABER: wir haben ihm die erste Chance gegeben. Sind wir mit Nummer 2 super zufrieden, werden wir keinen weiteren Taschenrechner runter laden und installieren, und seine Konkurrenz bekommt erst gar keine Chance, auch wenn diese evtl. gar nicht so schlecht gewesen wäre wie sein erster Eindruck vermittelte.

Genau das zählt: der erste Eindruck. Und dieser geht bei Software meist über einen Screenshot.

Bänz

Am Samichlaus gab es dieses Jahr selbst gemachte Grittibänzen! Jeder hier bei Encodo hat sich einen personalisierten Bänz verdient. Unser gern gesehener Bürogast Alex hat selbstverständlich auch einen erhalten. Somit gab es zwei Velobänzen für die Sportler Marco und Alex, einen Rindfleischbänz für den Gourmet Remo, einen Fisch für den Taucher Marc, einen Pulli für Pascal der immer noch (mangels Pullover) im T-Shirt rumläuft und eine Kafitasse für Karin den Koffeinjunkie.

ins Bild klicken, um es zu vergrössern.

imageimageimage

Mercurial, Python & Debian

This article originally appeared on earthli News and has been cross-posted here.


After nearly a decade of using Perforce for my private source control, I'd decided to switch to Mercurial. Mercurial is a distributed version control system and open-source and all kinds of awesome and I won't go into why I made the switch here. Suffice it to say it makes it much easier to release code and work with others.

Mercurial itself is an easy installation and I had it running both on my OS X 10.4 and Windows XP in a flash. I even installed the newly released HgTortoise plugin, which works as advertised even though its user interface simply screams open-source. Now, Mercurial is nothing without a server, so I set about setting up one of those too. There's nothing like jumping in the deep end when you're a complete neophyte.

I've got one project that I'd like to share publicly and a handful of private projects that I'd like to store on the server, but work on from several places (e.g. the Mac or Windows). Now, with Mercurial, every repository contains a complete history of the project, so the designation of the server as the "main" storage is a feature of my deployment system, not a requirement imposed by Mercurial1.

Mercurial has decent instructions for setting up an http server; they provide CGI scripts for both single and multiple repositories. Once you've got that, you'll want to set up which users are allowed to push updates to your repository. Mercurial strongly recommends you use only SSL connections for push operations; you can shut off the requirement easily enough, but it's a good recommendation.

So far, no real problems have cropped up. Until you go to the web site and see the circa 1998-style horror that is the default style sheet. Running hg -v reveals that you're running version 0.9.1, released in 2005. Thanks, Debian. Way to stay on top of things.2 A quick check of Debian Backports reveals that a newer version, 1.0.1.2 is available. Grab that and install it, then enable one of the newer themes -- I chose paper, which looked nice and neat -- and refresh the page. Sweet! Click a link. Shit! Purple python error messages everywhere. Follow the stack trace to the bottom of the page and it seems to be complaining about an unset variable in a dictionary...bla, bla, bla.

Maybe there are some missing mercurial or python modules. A quick check of the recommend and suggested modules with apt-get reveals nothing significant.

Maybe the Python version is wrong. Well, look at that, Debian Etch is still using Python 2.4 by default. Python 2.5 was released in September of 2006. It's just possible that Mercurial -- especially the perhaps less-well-tested CGI script -- might be a wee bit surprised to find itself on a runtime that's almost three years out-of-date. That's a decent theory, I think. A quick apt-get install python2.5 grabs the latest version from Etch (which is probably also horribly old, but no matter) and ... has no effect. The Debian installer does not set up the newer Python as the system default; it doesn't even ask if you'd like to do this. Perhaps there's a good reason for this...3

Long story short, I couldn't get the Mercurial 1.0.1 web CGI any more stable than it was on my initial attempt and must instead assume that it's just broken in that version. I rolled back to Python 2.4 and Mercurial 0.9.1 and everything started working again, though my eyes still teared up when I had to look at the repository web site. I couldn't figure out any way of getting a newer version of Mercurial to run on Debian Etch. A pity, but there it is. I ended up making my own theme and adjusting the style sheet enough so that I could use it less than an hour after having eaten without endangering my keyboard.

I thought my Python/Debian adventures had ended until I started my server backup script, which uses rdiff-backup to synchronize several directories from my server. I use rdiff-backup 1.1.15 on my Mac and remotely control an instance on the Debian Etch server. Setting this up with compatible versions was not the easiest thing in the world and seems to have been more fragile than I thought. My Python reconfigurations had removed rdiff-backup from the server because it had the Python2.4 package as a dependency. I quickly re-installed rdiff-backup, but it was permanently offended and continued to give the same error message, which was, once again, something about some file or function or variable that it had expected to be set that was most emphatically not set and that it was going to, as a result, quit in a huff.

I know that it's my fault for having used Debian Backports instead of being happy with three-year--old software, but knowing that doesn't make me any happier to be, once again, debugging a version mismatch error in rdiff-backup. I have, in fact, decided to tempt fate and forget about that part of the backup for this week4. I'm sure my self from one week from now will bloody hate me for it, but I'm going to bed.



  1. This is perhaps the biggest difference between Perforce and Mercurial.

  2. An unfair jab, of course, because the whole point of using Debian is neither to stay on top of things nor to have things that look pretty, but to have a server that works, no matter what.

  3. That's what's known as "foreshadowing", because my decision to make Python 2.5 the default -- if only for a little while -- would prove to do horrible things to other software dependent on Python.

  4. Luckily, the Mercurial part is working as advertised. The backup for all of my projects now just executes a pull from all of those repositories, which happens in a flash. All in all, I'm quite happy with Mercurial, even though with 0.9.1, I'm still in 2005.

Vista, the Final Days

Vista under the Christmas tree

If you're planning to buy a computer this holiday season -- and you don't opt for the shiny goodness of an iMac or iBook -- then you'll probably be getting Windows Vista. Windows Vista is very shiny and pretty and probably sounds like a great alternative to its predecessor, Windows XP. However, the minor improvements to the file explorer and organization (and major ones to look-and-feel) are far outweighed by both hardware and software compatibility problems as well as a draconian approach to security. All in all, you spend far more of your precious time (and nerves) coddling the system rather than doing actual work.

Windows Vista is not a worthwhile upgrade to Windows XP for any user -- advanced or novice -- and should be avoided if at all possible. Most vendors now offer Windows XP as a drop-in replacement when buying a new computer, with an option to upgrade to Windows Vista when the first service pack is available.

What Happened?

All observations are based on a single-user trial of Windows Vista installed for 9 months; final impressions are based on the version of Vista available in the middle of November, 2007.

In February, Microsoft released Windows Vista, the long-awaited successor to Windows XP. Here at Encodo, we installed it on one developer machine1 for several reasons:

  1. To determine whether our products run on Vista and to fix any problems that cropped up.
  2. To test over a longer time period whether Vista truly offered advantages over Windows XP in terms of efficiency.
  3. Because "Aero Glass", the new user interface for Vista, looked really, really cool.

The initial -- very positive -- impressions are documented thoroughly in First Days with Microsoft Vista (parts I and II). After nearly 9 months of testing, the verdict is in:

Avoid Windows Vista until further notice.

And we're not the only ones; Vista recently made #10 in the list of Top ten terrible tech products. We, too, have capitulated and gone back to Windows XP installations for all desktop computers. Vista survives only inside a Virtual PC image, which we installed for testing purposes only.2

The breakdown

Though we don't recommend Vista for anyone, we especially don't recommend it as the primary operating system for developers3. Among the many reasons for our decisions are the following major ones:

Speed

Vista is noticeably slower than Windows XP in every way. Period. It is especially noticeable in something so banal as copying or deleting files. When you see Vista open a progress window to indicate that is "Calculating time remaining..." to delete a single 4Kb file, you figure it's a fluke; the tenth time it happens that day, it just makes you want to cry.

Security

Vista ships by default with a brand-spanking-new security system designed to make viruses, trojans and other nasties a thing of the past. It's called User Account Control (UAC) and involves you approving everything that happens on your computer. In practice, this means that every time you start an installer, you have to tell Vista that you did actually start that program and that, yes, you would like to actually run it. Get used to your screen going black and asking you that question. A lot.

Software Compatibility

Not only is the security bothersome and inefficient4, but many applications have no idea how to deal with it. One of the main advantages of Vista was that one was supposed to be able to work as a non-administrative user so that it was impossible to issue system-altering commands by mistake -- or for an application to do so maliciously. Even now, after years in beta and 9 months on the market, many applications (and most installations) don't properly support this mode of operation, sometimes installing non-functioning applications to the wrong locations or refusing to install at all. Though the latest Microsoft Office runs just fine for the average user, Microsoft's own venerated Visual Studio does not run in non-administrative mode on Vista.5

Hardware Compatibility

Though others have more thoroughly documented hardware woes, our experience was that something as simple as connecting to a network printer -- which Windows XP accomplishes in no time at all -- turned out to be impossible for Vista. The much-vaunted "Sleep" mode never worked on this relatively standard hardware (see first footnote), leading to incredibly long reboots after which Vista scolded you for not shutting down properly.

Windows XP, for all its faults, has achieved a relatively stable release after many years in the field and is a much better choice for any user. Users that have their hearts set on the upgraded Vista look-and-feel, "Aero Glass", should wait for until the first service pack has been out for a few months -- which should be around the middle of next year. Developers that have their hearts set on Vista should forget it: Vista is not ready for use as a developer operating system and offers no advantages in that regard over Windows XP.



  1. Just to set the record straight, Vista was installed on a newly purchased machine in Februrary: a dual-core 2GHz processor with a modern graphics card (which Vista itself indicates is adequate for it) as well as 2 GB of RAM and a 7200RPM hard drive -- basically upper-middle of the hardware requirements for Vista.

  2. And which weighs in at a frightening 10.5GB with no applications installed.

  3. As already indicated, we do agree that it's essential to test software on all platforms and have Windows Vista images with which to do testing -- we just don't use it for development.

  4. Not only your efficiency in that you're constantly "approving" commands you just issued, but that there are suspicions that Vista's new security concept accounts for many of the performance problems mentioned in the previous point.

  5. Visual Studio is a development tool for building Windows applications and web sites in many different languages. Postgresql, a popular open-source database, was not even capable of installing on Vista until the most recent beta came out a few weeks ago. Even tools like spec# -- again, produced by Microsoft itself -- recommend that users "try disabling the user account controls" (effectively removing all protection) if they can't install.