Paying down technical debt won’t necessarily increase your velocity

(This piece is a work in progress, a placeholder for developing thoughts on technical debt and measuring the performance of software development teams. I will update it as my opinions change.)

An oft-cited benefit of paying down technical debt is that it will allow you to move faster. This is typically characterized in terms of increased velocity.

But if you are measuring velocity by factoring in “accidental complexity”, then when you remove the source of that accidental complexity, your velocity will not actually appear to improve. This point is well put by Moneyball for software teams: an imperfect heuristic for quantifying dev performance. This runs contrary to what we’d generally expect from a measure of developer velocity, and also contrary to the business case that is usually made for paying down technical debt: to simplify, reduced debt = increased velocity.

The aforementioned article presents a reasonable way out of the impasse of a) needing to factor tech debt into your estimates, b) wanting tech debt reductions to be visible as increased velocity i.e. more “story points” per cycle. (Whether it is necessary or desirable to adopt this approach is debatable, and personally I’d still bias towards avoiding measurement of velocity at all, and having developers do PERT estimates only as necessary. This seems to be less common outside small, high trust teams.)

But even if you are measuring velocity this way, it is entirely possible that you could pay down all the tech debt and still not have your velocity  (as a measurement of essential complexity) increase:

  • new sources of accidental complexity can emerge that are not at all under your control, whether from vendors, dependencies that are suddenly EOLd or have hard to patch security flaws, and so on
  • eliminating a given set of sources of accidental complexity may enable your team to take on other types of projects that are subject to additional kinds of accidental complexity. These new tasks or projects may have higher business value, but that won’t be reflected on your velocity (or probably reflected anywhere because We can’t measure developer productivity). This is reminiscent of the way in which improving tail latency can actually cause median latency to increase: previously, requests from slow clients would time out or never be initiated in the first place, but after improving performance for those clients, their requests will now succeed, albeit at latencies greater than your current median.
  • even if your velocity does go up, will the signal be strong enough to be distinguishable from statistical noise (maybe one of your senior developers finally got their kid into an after school program and now they are back to “full performance” level)? do you have tools sophisticated enough to discern this?

Returning to the moneyball comparison from above: sport performance is generally easily and unambiguously measurable (did the ball go in the net?), and what it means to do well can be reduced to “win competitions”. Software development is inherently harder to measure, and has many axes along which something can be called “a success”. Moreover, whether a software project turns out to have been successful may not be discernible for months or years down the road, for reasons business or market reasons that have nothing to do with the development project “in itself”. For these reasons, it is at best a waste of time, if not actively harmful, to foist such methods into software management.

That said, my thoughts on this topic are always challenged by the ideas of Hubbard (e.g. How to Measure Anything) whose main argument boils down to, if it matters you can notice it, and if you can notice it, you can (at least sort of) measure it.

Configuring SoftEther VPN on Ubuntu with a firewall

DigitalOcean has a good article on setting SoftEther VPN on one of their droplets.

The instructions worked for me without just fine, with a couple exceptions:

1. When you attempt to use the command line vpncmd tool to set up the server, you may have to specify localhost:5555 rather than using the defaults.

2. You may have to create a group for the test user before creating them and assigning them to a test group. This can be done with the command `GroupCreate test`.

3. If you are using a firewall, you will need to open up the ports used by SoftEther. To figure out what ports it’s using, do `sudo netstat -atulpn  | grep vpnserver`. By default, SoftEther will listen on TCP ports 443, 992, and 5555. If you’re using L2TP/IPsec, make sure UDP ports 500 and 4500 are open as well. If you’re using ufw for your firewall, you can see which ports are open/blocked with `sudo ufw status verbose`. To get an idea of which ports each VPN protocol you’re using requires, check out the SoftEther specifications.

Throttle outbound Spotify traffic

When working on a shared network with limited bandwidth, it’s sometimes nice to be able to keep listening to Spotify without ruining your co-workers’ Internet connections.

Spotify is P2P (the desktop app is, anyway), so you both receive data from other Spotify users and transmit it to them. Blocking the outgoing traffic entirely would be un-neighbourly (probably also a violation of TOS), and might even prevent streaming from working altogether.

Fortunately, as I just discovered, ipfw allows you to add a pipe to a range of ports, all of which you can then throttle to a certain data transfer rate. It’s a shotgun approach, and will slow down any other services that are trying to send data over those ports as well, but since Spotify seems to stick to the range 10000 to 80000, and I rarely if ever run anything of consequence on those ports, this approach works for me.

sudo ipfw add pipe 1 ip from any to any out dst-port 10000-80000
sudo ipfw pipe 1 config bw 8KBytes/s

Cisco IPsec VPN on OSX Snow Leopard

For anyone else having issues setting up a VPN connection on Snow Leopard: if you are getting an “incorrect shared secret” error, quit wasting your time, and download Shimo, a VPN client for OSX. Haven’t paid for it yet, but will likely end up doing so, since it’s the only one of a half-dozen or so that I tried that not only worked, but wasn’t horribly designed.

I suspect my issue may have been related to a the VPN shared secret being too long, though that is mostly speculation. There was mention in a forum somewhere that OSX Lion silently truncates shared secret keys longer than 63 characters. Yay.

Errors in scripts loaded with jQuery $.getScript

Spent some time the other day trying to figure out why I wasn’t seeing JavaScript errors in certain files where I expected to, which was problematic both for development and also for the monitoring of client-side errors we’re doing in the application (assigning a handler to window.onerror that makes an ajax call with error information to an endpoint for this purpose).

Basically, if there are errors in any of the scripts you are loading with $.getScript, or the full $.ajax call for which $.getScript is shorthand, these will be caught by jQuery and will fail to show up in the console or be passed to any window.onerror handler you’ve defined. jQuery provides a global ajaxHandler you can attach to, for example, document, which you can then use to do whatever client-side error handling/logging you want to do.

The downside of this approach is (as far as I can tell) that you don’t get quite as much error information (specifically, the line number of the error) from jQuery as you do from other ways of loading scripts with js, because the error gets caught during a call to eval (or more specifically, jQuery’s globalEval method).

Objects in closure compiler record types

I’ve been diving much deeper into closure compiler’s type checking abilities lately, and while it is a great tool, the documentation could stand to be improved somewhat.

Sometimes when creating a record type, you will want to specify not just that some property is an array, or an object, but what sort of things are in the array, or what sort of properties are on the object.

A simple record type for an object with a string and number property:


/**
 * @typedef {{name: string, index: number}}
 */

A record type with an array of strings:


/**
 * @typedef {{networks: Array.<string>}}
 */

A record type, one of whose properties is an object:


/**
 * @typedef {{authorized:boolean, network_name: string,
 * details: Object.<{id: number, promotion_id: number}>, type: string}}
 */

 

This last one is the syntax I was having trouble coming up with, and it is not immediately obvious from the official documentation here. Hope this might help someone else who’s looking for the same thing.

Also, an example of objects inside an array (using this for a Vimeo video object):


/**
 * @typedef {{urls: {url: Array.<Object.<{type: string, _content: string}>>},
 * thumbnails: {thumbnail: Array.<Object.<{_content: string, width: string, height: string}>>} }}
 * }}
 */

 

You can also refer to other record types inside a record type:


/**
 * @typedef {{name: string, auth_provider: string, id: number}}
 */
MyRecordTypes.authorization;

/**
 * @typedef {{authorizations: Array.<MyRecordTypes.authorization> }}
 */
MyRecordTypes.authorizations;