Friday, September 17, 2010

Color-coding your scrum board

At my company we've been using Scrum for a few years now. And even though I'm quite sure we're not living up to Schwaber's and Sutherland's standards, we seem to have found a way to make the process work for us across many projects.

Like many companies we started out with a simple physical scrum wall:



Later we started putting the burndowns on our intranet, so that everyone could see them. And we started using colored post-its for indicating different types of work.

In our case we use yellow for development tasks, pink for non-development tasks (i.e. testing and documentation) and orange for defects. There's no specific reason for using these colors. They just happened to be the post-it colors we had available when we started using colors. Since then my mind got used to them and I haven't yet heard a reason to switch. Although some of our tech writers are frowning at being "the guys who do the pink work".



As we've been growing our use of Scrum, we've also been using more and more tools. First was the online burndown charts. And then we started using a virtual scrum wall, so that we could share the same wall between distributed teams. It was a great boost for our distributed development, although most team members still sometimes long back for the days when they had a physical wall. Nothing beats the satisfaction you get from moving a physical post-it across 5 inches of whiteboard space.

We've been using Scrum for Team System for a while now to keep track of our stories and tasks. And I thought we were quite happy with it. Sure, we lost the ability to color code our task types. But the task board we used has nice colors too, although it uses them to code the columns. So "done" is green, etc.

But recently I noticed that it was difficult to quickly understand the status of a sprint from simply looking at their board:



So is the status of this project? It's hard to say without looking at the details of each task. It could be that everything is going fine, it could be that things are horribly wrong. There is no way to tell which one it is, unless you get closer to the board and read.

So over the weekend I re-encoded one of our boards with the colors we used back in our "physical days". Now look at the same board where we've used colored post-its:


Yellow = development tasks, pink = non-development tasks, orange = bugs

When we applied this color coding to our existing scrum board, I was shocked at the amount of insight it could add.

So I wrote a quick tool to generate these color-coded task boards for us. The process is purely automatic: read the data from team system, apply a few simple rules (e.g. anything with the word "bug", "defect" or "issue" in it, must be a defect) and output a HTML version of the data. But even with such a simple system we've been able to more easily get a feeling of where all projects stand by simply glancing at the board.

So what is your experience with scrum walls? Do you have any tricks you use to help visualize the data?

Sunday, June 13, 2010

Block font

Many people seem to doodle while they're in meetings. Doodling is a nice way to distract yourself with something, while ensuring that you can still follow what is being presented. I guess that's why those meeting centers hand out those incredibly thin notebooks.

My doodles typically consist of writing down words that have to do with what is being presented. Now before you say "that is called: taking notes" - I take my notes in elaborate fonts.

One of the fonts I came up with this time is a 'block font'. And since the meeting was rather long, I decided to create all letters.



I should apologize to the speakers, because I wasn't paying too much attention near the end of their presentation. Coming up with all letters in a single style is hard work.

I'm quite happy with most of the letters, but there's some that I really can't get to look decent. The I and the J are especially painful. Is there somebody with real designer skills that can show me how to do those character properly in this style?

Saturday, April 10, 2010

What I use my iPad for

It's been a week since Apple released their latest revolution: the iPad. By now every major site has reviewed it (most positive), but unsure what the device is meant for. I got my iPad on the launch date, so have had a week to play around with it and show it off.

In the past week the number one question from everyone has been: what do you use your iPad for? As with any new device type, the most common use-cases are not immediately clear. I don't think Apple ever thought the appstore would be the greatest attractor of new users to the iPhone. The iPad is no different: it's a completely new beast and it'll take some time for people to figure out what to do with it.

In the past week, I've used my iPad primarily for these three things:


  • Games

  • Surfing

  • Email


In addition I've actually also spent quite some time showing the iPad off to various people. But that is of course a use-case that will be less relevant over time. Until that happens though, I'd advice every iPad owner to buy the apps in the iWorks suite. They show off the serious side of the iPad best for the moment.

