Friday, January 6, 2012

LaTeX-like Project Management

With project management role comes the need for a project scheduling tool.  The obvious choice would be Microsoft Project, but I thought I'd rather look for some free programs first.

I tried two that are very similar to MS-Project: OpenProj and GanttProject.  Both are open-source software.  Both have a GUI that's centered around a Gantt chart, and allow you to manipulate activities as in MS-Project.  But both also lack a very basic feature: resource leveling.  They don't have any intelligence whatsoever, and the user must check by himself that resources are not overloaded, and correct issues by adding spurious precedence or 'starts after' constraints.  I therefore don't really see how to actually use these tools for any serious work...

But then I discovered The TaskJuggler.  It's a command-line program written in Ruby that will read a text file and output HTML pages with various reports like a gantt chart or a resource allocation graph.  Installing the tool is as easy (once/if you have Ruby) as typing 'gem install taskjuggler'.

Using a text file as the input for all your project data has important benefits. The first one is that you can make use of your Source Management system to allow multiple people to collaborate on the file, while with a binary file (or even a text file that's completely rewritten by your tool when you save it) can't effectively permit sharing.  Another is that changes are much easier to track, as a change to a single part of the project will not touch the rest of the file.  So it's possible to revert individual changes...

This very much reminds me the difference between a word processing program, and LaTeX...


Here are some screenshots:

The Gantt chart
The resource allocation chart

And here is an example of a trivial project file. You'll find a much more complete example of a project file in the tutorial that is provided with the project.

project tiny "Example TJ3 Project"  2012-01-09 +12m {
  timezone "Europe/Paris"
}

resource Xavier "Xavier Nodet" {}

resource dev "Developers" {
  managers Xavier
  resource dev1 "Dev1" {}
  resource dev2 "Dev2" {}
}

task Tiny "Our Tiny Project" {
  responsible Xavier

  task t1 "Task 1" {
    
    task sub1 "Sub-task 1.1" {
      effort 30d
      allocate dev1
    }
    task sub2 "Sub-task 1.2" {
      effort 10d
      allocate dev1
    }
  }

  task t2 "Task 2" {
    effort 20d
    allocate dev2
    depends !t1.sub1
  }
  
  task deliveries "Milestones" {

    task start "Project start" {
      start ${projectstart}
    }
    
    task ega "EGA" {
      start 2012-11-01
      depends !!t1, !!t2
    }
  }
}

# Skipping the report generation part...


I only scratched the surface so far, but this seems very promising to me...

Saturday, December 3, 2011

Results are not enough

Of course, results are a very important part of a manager's evaluation.  But that's not the end of the story...

Consider the manager of a great team of engineers. One can expect that the team will deliver good results. And this may be the case even if the manager is not so great... Conversely, let's consider the manager of a bad team: it may be the case that whatever the manager will do, the team will not deliver. In the long run, a great manager will manage to improve the results he gets from the team, but it may take quite some time. Evaluation of the managers only from the team's results is thus not enough, and may even be completely counter-productive...

So what's a good manager to do?

Of course, a manager must make plans. But that's not enough: as Tom Foster puts it, managers should build robust plans that take external factors not under their control into account. And they should be judged on the fact that they not only planned for success, but made it more likely to happen. Luck, or the lack of it, should not play a role. Kids learning chess often setup traps, only to discover that their opponent did you fall into it. They plan for the best, instead of planning for the worst. Explicitly supposing that things can go wrong is a good way to not depend on random external factors, and improve the likelihood of getting good results.

Getting results is not the mark of a good manager.  Consistently getting results is.

Thursday, March 31, 2011

Partial working copies with Subversion

When working on large and/or multi-platform projects, it often happens that your Subversion working copy contains items that are useless: ports for other platforms, module that you don't need for your work, etc.

Recent Subversion versions allow to specify the maximum depth to use when updating a given target.  You can e.g. specify that you only want the files in a given directory, but not its sub-directories.  Or you want a given directory to be empty, or removed from the working copy entirely.

Let's consider you've checked out an entire working copy, with the following structure:

root
  dir1
    file1.1
    file1.2
    subdir1.1
    subdir1.2
    subdir1.3
    subdir1.4
  dir2
    file2.1
    subdir2.1
      file2.1.1
  dir3
    file3.1
    dir3.1

If you're not interested anymore in subdir2.1 and its content, you can issue the following commands:
> cd root/dir2
> svn update --set-depth exclude subdir2.1
Now dir2 only has file2.1, and an 'svn update' command that does not specify a '--set-depth' option will not bring back subdir2.1 or any of its content.

If you're only interested in the files inside dir1, but not the subdir1.*, you could do:
> cd root
> svn update --set-depth files dir1
This tells SVN to only get the files in dir1, but not its sub-directories

Similarly, you can use '--set-depth empty' to get an empty directory (this can be useful to ask for one of its sub-directories), '--set-depth immediates' to get the immediate sub-directories, and use '--set-depth infinity' to restore the default and get everything in a given directory.

On the reverse, if you have to checkout a working copy but you know you only need some parts of it, you can use the '--depth' option (not '--set-depth', go figure...) of the checkout command.  Then you create your paths in your working copy, step by step:
> svn checkout --depth empty [repository URL] root
> svn update --set-depth empty root/dir1
> svn update --set-depth infinity root/dir1/subdir1.2
> svn update --set-depth infinity root/dir3

Hope this helps!

Friday, March 18, 2011

New features in C++0x allow really impressive stuff...

Slack has a very interesting blog entry about Automatic memoization in C++0x...

Memoization is a pretty well-known optimization technique which consists in “remembering” (i.e.: caching) the results of previous calls to a function, so that repeated calls with the same parameters are resolved without repeating the original computation.

He starts with the short and simple Python function that returns a memoized function from any regular one, and then shows how C++0x features can be used to do the same!

Very impressive...

Wednesday, January 5, 2011

Opening the Fisheye page for your current file from your IDE

Our revision control system is SVN, with a Fisheye server providing easy browsing of the repository. I use Fisheye very much and was tired of having to dig through the deep hierarchies of our project to display the information about a file. I therefore wrote a very little script that I can launch from Visual Studio to open the Fisheye page for the current file, with the correct branch/trunk information.

This script assumes that the directory structure on the disk mimicks the directory structure on the SVN repository, with a prefix (here: f:\work): f:\work\theProject\branches\aBranch\foo.txt corresponds to file foo.txt in the root directory of project theProject on branch aBranch. This will open page https://your-fisheye-server/browse/theProject/branches/aBranch/foo.txt. Of course, you will probably want to edit the location of the server...

rem
rem Launch a browser directed to the Fisheye page for
rem a particular file.
rem
rem mailto:xavier.nodet@gmail.com
rem

@echo off

rem Assumption: first argument is a absolute path to the file
rem on the disk for which we would like to display the
rem corresponding Fisheye page.
set FILE=%1

rem Remove the "f:\work" prefix from the file path
set FILE=%FILE:f:\work=%
rem @echo %FILE%

rem Replace all the backslashes with forward slashes
set FILE=%FILE:\=/%
rem @echo %FILE%

start "c:\Program Files\Mozilla Firefox 4\firefox.exe" https://your-fisheye-server/browse%FILE%

You will have to launch this script from your IDE with as single argument the full path to the file you want to see in Fisheye. Here is how to do this from Visual Studio. Open the 'Tools' -> 'External tools...' menu, click the 'Add' button, and fill the information needed, using $(ItemPath) for the argument:
You now have a 'Current file on Fisheye' entry on your Tools menu, that you simply have to choose to launch Firefox...

I hope you'll find this useful...

Wednesday, December 15, 2010

Factoring exception handling code in C++

When writing exception handlers (e.g. around calls to database or network related code), you often end up with the same set of catch clauses at each call site. Suppose you end up with the following code:

try {
  //...
} catch (std::runtime_error& e) {
  std::cout << "Runtime error: " << e.what() << std::endl;
} catch (...) {
  std::cout << "Unknown exception!" << std::endl;
}

You have this piece of code repeated dozens of times, which is not really a Good Thing(tm)... A little C++ trick can help you avoid those repetitions: (1) factor the common code into a single function or method that rethrows the active exception and catches it immediately, and (2) catch all exceptions at the call sites and call this handler!

#include <iostream>
#include <stdexcept>

void exception_handler() {
  try {
    throw; //rethrow the current exception
  } catch (std::runtime_error& e) {
    std::cout << "Runtime error: " << e.what() << std::endl;
  } catch (...) {
    std::cout << "Unknown exception!" << std::endl;
  }
}

int main() {
  try {
    throw std::runtime_error("unable to comply...");
  } catch (...) { exception_handler(); }

  try {
    throw 1;
  } catch (...) { exception_handler(); }

  return 0;
}

You must be careful to not call exception_handler() outside a catch clause: the trow statement calls terminate() if there is no active exception to rethrow!

You may wonder if one could use the std::uncaught_exception() function to protect against this.

void exception_handler() {
  try {
    if (!std::uncaught_exception()) {
      return; // Nothing to rethrow...
    }
    throw; //rethrow the current exception
  } ...
}

But this does not work: uncaught_exception() "returns true after completing evaluation of the object to be thrown until completing the initialization of the exception-declaration in the matching handler", as the standard says. And when exception_handler is entered, this is generally from the catch clause, and the exception has thus been caught... Your handler would now simply swallow all the exceptions!...

Actually, uncaught_exception is pretty much useless...

Thursday, December 9, 2010

Using a VPN when connecting to open Wifi networks

You may have heard about Firesheep, a Firefox extension that allows to hijack other people's online accounts.  Basically, when you're using an open Wifi hotspot to check your Facebook, Twitter or Gmail[1] account, any other user connected to the same hotspot is able to sniff enough information to impersonate you and connect to your account.  Do you really want to risk having your online accounts used by anybody?

The issue is that although those sites use HTTPS (i.e. secured HTTP) during the login phase, they do not use it for the remainder of the session. And because the Wifi network is not encrypted either, your traffic can be intercepted and read by anyone connected to the same hotspot. This kind of vulnerability, known as session hijacking, exists since a long time, but it is only recently, with the advent of Firesheep, that it received wide coverage.

What can you do against it?

The simplest is that you stop using open Wifi networks... Pretty useful, no? Ok. Next one...

In order to prevent session hijacking, you can configure your accounts to always use HTTPS instead of using it only for the login phase.  This will encrypt the entire session, preventing attackers to read the cookie that identifies you against the site you're connecting to.  This is an option that's available for Gmail, but is not for Facebook or Twitter.

If you still want to use those sites when on the road, you should use a Virtual Private Network that will encrypt your entire traffic.  VPNs are usually set up to allow employees to connect to corporate servers from outside the company's network.  Mobile users can thus check their mails, access source code repositories, bug database, etc.

VPNs can also be used on your home network to gain access to your music or video library, or any other service, in a secure manner.  But the trick here will be to allow users connected through the VPN to access the Internet at large, not only your local network.  The link from your mobile device to your home server will be protected by the VPN, and the connexion from your home server to the Internet will be assumed to be safe: if you're worried about people who could wiretap your ADSL line, then you should probably not connect any Internet site at all...


Setting up a VPN server

There are tons of tutorials out there about how to configure a PC with any reasonably modern OS to act as a VPN server.  Here are some information for Windows 7 or Vista (Start > Control Panel > Network and Internet > Network and Sharing Center > Manage network connections, then hit the Alt key, choose File > New Incoming Connection), Mac OS X, or Linux.

Note that, on Windows, you are not offered the possibility to choose your type of VPN service: it's PPTP, and no other.  You need to know this when you configure your client...  And if you don't have a password on your main account, you must create a new user with a strong password.

Note also that if you have a router, you'll need to configure it to allow the traffic to come to your VPN server. The tutorial for Windows above has some information about this subject.

The only issue I had while setting up my Windows 7 box was that you really want to configure it so that IP addresses for incoming connexions use a specify range, with at least two possible addresses...


Setting up your client

Setting up a server is a necessary step, but you'll also want to configure your mobile device (be it a laptop or a smartphone) to connect to your VPN server.  For a Windows client, the 'Connect to a Workplace' wizard will do: Click the Start button > Control Panel > Network and Internet > Network and Sharing Center > Set up a connection or network > Connect to a workplace.

On the iPhone, you have to navigate to Settings > General > Network.  Don't worry, it'll be much easier to just turn it on once it's configured... On the lower part of the page, you'll find a button that will allow you to configure a VPN connexion.  Here is how I did it:




Use the PPTP protocol, with your own (external) IP address, the name of the account you chose when configuring the server, and its password.  Make sure 'Send All Traffic' is ON, so that you are really protected!

You can check your setup is correct by connecting with your iPhone to a service that shows your IP address (don't try this from home, it would not prove anything!  Or disconnect the Wifi on your iPhone!): it should tell you that your address is the one of your ADSL connection...

You can now enjoy free Wifi secure connexions!...