Games


Not surprisingly the iPad makes a great device for games. For me it has been mostly casual games so far, but I might try out some more serious games (red alert, need for speed) soon.


My favorite games so far have been Harbor Master, Rush Hour and Labyrinth. All three seem to have been made exactly for this type of device, but with Rush Hour the playing pieces feel a bit too big. Maybe they're ahead of the pack and already made their game for the smaller iPad that is rumored to come next year.

Surfing


The iPad comes with Apple's latest Safari release, which is perfect for surfing the web. It doesn't allow tabbed browsing, which I think is a miss. But you can keep multiple pages open and switching between them is relatively elegant. When you shut down/restart Safari the pages take a bit of time to reload, which I think should not be needed on a device with 16GB or more of memory.


The iPad will not soon replace your laptop as your main device for surfing. Although it is totally usable, it is just not made for using at your desk. Maybe that changes when I get a docking station later this month, but for the moment the laptop wins the battle for my desk.

Where the iPad really shines is on the couch. I had some discussion with friend who'd say they use their laptop on the couch too. Maybe they do, but it's not the same thing. Whether I'm talking to visiting friends or are watching TV, the iPad is always within immediate reach. You can just leave it turned on all the time, it turns off the screen automatically and never gets hot nor does it ever make any noise. A laptop would simply not fit comfortably in the same seat as me or be waiting on the armrest of my chair just in case I need to look something up. Hmm... I think I've seen that actor before, let's look on IMDB quickly - no need to wait for a commercial break.

Email


My number three usage has been reading and responding to email. The on-screen keyboard is quite good, especially in landscape mode. Although as Apple claimed the keys are almost full-size, it does take some time to adapt to them. Without tactile feedback it is very easy to touch a few keys without noticing. So what I see most people do very quickly is arch their fingers a bit more to avoid those accidental key presses.


Reading and writing email works like a charm. Switching between multiple mailboxes takes a bit of tapping, but nothing too bad. The iPad really came to my rescue this week when my laptop started acting up. Instead of having to fall back to my iPhone or having to steal somebody else laptop, I could switch to the iPad and write reasonably lengthy responses.

So those are my top 3 use cases of the past week: gaming, surfing and mailing. There were also some things that I didn't use my iPad for, even though there is great software available and people had predicted those use-cases.

Not for reading


Apple tries to position the iPad as the ultimate eBook reader. I was already skeptical about this before I got my iPad, simply because I've grown to love my Sony Reader over the past half year. The one thing I love about the Sony is the eInk screen.


Although the iPad's screen is gorgeous, for readability it doesn't compare to eInk. Try reading intensely for more than a few minutes and you'll notice the difference. The iPad might look fresher and cleaner, but reading an eInk screen is simply less stressful for the eyes than reading on the iPad.

Another thing holding the iPad back as an eBook reader is it's size and weight. I often read my Sony in bed, lying on my back holding it above me. I challenge anyone to do that with the iPad for more than a few minutes. It's simply too heavy and too clunky.

That said: the iBook software on the iPad looks great. Apple was kind enough to include an illustrated book of Winnie the Pooh, which perfectly shows the type of material the iPad was made for: colorful books with large fonts and pictures. I can easily see how the iPad might be a kids favorite reading device, even though I will stick to my Sony Reader for lengthy reading sessions.

Where the iPad had proven useful is for reading articles. Whenever I want to read an article from a website, I've always had to convert it for reading on my Sony Reader. Although it's not a huge amount of work, I love using Instapaper for the iPad. With a single click on my laptop, I transfer any article from the web over to Instapaper on the iPad - where it is automatically reformatted into easy reading format. I really wish a similar program/feature existed for my Sony Reader though, because I do miss the eInk screen when reading on my iPad.

Not for video


I've also not been watching much video on my iPad. I think I should say "not yet" here, because there seems to be no reason not to use the iPad for watching youtube or video's bought from iTunes.

Since the iPad doesn't support flash, many video sites may not work. But hopefully this is just a temporary nuisance until all sites do what youtube did: re-encode their video's to work in H.264 or whatever the format is.

When I did watch video, it looked great on the screen. At time the wireless network at some places couldn't keep up with the video, which is an unfortunate consequence of the HD resolution that the iPad supports.

Thursday, March 4, 2010

Who estimates the stories in Scrum?

A few years ago we did a project with an outsourcing company. We gave them the current source code of a product, we gave them the backlog for the new version and we asked them to start implementing that backlog top to bottom.

Since the external team was new to both the product and the Scrum process, I decided to also do some rough estimates myself. Given my experience with both the product and the process, those estimates could serve as a rough baseline.

The progress


The remote team worked on the project for 5 sprints. During that time we saw an interesting trend.

As the team finished each sprint, some of their work was accepted and other work wasn't. Work was sometimes rejected because of its quality, but often it was rejected because it simply didn't do everything that the product owner had expected. In those cases the team put another story on the backlog for the missing parts and provided a fresh estimate for the "new" work. They also re-estimated the existing stories based on the newly gained insight of the previous sprint.

So over time the team's backlog grew in size. That is not uncommon, but look at the burnup chart:


burnup 1: based on team estimates

Based on this burnup chart, the project is in serious trouble. The team seems to be doing great, delivering more work with every sprint. But even though more and more work is delivered every sprint, the team is not getting closer to the goal.

The fifth sprint here is really disastrous: the team stopped increasing delivering more work. Did they burn out? How are we ever going to get the project out the door?

For comparison let's look at the data based on my estimates - that the team never saw and didn't commit to:


burnup 2: based on estimates by an external stakeholder

On my end, for every new story the team added to the backlog I checked if I had considered that part in my original estimate. If so, I split the story points I had originally estimated over the two stories. If the new story was functionality I had not expected in my estimate, I would provide a fresh estimate - also increasing the backlog in my part. As you can see that happened only once, but the increase was substantial (about 10%).

In my chart too the team seems to be burning out a bit in sprint 5. But it doesn't seem half as bad as in the firsts burnup chart. They are still getting closer to the goal.

And in both charts the team seems to be about 20% done with the total amount of work.

Analysis


So what's the difference between the two charts and estimators. From my perspective there are two major differences: the stability of the progress and who made the estimates.

How stable is the progress


The charts below show the velocity per sprint as derived from the burndown charts above:


velocity 1: velocity from sprint to sprint in burndown 1

velocity 2: velocity from sprint to sprint in burndown 2

The burnups don't really have a lot of data. But if you look at the first velocity chart, you can see that sprints 1 to 4 show a somewhat exponential growth in velocity (1.3x, 1.4x, 1.8x).

The scope/goal line in the corresponding burnup chart (burnup 1) shows a constant growth, mostly because I don't have the exact data anymore.

So at some point the two lines in burnup 1 are going to intersect, but it is pretty difficult to determine where they'll intersect with the naked eye.

The second burnup doesn't have this growth in velocity and the scope increase is about 10% over 5 sprints.

It is a lot easier to see where this project is going to end. And isn't scrum all about transparency and easy to use charts and data?

Who provides the estimates?


The second question I posed above is who provides these estimates. With the whole hype about lean development I learned that one way to optimize output is to eliminate waste. Anything that isn't delivered to the customer is waste and should be eliminated.

Who needs these estimates? The customer? I don't think my customer cares about estimates. He cares about getting as much software as possible for his/her money. In fact, while the team was providing these estimates they could also have been building more software.

So is it the team that needs these estimates then? After all without those estimates, how do they know what they can commit to? Well... the team does need to know how big a story is before they can commit to it. But they only need to know that at the start of each sprint. And only for the stories that they think they might do in that sprint. So although the team needs to know the size of each story it commits to, it doesn't need to know the size of all stories at the start of the project. Nor do they need to re-estimate the stories (another form of waste).

So the only person that actually needs those estimates, is the guy drawing the charts. In this case that was me, an external stakeholder who is not a part of the team. In many cases it will be the scrum master, who needs those estimates to give his stakeholders some view of the progress towards the overall goal. In other cases it will be the product owner, since he is most interested in seeing his return on investment.

Conclusion


My suggestion: let the guy who needs them come up with the numbers. And if you don't feel comfortable, do a reasonable guess. And if you don't even feel comfortable guessing, set all stories to the same size. In the end it doesn't really matter too much and you'll allow the team to focus on what really matters: building working software.

Tuesday, August 25, 2009

JavaScript Particle Systems part 2: adding some life

The frame below shows a demonstration of the particle system we are about to build. Move your mouse through it to have some fun. If it doesn't work for you, click this link to open the same demo in a popup window.

Introduction

A few months ago I wrote an article that demonstrated how to create a very simple particle system in less than 50 lines of JavaScript code. That article has slowly been finding its way into the Google search indexes and by now it's becoming obvious that I'm not the only one who thinks particle systems deserve more attention and JavaScript is the perfect language to experiment with them. Back then I was quite pleased with being able to implement a fully functional particle system in less than 50 lines of (quite readable) JavaScript code. But I think we can agree that the particle system was not very exciting. Although the system contained all three required ingredients, the end result was a bit "static". From games and other places where particle systems are used, we are used to them being a lot more "alive". So that's the goal for this article: to add some life to our previous particle system, while still keeping it small. In this case I've increased my self-imposed limit to a 100 lines. A generous doubling from our previous system, so we'd better have some real interesting "life" to show for the increase. We'll add three behaviors to the particle sytem.
  • Gravity
  • Grow then shrink
  • Mouse control
Lot's of work to do, so let's get started with gravity straight away.

Gravity

In our first particle system, the particles would fall at a constant speed. The fall speed of each particle was determined at random when creating the particle.
      dy: Math.random() + 0.5
And then we just updated the y position every time:
      particles[i].y = particles[i].y + particles[i].dy;
I always find it very interesting to add some gravity-like behavior to systems I create. So how about making the particles fall at an increasing speed?
      p = particles[i];
      p.y += p.dy;
      p.dy += 0.1; // simulate gravity
So we increase the particle's y velocity every time we update its position. I realize that to make this work like real gravity I'd have to know the particle's mass and do a multiplication. But for this simple example just increasing the dy is already convincing enough. Since the particle will now drop at an ever increasing speed, it is nice to make it go up a bit at first:
      dy: -1 + Math.random()
So the particles will first move up a bit and then start falling down at an increasing speed. If all goes well, they'll move in a nice arc-like pattern. Again there is no real math or physics reason for these values, these just looked best to me. If you feel the need to make the system more physically correct, just download the source and go ahead. Let me know if you find an interesting combination.

Grow then shrink

Another thing I wasn't too happy with in the original particle system is the static size that the particles have. Each particle is created to be 3px in width and height. By the way: there was a bug in the original code such that the sizing didn't work in Internet Explorer. As my team often tells me: "you need to test more with IE Frank!" My bad. Let's hope I got it right this time. I changed the code to shrink the particle a bit in each update:
      p.s -= 0.1
This of course requires the particle to have a size when we create it:
      p.s: 3.0
With this setup the particle will slowly shrink from 3 pixels width and height to 0. Once the size of the particle reaches 0, we can kill it:
      if (p.ttl-- > 0 && p.s > 0) {
         updateParticleElement(p);
This leads to a working particle system, but somehow it's still a bit boring when all the particles start and shrink at the same speed. So let's make the size change random and instead of just shrinking, let's make the particles grow at first and then shrink until they die out. For the initialization the change is simple:
      p.ds: Math.random()+0.1,
This gives the particle a growth speed that is guaranteed to be positive. So instead of adding 0.1 each time we update the particle, we add the ds:
      p.s += p.ds;
If we leave this unchanged, the particle will grow forever - or at least until it's time to live runs out. To ensure the particle also starts shrinking at some point we give it a maximum size:
      if (p.s > 5) p.ds *= -1;
When the particle reaches its maximum size, we just flip the sign of the growth speed - so it becomes a shrink speed. After this the particle will keep shrinking until it reaches size zero at which point it dies out. By now I'm not really sure if the ttl property is still needed, as the particles already have an implicit time to live and get their randomness from the growth/shrink speed. But I left the ttl in as a safeguard against my own mistakes when manipulating the size. I wouldn't want to end up with an infinitely living and growing particle by mistake. If you would reconstruct the particle system at this point it is already quite lively and dynamic. Particles seem to be attracted by the bottom of your screen and particles seem to have a real life cycle, where they grow and shrink to die out. But this particle system is still a spectator sport. Let's see what happens when we give the user some control over what happens on screen.

Mouse control

As our last feature we want to give the user some control over the behavior of the particles. Specifically I want to accomplish two things:
  1. particles originate close to the position of the mouse cursor
  2. particles move roughly in the direction that the mouse is moving
To accomplish this we need to add four variables to the system.
      mx and my - the position of the mouse
      dmx and dmy - the direction and speed of the mouse's movement
These variables are global to the script, so they are not stored for each particle. These variables are updated whenever the user moves the mouse, by hooking the onmousemove event of the window. The mousemove handler is a bit tricky to read at first. But since the behavior of the particles only depends on the four variables mentioned here, I will not explain the mousemove handler of the code further. Now how do we use these variables when creating and updating a particle? The variables are actually only used when we create a new particle:
      x: mx,
      y: my,
      dx: dmx + Math.random(),
      dy: dmy + Math.random() - 1,
As you can see the code remain quite similar to how it was. We've just substituted our new "mouse dependent" variables for the previous hard coded and random values. We still make the dx and dy a bit random, since otherwise all particles would go in the same direction. The updating of the particles doesn't have to change for this behavior. The mouse just allows the user to control the position and the direction of the initial movement. After its creation each particle behaves as the rules of our system dictate, which ensures the particles still behave similar even with al their differing properties.

Summary

So in the first article we created a simple particle system in less then 50 lines of JavaScript code. The system was fully functional, but felt a bit static after you'd been looking at it for a while. In this second article we've added life to our particle system by adding by:
  1. making the environment a bit more "physical" with our simple gravity emulation
  2. making the life cycle of the particles more complex, by making them grow and then shrink and die out
  3. allowing the user to control the particles, by hooking their position and movement up to the mouse movement
All in all our particle system has now gotten quite complex. But weighing in at 90 lines, it is still well under my self-imposed limit of a 100 lines of code. I did my best to keep the code readable again and even added some constants to make it easier to change and debug the particle system. Now go ahead and play with the system. I find it's great fun to see how far up you can throw the particles. Remember: this is controlled by moving the mouse, so start pushing and pulling your mice!

The code

// constants
var NUM_PARTICLES = 50;
var FPS = 20;
var PARTICLE_LIFETIME = 4; // in seconds
var ANIMATION_LIFETIME = 300; // in seconds

// global variables
var container;
var particles = [];
var framecount = 0;
var mx = 20; // default origin, will be set to mouse position once the mouse moves
var my = 20;
var dmx = 1.0; // default movement, will be set to mouse movement once the mouse moves
var dmy = -1.0

function onMouseMove(e) {
 var IE = document.all?true:false
 var nmx, nmy;
 if (IE) { // grab the x-y pos.s if browser is IE
  nmx = event.clientX + document.body.scrollLeft
  nmy = event.clientY + document.body.scrollTop
 } else {  // grab the x-y pos.s if browser is NS
  nmx = e.pageX
  nmy = e.pageY
 }
 dmx = (nmx - mx) / 4;
 dmy = (nmy - my) / 4;
 mx = nmx;
 my = nmy;
}

function createParticle(container) {
 var p = {
  elm: createParticleElement(),
  x: mx,
  y: my,
  s: 0.0, // size
  dx: dmx + Math.random(),
  dy: dmy + Math.random() - 1,
  ds: Math.random()+0.1,
  ttl: PARTICLE_LIFETIME*FPS
 };
 container.appendChild(p.elm);
 updateParticleElement(p);
 return p;
}

function createParticleElement() {
 var elm = document.createElement('span');;
 elm.style.border = '1px solid blue';
 elm.style.position = 'absolute';
 return elm;
}
function updateParticleElement(p) {
 p.elm.style.width = p.elm.style.height = p.elm.minWidth = p.elm.minHeight = Math.floor(p.s) + 'px'; // doesn't work on IE
 p.elm.style.left = Math.floor(p.x) + 'px';
 p.elm.style.top = Math.floor(p.y) + 'px';
}

function updateParticles() {
 for (var i=particles.length-1; i >= 0; i--) {
  var p = particles[i];
  p.s += p.ds;
  if (p.s > 5) p.ds *= -1;
  p.x += p.dx;
  p.y += p.dy;
  p.dy += 0.1; // simulate gravity
  if (p.ttl-- > 0 && p.s > 0) {
   updateParticleElement(p);
  } else {
   container.removeChild(p.elm);
   particles[i] = createParticle(container);
  }
 }
 if (framecount++ < ANIMATION_LIFETIME*FPS) {
  setTimeout('updateParticles()', Math.floor(1000 / FPS));
 } else {
  alert("All done. Reload to watch again.");
 }
}

window.onload = function() {
 container = document.getElementById('particles');
 for (var i=0; i < NUM_PARTICLES; i++) {
  particles[i] = createParticle(container);
 }
 
 setTimeout('updateParticles()', Math.floor(1000 / FPS));
 document.onmousemove = onMouseMove;
}
Download it here. And the corresponding HTML:
<html>
    <head>
        <title>JavaScript Particle System</title>
        <script type="text/javascript" src="particles2.js"> </script>
    </head>
    <body>
        <div id="particles" style="width:640px; height: 480px; overflow: hidden"> </div>
    </body>
</html>
Download it here.

See also

Saturday, July 25, 2009

Seven rules to keep your twitter followers

With Jon's recent "top 40 list of CMS gurus" I suddenly got a lot of additional followers. And I added quite a few of those people to my twit-list too. From the first few days it looks like I might start unfollowing some soon, but there's definitely a few that are a great addition to the list. While scrolling through recent tweets, I sought what makes tweets interesting for me and things that I really don't like. So here is the list (in random order) of do's and don'ts to keep my as your follower on twitter.

1. Yet another link retweeter

"RT @cmswire Open Source for America: Change We Can Believe In? http://bit.ly/r9sj6 #opensource #oss"
"RT @cmswire 39 More Ways to Get Your CMS Twitter Fix http://bit.ly/YiV9I"
"RT @OpenText #OpenText Completes #Vignette Acquisition http://cli.gs/bUvzsN"


There's nothing wrong with retweeting something interesting you've heard somebody say. But if all you tweet is other people's tweets, I'd much rather listen to those other people.

2. Your opinion matters to me

Personal tweeting is fine. I love to hear what friends and family are doing. But from people that don't fall into those categories, I don't need:

"Ottawa Organic Market on Bank.... "

Don't just tell me that you're at an organic market in whatever city. If we're not emailing or calling each other at least once a week, chances are I don't care that much about your whereabouts. Sounds harsh? It's not meant to be harsh.

Because I apparently care enough about your opinion to follow your tweets. So how about you tell me your opinion on that market.

"the Ottawa organic market is really the best"

3. Challenge me

Even better, don't just tell me your opinion. Tease me to give you mine. It's OK to do a bit of trolling as long as long as you don't mind getting trolls back too.

"the Ottawa organic market is really the best, certainly beats the one in Amsterdam"

Now that's a great way start to a discussion. Because I really, really LOVE the organic market in Amsterdam.

4. Don't forget I'm eavesdropping

One of the worst things you can do to me on twitter is to start a 1-on-1 conversation with somebody where you forgot that I'm still there reading along.

"Sorry? Barbecue where? Are you kidding me?"

What barbecue are you talking about? Where is it? Am I invited too? If you don't want to let me know these things, why are you sending your private message as a public tweet?

Remember: if you want to have a private conversation, there is always still email or Google talk. Or even better dm in twitter.

But if you feel like you want to share your conversation with the world, give me enough context.

5. Give me context

If talking to someone in a public tweet, make it clear who you're talking to.

"@john123 glad you liked the post!"

It certainly beats the tweet about that barbecue. Because here I can at least check the other part of the conversation in john123's tweet stream. But still, giving me a bit of context goes a long way.

"@john123 glad you liked the post! http://is.gd/1JyHY"

One click and I'm at the post. But then again, why not just retweet the original and add your comment to it:

"glad you liked the post! RT @john123 good analogy on how to mix persuasive content with relational data in modern CMS. http://is.gd/1JyHY"

Phew... 138 characters, that's cutting it close. But it has as much context as you can add to your tweet.

6. Tell me something I can use

A friend was apparently enjoying a milkshake a bit too much and had to share it with the world:

"Just had the best strawberry milkshake ever"

I like reading it, because I always love to hear about good food - especially from someone whose taste buds I trust.

But how am I going to be able to do something with that? Tell me where you had that milkshake. Believe me, I'd love to know - even if it is a three hour drive.

"had the best strawberry milkshake ever at Mill Creek near Mineral, NoCal"

Honey start the car, we're going for a drive!

7. I don't need more spam^H^H^H^H marketing

Corporate tweeting is hot these days. There are many corporate twitter accounts that are easily recognizable, because they consist of the company's name. But there are also plenty of innocent-looking names that generate a remarkable number of tweets that simply reiterate how great product xyz is or how well product abc would solve the problem I'm (not) having).

I don't have a problem with these accounts. We're all luckily free to tweet whatever we want. But if you'd like to keep your followers: keep your marketing to content ratio below 20%.

8. I read your blog already

Some people see link tweets as the natural progression of feed readers. I don't. I still have a healthy 100 or something feeds that I follow, just because they regularly show content that I like to read.

In other words: if you have a blog that is of interest to me, chances are it's already in my Google reader. So if all that you are tweeting is links to your blog posts, following you is not really adding any value for me. Now where's that unfollow link?

Mind you: there's nothing wrong with tweeting about your own posts. In fact that's what I'll do once I click the publish button for this post too. But if you want to keep me as a follower, be sure to add some original content into your tweets too.

And yours?


Those are the rules that sprang to my mind as I was scrolling through the rapidly growing stream of tweets. Do you have any rules of your own? How can I make sure that you don't click the "unfollow" link on me?

Monday, July 6, 2009

Safari 4 on Windows crashes when you provide incorrect credentials

Sorry for the long title of this post. But it is the most accurate description of a problem we're having in a project at the moment. Safari 4 on Windows seems to crash in what I perceive to be a common use case. Here are the minimal steps that I came up with for reproducing the problem:


  1. create a new web site in IIS

  2. enable integrated authentication on this web site

  3. connect to the new web site with Safari 4 for Windows

  4. provide incorrect credentials



At this point Safari 4 crashes on my system.

Providing correct credentials allows us to continue. But in the application my team is building, we will often encounter another authentication problems later on.

  • Safari pops up a log on box when we open a page from the same web site in an iframe. No matter which credentials we provide, we can't continue.


I've been searching whether this is a known problem for a while now. But I haven't found a single page that points out the problem we encountered here. Hence this simple write-up.

Has anyone had similar problems with Safari on Windows? If so, is there any way to work around the problem? Because as it is, we can't really support our application on Safari 4 on Windows. :-/